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

@ -9,10 +9,6 @@ parameters:
checkInternalClassCaseSensitivity: true
checkTooWideReturnTypesInProtectedAndPublicMethods: true
ignoreErrors:
# Ignore errors caused by `Template` static class reflection.
- '#Call to an undefined static method Template::[a-zA-Z0-9\\_]+\(\)\.#'
bootstrapFiles:
- %rootDir%/../../../lib/Constants.php

View file

@ -26,7 +26,9 @@ class AtomFeed extends Feed{
protected function GetXmlString(): string{
if(!isset($this->_XmlString)){
$feed = Template::AtomFeed(['id' => $this->Id, 'url' => $this->Url, 'title' => $this->Title, 'subtitle' => $this->Subtitle, 'updated' => $this->Updated, 'entries' => $this->Entries]);
/** @var array<Ebook> $entries */
$entries = $this->Entries;
$feed = Template::AtomFeed(id: $this->Id, url: $this->Url, title: $this->Title, subtitle: $this->Subtitle, updated: $this->Updated, entries: $entries);
$this->_XmlString = $this->CleanXmlString($feed);
}

View file

@ -98,8 +98,8 @@ class NewsletterSubscription{
$em->ToName = $this->User->Name;
}
$em->Subject = 'Action required: confirm your newsletter subscription';
$em->Body = Template::EmailNewsletterConfirmation(['subscription' => $this, 'isSubscribedToSummary' => $this->IsSubscribedToSummary, 'isSubscribedToNewsletter' => $this->IsSubscribedToNewsletter]);
$em->TextBody = Template::EmailNewsletterConfirmationText(['subscription' => $this, 'isSubscribedToSummary' => $this->IsSubscribedToSummary, 'isSubscribedToNewsletter' => $this->IsSubscribedToNewsletter]);
$em->Body = Template::EmailNewsletterConfirmation(subscription: $this, isSubscribedToSummary: $this->IsSubscribedToSummary, isSubscribedToNewsletter: $this->IsSubscribedToNewsletter);
$em->TextBody = Template::EmailNewsletterConfirmationText(subscription: $this, isSubscribedToSummary: $this->IsSubscribedToSummary, isSubscribedToNewsletter: $this->IsSubscribedToNewsletter);
$em->Send();
}

View file

@ -1,7 +1,13 @@
<?
/**
* @property array<Ebook> $Entries
*/
class OpdsAcquisitionFeed extends OpdsFeed{
public bool $IsCrawlable;
/**
* @param array<Ebook> $entries
*/
public function __construct(string $title, string $subtitle, string $url, string $path, array $entries, ?OpdsNavigationFeed $parent, bool $isCrawlable = false){
parent::__construct($title, $subtitle, $url, $path, $entries, $parent);
$this->IsCrawlable = $isCrawlable;
@ -13,6 +19,6 @@ class OpdsAcquisitionFeed extends OpdsFeed{
// *******
protected function GetXmlString(): string{
return $this->_XmlString ??= $this->CleanXmlString(Template::OpdsAcquisitionFeed(['id' => $this->Id, 'url' => $this->Url, 'title' => $this->Title, 'parentUrl' => $this->Parent ? $this->Parent->Url : null, 'updated' => $this->Updated, 'isCrawlable' => $this->IsCrawlable, 'subtitle' => $this->Subtitle, 'entries' => $this->Entries]));
return $this->_XmlString ??= $this->CleanXmlString(Template::OpdsAcquisitionFeed(id: $this->Id, url: $this->Url, title: $this->Title, parentUrl: $this->Parent->Url ?? '', updated: $this->Updated, isCrawlable: $this->IsCrawlable, subtitle: $this->Subtitle, entries: $this->Entries));
}
}

View file

@ -2,6 +2,9 @@
use Safe\DateTimeImmutable;
use function Safe\file_put_contents;
/**
* @property array<Ebook|OpdsNavigationEntry> $Entries
*/
abstract class OpdsFeed extends AtomFeed{
public ?OpdsNavigationFeed $Parent = null;

View file

@ -2,6 +2,9 @@
use Safe\DateTimeImmutable;
use function Safe\file_get_contents;
/**
* @property array<OpdsNavigationEntry> $Entries
*/
class OpdsNavigationFeed extends OpdsFeed{
/**
* @param array<OpdsNavigationEntry> $entries
@ -38,6 +41,6 @@ class OpdsNavigationFeed extends OpdsFeed{
// *******
protected function GetXmlString(): string{
return $this->_XmlString ??= $this->CleanXmlString(Template::OpdsNavigationFeed(['id' => $this->Id, 'url' => $this->Url, 'title' => $this->Title, 'parentUrl' => $this->Parent ? $this->Parent->Url : null, 'updated' => $this->Updated, 'subtitle' => $this->Subtitle, 'entries' => $this->Entries]));
return $this->_XmlString ??= $this->CleanXmlString(Template::OpdsNavigationFeed(id: $this->Id, url: $this->Url, title: $this->Title, parentUrl: $this->Parent->Url ?? '', updated: $this->Updated, subtitle: $this->Subtitle, entries: $this->Entries));
}
}

View file

@ -83,8 +83,8 @@ class Patron{
$em->From = EDITOR_IN_CHIEF_EMAIL_ADDRESS;
$em->FromName = EDITOR_IN_CHIEF_NAME;
$em->Subject = 'Thank you for supporting Standard Ebooks!';
$em->Body = Template::EmailPatronsCircleWelcome(['isAnonymous' => $this->IsAnonymous, 'isReturning' => $isReturning]);
$em->TextBody = Template::EmailPatronsCircleWelcomeText(['isAnonymous' => $this->IsAnonymous, 'isReturning' => $isReturning]);
$em->Body = Template::EmailPatronsCircleWelcome(isAnonymous: $this->IsAnonymous, isReturning: $isReturning);
$em->TextBody = Template::EmailPatronsCircleWelcomeText(isAnonymous: $this->IsAnonymous, isReturning: $isReturning);
$em->Send();
if(!$isReturning){
@ -92,8 +92,8 @@ class Patron{
$em->To = ADMIN_EMAIL_ADDRESS;
$em->From = ADMIN_EMAIL_ADDRESS;
$em->Subject = 'New Patrons Circle member';
$em->Body = Template::EmailAdminNewPatron(['patron' => $this, 'payment' => $this->User->Payments[0]]);
$em->TextBody = Template::EmailAdminNewPatronText(['patron' => $this, 'payment' => $this->User->Payments[0]]);;
$em->Body = Template::EmailAdminNewPatron(patron: $this, payment: $this->User->Payments[0]);
$em->TextBody = Template::EmailAdminNewPatronText(patron: $this, payment: $this->User->Payments[0]);;
$em->Send();
}
}
@ -134,8 +134,8 @@ class Patron{
}
else{
// Email one time donors who have expired after one year.
$em->Body = Template::EmailPatronsCircleCompleted(['ebooksThisYear' => $ebooksThisYear]);
$em->TextBody = Template::EmailPatronsCircleCompletedText(['ebooksThisYear' => $ebooksThisYear]);
$em->Body = Template::EmailPatronsCircleCompleted(ebooksThisYear: $ebooksThisYear);
$em->TextBody = Template::EmailPatronsCircleCompletedText(ebooksThisYear: $ebooksThisYear);
}
$em->Send();

View file

@ -397,8 +397,8 @@ final class Project{
$em->From = ADMIN_EMAIL_ADDRESS;
$em->To = $this->Manager->Email;
$em->Subject = 'New ebook project to manage and review';
$em->Body = Template::EmailManagerNewProject(['project' => $this, 'role' => 'manage and review', 'user' => $this->Manager]);
$em->TextBody = Template::EmailManagerNewProjectText(['project' => $this, 'role' => 'manage and review', 'user' => $this->Manager]);
$em->Body = Template::EmailManagerNewProject(project: $this, role: 'manage and review', user: $this->Manager);
$em->TextBody = Template::EmailManagerNewProjectText(project: $this, role: 'manage and review', user: $this->Manager);
$em->Send();
}
}
@ -409,8 +409,8 @@ final class Project{
$em->From = ADMIN_EMAIL_ADDRESS;
$em->To = $this->Manager->Email;
$em->Subject = 'New ebook project to manage';
$em->Body = Template::EmailManagerNewProject(['project' => $this, 'role' => 'manage', 'user' => $this->Manager]);
$em->TextBody = Template::EmailManagerNewProjectText(['project' => $this, 'role' => 'manage', 'user' => $this->Manager]);
$em->Body = Template::EmailManagerNewProject(project: $this, role: 'manage', user: $this->Manager);
$em->TextBody = Template::EmailManagerNewProjectText(project: $this, role: 'manage', user: $this->Manager);
$em->Send();
}
@ -420,8 +420,8 @@ final class Project{
$em->From = ADMIN_EMAIL_ADDRESS;
$em->To = $this->Reviewer->Email;
$em->Subject = 'New ebook project to review';
$em->Body = Template::EmailManagerNewProject(['project' => $this, 'role' => 'review', 'user' => $this->Reviewer]);
$em->TextBody = Template::EmailManagerNewProjectText(['project' => $this, 'role' => 'review', 'user' => $this->Reviewer]);
$em->Body = Template::EmailManagerNewProject(project: $this, role: 'review', user: $this->Reviewer);
$em->TextBody = Template::EmailManagerNewProjectText(project: $this, role: 'review', user: $this->Reviewer);
$em->Send();
}
}

View file

@ -3,6 +3,9 @@ use function Safe\file_get_contents;
use function Safe\filesize;
use function Safe\preg_replace;
/**
* @property array<Ebook> $Entries
*/
class RssFeed extends Feed{
public string $Description;
@ -21,7 +24,7 @@ class RssFeed extends Feed{
// *******
protected function GetXmlString(): string{
return $this->_XmlString ??= $this->CleanXmlString(Template::RssFeed(['url' => $this->Url, 'description' => $this->Description, 'title' => $this->Title, 'entries' => $this->Entries, 'updated' => NOW]));
return $this->_XmlString ??= $this->CleanXmlString(Template::RssFeed(url: $this->Url, description: $this->Description, title: $this->Title, entries: $this->Entries, updated: NOW));
}
public function SaveIfChanged(): bool{

View file

@ -1,62 +1,65 @@
<?
use function Safe\ob_end_clean;
use function Safe\ob_start;
use Safe\DateTimeImmutable;
class Template{
/**
* @param array<mixed> $arguments
/**
* @method static string ArtworkForm(Artwork $artwork, $isEditForm = false)
* @method static string ArtworkList(array<Artwork> $artworks)
* @method static string AtomFeed(string $id, string $url, string $title, ?string $subtitle = null, DateTimeImmutable $updated, array<Ebook> $entries)
* @method static string AtomFeedEntry(Ebook $entry)
* @method static string BulkDownloadTable(string $label, array<stdClass> $collections)
* @method static string CollectionDescriptor(?CollectionMembership $collectionMembership)
* @method static string ContributeAlert()
* @method static string DonationAlert()
* @method static string DonationCounter(bool $autoHide = true, bool $showDonateButton = true)
* @method static string DonationProgress(bool $autoHide = true, bool $showDonateButton = true)
* @method static string EbookCarousel(array<Ebook> $carousel, bool $isMultiSize = false)
* @method static string EbookGrid(array<Ebook> $ebooks, ?Collection $collection = null, Enums\ViewType $view = Enums\ViewType::Grid)
* @method static string EbookMetadata(Ebook $ebook, bool $showPlaceholderMetadata = false)
* @method static string EbookPlaceholderForm(Ebook $ebook, bool $isEditForm = false, bool $showProjectForm = true)
* @method static string EmailAdminNewPatron(Patron $patron, Payment $payment)
* @method static string EmailAdminNewPatronText(Patron $patron, Payment $payment)
* @method static string EmailAdminUnprocessedDonations()
* @method static string EmailAdminUnprocessedDonationsText()
* @method static string EmailDonationProcessingFailed(string $exception)
* @method static string EmailDonationProcessingFailedText(string $exception)
* @method static string EmailDonationThankYou()
* @method static string EmailDonationThankYouText()
* @method static string EmailFooter(bool $includeLinks = true)
* @method static string EmailFooterText()
* @method static string EmailHeader(?string $preheader = null, bool $hasLetterhead = false, bool $hasAdminTable = false)
* @method static string EmailManagerNewProject(Project $project, string $role, User $user)
* @method static string EmailManagerNewProjectText(Project $project, string $role, User $user)
* @method static string EmailNewsletterConfirmation(bool $isSubscribedToNewsletter, bool $isSubscribedToSummary, NewsletterSubscription $subscription)
* @method static string EmailNewsletterConfirmationText(bool $isSubscribedToNewsletter, bool $isSubscribedToSummary, NewsletterSubscription $subscription)
* @method static string EmailPatronsCircleCompleted(int $ebooksThisYear)
* @method static string EmailPatronsCircleCompletedText(int $ebooksThisYear)
* @method static string EmailPatronsCircleRecurringCompleted()
* @method static string EmailPatronsCircleRecurringCompletedText()
* @method static string EmailPatronsCircleWelcome(bool $isAnonymous, bool $isReturning)
* @method static string EmailPatronsCircleWelcomeText(bool $isAnonymous, bool $isReturning)
* @method static string EmailProjectAbandoned()
* @method static string EmailProjectAbandonedText()
* @method static string EmailProjectStalled()
* @method static string EmailProjectStalledText()
* @method static string Error(?Exception $exception)
* @method static string FeedHowTo()
* @method static string Footer()
* @method static string Header(?string $title = null, ?string $highlight = null, ?string $description = null, bool $isManual = false, bool $isXslt = false, ?string $feedUrl = null, ?string $feedTitle = null, bool $isErrorPage = false, ?string $downloadUrl = null, ?string $canonicalUrl = null, ?string $coverUrl = null, string $ogType = 'website', array<string> $css = [])
* @method static string ImageCopyrightNotice()
* @method static string OpdsAcquisitionEntry(Ebook $entry)
* @method static string OpdsAcquisitionFeed(string $id, string $url, string $parentUrl, string $title, ?string $subtitle, DateTimeImmutable $updated, array<Ebook> $entries, bool $isCrawlable = false)
* @method static string OpdsNavigationFeed(string $id, string $url, ?string $parentUrl, string $title, ?string $subtitle, DateTimeImmutable $updated, array<OpdsNavigationEntry> $entries)
* @method static string ProjectDetailsTable(Project $project, bool $useFullyQualifiedUrls = false, bool $showTitle = true, bool $showArtworkStatus = true)
* @method static string ProjectForm(Project $project, $areFieldsRequired = true, $isEditForm = false)
* @method static string ProjectsTable(array<Project> $projects, bool $includeTitle = true, bool $includeStatus = true, bool $showEditButton = false)
* @method static string RealisticEbook(Ebook $ebook)
* @method static string RssEntry(Ebook $entry)
* @method static string RssFeed(string $title, string $description, DateTimeImmutable $updated, string $url, array<Ebook> $entries)
* @method static string SearchForm(string $query, array<string> $tags, Enums\EbookSortType $sort, Enums\ViewType $view, int $perPage)
* @method static string UserForm(User $user, Enums\PasswordActionType $passwordAction, bool $generateNewUuid, bool $isEditForm = false)
* @method static string WantedEbooksList(array<Ebook> $ebooks, bool $showPlaceholderMetadata)
*/
protected static function Get(string $templateName, array $arguments = []): string{
// Expand the passed variables to make them available to the included template.
// We use these funny names so that we can use 'name' and 'value' as template variables if we want to.
foreach($arguments as $innerName => $innerValue){
$$innerName = $innerValue;
}
ob_start();
include(TEMPLATES_PATH . '/' . $templateName . '.php');
$contents = ob_get_contents() ?: '';
ob_end_clean();
return $contents;
}
/**
* @param array<mixed> $arguments
*/
public static function __callStatic(string $function, array $arguments): string{
if(isset($arguments[0]) && is_array($arguments[0])){
return self::Get($function, $arguments[0]);
}
else{
return self::Get($function, $arguments);
}
}
/**
* Exit the script while outputting the given HTTP code.
*
* @param bool $showPage If **`TRUE`**, show a special page given the HTTP code (like a 404 page).
*
* @return never
*/
public static function ExitWithCode(Enums\HttpCode $httpCode, bool $showPage = true, Enums\HttpRequestType $requestType = Enums\HttpRequestType::Web): void{
http_response_code($httpCode->value);
if($requestType == Enums\HttpRequestType::Web && $showPage){
switch($httpCode){
case Enums\HttpCode::Forbidden:
include(WEB_ROOT . '/403.php');
break;
case Enums\HttpCode::NotFound:
include(WEB_ROOT . '/404.php');
break;
}
}
exit();
}
class Template extends TemplateBase{
/**
* Redirect the user to the login page.
*

80
lib/TemplateBase.php Normal file
View file

@ -0,0 +1,80 @@
<?
use function Safe\ob_end_clean;
use function Safe\ob_start;
/**
* A simple templating class that reads directly from PHP files.
*
* The `Template` class must extend the `TemplateBase` class. Methods corresponding to each template file are annotated using PHPDoc on the `Template` class.
*
* Place template files in `TEMPLATES_PATH`. Calling `Template::MyTemplateFilename(variable: value ...)` will expand passed in variables, execute `TEMPLATES_PATH/MyTemplateFilename.php`, and output the string contents of the executed PHP. For example:
*
* ````php
* <?
* // Outputs the contents of ``TEMPLATES_PATH`/Header.php`. Inside that file, the variable `$title` will be available.
* print(Template::Header(title: 'My Title'));
* ````
*
* # Template conventions
*
* At the top of each template file, use PHPDoc to define required variables and nullable optional variables that are `null` by default. Next, optional variables with default values are defined with the `$varName ??= $defaultValue;` pattern. For example:
*
* ````php
* <?
* // TEMPLATES_PATH/Header.php
*
* // @var string $title Required.
* // @var ?string $url Optional but nullable, we define the type of `string` here.
*
* $url ??= null; // Optional and nullable. The type was defined in the above PHPDoc, and we set the default as `null` here.
* $isFeed ??= false; // Optional and not nullable. Both the type and the default value are set here.
* ?>
* <?= $title ?>
* <? if($isFeed){ ?>
* <?= $url ?>
* <? } ?>
* ````
*/
abstract class TemplateBase{
/**
* @param array<string, mixed> $arguments
*/
public static function __callStatic(string $function, array $arguments): string{
// Expand the passed variables to make them available to the included template.
// We use these funny names so that we can use `name` and `value` as template variables if we want to.
foreach($arguments as $innerName => $innerValue){
$$innerName = $innerValue;
}
ob_start();
include(TEMPLATES_PATH . '/' . $function . '.php');
$contents = ob_get_contents() ?: '';
ob_end_clean();
return $contents;
}
/**
* Exit the script while outputting the given HTTP code.
*
* @param bool $showPage If **`TRUE`**, show a special page given the HTTP code (like a 404 page).
*
* @return never
*/
public static function ExitWithCode(Enums\HttpCode $httpCode, bool $showPage = true, Enums\HttpRequestType $requestType = Enums\HttpRequestType::Web): void{
http_response_code($httpCode->value);
if($requestType == Enums\HttpRequestType::Web && $showPage){
switch($httpCode){
case Enums\HttpCode::Forbidden:
include(WEB_ROOT . '/403.php');
break;
case Enums\HttpCode::NotFound:
include(WEB_ROOT . '/404.php');
break;
}
}
exit();
}
}

View file

@ -217,8 +217,8 @@ catch(Exception $ex){
$em = new Email(true);
$em->To = ADMIN_EMAIL_ADDRESS;
$em->Subject = 'Ingesting FA donations failed';
$em->Body = Template::EmailDonationProcessingFailed(['exception' => preg_replace('/^/m', "\t", $exceptionString)]);
$em->TextBody = Template::EmailDonationProcessingFailedText(['exception' => preg_replace('/^/m', "\t", $exceptionString)]);
$em->Body = Template::EmailDonationProcessingFailed(exception: preg_replace('/^/m', "\t", $exceptionString));
$em->TextBody = Template::EmailDonationProcessingFailedText(exception: preg_replace('/^/m', "\t", $exceptionString));
$em->Send();
throw $ex;

View file

@ -277,8 +277,8 @@ try{
$em->To = ADMIN_EMAIL_ADDRESS;
$em->From = ADMIN_EMAIL_ADDRESS;
$em->Subject = 'New Patrons Circle member';
$em->Body = Template::EmailAdminNewPatron(['patron' => $patron, 'payment' => $payment]);
$em->TextBody = Template::EmailAdminNewPatronText(['patron' => $patron, 'payment' => $payment]);;
$em->Body = Template::EmailAdminNewPatron(patron: $patron, payment: $payment);
$em->TextBody = Template::EmailAdminNewPatronText(patron: $patron, payment: $payment);;
$em->Send();
}
}
@ -324,8 +324,8 @@ catch(Exception $ex){
$em = new Email(true);
$em->To = ADMIN_EMAIL_ADDRESS;
$em->Subject = 'Donation processing failed';
$em->Body = Template::EmailDonationProcessingFailed(['exception' => preg_replace('/^/m', "\t", $exceptionString)]);
$em->TextBody = Template::EmailDonationProcessingFailedText(['exception' => preg_replace('/^/m', "\t", $exceptionString)]);
$em->Body = Template::EmailDonationProcessingFailed(exception: preg_replace('/^/m', "\t", $exceptionString));
$em->TextBody = Template::EmailDonationProcessingFailedText(exception: preg_replace('/^/m', "\t", $exceptionString));
$em->Send();
throw $ex;

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,7 +4,7 @@
*/
?>
<ol class="artwork-list">
<? foreach($artworks as $artwork){ ?>
<? foreach($artworks as $artwork){ ?>
<?
$class = '';
@ -32,5 +32,5 @@
</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)){ ?>

View file

@ -1,4 +1,4 @@
<?= Template::Header(['title' => 'You Dont Have Permission to View This Page', 'highlight' => '', 'description' => 'You dont have permission to view this page.', 'isErrorPage' => true]) ?>
<?= Template::Header(title: 'You Dont Have Permission to View This Page', description: 'You dont have permission to view this page.', isErrorPage: true) ?>
<main>
<section class="narrow has-hero">
<hgroup>

View file

@ -1,4 +1,4 @@
<?= Template::Header(['title' => 'We Couldnt Find That Document', 'highlight' => '', 'description' => 'We couldnt find that document.', 'isErrorPage' => true]) ?>
<?= Template::Header(title: 'We Couldnt Find That Document', description: 'We couldnt find that document.', isErrorPage: true) ?>
<main>
<section class="narrow has-hero">
<hgroup>

View file

@ -1,4 +1,4 @@
<?= Template::Header(['title' => 'This Ebook Is No Longer Available', 'highlight' => '', 'description' => 'This ebook is unavailable due to legal reasons.']) ?>
<?= Template::Header(title: 'This Ebook Is No Longer Available', description: 'This ebook is unavailable due to legal reasons.') ?>
<main>
<section class="narrow has-hero">
<hgroup>

View file

@ -1,4 +1,4 @@
<?= Template::Header(['title' => 'Accessibility', 'highlight' => 'about', 'description' => 'How we make Standard Ebooks accessible.']) ?>
<?= Template::Header(title: 'Accessibility', highlight: 'about', description: 'How we make Standard Ebooks accessible.') ?>
<main>
<section class="accessibility narrow">
<h1>Accessibility</h1>

View file

@ -35,7 +35,7 @@ $anonymousPatronCount = Db::QueryInt('
) x
');
?><?= Template::Header(['title' => 'About Standard Ebooks', 'highlight' => 'about', 'description' => 'Standard Ebooks is a volunteer-driven effort to produce a collection of high quality, carefully formatted, accessible, open source, and free public domain ebooks that meet or exceed the quality of commercially produced ebooks. The text and cover art in our ebooks is already believed to be in the public domain, and Standard Ebook dedicates its own work to the public domain, thus releasing the entirety of each ebook file into the public domain.']) ?>
?><?= Template::Header(title: 'About Standard Ebooks', highlight: 'about', description: 'Standard Ebooks is a volunteer-driven effort to produce a collection of high quality, carefully formatted, accessible, open source, and free public domain ebooks that meet or exceed the quality of commercially produced ebooks. The text and cover art in our ebooks is already believed to be in the public domain, and Standard Ebook dedicates its own work to the public domain, thus releasing the entirety of each ebook file into the public domain.') ?>
<main>
<article>
<h1>About Standard Ebooks</h1>

View file

@ -1,4 +1,4 @@
<?= Template::Header(['title' => 'Our Goals', 'highlight' => 'about', 'description' => 'The goals of Standard Ebooks.']) ?>
<?= Template::Header(title: 'Our Goals', highlight: 'about', description: 'The goals of Standard Ebooks.') ?>
<main>
<article>
<h1>Our Goals</h1>

View file

@ -1,4 +1,4 @@
<?= Template::Header(['title' => 'Standard Ebooks and the Public Domain', 'highlight' => 'about', 'description' => 'The Standard Ebooks philosophy on copyright and the public domain.']) ?>
<?= Template::Header(title: 'Standard Ebooks and the Public Domain', highlight: 'about', description: 'The Standard Ebooks philosophy on copyright and the public domain.') ?>
<main>
<article>
<h1>Standard Ebooks and the Public Domain</h1>

View file

@ -1,4 +1,4 @@
<?= Template::Header(['title' => 'What Makes Standard Ebooks Different', 'highlight' => 'about', 'description' => 'How Standard Ebooks differs from other free ebook projects.']) ?>
<?= Template::Header(title: 'What Makes Standard Ebooks Different', highlight: 'about', description: 'How Standard Ebooks differs from other free ebook projects.') ?>
<main>
<article>
<h1>What Makes Standard Ebooks Different</h1>

View file

@ -23,14 +23,14 @@ try{
catch(Exceptions\ArtistNotFoundException){
Template::ExitWithCode(Enums\HttpCode::NotFound);
}
?><?= Template::Header(['title' => 'Artwork by ' . $artworks[0]->Artist->Name, 'css' => ['/css/artwork.css']]) ?>
?><?= Template::Header(title: 'Artwork by ' . $artworks[0]->Artist->Name, css: ['/css/artwork.css']) ?>
<main class="artworks">
<section class="narrow">
<h1>Artwork by <?= Formatter::EscapeHtml($artworks[0]->Artist->Name) ?></h1>
<?= Template::ImageCopyrightNotice() ?>
<?= Template::ArtworkList(['artworks' => $artworks]) ?>
<?= Template::ArtworkList(artworks: $artworks) ?>
</section>
</main>
<?= Template::Footer() ?>

View file

@ -1,16 +1,16 @@
<?
use function Safe\session_unset;
session_start();
$exception = HttpInput::SessionObject('exception', Exceptions\AppException::class);
$artwork = HttpInput::SessionObject('artwork', Artwork::class);
try{
if(Session::$User === null){
throw new Exceptions\LoginRequiredException();
}
session_start();
$exception = HttpInput::SessionObject('exception', Exceptions\AppException::class);
$artwork = HttpInput::SessionObject('artwork', Artwork::class);
if($artwork === null){
$artwork = Artwork::GetByUrl(HttpInput::Str(GET, 'artist-url-name'), HttpInput::Str(GET, 'artwork-url-name'));
}
@ -34,21 +34,17 @@ catch(Exceptions\LoginRequiredException){
catch(Exceptions\InvalidPermissionsException){
Template::ExitWithCode(Enums\HttpCode::Forbidden); // No permissions to edit artwork.
}
?>
<?= Template::Header(
[
'title' => 'Edit ' . $artwork->Name . ', by ' . $artwork->Artist->Name,
'css' => ['/css/artwork.css'],
'highlight' => '',
'description' => 'Edit ' . $artwork->Name . ', by ' . $artwork->Artist->Name . ' in the Standard Ebooks cover art database.'
]
title: 'Edit ' . $artwork->Name . ', by ' . $artwork->Artist->Name,
css: ['/css/artwork.css'],
description: 'Edit ' . $artwork->Name . ', by ' . $artwork->Artist->Name . ' in the Standard Ebooks cover art database.'
) ?>
<main>
<section class="narrow">
<h1>Edit Artwork</h1>
<?= Template::Error(['exception' => $exception]) ?>
<?= Template::Error(exception: $exception) ?>
<picture>
<source srcset="<?= $artwork->Thumb2xUrl ?> 2x, <?= $artwork->ThumbUrl ?> 1x" type="image/jpg"/>
@ -57,7 +53,7 @@ catch(Exceptions\InvalidPermissionsException){
<form class="create-update-artwork" method="<?= Enums\HttpMethod::Post->value ?>" action="<?= $artwork->Url ?>" enctype="multipart/form-data" autocomplete="off">
<input type="hidden" name="_method" value="<?= Enums\HttpMethod::Put->value ?>" />
<?= Template::ArtworkForm(['artwork' => $artwork, 'isEditForm' => true]) ?>
<?= Template::ArtworkForm(artwork: $artwork, isEditForm: true) ?>
</form>
</section>
</main>

View file

@ -64,12 +64,12 @@ catch(Exceptions\InvalidPermissionsException){
Template::ExitWithCode(Enums\HttpCode::Forbidden);
}
?><?= Template::Header(['title' => $artwork->Name, 'css' => ['/css/artwork.css']]) ?>
?><?= Template::Header(title: $artwork->Name, css: ['/css/artwork.css']) ?>
<main class="artworks">
<section class="narrow">
<h1><?= Formatter::EscapeHtml($artwork->Name) ?></h1>
<?= Template::Error(['exception' => $exception]) ?>
<?= Template::Error(exception: $exception) ?>
<? if($isSaved){ ?>
<p class="message success">Artwork saved!</p>
@ -118,7 +118,19 @@ catch(Exceptions\InvalidPermissionsException){
</tr>
<tr>
<td>Status:</td>
<td><?= Template::ArtworkStatus(['artwork' => $artwork]) ?></td>
<td>
<?= 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)
<? } ?>
<? } ?>
</td>
</tr>
<? if($isReviewerView){ ?>
<tr>

View file

@ -136,7 +136,7 @@ catch(Exceptions\PageOutOfBoundsException){
header('Location: ' . $url);
exit();
}
?><?= Template::Header(['title' => $pageTitle, 'css' => ['/css/artwork.css'], 'description' => $pageDescription, 'canonicalUrl' => $canonicalUrl]) ?>
?><?= Template::Header(title: $pageTitle, css: ['/css/artwork.css'], description: $pageDescription, canonicalUrl: $canonicalUrl) ?>
<main class="artworks">
<section class="narrow">
<h1>Browse U.S. Public Domain Artwork</h1>
@ -199,7 +199,7 @@ catch(Exceptions\PageOutOfBoundsException){
<? if($totalArtworkCount == 0){ ?>
<p class="no-results">No artwork matched your filters. You can try different filters, or <a href="/artworks">browse all artwork</a>.</p>
<? }else{ ?>
<?= Template::ArtworkList(['artworks' => $artworks]) ?>
<?= Template::ArtworkList(artworks: $artworks) ?>
<? } ?>
<? if($totalArtworkCount > 0){ ?>

View file

@ -1,17 +1,17 @@
<?
use function Safe\session_unset;
session_start();
$isCreated = HttpInput::Bool(SESSION, 'is-artwork-created') ?? false;
$exception = HttpInput::SessionObject('exception', Exceptions\AppException::class);
$artwork = HttpInput::SessionObject('artwork', Artwork::class);
try{
if(Session::$User === null){
throw new Exceptions\LoginRequiredException();
}
session_start();
$isCreated = HttpInput::Bool(SESSION, 'is-artwork-created') ?? false;
$exception = HttpInput::SessionObject('exception', Exceptions\AppException::class);
$artwork = HttpInput::SessionObject('artwork', Artwork::class);
if(!Session::$User->Benefits->CanUploadArtwork){
throw new Exceptions\InvalidPermissionsException();
}
@ -46,25 +46,22 @@ catch(Exceptions\InvalidPermissionsException){
?>
<?= Template::Header(
[
'title' => 'Submit an Artwork',
'css' => ['/css/artwork.css'],
'highlight' => '',
'description' => 'Submit public domain artwork to the database for use as cover art.'
]
title: 'Submit an Artwork',
css: ['/css/artwork.css'],
description: 'Submit public domain artwork to the database for use as cover art.'
) ?>
<main>
<section class="narrow">
<h1>Submit an Artwork</h1>
<?= Template::Error(['exception' => $exception]) ?>
<?= Template::Error(exception: $exception) ?>
<? if($isCreated){ ?>
<p class="message success">Artwork submitted!</p>
<? } ?>
<form class="create-update-artwork" method="<?= Enums\HttpMethod::Post->value ?>" action="/artworks" enctype="multipart/form-data" autocomplete="off">
<?= Template::ArtworkForm(['artwork' => $artwork]) ?>
<?= Template::ArtworkForm(artwork: $artwork) ?>
</form>
</section>
</main>

View file

@ -31,7 +31,7 @@ try{
catch(Exceptions\AuthorNotFoundException){
Template::ExitWithCode(Enums\HttpCode::NotFound);
}
?><?= Template::Header(['title' => 'Ebooks by ' . $author, 'feedUrl' => str_replace('/ebooks/', '/authors/', $authorUrl), 'feedTitle' => 'Standard Ebooks - Ebooks by ' . $author, 'highlight' => 'ebooks', 'description' => 'All of the Standard Ebooks ebooks by ' . $author, 'canonicalUrl' => SITE_URL . $authorUrl]) ?>
?><?= Template::Header(title: 'Ebooks by ' . $author, feedUrl: str_replace('/ebooks/', '/authors/', $authorUrl), feedTitle: 'Standard Ebooks - Ebooks by ' . $author, highlight: 'ebooks', description: 'All of the Standard Ebooks ebooks by ' . $author, canonicalUrl: SITE_URL . $authorUrl) ?>
<main class="ebooks">
<h1 class="is-collection">Ebooks by <?= $ebooks[0]->AuthorsHtml ?></h1>
<? if($showLinks){ ?>
@ -40,7 +40,7 @@ catch(Exceptions\AuthorNotFoundException){
<a class="button" href="<?= Formatter::EscapeHtml($authorUrl) ?>/feeds">Feeds for this author</a>
</p>
<? } ?>
<?= Template::EbookGrid(['ebooks' => $ebooks, 'view' => Enums\ViewType::Grid]) ?>
<?= Template::EbookGrid(ebooks: $ebooks, view: Enums\ViewType::Grid) ?>
<p class="feeds-alert">We also have <a href="/bulk-downloads">bulk ebook downloads</a> and a <a href="/collections">list of collections</a> available, as well as <a href="/feeds">ebook catalog feeds</a> for use directly in your ereader app or RSS reader.</p>
<?= Template::ContributeAlert() ?>
</main>

View file

@ -2,7 +2,7 @@
$ebookIds = [1085, 1052];
$carousel = Db::Query('SELECT * from Ebooks where EbookId in ' . Db::CreateSetSql($ebookIds), $ebookIds, Ebook::class);
?>
<?= Template::Header(['title' => 'Death and Beauty in the Alps', 'css' => ['/css/blog.css'], 'highlight' => '', 'description' => '']) ?>
<?= Template::Header(title: 'Death and Beauty in the Alps', css: ['/css/blog.css']) ?>
<main>
<section class="narrow blog">
<nav class="breadcrumbs"><a href="/blog">Blog</a> </nav>
@ -31,7 +31,7 @@ $carousel = Db::Query('SELECT * from Ebooks where EbookId in ' . Db::CreateSetSq
<p><em>Scrambles Amongst the Alps</em> has something of both “darkling thrush” and “darkling plain”: an “eternal note of sadness” following terrible loss, but also real, if fleeting, notes of joy in laboring for, and realizing, a hope widely believed impossible.</p>
<p>The woe of its best-known story is confounded by lesser-known, brighter elements—even if theyre only “thin atomies” in comparison—praising the worth of the endeavour and the value of the toil it required, despite the cruel hand the explorers were dealt.</p>
<h2 id="ebooks-in-this-newsletter">Free ebooks in this post</h2>
<?= Template::EbookCarousel(['carousel' => $carousel]) ?>
<?= Template::EbookCarousel(carousel: $carousel) ?>
</section>
</main>
<?= Template::Footer() ?>

View file

@ -2,7 +2,7 @@
$ebookIds = [288, 485, 289, 908, 565, 2114];
$carousel = Db::Query('SELECT * from Ebooks where EbookId in ' . Db::CreateSetSql($ebookIds), $ebookIds, Ebook::class);
?>
<?= Template::Header(['title' => 'Edith Whartons Vision of Literary Art', 'css' => ['/css/blog.css'], 'highlight' => '', 'description' => '']) ?>
<?= Template::Header(title: 'Edith Whartons Vision of Literary Art', css: ['/css/blog.css']) ?>
<main>
<section class="narrow blog">
<nav class="breadcrumbs"><a href="/blog">Blog</a> </nav>
@ -33,7 +33,7 @@ $carousel = Db::Query('SELECT * from Ebooks where EbookId in ' . Db::CreateSetSq
<p><em>Hudson River Bracketed</em> is long out of print. Wharton patently lost the critical and commercial <em>Wettgesang</em> of the 1930s; even her sympathizers tend to admit that shes in no way a star of that period, so the analogy to the Prologue in Heaven falls (or sounds) very flat.</p>
<p>But whether Whartons second-last work falls entirely flat too is something that can be judged, if at all, only in the old way, by reading it. This wasnt very easy to do until January 1, but now you can read our <a href="https://standardebooks.org/ebooks/edith-wharton/hudson-river-bracketed">new ebook edition for free</a> at Standard Ebooks.</p>
<h2 id="ebooks-in-this-newsletter">Free ebooks in this post</h2>
<?= Template::EbookCarousel(['carousel' => $carousel]) ?>
<?= Template::EbookCarousel(carousel: $carousel) ?>
</section>
</main>
<?= Template::Footer() ?>

View file

@ -1,4 +1,4 @@
<?= Template::Header(['title' => 'Blog', 'highlight' => '', 'description' => 'The Standard Ebooks blog.']) ?>
<?= Template::Header(title: 'Blog', description: 'The Standard Ebooks blog.') ?>
<main>
<section class="narrow">
<h1>Blog</h1>

View file

@ -2,7 +2,7 @@
$ebookIds = [565, 778, 561, 1059];
$carousel = Db::Query('SELECT * from Ebooks where EbookId in ' . Db::CreateSetSql($ebookIds), $ebookIds, Ebook::class);
?>
<?= Template::Header(['title' => 'Joyces Ulysses, the Rubáiyát, and “Yes”', 'css' => ['/css/blog.css'], 'highlight' => '', 'description' => '']) ?>
<?= Template::Header(title: 'Joyces Ulysses, the Rubáiyát, and “Yes”', css: ['/css/blog.css']) ?>
<main>
<section class="narrow blog">
<nav class="breadcrumbs"><a href="/blog">Blog</a> </nav>
@ -44,7 +44,7 @@ $carousel = Db::Query('SELECT * from Ebooks where EbookId in ' . Db::CreateSetSq
<p>Speaking of consonance: Brown says, citing Ellmann, that the last record Joyce heard before he died was a performance of Lehmanns setting of Fitzgeralds Omar. As is often the case with Ellmann, this might not be true, but its not absurd to suppose that it could be.</p>
<p>And as very often with <i>Ulysses</i>, what first seems like nothing, or like material for a joke, may also turn out to be something else too, even something that matters. If <i>Ulysses</i> doesnt entirely affirm life, then it does, in this respect at least, reflect it.</p>
<h2 id="ebooks-in-this-newsletter">Free ebooks in this post</h2>
<?= Template::EbookCarousel(['carousel' => $carousel]) ?>
<?= Template::EbookCarousel(carousel: $carousel) ?>
</section>
</main>
<?= Template::Footer() ?>

View file

@ -100,7 +100,7 @@ foreach($ebooks as $ebook){
ksort($ebooksWithDescriptions);
?><?= Template::Header(['title' => 'Public Domain Day 2025 in Literature - Blog', 'highlight' => '', 'description' => 'Read about the new ebooks Standard Ebooks is releasing for Public Domain Day 2025!', 'css' => ['/css/public-domain-day.css']]) ?>
?><?= Template::Header(title: 'Public Domain Day 2025 in Literature - Blog', description: 'Read about the new ebooks Standard Ebooks is releasing for Public Domain Day 2025!', css: ['/css/public-domain-day.css']) ?>
<main>
<section class="narrow blog has-hero">
<nav class="breadcrumbs"><a href="/blog">Blog</a> </nav>
@ -133,7 +133,7 @@ ksort($ebooksWithDescriptions);
<li>
<div>
<a href="<?= $ebookGroup['ebook']->Url ?>">
<?= Template::RealisticEbook(['ebook' => $ebookGroup['ebook']]) ?>
<?= Template::RealisticEbook(ebook: $ebookGroup['ebook']) ?>
</a>
</div>
<div>

View file

@ -27,7 +27,7 @@ catch(Safe\Exceptions\ApcuException){
$title = preg_replace('/s$/', '', ucfirst($class));
?><?= Template::Header(['title' => 'Downloads by ' . $title, 'highlight' => '', 'description' => 'Download zip files containing all of the Standard Ebooks in a given collection.']) ?>
?><?= Template::Header(title: 'Downloads by ' . $title, description: 'Download zip files containing all of the Standard Ebooks in a given collection.') ?>
<main>
<section class="bulk-downloads">
<h1>Down­loads by <?= $title ?></h1>
@ -80,7 +80,10 @@ $title = preg_replace('/s$/', '', ucfirst($class));
</tbody>
</table>
<? }else{ ?>
<?= Template::BulkDownloadTable(['label' => $title, 'collections' => $collection]); ?>
<?
/** @var array<stdClass> $collection */
?>
<?= Template::BulkDownloadTable(label: $title, collections: $collection); ?>
<? } ?>
</section>
</main>

View file

@ -54,7 +54,7 @@ catch(Exceptions\InvalidFileException){
Template::ExitWithCode(Enums\HttpCode::NotFound);
}
?><?= Template::Header(['title' => 'Downloading Ebook Collections', 'highlight' => '', 'description' => 'Download zip files containing all of the Standard Ebooks released in a given month.']) ?>
?><?= Template::Header(title: 'Downloading Ebook Collections', description: 'Download zip files containing all of the Standard Ebooks released in a given month.') ?>
<main>
<section class="narrow">
<h1>Downloading Ebook Collections</h1>

View file

@ -1,13 +1,12 @@
<?
use function Safe\apcu_fetch;
$collection = null;
$collectionUrlName = HttpInput::Str(GET, 'collection');
$collection = null;
$authorUrlName = HttpInput::Str(GET, 'author');
$canDownload = false;
try{
$collection = null;
$collectionUrlName = HttpInput::Str(GET, 'collection');
$authorUrlName = HttpInput::Str(GET, 'author');
$canDownload = false;
if(Session::$User?->Benefits->CanBulkDownload){
$canDownload = true;
}
@ -33,10 +32,6 @@ try{
break;
}
}
if($collection === null){
throw new Exceptions\CollectionNotFoundException();
}
}
if($authorUrlName !== null){
@ -65,6 +60,10 @@ try{
throw new Exceptions\AuthorNotFoundException();
}
}
if($collection === null){
throw new Exceptions\CollectionNotFoundException();
}
}
catch(Exceptions\AuthorNotFoundException){
Template::ExitWithCode(Enums\HttpCode::NotFound);
@ -73,17 +72,17 @@ catch(Exceptions\CollectionNotFoundException){
Template::ExitWithCode(Enums\HttpCode::NotFound);
}
?><?= Template::Header(['title' => 'Download ', 'highlight' => '', 'description' => 'Download zip files containing all of the Standard Ebooks released in a given month.']) ?>
?><?= Template::Header(title: 'Download ', description: 'Download zip files containing all of the Standard Ebooks released in a given month.') ?>
<main>
<section class="bulk-downloads">
<h1>Download the <?= $collection?->Label ?> Collection</h1>
<h1>Download the <?= $collection->Label ?> Collection</h1>
<? if($canDownload){ ?>
<p>Select the ebook format in which youd like to download this collection.</p>
<p>You can also read about <a href="/help/how-to-use-our-ebooks#which-file-to-download">which ebook format to download</a>.</p>
<? }else{ ?>
<p><a href="/about#patrons-circle">Patrons circle members</a> can download zip files containing all of the ebooks in a collection. You can <a href="/donate#patrons-circle">join the Patrons Circle</a> with a small donation in support of our continuing mission to create free, beautiful digital literature.</p>
<? } ?>
<?= Template::BulkDownloadTable(['label' => 'Collection', 'collections' => [$collection]]); ?>
<?= Template::BulkDownloadTable(label: 'Collection', collections: [$collection]); ?>
</section>
</main>
<?= Template::Footer() ?>

View file

@ -4,7 +4,7 @@ if(Session::$User?->Benefits->CanBulkDownload){
$canDownload = true;
}
?><?= Template::Header(['title' => 'Bulk Ebook Downloads', 'highlight' => '', 'description' => 'Download zip files containing all of the Standard Ebooks released in a given month.']) ?>
?><?= Template::Header(title: 'Bulk Ebook Downloads', description: 'Download zip files containing all of the Standard Ebooks released in a given month.') ?>
<main>
<section class="narrow has-hero">
<h1>Bulk Ebook Down­loads</h1>

View file

@ -16,7 +16,7 @@ try{
catch(Exceptions\CollectionNotFoundException){
Template::ExitWithCode(Enums\HttpCode::NotFound);
}
?><?= Template::Header(['title' => $pageTitle, 'feedUrl' => $feedUrl, 'feedTitle' => $feedTitle, 'highlight' => 'ebooks', 'description' => $pageDescription]) ?>
?><?= Template::Header(title: $pageTitle, feedUrl: $feedUrl, feedTitle: $feedTitle, highlight: 'ebooks', description: $pageDescription) ?>
<main class="ebooks">
<h1 class="is-collection"><?= $pageHeader ?></h1>
<?= Template::DonationCounter() ?>
@ -32,7 +32,7 @@ catch(Exceptions\CollectionNotFoundException){
<? if(sizeof($collection->Ebooks) == 0){ ?>
<p class="no-results">No ebooks matched your filters. You can try different filters, or <a href="/ebooks">browse all of our ebooks</a>.</p>
<? }else{ ?>
<?= Template::EbookGrid(['ebooks' => $collection->Ebooks, 'view' => Enums\ViewType::Grid, 'collection' => $collection]) ?>
<?= Template::EbookGrid(ebooks: $collection->Ebooks, view: Enums\ViewType::Grid, collection: $collection) ?>
<? } ?>
<? if(Session::$User?->Benefits->CanEditCollections){ ?>

View file

@ -1,7 +1,7 @@
<?
$collections = Collection::GetAll();
?><?= Template::Header(['title' => 'Ebook Collections', 'highlight' => '', 'description' => 'Browse collections of Standard Ebooks.']) ?>
?><?= Template::Header(title: 'Ebook Collections', description: 'Browse collections of Standard Ebooks.') ?>
<main>
<section class="narrow has-hero">
<h1>Ebook Collections</h1>

View file

@ -1,4 +1,4 @@
<?= Template::Header(['title' => 'A Basic Standard Ebooks Source Folder', 'manual' => true, 'highlight' => 'contribute', 'description' => 'All Standard Ebooks source folders have the same basic structure, described here.']) ?>
<?= Template::Header(title: 'A Basic Standard Ebooks Source Folder', isManual: true, highlight: 'contribute', description: 'All Standard Ebooks source folders have the same basic structure, described here.') ?>
<main>
<article id="a-basic-standard-ebooks-source-folder">
<h1>A Basic Standard Ebooks Source Folder</h1>

View file

@ -1,4 +1,4 @@
<?= Template::Header(['title' => 'Collections Policy', 'highlight' => 'contribute', 'description' => 'Standard Ebooks only accepts certain kinds of ebooks for production and hosting. This is the full list.']) ?>
<?= Template::Header(title: 'Collections Policy', highlight: 'contribute', description: 'Standard Ebooks only accepts certain kinds of ebooks for production and hosting. This is the full list.') ?>
<main>
<article>
<hgroup>

View file

@ -1,4 +1,4 @@
<?= Template::Header(['title' => 'Common Issues When Working on Public Domain Ebooks', 'manual' => true, 'highlight' => 'contribute', 'description' => 'A list of common issues encountered when converting from public domain transcriptions.']) ?>
<?= Template::Header(title: 'Common Issues When Working on Public Domain Ebooks', isManual: true, highlight: 'contribute', description: 'A list of common issues encountered when converting from public domain transcriptions.') ?>
<main>
<article>
<h1>Common Issues When Working on Public Domain Ebooks</h1>

View file

@ -1,4 +1,4 @@
<?= Template::Header(['title' => 'How to Choose and Create a Cover Image', 'manual' => true, 'highlight' => 'contribute', 'description' => 'A guide to choosing, clearing, and formatting your cover image.']) ?>
<?= Template::Header(title: 'How to Choose and Create a Cover Image', isManual: true, highlight: 'contribute', description: 'A guide to choosing, clearing, and formatting your cover image.') ?>
<main class="manual">
<article class="step-by-step-guide">
<h1>How to Choose and Create a Cover Image</h1>

View file

@ -1,4 +1,4 @@
<?= Template::Header(['title' => 'How to Conquer Complex Drama Formatting', 'manual' => true, 'highlight' => 'contribute', 'description' => 'A guide to formatting any complex plays or dramatic dialog sections.']) ?>
<?= Template::Header(title: 'How to Conquer Complex Drama Formatting', isManual: true, highlight: 'contribute', description: 'A guide to formatting any complex plays or dramatic dialog sections.') ?>
<main class="manual">
<article class="step-by-step-guide">
<h1>How to Conquer Complex Drama Formatting</h1>

View file

@ -1,4 +1,4 @@
<?= Template::Header(['title' => 'How to Create Figures for Music Scores', 'manual' => true, 'highlight' => 'contribute', 'description' => 'A guide to producing SVG figures of music notation.']) ?>
<?= Template::Header(title: 'How to Create Figures for Music Scores', isManual: true, highlight: 'contribute', description: 'A guide to producing SVG figures of music notation.') ?>
<main class="manual">
<article class="step-by-step-guide">
<h1>How to Create Figures for Music Scores</h1>

View file

@ -1,4 +1,4 @@
<?= Template::Header(['title' => 'How to Create SVGs from Maps with Several Colors', 'manual' => true, 'highlight' => 'contribute', 'description' => 'A guide to producing SVG from images such as maps with more than a single color.']) ?>
<?= Template::Header(title: 'How to Create SVGs from Maps with Several Colors', isManual: true, highlight: 'contribute', description: 'A guide to producing SVG from images such as maps with more than a single color.') ?>
<main class="manual">
<article class="step-by-step-guide">
<h1>How to Create SVGs from Maps with Several Colors</h1>

View file

@ -1,4 +1,4 @@
<?= Template::Header(['title' => 'How to Review an Ebook Production for Publication', 'manual' => true, 'highlight' => 'contribute', 'description' => 'A guide to proofread and review an ebook production for publication.']) ?>
<?= Template::Header(title: 'How to Review an Ebook Production for Publication', isManual: true, highlight: 'contribute', description: 'A guide to proofread and review an ebook production for publication.') ?>
<main class="manual">
<article class="step-by-step-guide">
<h1>How to Review an Ebook Production for Publication</h1>

View file

@ -1,4 +1,4 @@
<?= Template::Header(['title' => 'How to Structure and Style Large Poetic Productions', 'manual' => true, 'highlight' => 'contribute', 'description' => 'A guide to formatting poetry collections, long narrative poems, and unusual poetic features.']) ?>
<?= Template::Header(title: 'How to Structure and Style Large Poetic Productions', isManual: true, highlight: 'contribute', description: 'A guide to formatting poetry collections, long narrative poems, and unusual poetic features.') ?>
<main class="manual">
<article class="step-by-step-guide">
<h1>How to Structure and Style Large Poetic Productions</h1>

View file

@ -1,4 +1,4 @@
<?= Template::Header(['title' => 'How-to Guides For Difficult Productions', 'manual' => true, 'highlight' => 'contribute', 'description' => 'Guides on how to produce more difficult productions.']) ?>
<?= Template::Header(title: 'How-to Guides For Difficult Productions', isManual: true, highlight: 'contribute', description: 'Guides on how to produce more difficult productions.') ?>
<main>
<article>
<h1>How-to Guides</h1>

View file

@ -1,4 +1,4 @@
<?= Template::Header(['title' => 'Things to Look Out For When Proofreading', 'manual' => true, 'highlight' => 'contribute', 'description' => 'A list of things to look out for when proofreading.']) ?>
<?= Template::Header(title: 'Things to Look Out For When Proofreading', isManual: true, highlight: 'contribute', description: 'A list of things to look out for when proofreading.') ?>
<main>
<article>
<h1>Things to Look Out For When Proofreading</h1>

View file

@ -1,4 +1,4 @@
<?= Template::Header(['title' => 'Get Involved', 'highlight' => 'contribute', 'description' => 'Details on how to contribute your time and talent to the volunteer-driven Standard Ebooks project.']) ?>
<?= Template::Header(title: 'Get Involved', highlight: 'contribute', description: 'Details on how to contribute your time and talent to the volunteer-driven Standard Ebooks project.') ?>
<main>
<article class="has-hero">
<hgroup>

View file

@ -1,4 +1,4 @@
<?= Template::Header(['title' => 'Producing an Ebook for Standard Ebooks', 'highlight' => 'contribute', 'description' => 'A high-level outline of the process of producing an ebook for Standard Ebooks.']) ?>
<?= Template::Header(title: 'Producing an Ebook for Standard Ebooks', highlight: 'contribute', description: 'A high-level outline of the process of producing an ebook for Standard Ebooks.') ?>
<main>
<article>
<h1>Producing an Ebook for Standard Ebooks</h1>

View file

@ -1,4 +1,4 @@
<?= Template::Header(['title' => 'Producing an Ebook, Step by Step', 'manual' => true, 'highlight' => 'contribute', 'description' => 'A detailed step-by-step description of the complete process of producing an ebook for Standard Ebooks, start to finish.']) ?>
<?= Template::Header(title: 'Producing an Ebook, Step by Step', isManual: true, highlight: 'contribute', description: 'A detailed step-by-step description of the complete process of producing an ebook for Standard Ebooks, start to finish.') ?>
<main class="manual">
<article class="step-by-step-guide">
<h1>Producing an Ebook, Step by Step</h1>

View file

@ -1,4 +1,4 @@
<?= Template::Header(['title' => 'Report Errors Upstream', 'highlight' => 'contribute', 'description' => 'Our guide to reporting errors to Gutenberg and other sources.']) ?>
<?= Template::Header(title: 'Report Errors Upstream', highlight: 'contribute', description: 'Our guide to reporting errors to Gutenberg and other sources.') ?>
<main>
<article>
<h1>Report Errors Upstream</h1>

View file

@ -1,4 +1,4 @@
<?= Template::Header(['title' => 'Report Errors', 'highlight' => 'contribute', 'description' => 'How to report a typo or error youve found in a Standard Ebooks ebook.']) ?>
<?= Template::Header(title: 'Report Errors', highlight: 'contribute', description: 'How to report a typo or error youve found in a Standard Ebooks ebook.') ?>
<main>
<article>
<h1>Report Errors</h1>

View file

@ -1,4 +1,4 @@
<?= Template::Header(['title' => 'Research Spreadsheets', 'highlight' => 'contribute', 'description' => 'A list of spreadsheets created and used by Standard Ebooks producers.']) ?>
<?= Template::Header(title: 'Research Spreadsheets', highlight: 'contribute', description: 'A list of spreadsheets created and used by Standard Ebooks producers.') ?>
<main>
<article>
<h1>Research Spreadsheets</h1>

View file

@ -1,4 +1,4 @@
<?= Template::Header(['title' => 'Tips for Editors and Proofreaders', 'manual' => true, 'highlight' => 'contribute', 'description' => 'A list of tips and tricks for people whod like to proofread a Standard Ebooks ebook.']) ?>
<?= Template::Header(title: 'Tips for Editors and Proofreaders', isManual: true, highlight: 'contribute', description: 'A list of tips and tricks for people whod like to proofread a Standard Ebooks ebook.') ?>
<main>
<article>
<h1>Tips for Editors and Proofreaders</h1>

View file

@ -1,4 +1,4 @@
<?= Template::Header(['title' => 'Uncategorized Art Resources', 'highlight' => 'contribute', 'description' => 'A list of US-PD art books for use when conducting cover art research.']) ?>
<?= Template::Header(title: 'Uncategorized Art Resources', highlight: 'contribute', description: 'A list of US-PD art books for use when conducting cover art research.') ?>
<main>
<article id="a-basic-standard-ebooks-source-folder">
<h1>Uncategorized Art Resources</h1>

View file

@ -3,7 +3,7 @@ $beginnerEbooks = Ebook::GetByIsWantedAndDifficulty(Enums\EbookPlaceholderDiffic
$intermediateEbooks = Ebook::GetByIsWantedAndDifficulty(Enums\EbookPlaceholderDifficulty::Intermediate);
$advancedEbooks = Ebook::GetByIsWantedAndDifficulty(Enums\EbookPlaceholderDifficulty::Advanced);
?>
<?= Template::Header(['title' => 'Wanted Ebooks', 'highlight' => 'contribute', 'description' => 'A list of ebooks the Standard Ebooks editor would like to see produced, including suggestions for first-time producers.']) ?>
<?= Template::Header(title: 'Wanted Ebooks', highlight: 'contribute', description: 'A list of ebooks the Standard Ebooks editor would like to see produced, including suggestions for first-time producers.') ?>
<main>
<article>
<h1>Wanted Ebooks</h1>
@ -18,13 +18,13 @@ $advancedEbooks = Ebook::GetByIsWantedAndDifficulty(Enums\EbookPlaceholderDiffic
<h2>For your first production</h2>
<p>If nothing on the list below interests you, you can pitch us something else youd like to work on.</p>
<p>First productions should be on the shorter side (less than 100,000 words maximum) and without too many complex formatting issues like illustrations, significant endnotes, letters, poems, etc. Most short plain fiction novels fall in this category.</p>
<?= Template::WantedEbooksList(['ebooks' => $beginnerEbooks, 'showPlaceholderMetadata' => Session::$User?->Benefits->CanEditEbookPlaceholders]) ?>
<?= Template::WantedEbooksList(ebooks: $beginnerEbooks, showPlaceholderMetadata: Session::$User->Benefits->CanEditEbookPlaceholders ?? false) ?>
<h2>Moderate-difficulty productions</h2>
<?= Template::WantedEbooksList(['ebooks' => $intermediateEbooks, 'showPlaceholderMetadata' => Session::$User?->Benefits->CanEditEbookPlaceholders]) ?>
<?= Template::WantedEbooksList(ebooks: $intermediateEbooks, showPlaceholderMetadata: Session::$User->Benefits->CanEditEbookPlaceholders ?? false) ?>
<h2>Advanced productions</h2>
<?= Template::WantedEbooksList(['ebooks' => $advancedEbooks, 'showPlaceholderMetadata' => Session::$User?->Benefits->CanEditEbookPlaceholders]) ?>
<?= Template::WantedEbooksList(ebooks: $advancedEbooks, showPlaceholderMetadata: Session::$User->Benefits->CanEditEbookPlaceholders ?? false) ?>
<h2 id="verne">Jules Verne</h2>
<p>Verne has a complex publication and translation history. Please review these notes before starting any Verne books.</p>

View file

@ -5,15 +5,15 @@ $newsletterSubscriberCount = floor(Db::QueryInt('
where IsConfirmed = true
') / 100) * 100;
?><?= Template::Header(['title' => 'Donate', 'highlight' => 'donate', 'description' => 'Donate to Standard Ebooks.']) ?>
?><?= Template::Header(title: 'Donate', highlight: 'donate', description: 'Donate to Standard Ebooks.') ?>
<main>
<section class="donate narrow has-hero">
<hgroup>
<h1>Donate to Standard Ebooks</h1>
<p>and help bring the beauty of literature to the digital age</p>
</hgroup>
<?= Template::DonationCounter(['autoHide' => false, 'showDonateButton' => false]) ?>
<?= Template::DonationProgress(['autoHide' => false, 'showDonateButton' => false]) ?>
<?= Template::DonationCounter(autoHide: false, showDonateButton: false) ?>
<?= Template::DonationProgress(autoHide: false, showDonateButton: false) ?>
<picture data-caption="The Quiet Hour. Albert Chevallier Tayler, 1925">
<source srcset="/images/the-quiet-hour@2x.avif 2x, /images/the-quiet-hour.avif 1x" type="image/avif"/>
<source srcset="/images/the-quiet-hour@2x.jpg 2x, /images/the-quiet-hour.jpg 1x" type="image/jpg"/>

View file

@ -40,12 +40,9 @@ catch(Exceptions\InvalidPermissionsException){
}
?>
<?= Template::Header(
[
'title' => 'Delete ' . $ebook->Title,
'css' => ['/css/ebook-placeholder.css'],
'highlight' => '',
'description' => 'Delete ' . $ebook->Title
]
title: 'Delete ' . $ebook->Title,
css: ['/css/ebook-placeholder.css'],
description: 'Delete ' . $ebook->Title
) ?>
<main>
<section class="narrow">
@ -55,7 +52,7 @@ catch(Exceptions\InvalidPermissionsException){
</nav>
<h1>Delete</h1>
<?= Template::Error(['exception' => $exception]) ?>
<?= Template::Error(exception: $exception) ?>
<form method="<?= Enums\HttpMethod::Post->value ?>" action="<?= $ebook->Url ?>">
<input type="hidden" name="_method" value="<?= Enums\HttpMethod::Delete->value ?>" />

View file

@ -40,12 +40,9 @@ catch(Exceptions\InvalidPermissionsException){
}
?>
<?= Template::Header(
[
'title' => 'Edit ' . $ebook->Title,
'css' => ['/css/ebook-placeholder.css'],
'highlight' => '',
'description' => 'Edit ' . $ebook->Title
]
title: 'Edit ' . $ebook->Title,
css: ['/css/ebook-placeholder.css'],
description: 'Edit ' . $ebook->Title
) ?>
<main>
<section class="narrow">
@ -55,11 +52,11 @@ catch(Exceptions\InvalidPermissionsException){
</nav>
<h1>Edit</h1>
<?= Template::Error(['exception' => $exception]) ?>
<?= Template::Error(exception: $exception) ?>
<form class="create-update-ebook-placeholder" method="<?= Enums\HttpMethod::Post->value ?>" action="<?= $ebook->Url ?>" autocomplete="off">
<input type="hidden" name="_method" value="<?= Enums\HttpMethod::Put->value ?>" />
<?= Template::EbookPlaceholderForm(['ebook' => $ebook, 'isEditForm' => true]) ?>
<?= Template::EbookPlaceholderForm(ebook: $ebook, isEditForm: true) ?>
<div class="footer">
<button>Save</button>
</div>

View file

@ -24,12 +24,11 @@ catch(Exceptions\EbookNotFoundException){
Template::ExitWithCode(Enums\HttpCode::NotFound);
}
?><?= Template::Header(
[
'title' => strip_tags($ebook->TitleWithCreditsHtml),
'css' => ['/css/ebook-placeholder.css'],
'highlight' => 'ebooks',
'canonicalUrl' => SITE_URL . $ebook->Url
])
title: strip_tags($ebook->TitleWithCreditsHtml),
css: ['/css/ebook-placeholder.css'],
highlight: 'ebooks',
canonicalUrl: SITE_URL . $ebook->Url
)
?>
<main>
<article class="ebook ebook-placeholder" typeof="schema:Book" about="<?= $ebook->Url ?>">
@ -77,7 +76,7 @@ catch(Exceptions\EbookNotFoundException){
<? if(sizeof($ebook->CollectionMemberships) > 0){ ?>
<? foreach($ebook->CollectionMemberships as $collectionMembership){ ?>
<p>
<?= Template::CollectionDescriptor(['collectionMembership' => $collectionMembership]) ?>.
<?= Template::CollectionDescriptor(collectionMembership: $collectionMembership) ?>.
</p>
<? } ?>
<? } ?>
@ -111,7 +110,7 @@ catch(Exceptions\EbookNotFoundException){
</section>
<? if(Session::$User?->Benefits->CanEditEbooks || Session::$User?->Benefits->CanEditEbookPlaceholders){ ?>
<?= Template::EbookMetadata(['ebook' => $ebook, 'showPlaceholderMetadata' => Session::$User->Benefits->CanEditEbookPlaceholders]) ?>
<?= Template::EbookMetadata(ebook: $ebook, showPlaceholderMetadata: Session::$User->Benefits->CanEditEbookPlaceholders) ?>
<? } ?>
<? if(Session::$User?->Benefits->CanEditProjects || Session::$User?->Benefits->CanManageProjects || Session::$User?->Benefits->CanReviewProjects){ ?>
@ -123,7 +122,7 @@ catch(Exceptions\EbookNotFoundException){
<a href="<?= $ebook->ProjectInProgress->EditUrl ?>">Edit project</a>
</p>
<? } ?>
<?= Template::ProjectDetailsTable(['project' => $ebook->ProjectInProgress, 'showTitle' => false]) ?>
<?= Template::ProjectDetailsTable(project: $ebook->ProjectInProgress, showTitle: false) ?>
</section>
<? } ?>
@ -137,7 +136,7 @@ catch(Exceptions\EbookNotFoundException){
<? if(sizeof($ebook->PastProjects) == 0){ ?>
<p class="empty-notice">None.</p>
<? }else{ ?>
<?= Template::ProjectsTable(['projects' => $ebook->PastProjects, 'includeTitle' => false, 'showEditButton' => Session::$User->Benefits->CanEditProjects]) ?>
<?= Template::ProjectsTable(projects: $ebook->PastProjects, includeTitle: false, showEditButton: Session::$User->Benefits->CanEditProjects) ?>
<? } ?>
</section>
<? } ?>

View file

@ -1,15 +1,6 @@
<?
use function Safe\session_unset;
session_start();
$isCreated = HttpInput::Bool(SESSION, 'is-ebook-placeholder-created') ?? false;
$isOnlyProjectCreated = HttpInput::Bool(SESSION, 'is-only-ebook-project-created') ?? false;
$isDeleted = HttpInput::Bool(SESSION, 'is-ebook-placeholder-deleted') ?? false;
$exception = HttpInput::SessionObject('exception', Exceptions\AppException::class);
$ebook = HttpInput::SessionObject('ebook', Ebook::class);
$project = HttpInput::SessionObject('project', Project::class);
$deletedEbookTitle = '';
try{
if(Session::$User === null){
@ -20,6 +11,16 @@ try{
throw new Exceptions\InvalidPermissionsException();
}
session_start();
$isCreated = HttpInput::Bool(SESSION, 'is-ebook-placeholder-created') ?? false;
$isOnlyProjectCreated = HttpInput::Bool(SESSION, 'is-only-ebook-project-created') ?? false;
$isDeleted = HttpInput::Bool(SESSION, 'is-ebook-placeholder-deleted') ?? false;
$exception = HttpInput::SessionObject('exception', Exceptions\AppException::class);
$ebook = HttpInput::SessionObject('ebook', Ebook::class);
$project = HttpInput::SessionObject('project', Project::class);
$deletedEbookTitle = '';
if($isCreated || $isOnlyProjectCreated){
// We got here because an `Ebook` was successfully created.
http_response_code(Enums\HttpCode::Created->value);
@ -70,18 +71,15 @@ catch(Exceptions\InvalidPermissionsException){
}
?>
<?= Template::Header(
[
'title' => 'Create an Ebook Placeholder',
'css' => ['/css/ebook-placeholder.css', '/css/project.css'],
'highlight' => '',
'description' => 'Create a placeholder for an ebook not yet in the collection.'
]
title: 'Create an Ebook Placeholder',
css: ['/css/ebook-placeholder.css', '/css/project.css'],
description: 'Create a placeholder for an ebook not yet in the collection.'
) ?>
<main>
<section class="narrow">
<h1>Create an Ebook Placeholder</h1>
<?= Template::Error(['exception' => $exception]) ?>
<?= Template::Error(exception: $exception) ?>
<? if(isset($createdEbook)){ ?>
<? if($isOnlyProjectCreated){ ?>
@ -94,7 +92,7 @@ catch(Exceptions\InvalidPermissionsException){
<? } ?>
<form class="create-update-ebook-placeholder" method="<?= Enums\HttpMethod::Post->value ?>" action="/ebook-placeholders" autocomplete="off">
<?= Template::EbookPlaceholderForm(['ebook' => $ebook]) ?>
<?= Template::EbookPlaceholderForm(ebook: $ebook ?? new Ebook()) ?>
<div class="footer">
<button>Submit</button>
</div>

View file

@ -61,7 +61,7 @@ try{
catch(Exceptions\InvalidFileException | Exceptions\EbookNotFoundException){
Template::ExitWithCode(Enums\HttpCode::NotFound);
}
?><?= Template::Header(['title' => 'Your Download Has Started!', 'downloadUrl' => $downloadUrl]) ?>
?><?= Template::Header(title: 'Your Download Has Started!', downloadUrl: $downloadUrl) ?>
<main class="donate">
<h1>Your Download Has Started!</h1>
<div class="thank-you-container">

View file

@ -71,7 +71,7 @@ catch(Exceptions\EbookNotFoundException){
Template::ExitWithCode(Enums\HttpCode::NotFound);
}
?><?= Template::Header(['title' => strip_tags($ebook->TitleWithCreditsHtml) . ' - Free ebook download', 'ogType' => 'book', 'coverUrl' => $ebook->DistCoverUrl, 'highlight' => 'ebooks', 'description' => 'Free epub ebook download of the Standard Ebooks edition of ' . $ebook->Title . ': ' . $ebook->Description, 'canonicalUrl' => SITE_URL . $ebook->Url]) ?>
?><?= Template::Header(title: strip_tags($ebook->TitleWithCreditsHtml) . ' - Free ebook download', ogType: 'book', coverUrl: $ebook->DistCoverUrl, highlight: 'ebooks', description: 'Free epub ebook download of the Standard Ebooks edition of ' . $ebook->Title . ': ' . $ebook->Description, canonicalUrl: SITE_URL . $ebook->Url) ?>
<main>
<article class="ebook" typeof="schema:Book" about="<?= $ebook->Url ?>">
<meta property="schema:description" content="<?= Formatter::EscapeHtml($ebook->Description) ?>"/>
@ -126,7 +126,7 @@ catch(Exceptions\EbookNotFoundException){
<? if(sizeof($ebook->CollectionMemberships) > 0){ ?>
<? foreach($ebook->CollectionMemberships as $collectionMembership){ ?>
<p>
<?= Template::CollectionDescriptor(['collectionMembership' => $collectionMembership]) ?>.
<?= Template::CollectionDescriptor(collectionMembership: $collectionMembership) ?>.
</p>
<? } ?>
<? } ?>
@ -188,7 +188,7 @@ catch(Exceptions\EbookNotFoundException){
<p class="us-pd-warning">This ebook is thought to be free of copyright restrictions in the United States. It may still be under copyright in other countries. If youre not located in the United States, you must check your local laws to verify that this ebook is free of copyright restrictions in the country youre located in before accessing, downloading, or using it.</p>
<div class="downloads-container">
<?= Template::RealisticEbook(['ebook' => $ebook]) ?>
<?= Template::RealisticEbook(ebook: $ebook) ?>
<div>
<section id="download">
<h3>Download for ereaders</h3>
@ -383,13 +383,13 @@ catch(Exceptions\EbookNotFoundException){
</section>
<? if(Session::$User?->Benefits->CanEditEbooks){ ?>
<?= Template::EbookMetadata(['ebook' => $ebook]) ?>
<?= Template::EbookMetadata(ebook: $ebook) ?>
<? } ?>
<? if(sizeof($carousel) > 0){ ?>
<aside id="more-ebooks">
<h2>More free<? if($carouselTag !== null){ ?> <?= strtolower($carouselTag->Name) ?><? } ?> ebooks</h2>
<?= Template::EbookCarousel(['carousel' => $carousel, 'isMultiSize' => true]) ?>
<?= Template::EbookCarousel(carousel: $carousel, isMultiSize: true) ?>
</aside>
<? } ?>
</article>

View file

@ -7,8 +7,8 @@ $pages = 0;
$perPage = HttpInput::Int(GET, 'per-page') ?? EBOOKS_PER_PAGE;
$query = HttpInput::Str(GET, 'query') ?? '';
$tags = HttpInput::Array(GET, 'tags') ?? [];
$view = Enums\ViewType::tryFrom(HttpInput::Str(GET, 'view') ?? '');
$sort = Enums\EbookSortType::tryFrom(HttpInput::Str(GET, 'sort') ?? '');
$view = Enums\ViewType::tryFrom(HttpInput::Str(GET, 'view') ?? '') ?? Enums\ViewType::Grid;
$sort = Enums\EbookSortType::tryFrom(HttpInput::Str(GET, 'sort') ?? '') ?? Enums\EbookSortType::Default;
$queryString = '';
$queryStringParams = [];
$queryStringWithoutPage = '';
@ -38,13 +38,8 @@ try{
$sort = Enums\EbookSortType::Newest;
}
// If we're passed string values that are the same as the defaults, set them to null so that we can have cleaner query strings in the navigation footer.
if($view === Enums\ViewType::Grid){
$view = null;
}
if(($sort == Enums\EbookSortType::Newest && $query == '') || ($sort == Enums\EbookSortType::Relevance && $query != '')){
$sort = null;
$sort = Enums\EbookSortType::Default;
}
if(sizeof($tags) == 1 && mb_strtolower($tags[0]) == 'all'){
@ -61,11 +56,12 @@ try{
$queryStringParams['tags'] = $tags;
}
if($view !== null){
// If we're passed string values that are the same as the defaults, don't include them in the query string so that we can have cleaner query strings in the navigation footer.
if($view != Enums\ViewType::Grid){
$queryStringParams['view'] = $view->value;
}
if($sort !== null){
if($sort != Enums\EbookSortType::Default){
$queryStringParams['sort'] = $sort->value;
}
@ -134,7 +130,7 @@ catch(Exceptions\PageOutOfBoundsException){
header('Location: ' . $url);
exit();
}
?><?= Template::Header(['title' => $pageTitle, 'highlight' => 'ebooks', 'description' => $pageDescription, 'canonicalUrl' => $canonicalUrl]) ?>
?><?= Template::Header(title: $pageTitle, highlight: 'ebooks', description: $pageDescription, canonicalUrl: $canonicalUrl) ?>
<main class="ebooks">
<h1><?= $pageHeader ?></h1>
<?= Template::DonationCounter() ?>
@ -142,11 +138,12 @@ catch(Exceptions\PageOutOfBoundsException){
<?= Template::DonationAlert() ?>
<?= Template::SearchForm(['query' => $query, 'tags' => $tags, 'sort' => $sort, 'view' => $view, 'perPage' => $perPage]) ?>
<?= Template::SearchForm(query: $query, tags: $tags, sort: $sort, view: $view, perPage: $perPage) ?>
<? if(sizeof($ebooks) == 0){ ?>
<p class="no-results">No ebooks matched your filters. You can try different filters, or <a href="/ebooks">browse all of our ebooks</a>.</p>
<? }else{ ?>
<?= Template::EbookGrid(['ebooks' => $ebooks, 'view' => $view]) ?>
<?= Template::EbookGrid(ebooks: $ebooks, view: $view) ?>
<? } ?>
<? if(sizeof($ebooks) > 0){ ?>
<nav class="pagination">

View file

@ -23,7 +23,7 @@ if($feedType == Enums\FeedType::Atom){
$title = 'Standard Ebooks Atom Feeds';
}
?><?= Template::Header(['title' => 'The Standard Ebooks OPDS feed', 'highlight' => '', 'description' => 'Get access to the Standard Ebooks OPDS feed for use in ereading programs in scripting.']) ?>
?><?= Template::Header(title: 'The Standard Ebooks OPDS feed', description: 'Get access to the Standard Ebooks OPDS feed for use in ereading programs in scripting.') ?>
<main>
<section class="narrow has-hero">
<? if($feedType == Enums\FeedType::Opds){ ?>

View file

@ -1,4 +1,4 @@
<?= Template::Header(['title' => 'Atom 1.0 Ebook Feeds', 'description' => 'A list of available Atom 1.0 feeds of Standard Ebooks ebooks.']) ?>
<?= Template::Header(title: 'Atom 1.0 Ebook Feeds', description: 'A list of available Atom 1.0 feeds of Standard Ebooks ebooks.') ?>
<main>
<section class="narrow">
<h1>Atom 1.0 Ebook Feeds</h1>

View file

@ -24,7 +24,7 @@ print("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<?xml-stylesheet href=\"" . S
<uri><?= SITE_URL ?></uri>
</author>
<opensearch:totalResults><?= sizeof($ebooks) ?></opensearch:totalResults>
<? foreach($ebooks as $ebook){ ?>
<?= Template::AtomFeedEntry(['entry' => $ebook]) ?>
<? } ?>
<? foreach($ebooks as $ebook){ ?>
<?= Template::AtomFeedEntry(entry: $ebook) ?>
<? } ?>
</feed>

View file

@ -15,7 +15,7 @@ print("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n")
<xsl:stylesheet version="3.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/">
<xsl:output method="html" html-version="5.0" encoding="utf-8" indent="yes" doctype-system="about:legacy-compat"/> <? /* doctype-system outputs the HTML5 doctype */ ?>
<xsl:template match="/">
<?= Template::Header(['isXslt' => true]) ?>
<?= Template::Header(isXslt: true) ?>
<main class="opds">
<xsl:choose>
<xsl:when test="contains(/atom:feed/atom:title, 'Standard Ebooks - ')">

View file

@ -29,7 +29,7 @@ catch(Safe\Exceptions\ApcuException){
Template::ExitWithCode(Enums\HttpCode::NotFound);
}
}
?><?= Template::Header(['title' => $type->GetDisplayName() . ' Ebook Feeds by ' . $ucTitle, 'description' => 'A list of available ' . $type->GetDisplayName() . ' feeds of Standard Ebooks ebooks by ' . $lcTitle . '.']) ?>
?><?= Template::Header(title: $type->GetDisplayName() . ' Ebook Feeds by ' . $ucTitle, description: 'A list of available ' . $type->GetDisplayName() . ' feeds of Standard Ebooks ebooks by ' . $lcTitle . '.') ?>
<main>
<article>
<h1><?= $type->GetDisplayName() ?> Ebook Feeds by <?= $ucTitle ?></h1>

View file

@ -55,7 +55,7 @@ try{
catch(Exceptions\CollectionNotFoundException){
Template::ExitWithCode(Enums\HttpCode::NotFound);
}
?><?= Template::Header(['title' => $title, 'feedTitle' => $feedTitle, 'feedUrl' => $feedUrl, 'description' => $description]) ?>
?><?= Template::Header(title: $title, feedTitle: $feedTitle, feedUrl: $feedUrl, description: $description) ?>
<main>
<article>
<h1>Ebook Feeds for <?= Formatter::EscapeHtml($label) ?></h1>

View file

@ -1,4 +1,4 @@
<?= Template::Header(['title' => 'Ebook Feeds', 'description' => 'A list of available feeds of Standard Ebooks ebooks.']) ?>
<?= Template::Header(title: 'Ebook Feeds', description: 'A list of available feeds of Standard Ebooks ebooks.') ?>
<main>
<section class="narrow has-hero">
<h1>Ebook Feeds</h1>

View file

@ -27,6 +27,6 @@ print("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<?xml-stylesheet href=\"". SI
</author>
<opensearch:totalResults><?= sizeof($ebooks) ?></opensearch:totalResults>
<? foreach($ebooks as $ebook){ ?>
<?= Template::OpdsAcquisitionEntry(['entry' => $ebook]) ?>
<?= Template::OpdsAcquisitionEntry(entry: $ebook) ?>
<? } ?>
</feed>

Some files were not shown because too many files have changed in this diff Show more