Add more type hints and work on some code formatting

This commit is contained in:
Alex Cabal 2024-11-08 13:50:59 -06:00
parent 06b82cdaaa
commit c3c588cc1b
35 changed files with 343 additions and 167 deletions

View file

@ -24,7 +24,7 @@ parameters:
- %rootDir%/../../../templates
dynamicConstantNames:
- SITE_STATUS
- DONATION_DRIVE_ENABLED
- DONATION_DRIVES_ENABLED
- DONATION_DRIVE_COUNTER_ENABLED
earlyTerminatingMethodCalls:
Template:

View file

@ -2,7 +2,7 @@
use Safe\DateTimeImmutable;
use function Safe\file_put_contents;
class OpdsFeed extends AtomFeed{
abstract class OpdsFeed extends AtomFeed{
public ?OpdsNavigationFeed $Parent = null;
/**

View file

@ -22,8 +22,7 @@ class RssFeed extends Feed{
protected function GetXmlString(): string{
if($this->XmlString === null){
$timestamp = NOW->format('r');
$feed = Template::RssFeed(['url' => $this->Url, 'description' => $this->Description, 'title' => $this->Title, 'entries' => $this->Entries, 'updated' => $timestamp]);
$feed = Template::RssFeed(['url' => $this->Url, 'description' => $this->Description, 'title' => $this->Title, 'entries' => $this->Entries, 'updated' => NOW]);
$this->XmlString = $this->CleanXmlString($feed);
}

View file

@ -82,7 +82,7 @@ try{
)
', [$pendingPayment->TransactionId])){
$log->Write('Donation already exists in database.');
continue;
continue 2; // `switch` is considered a "loop" by PHP.
}
$driver->get('https://fundraising.fracturedatlas.org/admin/donations?query=' . $pendingPayment->TransactionId);
@ -115,7 +115,7 @@ try{
}
catch(Exception){
$log->Write('Error: Couldn\'t find donation.');
continue;
continue 2; // `switch` is considered a "loop" by PHP.
}
$toggleButton->click();
@ -185,7 +185,7 @@ try{
catch(Exceptions\PaymentExistsException){
// Payment already exists, just continue.
$log->Write('Donation already in database.');
continue;
continue 2; // `switch` is considered a "loop" by PHP.
}
// Does this payment create a new Patron in the Patrons Circle?

View file

@ -1,7 +1,7 @@
<?
use Safe\DateTimeImmutable;
$artwork = $artwork ?? null;
/**
* @var ?Artwork $artwork
*/
if($artwork === null){
$artwork = new Artwork();
@ -18,8 +18,8 @@ $isEditForm = $isEditForm ?? false;
<datalist id="artist-names">
<? foreach(Library::GetArtists() as $artist){ ?>
<option value="<?= Formatter::EscapeHtml($artist->Name) ?>"><?= Formatter::EscapeHtml($artist->Name) ?>, d. <? if($artist->DeathYear !== null){ ?><?= $artist->DeathYear ?><? }else{ ?>unknown<? } ?></option>
<? foreach($artist->AlternateNames as $alternateName){ ?>
<option value="<?= Formatter::EscapeHtml($alternateName) ?>"><?= Formatter::EscapeHtml($alternateName) ?>, d. <? if($artist->DeathYear !== null){ ?><?= Formatter::EscapeHtml($artist->DeathYear) ?><? }else{ ?>unknown<? } ?></option>
<? foreach(($artist->AlternateNames ?? []) as $alternateName){ ?>
<option value="<?= Formatter::EscapeHtml($alternateName) ?>"><?= Formatter::EscapeHtml($alternateName) ?>, d. <? if($artist->DeathYear !== null){ ?><?= Formatter::EscapeHtml((string)$artist->DeathYear) ?><? }else{ ?>unknown<? } ?></option>
<? } ?>
<? } ?>
</datalist>
@ -40,7 +40,7 @@ $isEditForm = $isEditForm ?? false;
name="artist-year-of-death"
inputmode="numeric"
pattern="[0-9]{1,4}"
value="<?= Formatter::EscapeHtml($artwork->Artist->DeathYear) ?>"
value="<?= Formatter::EscapeHtml((string)$artwork->Artist->DeathYear) ?>"
/>
</label>
</fieldset>
@ -59,7 +59,7 @@ $isEditForm = $isEditForm ?? false;
name="artwork-year"
inputmode="numeric"
pattern="[0-9]{1,4}"
value="<?= Formatter::EscapeHtml($artwork->CompletedYear) ?>"
value="<?= Formatter::EscapeHtml((string)$artwork->CompletedYear) ?>"
/>
</label>
<label>
@ -123,7 +123,7 @@ $isEditForm = $isEditForm ?? false;
name="artwork-publication-year"
inputmode="numeric"
pattern="[0-9]{4}"
value="<?= Formatter::EscapeHtml($artwork->PublicationYear) ?>"
value="<?= Formatter::EscapeHtml((string)$artwork->PublicationYear) ?>"
/>
</label>
<label>

View file

@ -1,5 +1,7 @@
<?
$artworks = $artworks ?? [];
/**
* @var array<Artwork> $artworks
*/
?>
<ol class="artwork-list">
<? foreach($artworks as $artwork){ ?>

View file

@ -1,9 +1,23 @@
<?
$artwork = $artwork ?? null;
/**
* @var Artwork $artwork
*/
?>
<? if($artwork !== null){ ?>
<? if($artwork->Status == ArtworkStatusType::Approved){ ?>Approved<? } ?>
<? if($artwork->Status == ArtworkStatusType::Declined){ ?>Declined<? } ?>
<? if($artwork->Status == ArtworkStatusType::Unverified){ ?>Unverified<? } ?>
<? if($artwork->EbookUrl !== null){ ?> — in use<? if($artwork->EbookUrl !== null){ ?> by <? if($artwork->Ebook !== null && $artwork->Ebook->Url !== null){ ?><i><a href="<?= $artwork->Ebook->Url ?>"><?= Formatter::EscapeHtml($artwork->Ebook->Title) ?></a></i><? }else{ ?><code><?= Formatter::EscapeHtml($artwork->EbookUrl) ?></code> (unreleased)<? } ?><? } ?><? } ?>
<? if($artwork->Status !== null){ ?>
<?= ucfirst($artwork->Status->value) ?>
<? } ?>
<? if($artwork->EbookUrl !== null){ ?>
in use
<? if($artwork->EbookUrl !== null){ ?>
by
<? if($artwork->Ebook !== null && $artwork->Ebook->Url !== null){ ?>
<i>
<a href="<?= $artwork->Ebook->Url ?>"><?= Formatter::EscapeHtml($artwork->Ebook->Title) ?></a>
</i>
<? }else{ ?>
<code><?= Formatter::EscapeHtml($artwork->EbookUrl) ?></code> (unreleased)
<? } ?>
<? } ?>
<? } ?>
<? } ?>

View file

@ -1,4 +1,13 @@
<?
/**
* @var string $id
* @var string $url
* @var string $title
* @var ?string $subtitle
* @var DateTimeImmutable $updated
* @var array<Ebook> $entries
*/
$subtitle = $subtitle ?? null;
// Note that the XSL stylesheet gets stripped during `se clean` when we generate the feed.

View file

@ -1,3 +1,11 @@
<?
use function Safe\filesize;
/**
* @var Ebook $entry
*/
?>
<entry>
<id><?= SITE_URL . $entry->Url ?></id>
<title><?= Formatter::EscapeXml($entry->Title) ?></title>
@ -20,9 +28,19 @@
<? } ?>
<media:thumbnail url="<?= SITE_URL . $entry->Url ?>/downloads/cover-thumbnail.jpg" height="525" width="350"/>
<link href="<?= SITE_URL . $entry->Url ?>" rel="alternate" title="This ebooks page at Standard Ebooks" type="application/xhtml+xml"/>
<? if(file_exists(WEB_ROOT . $entry->EpubUrl)){ ?><link href="<?= SITE_URL . $entry->EpubUrl ?>" length="<?= filesize(WEB_ROOT . $entry->EpubUrl) ?>" rel="enclosure" title="Recommended compatible epub" type="application/epub+zip" /><? } ?>
<? if(file_exists(WEB_ROOT . $entry->AdvancedEpubUrl)){ ?><link href="<?= SITE_URL . $entry->AdvancedEpubUrl ?>" length="<?= filesize(WEB_ROOT . $entry->AdvancedEpubUrl) ?>" rel="enclosure" title="Advanced epub" type="application/epub+zip" /><? } ?>
<? if(file_exists(WEB_ROOT . $entry->KepubUrl)){ ?><link href="<?= SITE_URL . $entry->KepubUrl ?>" length="<?= filesize(WEB_ROOT . $entry->KepubUrl) ?>" rel="enclosure" title="Kobo Kepub epub" type="application/kepub+zip" /><? } ?>
<? if(file_exists(WEB_ROOT . $entry->Azw3Url)){ ?><link href="<?= SITE_URL . $entry->Azw3Url ?>" length="<?= filesize(WEB_ROOT . $entry->Azw3Url) ?>" rel="enclosure" title="Amazon Kindle azw3" type="application/x-mobipocket-ebook" /><? } ?>
<? if(file_exists(WEB_ROOT . $entry->TextSinglePageUrl)){ ?><link href="<?= SITE_URL . $entry->TextSinglePageUrl ?>" length="<?= filesize(WEB_ROOT . $entry->TextSinglePageUrl) ?>" rel="enclosure" title="XHTML" type="application/xhtml+xml" /><? } ?>
<? if(file_exists(WEB_ROOT . $entry->EpubUrl)){ ?>
<link href="<?= SITE_URL . $entry->EpubUrl ?>" length="<?= filesize(WEB_ROOT . $entry->EpubUrl) ?>" rel="enclosure" title="Recommended compatible epub" type="application/epub+zip" />
<? } ?>
<? if(file_exists(WEB_ROOT . $entry->AdvancedEpubUrl)){ ?>
<link href="<?= SITE_URL . $entry->AdvancedEpubUrl ?>" length="<?= filesize(WEB_ROOT . $entry->AdvancedEpubUrl) ?>" rel="enclosure" title="Advanced epub" type="application/epub+zip" />
<? } ?>
<? if(file_exists(WEB_ROOT . $entry->KepubUrl)){ ?>
<link href="<?= SITE_URL . $entry->KepubUrl ?>" length="<?= filesize(WEB_ROOT . $entry->KepubUrl) ?>" rel="enclosure" title="Kobo Kepub epub" type="application/kepub+zip" />
<? } ?>
<? if(file_exists(WEB_ROOT . $entry->Azw3Url)){ ?>
<link href="<?= SITE_URL . $entry->Azw3Url ?>" length="<?= filesize(WEB_ROOT . $entry->Azw3Url) ?>" rel="enclosure" title="Amazon Kindle azw3" type="application/x-mobipocket-ebook" />
<? } ?>
<? if(file_exists(WEB_ROOT . $entry->TextSinglePageUrl)){ ?>
<link href="<?= SITE_URL . $entry->TextSinglePageUrl ?>" length="<?= filesize(WEB_ROOT . $entry->TextSinglePageUrl) ?>" rel="enclosure" title="XHTML" type="application/xhtml+xml" />
<? } ?>
</entry>

View file

@ -1,8 +1,14 @@
<?
/**
* @var string $label
* @var array<stdClass> $collections
*/
?>
<table class="download-list">
<caption aria-hidden="hidden">Scroll right </caption>
<thead>
<tr class="mid-header">
<th scope="col"><?= $label ?></th>
<th scope="col"><?= Formatter::EscapeHtml($label) ?></th>
<th scope="col">Ebooks</th>
<th scope="col">Updated</th>
<th scope="col" colspan="10">Ebook format</th>
@ -14,7 +20,6 @@
<td class="row-header"><a href="<?= $collection->Url ?>"><?= Formatter::EscapeHtml($collection->Label) ?></a></td>
<td class="number"><?= Formatter::EscapeHtml(number_format($collection->EbookCount)) ?></td>
<td class="number"><?= Formatter::EscapeHtml($collection->UpdatedString) ?></td>
<? foreach($collection->ZipFiles as $item){ ?>
<td class="download"><a href="<?= $item->Url ?>"><?= $item->Type ?></a></td>
<td>(<?= Formatter::EscapeHtml($item->Size) ?>)</td>

View file

@ -37,7 +37,7 @@ else{
}
}
$digits = str_split(str_pad($current, 3, "0", STR_PAD_LEFT))
$digits = str_split(str_pad((string)$current, 3, "0", STR_PAD_LEFT))
?>
<aside class="donation counter closable">
<? if($autoHide){ ?>

View file

@ -1,17 +1,17 @@
<?
/**
* @var ?Collection $collection
* @var array<Ebook> $ebooks
*/
if($view == ''){
$view = ViewType::Grid;
}
$view = $view ?? ViewType::Grid;
$collection = $collection ?? null;
$ebooks = $ebooks ?? [];
?>
<ol class="ebooks-list<? if($view == ViewType::List){ ?> list<? }else{ ?> grid<? } ?>"<? if($collection !== null){ ?> typeof="schema:BookSeries" about="<?= $collection->Url ?>"<? } ?>>
<? if($collection !== null){ ?>
<? if($collection !== null){ ?>
<meta property="schema:name" content="<?= Formatter::EscapeHtml($collection->Name) ?>"/>
<? } ?>
<? foreach($ebooks as $ebook){ ?>
<? } ?>
<? foreach($ebooks as $ebook){ ?>
<li typeof="schema:Book"<? if($collection !== null){ ?> resource="<?= $ebook->Url ?>" property="schema:hasPart"<? if($ebook->GetCollectionPosition($collection) !== null){ ?> value="<?= $ebook->GetCollectionPosition($collection) ?>"<? } ?><? }else{ ?> about="<?= $ebook->Url ?>"<? } ?>>
<? if($collection !== null && $ebook->GetCollectionPosition($collection) !== null){ ?>
<meta property="schema:position" content="<?= $ebook->GetCollectionPosition($collection) ?>"/>
@ -47,5 +47,5 @@ $ebooks = $ebooks ?? [];
</div>
<? } ?>
</li>
<? } ?>
<? } ?>
</ol>

View file

@ -1,4 +1,9 @@
<!DOCTYPE html>
<?
/**
* @var Patron $patron
* @var Payment $payment
*/
?><!DOCTYPE html>
<html>
<head>
<title>New Patrons Circle member</title>

View file

@ -1,4 +1,9 @@
Name: <? if($patron->User->Name === null){ ?>Anonymous <? }else{ ?><?= Formatter::EscapeHtml($patron->User->Name) ?><? if($patron->IsAnonymous){ ?> (Anonymous)<? } ?><? } ?>
<?
/**
* @var Patron $patron
* @var Payment $payment
*/
?>Name: <? if($patron->User->Name === null){ ?>Anonymous <? }else{ ?><?= Formatter::EscapeHtml($patron->User->Name) ?><? if($patron->IsAnonymous){ ?> (Anonymous)<? } ?><? } ?>
Donation type: <? if($payment->IsRecurring){ ?>Recurring<? }else{ ?>One-time<? } ?>

View file

@ -1,13 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<title>Donation ingestion failed</title>
</head>
<body>
<h1>Donation ingestion failed</h1>
<p>The donation ingestion script failed with this exception:</p>
<pre>
<?= $exception ?>
</pre>
</body>
</html>

View file

@ -1,7 +0,0 @@
# Donation ingestion failed
The donation ingestion script failed with this exception:
````
<?= $exception ?>
````

View file

@ -1,4 +1,8 @@
<!DOCTYPE html>
<?
/**
* @var string $exception
*/
?><!DOCTYPE html>
<html lang="en">
<head>
<title>Donation processing failed</title>

View file

@ -1,3 +1,8 @@
<?
/**
* @var string $exception
*/
?>
# Donation processing failed
The donation processing script failed with this exception:

View file

@ -13,6 +13,6 @@
<p>Chicago, IL 60622</p>
</address>
</div>
</div>
</div>
</body>
</html>

View file

@ -191,4 +191,6 @@ $letterhead = $letterhead ?? false;
</head>
<body>
<div class="body<? if($letterhead){ ?> letterhead<? } ?>">
<? if($preheader){ ?><p class="preheader"><?= Formatter::EscapeHtml($preheader) ?><? for($i = 0; $i < 150 - strlen($preheader); $i++){ ?>&zwnj;&nbsp;<? } ?></p><? } ?>
<? if($preheader){ ?>
<p class="preheader"><?= Formatter::EscapeHtml($preheader) ?><? for($i = 0; $i < 150 - strlen($preheader); $i++){ ?>&zwnj;&nbsp;<? } ?></p>
<? } ?>

View file

@ -1,10 +1,24 @@
<?= Template::EmailHeader() ?>
<?
/**
* @var bool $isSubscribedToNewsletter
* @var bool $isSubscribedToSummary
* @var NewsletterSubscription $subscription
*/
?><?= Template::EmailHeader() ?>
<h1>Confirm your newsletter subscription</h1>
<p>Thank you for subscribing to the Standard Ebooks newsletter!</p>
<p>You subscribed to:</p>
<ul>
<? if($isSubscribedToNewsletter){ ?><li><p>The occasional Standard Ebooks newsletter</p></li><? } ?>
<? if($isSubscribedToSummary){ ?><li><p>A monthly summary of new ebook releases</p></li><? } ?>
<? if($isSubscribedToNewsletter){ ?>
<li>
<p>The occasional Standard Ebooks newsletter</p>
</li>
<? } ?>
<? if($isSubscribedToSummary){ ?>
<li>
<p>A monthly summary of new ebook releases</p>
</li>
<? } ?>
</ul>
<p>Please use the button below to confirm your subscription—you wont receive email from us until you do.</p>
<p class="button-row">

View file

@ -1,3 +1,10 @@
<?
/**
* @var bool $isSubscribedToNewsletter
* @var bool $isSubscribedToSummary
* @var NewsletterSubscription $subscription
*/
?>
# Confirm your newsletter subscription
Thank you for subscribing to the Standard Ebooks newsletter!

View file

@ -1,4 +1,8 @@
<?= Template::EmailHeader(['letterhead' => true]) ?>
<?
/**
* @var int $ebooksThisYear
*/
?><?= Template::EmailHeader(['letterhead' => 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,3 +1,8 @@
<?
/**
* @var int $ebooksThisYear
*/
?>
Hello,
Last year, your generous donation to Standard Ebooks made it possible for us to continue producing beautiful ebook editions for free distribution.

View file

@ -1,4 +1,9 @@
<!DOCTYPE html>
<?
/**
* @var bool $isAnonymous
* @var bool $isReturning
*/
?><!DOCTYPE html>
<html lang="en">
<head>
<title>Thank you for supporting Standard Ebooks!</title>

View file

@ -1,3 +1,9 @@
<?
/**
* @var bool $isAnonymous
* @var bool $isReturning
*/
?>
Hello,
I wanted to thank you personally for your recent donation to Standard Ebooks. Your donation will go towards continuing our mission of producing and distributing high-quality ebooks that are free of cost and free of copyright restrictions. Donations like yours help ensure that the worlds literature is available in beautiful editions made for the digital age.

View file

@ -1,6 +1,9 @@
<?
/**
* @var ?Exception $exception
*/
if(!$exception){
if($exception === null){
return;
}

View file

@ -3,15 +3,27 @@
<? if($GLOBALS['User'] === null){ ?>
<p>Our New Releases feeds are open to everyone. Our other feeds are a benefit of Patrons Circle membership.</p>
<ul>
<li><p><a href="/donate#patrons-circle">Join the Patrons Circle</a> by making a small donation in support of our mission. Patrons have full access to our ebook feeds for the duration of their gift.</p></li>
<li><p><a href="/contribute">Produce an ebook</a> for Standard Ebooks to get lifetime access to our ebook feeds. (If youve already done that, <a href="/about#editor-in-chief">contact us</a> to enable your access.)</p></li>
<li><p><a href="/donate#corporate-sponsors">Corporate sponsors</a> get access to all of our ebook feeds for the duration of their sponsorship. <a href="/about#editor-in-chief">Contact us</a> to chat about having your organization sponsor our mission.</p></li>
<li><p>Open source projects can get free access to all of our ebook feeds if they meet certain criteria. <a href="/about#editor-in-chief">Contact us</a> to discuss free access for your open source project.</p></li>
<li>
<p><a href="/donate#patrons-circle">Join the Patrons Circle</a> by making a small donation in support of our mission. Patrons have full access to our ebook feeds for the duration of their gift.</p>
</li>
<li>
<p><a href="/contribute">Produce an ebook</a> for Standard Ebooks to get lifetime access to our ebook feeds. (If youve already done that, <a href="/about#editor-in-chief">contact us</a> to enable your access.)</p>
</li>
<li>
<p><a href="/donate#corporate-sponsors">Corporate sponsors</a> get access to all of our ebook feeds for the duration of their sponsorship. <a href="/about#editor-in-chief">Contact us</a> to chat about having your organization sponsor our mission.</p>
</li>
<li>
<p>Open source projects can get free access to all of our ebook feeds if they meet certain criteria. <a href="/about#editor-in-chief">Contact us</a> to discuss free access for your open source project.</p>
</li>
</ul>
<p><i>If youre a Patrons Circle member, when prompted enter your email address and leave the password field blank to access a feed.</i></p>
<p>
<i>If youre a Patrons Circle member, when prompted enter your email address and leave the password field blank to access a feed.</i>
</p>
<? }elseif($GLOBALS['User']->Benefits->CanAccessFeeds){ ?>
<p>When prompted enter your email address and leave the password field blank to access a feed.</p>
<? }else{ ?>
<p><i>If youre a Patrons Circle member, when prompted enter your email address and leave the password field blank to access a feed.</i></p>
<p>
<i>If youre a Patrons Circle member, when prompted enter your email address and leave the password field blank to access a feed.</i>
</p>
<? } ?>
</section>

View file

@ -1,4 +1,5 @@
<?
use function Safe\filemtime;
$title = $title ?? '';
$highlight = $highlight ?? '';

View file

@ -1,3 +1,10 @@
<?
use function Safe\filesize;
/**
* @var Ebook $entry
*/
?>
<entry>
<id><?= SITE_URL . $entry->Url ?></id>
<dc:identifier><?= Formatter::EscapeXml($entry->Identifier) ?></dc:identifier>
@ -6,9 +13,15 @@
<author>
<name><?= Formatter::EscapeXml($author->Name) ?></name>
<uri><?= SITE_URL . Formatter::EscapeXml($entry->AuthorsUrl) ?></uri>
<? if($author->FullName !== null){ ?><schema:alternateName><?= Formatter::EscapeXml($author->FullName) ?></schema:alternateName><? } ?>
<? if($author->WikipediaUrl !== null){ ?><schema:sameAs><?= Formatter::EscapeXml($author->WikipediaUrl) ?></schema:sameAs><? } ?>
<? if($author->NacoafUrl !== null){ ?><schema:sameAs><?= Formatter::EscapeXml($author->NacoafUrl) ?></schema:sameAs><? } ?>
<? if($author->FullName !== null){ ?>
<schema:alternateName><?= Formatter::EscapeXml($author->FullName) ?></schema:alternateName>
<? } ?>
<? if($author->WikipediaUrl !== null){ ?>
<schema:sameAs><?= Formatter::EscapeXml($author->WikipediaUrl) ?></schema:sameAs>
<? } ?>
<? if($author->NacoafUrl !== null){ ?>
<schema:sameAs><?= Formatter::EscapeXml($author->NacoafUrl) ?></schema:sameAs>
<? } ?>
</author>
<? } ?>
<published><?= $entry->EbookCreated->format('Y-m-d\TH:i:s\Z') ?></published>
@ -28,9 +41,19 @@
<link href="<?= SITE_URL . $entry->Url ?>/downloads/cover.jpg" rel="http://opds-spec.org/image" type="image/jpeg"/>
<link href="<?= SITE_URL . $entry->Url ?>/downloads/cover-thumbnail.jpg" rel="http://opds-spec.org/image/thumbnail" type="image/jpeg"/>
<link href="<?= SITE_URL . $entry->Url ?>" rel="alternate" title="This ebooks page at Standard Ebooks" type="application/xhtml+xml"/>
<? if(file_exists(WEB_ROOT . $entry->EpubUrl)){ ?><link href="<?= SITE_URL . $entry->EpubUrl ?>" length="<?= filesize(WEB_ROOT . $entry->EpubUrl) ?>" rel="http://opds-spec.org/acquisition/open-access" title="Recommended compatible epub" type="application/epub+zip" /><? } ?>
<? if(file_exists(WEB_ROOT . $entry->AdvancedEpubUrl)){ ?><link href="<?= SITE_URL . $entry->AdvancedEpubUrl ?>" length="<?= filesize(WEB_ROOT . $entry->AdvancedEpubUrl) ?>" rel="http://opds-spec.org/acquisition/open-access" title="Advanced epub" type="application/epub+zip" /><? } ?>
<? if(file_exists(WEB_ROOT . $entry->KepubUrl)){ ?><link href="<?= SITE_URL . $entry->KepubUrl ?>" length="<?= filesize(WEB_ROOT . $entry->KepubUrl) ?>" rel="http://opds-spec.org/acquisition/open-access" title="Kobo Kepub epub" type="application/kepub+zip" /><? } ?>
<? if(file_exists(WEB_ROOT . $entry->Azw3Url)){ ?><link href="<?= SITE_URL . $entry->Azw3Url ?>" length="<?= filesize(WEB_ROOT . $entry->Azw3Url) ?>" rel="http://opds-spec.org/acquisition/open-access" title="Amazon Kindle azw3" type="application/x-mobipocket-ebook" /><? } ?>
<? if(file_exists(WEB_ROOT . $entry->TextSinglePageUrl)){ ?><link href="<?= SITE_URL . $entry->TextSinglePageUrl ?>" length="<?= filesize(WEB_ROOT . $entry->TextSinglePageUrl) ?>" rel="http://opds-spec.org/acquisition/open-access" title="XHTML" type="application/xhtml+xml; charset=utf-8" /><? } ?>
<? if(file_exists(WEB_ROOT . $entry->EpubUrl)){ ?>
<link href="<?= SITE_URL . $entry->EpubUrl ?>" length="<?= filesize(WEB_ROOT . $entry->EpubUrl) ?>" rel="http://opds-spec.org/acquisition/open-access" title="Recommended compatible epub" type="application/epub+zip" />
<? } ?>
<? if(file_exists(WEB_ROOT . $entry->AdvancedEpubUrl)){ ?>
<link href="<?= SITE_URL . $entry->AdvancedEpubUrl ?>" length="<?= filesize(WEB_ROOT . $entry->AdvancedEpubUrl) ?>" rel="http://opds-spec.org/acquisition/open-access" title="Advanced epub" type="application/epub+zip" />
<? } ?>
<? if(file_exists(WEB_ROOT . $entry->KepubUrl)){ ?>
<link href="<?= SITE_URL . $entry->KepubUrl ?>" length="<?= filesize(WEB_ROOT . $entry->KepubUrl) ?>" rel="http://opds-spec.org/acquisition/open-access" title="Kobo Kepub epub" type="application/kepub+zip" />
<? } ?>
<? if(file_exists(WEB_ROOT . $entry->Azw3Url)){ ?>
<link href="<?= SITE_URL . $entry->Azw3Url ?>" length="<?= filesize(WEB_ROOT . $entry->Azw3Url) ?>" rel="http://opds-spec.org/acquisition/open-access" title="Amazon Kindle azw3" type="application/x-mobipocket-ebook" />
<? } ?>
<? if(file_exists(WEB_ROOT . $entry->TextSinglePageUrl)){ ?>
<link href="<?= SITE_URL . $entry->TextSinglePageUrl ?>" length="<?= filesize(WEB_ROOT . $entry->TextSinglePageUrl) ?>" rel="http://opds-spec.org/acquisition/open-access" title="XHTML" type="application/xhtml+xml; charset=utf-8" />
<? } ?>
</entry>

View file

@ -1,12 +1,20 @@
<?
/**
* 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>.
*/
/* 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
*/
/**
* @var string $id
* @var string $url
* @var string $parentUrl
* @var string $title
* @var ?string $subtitle
* @var DateTimeImmutable $updated
* @var array<Ebook> $entries
*/
$isCrawlable = $isCrawlable ?? false;
$subtitle = $subtitle ?? null;
@ -24,10 +32,14 @@ print("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n");
<link href="<?= SITE_URL ?>/feeds/opds/all" rel="http://opds-spec.org/crawlable" type="application/atom+xml;profile=opds-catalog;kind=acquisition; charset=utf-8"/>
<link href="<?= SITE_URL ?>/ebooks/opensearch" rel="search" type="application/opensearchdescription+xml; charset=utf-8"/>
<title><?= Formatter::EscapeXml($title) ?></title>
<? if($subtitle !== null){ ?><subtitle><?= Formatter::EscapeXml($subtitle) ?></subtitle><? } ?>
<? if($subtitle !== null){ ?>
<subtitle><?= Formatter::EscapeXml($subtitle) ?></subtitle>
<? } ?>
<icon><?= SITE_URL ?>/images/logo.png</icon>
<updated><?= $updated->format('Y-m-d\TH:i:s\Z') ?></updated>
<? if($isCrawlable){ ?><fh:complete/><? } ?>
<? if($isCrawlable){ ?>
<fh:complete/>
<? } ?>
<author>
<name>Standard Ebooks</name>
<uri><?= SITE_URL ?></uri>

View file

@ -1,4 +1,13 @@
<?
/**
* @var string $id
* @var string $url
* @var string $parentUrl
* @var string $title
* @var ?string $subtitle
* @var DateTimeImmutable $updated
* @var array<OpdsNavigationEntry> $entries
*/
$subtitle = $subtitle ?? null;
@ -13,9 +22,13 @@ print("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n");
<link href="<?= SITE_URL ?>/feeds/opds" rel="start" type="application/atom+xml;profile=opds-catalog;kind=navigation; charset=utf-8"/>
<link href="<?= SITE_URL ?>/feeds/opds/all" rel="http://opds-spec.org/crawlable" type="application/atom+xml;profile=opds-catalog;kind=acquisition; charset=utf-8"/>
<link href="<?= SITE_URL ?>/ebooks/opensearch" rel="search" type="application/opensearchdescription+xml; charset=utf-8"/>
<? if($parentUrl !== null){ ?><link href="<?= SITE_URL ?><?= Formatter::EscapeXml($parentUrl) ?>" rel="up" type="application/atom+xml;profile=opds-catalog;kind=navigation; charset=utf-8"/><? } ?>
<? if($parentUrl !== null){ ?>
<link href="<?= SITE_URL ?><?= Formatter::EscapeXml($parentUrl) ?>" rel="up" type="application/atom+xml;profile=opds-catalog;kind=navigation; charset=utf-8"/>
<? } ?>
<title><?= Formatter::EscapeXml($title) ?></title>
<? if($subtitle !== null){ ?><subtitle><?= Formatter::EscapeXml($subtitle) ?></subtitle><? } ?>
<? if($subtitle !== null){ ?>
<subtitle><?= Formatter::EscapeXml($subtitle) ?></subtitle>
<? } ?>
<icon><?= SITE_URL ?>/images/logo.png</icon>
<updated><?= $updated->format('Y-m-d\TH:i:s\Z') ?></updated>
<author>

View file

@ -1,3 +1,11 @@
<?
use function Safe\filesize;
use function Safe\preg_replace;
/**
* @var Ebook $entry
*/
?>
<item>
<title><?= Formatter::EscapeXml($entry->Title) ?>, by <?= Formatter::EscapeXml(strip_tags($entry->AuthorsHtml)) ?></title>
<link><?= SITE_URL . Formatter::EscapeXml($entry->Url) ?></link>

View file

@ -1,4 +1,12 @@
<?
/**
* @var string $title
* @var string $description
* @var DateTimeImmutable $updated
* @var string $url
* @var array<Ebook> $entries
*/
// 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.
// We have to add it programmatically when saving the feed file.
@ -10,7 +18,7 @@ print("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n");
<description><?= Formatter::EscapeXml($description) ?></description>
<language>en-US</language>
<copyright>https://creativecommons.org/publicdomain/zero/1.0/</copyright>
<lastBuildDate><?= $updated ?></lastBuildDate>
<lastBuildDate><?= $updated->format('r'); ?></lastBuildDate>
<docs>http://blogs.law.harvard.edu/tech/rss</docs>
<atom:link href="<?= SITE_URL . Formatter::EscapeXml($url) ?>" rel="self" type="application/rss+xml"/>
<atom:link href="<?= SITE_URL ?>/ebooks/opensearch" rel="search" type="application/opensearchdescription+xml" />

View file

@ -1,12 +1,19 @@
<?
$allSelected = sizeof($tags) == 0 || in_array('all', $tags);
/**
* @var array<string> $tags
* @var EbookSortType $sort
* @var ViewType $view
* @var int $perPage
*/
$isAllSelected = sizeof($tags) == 0 || in_array('all', $tags);
?>
<form action="/ebooks" method="get" rel="search">
<label class="tags">Subjects
<select <? if(!Template::IsEreaderBrowser()){ ?> multiple="multiple"<? } ?> name="tags[]" size="1">
<option value="all">All</option>
<? foreach(Library::GetTags() as $tag){ ?>
<option value="<?= $tag->UrlName ?>"<? if(!$allSelected && in_array($tag->UrlName, $tags)){ ?> selected="selected"<? } ?>><?= Formatter::EscapeHtml($tag->Name) ?></option>
<option value="<?= $tag->UrlName ?>"<? if(!$isAllSelected && in_array($tag->UrlName, $tags)){ ?> selected="selected"<? } ?>><?= Formatter::EscapeHtml($tag->Name) ?></option>
<? } ?>
</select>
</label>