mirror of
https://github.com/standardebooks/web.git
synced 2025-07-07 07:10:29 -04:00
Add automatic donation drives
This commit is contained in:
parent
d8ed44e20e
commit
19cf14c1aa
10 changed files with 396 additions and 296 deletions
|
@ -15,10 +15,8 @@ parameters:
|
||||||
- %rootDir%/../../../scripts
|
- %rootDir%/../../../scripts
|
||||||
dynamicConstantNames:
|
dynamicConstantNames:
|
||||||
- SITE_STATUS
|
- SITE_STATUS
|
||||||
- DONATION_HOLIDAY_ALERT_ON
|
- DONATION_DRIVE_ENABLED
|
||||||
- DONATION_ALERT_ON
|
- DONATION_DRIVE_COUNTER_ENABLED
|
||||||
- DONATION_DRIVE_ON
|
|
||||||
- DONATION_DRIVE_COUNTER_ON
|
|
||||||
earlyTerminatingMethodCalls:
|
earlyTerminatingMethodCalls:
|
||||||
Template:
|
Template:
|
||||||
- Emit403
|
- Emit403
|
||||||
|
|
|
@ -74,15 +74,26 @@ const ARTWORK_UPLOADS_LOG_FILE_PATH = '/var/log/local/artwork-uploads.log'; // M
|
||||||
define('PD_YEAR', intval((new DateTimeImmutable('now', new DateTimeZone('America/Juneau')))->format('Y')) - 96); // Latest continental US time zone.
|
define('PD_YEAR', intval((new DateTimeImmutable('now', new DateTimeZone('America/Juneau')))->format('Y')) - 96); // Latest continental US time zone.
|
||||||
define('PD_STRING', 'January 1, ' . (PD_YEAR + 1));
|
define('PD_STRING', 'January 1, ' . (PD_YEAR + 1));
|
||||||
|
|
||||||
define('DONATION_HOLIDAY_ALERT_ON', NOW > new DateTimeImmutable('November 15, ' . NOW->format('Y')) || NOW < new DateTimeImmutable('January 7, ' . NOW->add(new DateInterval('P1Y'))->format('Y')));
|
// Controls the progress bar donation dialog.
|
||||||
define('DONATION_ALERT_ON', DONATION_HOLIDAY_ALERT_ON || rand(1, 4) == 2);
|
const DONATION_DRIVES_ENABLED = true; // **`TRUE`** to enable automatic donation drives; **`FALSE`** to disable all donation drives.
|
||||||
|
const DONATION_DRIVE_DATES = [
|
||||||
|
new DonationDrive(
|
||||||
|
'Spring drive',
|
||||||
|
new DateTimeImmutable('Second Monday of May'),
|
||||||
|
new DateTimeImmutable('Second Monday of May +2 weeks'),
|
||||||
|
50,
|
||||||
|
20
|
||||||
|
),
|
||||||
|
new DonationDrive(
|
||||||
|
'Holiday drive',
|
||||||
|
NOW < new DateTimeImmutable('November 15') ? new DateTimeImmutable('November 15') : new DateTimeImmutable('November 15 -1 year'),
|
||||||
|
NOW < new DateTimeImmutable('January 7') ? new DateTimeImmutable('January 7') : new DateTimeImmutable('January 7 +1 year'),
|
||||||
|
50,
|
||||||
|
20
|
||||||
|
)
|
||||||
|
];
|
||||||
|
|
||||||
// Controls the progress bar donation dialog
|
// Controls the countdown donation dialog, basically unused right now.
|
||||||
const DONATION_DRIVE_ON = true;
|
const DONATION_DRIVE_COUNTER_ENABLED = false;
|
||||||
const DONATION_DRIVE_START = new DateTimeImmutable('May 20, 2024 00:00:00 America/New_York');
|
|
||||||
const DONATION_DRIVE_END = new DateTimeImmutable('June 3, 2024 23:59:00 America/New_York');
|
|
||||||
|
|
||||||
// Controls the countdown donation dialog
|
|
||||||
const DONATION_DRIVE_COUNTER_ON = false;
|
|
||||||
const DONATION_DRIVE_COUNTER_START = new DateTimeImmutable('May 2, 2022 00:00:00 America/New_York');
|
const DONATION_DRIVE_COUNTER_START = new DateTimeImmutable('May 2, 2022 00:00:00 America/New_York');
|
||||||
const DONATION_DRIVE_COUNTER_END = new DateTimeImmutable('May 8, 2022 23:59:00 America/New_York');
|
const DONATION_DRIVE_COUNTER_END = new DateTimeImmutable('May 8, 2022 23:59:00 America/New_York');
|
||||||
|
|
95
lib/DonationDrive.php
Normal file
95
lib/DonationDrive.php
Normal file
|
@ -0,0 +1,95 @@
|
||||||
|
<?
|
||||||
|
use Safe\DateTimeImmutable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @property int $DonationCount
|
||||||
|
* @property int $StretchDonationCount
|
||||||
|
* @property bool $IsStretchEnabled
|
||||||
|
* @property int $TargetDonationCount
|
||||||
|
*/
|
||||||
|
class DonationDrive{
|
||||||
|
use Traits\Accessor;
|
||||||
|
|
||||||
|
protected int $_DonationCount;
|
||||||
|
protected int $_StretchDonationCount;
|
||||||
|
protected bool $_IsStretchEnabled;
|
||||||
|
protected int $_TargetDonationCount;
|
||||||
|
|
||||||
|
public function __construct(public string $Name, public DateTimeImmutable $Start, public DateTimeImmutable $End, public int $BaseTargetDonationCount, public int $StretchTargetDonationCount){
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function GetByIsRunning(): ?DonationDrive{
|
||||||
|
foreach(DONATION_DRIVE_DATES as $donationDrive){
|
||||||
|
if(NOW > $donationDrive->Start && NOW < $donationDrive->End){
|
||||||
|
return $donationDrive;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function GetDonationCount(): int{
|
||||||
|
if(!isset($this->_DonationCount)){
|
||||||
|
$this->_DonationCount = Db::QueryInt('
|
||||||
|
SELECT sum(cnt)
|
||||||
|
from
|
||||||
|
(
|
||||||
|
(
|
||||||
|
# Anonymous patrons, i.e. from AOGF
|
||||||
|
select count(*) cnt from Payments
|
||||||
|
where
|
||||||
|
UserId is null
|
||||||
|
and
|
||||||
|
(
|
||||||
|
(IsRecurring = true and Amount >= 10 and Created >= ?)
|
||||||
|
or
|
||||||
|
(IsRecurring = false and Amount >= 100 and Created >= ?)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
union all
|
||||||
|
(
|
||||||
|
# All non-anonymous patrons
|
||||||
|
select count(*) as cnt from Patrons
|
||||||
|
where Created >= ?
|
||||||
|
)
|
||||||
|
) x
|
||||||
|
', [$this->Start, $this->Start, $this->Start]);
|
||||||
|
}
|
||||||
|
return $this->_DonationCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function GetTargetDonationCount(): int{
|
||||||
|
if(!isset($this->_TargetDonationCount)){
|
||||||
|
$this->_TargetDonationCount = $this->BaseTargetDonationCount;
|
||||||
|
|
||||||
|
if($this->DonationCount > $this->BaseTargetDonationCount){
|
||||||
|
$this->_TargetDonationCount = $this->_TargetDonationCount + $this->StretchTargetDonationCount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->_TargetDonationCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function GetStretchDonationCount(): int{
|
||||||
|
if(!isset($this->_StretchDonationCount)){
|
||||||
|
$this->_StretchDonationCount = $this->DonationCount - $this->BaseTargetDonationCount;
|
||||||
|
if($this->_StretchDonationCount < 0){
|
||||||
|
$this->_StretchDonationCount = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->_StretchDonationCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function GetIsStretchEnabled(): bool{
|
||||||
|
if(!isset($this->_IsStretchEnabled)){
|
||||||
|
$this->_IsStretchEnabled = false;
|
||||||
|
|
||||||
|
if($this->StretchTargetDonationCount > 0 && $this->DonationCount >= $this->BaseTargetDonationCount){
|
||||||
|
$this->_IsStretchEnabled = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->_IsStretchEnabled;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,5 +1,20 @@
|
||||||
<? if($GLOBALS['User'] === null){
|
<?
|
||||||
// The Kindle browsers renders <aside> as an undismissable popup. Serve a <div> to Kindle instead. See https://github.com/standardebooks/web/issues/204
|
// Hide this alert if...
|
||||||
|
$donationDrive = DonationDrive::GetByIsRunning();
|
||||||
|
|
||||||
|
if(
|
||||||
|
$GLOBALS['User'] !== null // If a user is logged in.
|
||||||
|
||
|
||||||
|
$donationDrive !== null // There is a currently-running donation drive.
|
||||||
|
||
|
||||||
|
rand(1, 5) <= 3 // A 3-in-5 chance occurs.
|
||||||
|
){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if($GLOBALS['User'] === null){
|
||||||
|
// The Kindle browsers renders `<aside>` as an undismissable popup. Serve a `<div>` to Kindle instead.
|
||||||
|
// See <https://github.com/standardebooks/web/issues/204>.
|
||||||
$element = 'aside';
|
$element = 'aside';
|
||||||
|
|
||||||
if(stripos($_SERVER['HTTP_USER_AGENT'] ?? '', 'kindle') !== false){
|
if(stripos($_SERVER['HTTP_USER_AGENT'] ?? '', 'kindle') !== false){
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<?
|
<?
|
||||||
// Hide the alert if the user has closed it.
|
// Hide the alert if the user has closed it.
|
||||||
if(!DONATION_DRIVE_COUNTER_ON || ($autoHide ?? $_COOKIE['hide-donation-alert'] ?? false) || NOW > DONATION_DRIVE_COUNTER_END){
|
if(!DONATION_DRIVE_COUNTER_ENABLED || ($autoHide ?? $_COOKIE['hide-donation-alert'] ?? false) || NOW > DONATION_DRIVE_COUNTER_END){
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,54 +1,23 @@
|
||||||
<?
|
<?
|
||||||
$totalCurrent = 0;
|
$donationDrive = DonationDrive::GetByIsRunning();
|
||||||
$baseTarget = 50;
|
|
||||||
$stretchCurrent = 0;
|
|
||||||
$stretchTarget = 20;
|
|
||||||
|
|
||||||
// Hide the alert if...
|
|
||||||
if(
|
if(
|
||||||
!DONATION_DRIVE_ON // The drive isn't running
|
!DONATION_DRIVES_ENABLED // Drives aren't enabled.
|
||||||
||
|
||
|
||||||
($autoHide ?? $_COOKIE['hide-donation-alert'] ?? false) // If the user has hidden the box
|
($autoHide ?? $_COOKIE['hide-donation-alert'] ?? false) // If the user has hidden the box.
|
||||||
||
|
||
|
||||||
$GLOBALS['User'] !== null // If a user is logged in
|
$GLOBALS['User'] !== null // If a user is logged in.
|
||||||
||
|
||
|
||||||
DONATION_DRIVE_START > NOW // If the drive hasn't started yet
|
$donationDrive === null // There is no donation drive running right now.
|
||||||
||
|
|
||||||
NOW > DONATION_DRIVE_END // If the drive has ended
|
|
||||||
){
|
){
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
$autoHide = $autoHide ?? true;
|
$autoHide = $autoHide ?? true;
|
||||||
$showDonateButton = $showDonateButton ?? true;
|
$showDonateButton = $showDonateButton ?? true;
|
||||||
$totalCurrent = Db::QueryInt('
|
|
||||||
SELECT sum(cnt)
|
|
||||||
from
|
|
||||||
(
|
|
||||||
(
|
|
||||||
# Anonymous patrons, i.e. from AOGF
|
|
||||||
select count(*) cnt from Payments
|
|
||||||
where
|
|
||||||
UserId is null
|
|
||||||
and
|
|
||||||
(
|
|
||||||
(IsRecurring = true and Amount >= 10 and Created >= ?)
|
|
||||||
or
|
|
||||||
(IsRecurring = false and Amount >= 100 and Created >= ?)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
union all
|
|
||||||
(
|
|
||||||
# All non-anonymous patrons
|
|
||||||
select count(*) as cnt from Patrons
|
|
||||||
where Created >= ?
|
|
||||||
)
|
|
||||||
) x
|
|
||||||
', [DONATION_DRIVE_START, DONATION_DRIVE_START, DONATION_DRIVE_START]);
|
|
||||||
|
|
||||||
$totalTarget = $baseTarget;
|
$deadline = $donationDrive->End->format('F j');
|
||||||
$deadline = DONATION_DRIVE_END->format('F j');
|
$timeLeft = NOW->diff($donationDrive->End);
|
||||||
$timeLeft = NOW->diff(DONATION_DRIVE_END);
|
|
||||||
$timeString = '';
|
$timeString = '';
|
||||||
if($timeLeft->days < 1 && $timeLeft->h < 20){
|
if($timeLeft->days < 1 && $timeLeft->h < 20){
|
||||||
$timeString = 'Just hours';
|
$timeString = 'Just hours';
|
||||||
|
@ -71,13 +40,6 @@ else{
|
||||||
$timeString = 'Only ' . $timeString;
|
$timeString = 'Only ' . $timeString;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$stretchOn = false;
|
|
||||||
if($stretchTarget > 0 && $totalCurrent >= $baseTarget){
|
|
||||||
$stretchOn = true;
|
|
||||||
$stretchCurrent = $totalCurrent - $baseTarget;
|
|
||||||
$totalTarget = $baseTarget + $stretchTarget;
|
|
||||||
}
|
|
||||||
?>
|
?>
|
||||||
<aside class="donation closable">
|
<aside class="donation closable">
|
||||||
<? if($autoHide){ ?>
|
<? if($autoHide){ ?>
|
||||||
|
@ -86,38 +48,38 @@ if($stretchTarget > 0 && $totalCurrent >= $baseTarget){
|
||||||
<button class="close" title="Close this box">Close this box</button>
|
<button class="close" title="Close this box">Close this box</button>
|
||||||
</form>
|
</form>
|
||||||
<? } ?>
|
<? } ?>
|
||||||
<? if(!$stretchOn){ ?>
|
<? if(!$donationDrive->IsStretchEnabled){ ?>
|
||||||
<header>
|
<header>
|
||||||
<? if($timeLeft->days > 5){ ?>
|
<? if($timeLeft->days > 5){ ?>
|
||||||
<p>Help us reach <?= number_format($baseTarget) ?> new patrons by <?= $deadline ?></p>
|
<p>Help us reach <?= number_format($donationDrive->TargetDonationCount) ?> new patrons by <?= $deadline ?></p>
|
||||||
<? }else{ ?>
|
<? }else{ ?>
|
||||||
<p><?= $timeString ?> left to help us reach <?= number_format($baseTarget) ?> new patrons!</p>
|
<p><?= $timeString ?> left to help us reach <?= number_format($donationDrive->TargetDonationCount) ?> new patrons!</p>
|
||||||
<? } ?>
|
<? } ?>
|
||||||
</header>
|
</header>
|
||||||
<? }else{ ?>
|
<? }else{ ?>
|
||||||
<header>
|
<header>
|
||||||
<p>Help us meet our stretch goal of<br/> <?= number_format($totalTarget) ?> new patrons by <?= $deadline ?></p>
|
<p>Help us meet our stretch goal of<br/> <?= number_format($donationDrive->TargetDonationCount) ?> new patrons by <?= $deadline ?></p>
|
||||||
</header>
|
</header>
|
||||||
<? } ?>
|
<? } ?>
|
||||||
<div class="progress">
|
<div class="progress">
|
||||||
<div aria-hidden="true">
|
<div aria-hidden="true">
|
||||||
<p class="start">0</p>
|
<p class="start">0</p>
|
||||||
<p><?= number_format($totalCurrent) ?>/<?= number_format($totalTarget) ?></p>
|
<p><?= number_format($donationDrive->DonationCount) ?>/<?= number_format($donationDrive->TargetDonationCount) ?></p>
|
||||||
<? if($stretchOn){ ?>
|
<? if($donationDrive->IsStretchEnabled){ ?>
|
||||||
<p class="stretch-base"><?= number_format($baseTarget) ?></p>
|
<p class="stretch-base"><?= number_format($donationDrive->BaseTargetDonationCount) ?></p>
|
||||||
<? } ?>
|
<? } ?>
|
||||||
<p class="target"><?= number_format($totalTarget) ?></p>
|
<p class="target"><?= number_format($donationDrive->TargetDonationCount) ?></p>
|
||||||
</div>
|
</div>
|
||||||
<progress max="<?= $baseTarget ?>" value="<?= $totalCurrent - $stretchCurrent ?>"></progress>
|
<progress max="<?= $donationDrive->TargetDonationCount ?>" value="<?= $donationDrive->DonationCount - $donationDrive->StretchDonationCount ?>"></progress>
|
||||||
<? if($stretchOn){ ?>
|
<? if($donationDrive->IsStretchEnabled){ ?>
|
||||||
<progress class="stretch" max="<?= $stretchTarget ?>" value="<?= $stretchCurrent ?>"></progress>
|
<progress class="stretch" max="<?= $donationDrive->StretchTargetDonationCount ?>" value="<?= $donationDrive->StretchDonationCount ?>"></progress>
|
||||||
<? } ?>
|
<? } ?>
|
||||||
</div>
|
</div>
|
||||||
<? if($stretchOn){ ?>
|
<? if($donationDrive->IsStretchEnabled){ ?>
|
||||||
<p>When we started this drive, we set a goal of <?= number_format($baseTarget) ?> Patrons Circle members by <?= $deadline ?>. Thanks to the incredible generosity of literature lovers like you, we hit that goal!</p>
|
<p>When we started this drive, we set a goal of <?= number_format($donationDrive->BaseTargetDonationCount) ?> Patrons Circle members by <?= $deadline ?>. Thanks to the incredible generosity of literature lovers like you, we hit that goal!</p>
|
||||||
<p>Since there’s still some time left in our drive, we thought we’d challenge our readers to help us reach our stretch goal of <?= number_format($totalTarget) ?> patrons, so that we can continue on a rock-solid financial footing. Will you help us with a donation, and support free and unrestricted digital literature?</p>
|
<p>Since there’s still some time left in our drive, we thought we’d challenge our readers to help us reach our stretch goal of <?= number_format($donationDrive->TargetDonationCount) ?> patrons, so that we can continue on a rock-solid financial footing. Will you help us with a donation, and support free and unrestricted digital literature?</p>
|
||||||
<? }else{ ?>
|
<? }else{ ?>
|
||||||
<p>It takes a huge amount of resources and highly-skilled work to create and distribute each of our free ebooks, and we need your support to keep it up. That’s why we want to welcome <?= number_format($baseTarget) ?> new patrons by <?= $deadline ?>. It’s our patrons who keep us on the stable financial footing we need to continue producing and giving away beautiful ebooks.</p>
|
<p>It takes a huge amount of resources and highly-skilled work to create and distribute each of our free ebooks, and we need your support to keep it up. That’s why we want to welcome <?= number_format($donationDrive->TargetDonationCount) ?> new patrons by <?= $deadline ?>. It’s our patrons who keep us on the stable financial footing we need to continue producing and giving away beautiful ebooks.</p>
|
||||||
<p>Will you become a patron, and support free and unrestricted digital literature?</p>
|
<p>Will you become a patron, and support free and unrestricted digital literature?</p>
|
||||||
<? } ?>
|
<? } ?>
|
||||||
<? if($showDonateButton){ ?>
|
<? if($showDonateButton){ ?>
|
||||||
|
|
|
@ -39,9 +39,9 @@ catch(Exceptions\CollectionNotFoundException){
|
||||||
<h1 class="is-collection"><?= $pageHeader ?></h1>
|
<h1 class="is-collection"><?= $pageHeader ?></h1>
|
||||||
<?= Template::DonationCounter() ?>
|
<?= Template::DonationCounter() ?>
|
||||||
<?= Template::DonationProgress() ?>
|
<?= Template::DonationProgress() ?>
|
||||||
<? if(!DONATION_DRIVE_ON && !DONATION_DRIVE_COUNTER_ON && DONATION_HOLIDAY_ALERT_ON){ ?>
|
|
||||||
<?= Template::DonationAlert() ?>
|
<?= Template::DonationAlert() ?>
|
||||||
<? } ?>
|
|
||||||
<p class="ebooks-toolbar">
|
<p class="ebooks-toolbar">
|
||||||
<a class="button" href="/collections/<?= Formatter::EscapeHtml($collection) ?>/downloads">Download collection</a>
|
<a class="button" href="/collections/<?= Formatter::EscapeHtml($collection) ?>/downloads">Download collection</a>
|
||||||
<a class="button" href="/collections/<?= Formatter::EscapeHtml($collection) ?>/feeds">Collection feeds</a>
|
<a class="button" href="/collections/<?= Formatter::EscapeHtml($collection) ?>/feeds">Collection feeds</a>
|
||||||
|
|
|
@ -2314,7 +2314,7 @@ h1 + ul.message,
|
||||||
.masthead ol li p{
|
.masthead ol li p{
|
||||||
margin-top: 0;
|
margin-top: 0;
|
||||||
text-align: left;
|
text-align: left;
|
||||||
display: inline-block; /* Prevent a p from breaking across column division */
|
display: inline-block; /* Prevent a `<p>` from breaking across column division */
|
||||||
}
|
}
|
||||||
|
|
||||||
.masthead hgroup * + *{
|
.masthead hgroup * + *{
|
||||||
|
@ -2326,7 +2326,6 @@ h1 + ul.message,
|
||||||
}
|
}
|
||||||
|
|
||||||
.donate aside{
|
.donate aside{
|
||||||
border-top: 1px dashed var(--sub-text);
|
|
||||||
margin-top: 1rem;
|
margin-top: 1rem;
|
||||||
padding-top: 1rem;
|
padding-top: 1rem;
|
||||||
font-style: italic;
|
font-style: italic;
|
||||||
|
|
|
@ -129,15 +129,21 @@ catch(Exceptions\EbookNotFoundException){
|
||||||
<h2><a property="schema:author" typeof="schema:Person" href="<?= Formatter::EscapeHtml($ebook->AuthorsUrl) ?>" resource="<?= '/ebooks/' . $author->UrlName ?>">
|
<h2><a property="schema:author" typeof="schema:Person" href="<?= Formatter::EscapeHtml($ebook->AuthorsUrl) ?>" resource="<?= '/ebooks/' . $author->UrlName ?>">
|
||||||
<span property="schema:name"><?= Formatter::EscapeHtml($author->Name) ?></span>
|
<span property="schema:name"><?= Formatter::EscapeHtml($author->Name) ?></span>
|
||||||
<meta property="schema:url" content="<?= SITE_URL . Formatter::EscapeHtml($ebook->AuthorsUrl) ?>"/>
|
<meta property="schema:url" content="<?= SITE_URL . Formatter::EscapeHtml($ebook->AuthorsUrl) ?>"/>
|
||||||
<? if($author->NacoafUrl){ ?><meta property="schema:sameAs" content="<?= Formatter::EscapeHtml($author->NacoafUrl) ?>"/><? } ?>
|
<? if($author->NacoafUrl){ ?>
|
||||||
<? if($author->WikipediaUrl){ ?><meta property="schema:sameAs" content="<?= Formatter::EscapeHtml($author->WikipediaUrl) ?>"/><? } ?>
|
<meta property="schema:sameAs" content="<?= Formatter::EscapeHtml($author->NacoafUrl) ?>"/>
|
||||||
|
<? } ?>
|
||||||
|
<? if($author->WikipediaUrl){ ?>
|
||||||
|
<meta property="schema:sameAs" content="<?= Formatter::EscapeHtml($author->WikipediaUrl) ?>"/>
|
||||||
|
<? } ?>
|
||||||
</a>
|
</a>
|
||||||
</h2>
|
</h2>
|
||||||
<? } ?>
|
<? } ?>
|
||||||
<? } ?>
|
<? } ?>
|
||||||
</hgroup>
|
</hgroup>
|
||||||
<picture>
|
<picture>
|
||||||
<? if($ebook->HeroImage2xAvifUrl !== null){ ?><source srcset="<?= $ebook->HeroImage2xAvifUrl ?> 2x, <?= $ebook->HeroImageAvifUrl ?> 1x" type="image/avif"/><? } ?>
|
<? if($ebook->HeroImage2xAvifUrl !== null){ ?>
|
||||||
|
<source srcset="<?= $ebook->HeroImage2xAvifUrl ?> 2x, <?= $ebook->HeroImageAvifUrl ?> 1x" type="image/avif"/>
|
||||||
|
<? } ?>
|
||||||
<source srcset="<?= $ebook->HeroImage2xUrl ?> 2x, <?= $ebook->HeroImageUrl ?> 1x" type="image/jpg"/>
|
<source srcset="<?= $ebook->HeroImage2xUrl ?> 2x, <?= $ebook->HeroImageUrl ?> 1x" type="image/jpg"/>
|
||||||
<img src="<?= $ebook->HeroImage2xUrl ?>" alt="" height="439" width="1318" />
|
<img src="<?= $ebook->HeroImage2xUrl ?>" alt="" height="439" width="1318" />
|
||||||
</picture>
|
</picture>
|
||||||
|
@ -169,9 +175,9 @@ catch(Exceptions\EbookNotFoundException){
|
||||||
<h2>Description</h2>
|
<h2>Description</h2>
|
||||||
<?= Template::DonationCounter() ?>
|
<?= Template::DonationCounter() ?>
|
||||||
<?= Template::DonationProgress() ?>
|
<?= Template::DonationProgress() ?>
|
||||||
<? if(!DONATION_DRIVE_ON && !DONATION_DRIVE_COUNTER_ON && DONATION_ALERT_ON){ ?>
|
|
||||||
<?= Template::DonationAlert() ?>
|
<?= Template::DonationAlert() ?>
|
||||||
<? } ?>
|
|
||||||
<? if($ebook->LongDescription === null){ ?>
|
<? if($ebook->LongDescription === null){ ?>
|
||||||
<p><i>There’s no description for this ebook yet.</i></p>
|
<p><i>There’s no description for this ebook yet.</i></p>
|
||||||
<? }else{ ?>
|
<? }else{ ?>
|
||||||
|
@ -340,11 +346,16 @@ catch(Exceptions\EbookNotFoundException){
|
||||||
<? foreach($transcriptionSources as $source){ ?>
|
<? foreach($transcriptionSources as $source){ ?>
|
||||||
<li>
|
<li>
|
||||||
<p>
|
<p>
|
||||||
<? if($source->Type == EbookSourceType::ProjectGutenberg){ ?><a href="<?= Formatter::EscapeHtml($source->Url) ?>" class="project-gutenberg">Transcription at Project Gutenberg</a>
|
<? if($source->Type == EbookSourceType::ProjectGutenberg){ ?>
|
||||||
<? }elseif($source->Type == EbookSourceType::ProjectGutenbergAustralia){ ?><a href="<?= Formatter::EscapeHtml($source->Url) ?>" class="project-gutenberg">Transcription at Project Gutenberg Australia</a>
|
<a href="<?= Formatter::EscapeHtml($source->Url) ?>" class="project-gutenberg">Transcription at Project Gutenberg</a>
|
||||||
<? }elseif($source->Type == EbookSourceType::ProjectGutenbergCanada){ ?><a href="<?= Formatter::EscapeHtml($source->Url) ?>" class="project-gutenberg">Transcription at Project Gutenberg Canada</a>
|
<? }elseif($source->Type == EbookSourceType::ProjectGutenbergAustralia){ ?>
|
||||||
<? }elseif($source->Type == EbookSourceType::Wikisource){ ?><a href="<?= Formatter::EscapeHtml($source->Url) ?>" class="wikisource">Transcription at Wikisource</a>
|
<a href="<?= Formatter::EscapeHtml($source->Url) ?>" class="project-gutenberg">Transcription at Project Gutenberg Australia</a>
|
||||||
<? }elseif($source->Type == EbookSourceType::FadedPage){ ?><a href="<?= Formatter::EscapeHtml($source->Url) ?>" class="globe">Transcription at Faded Page</a>
|
<? }elseif($source->Type == EbookSourceType::ProjectGutenbergCanada){ ?>
|
||||||
|
<a href="<?= Formatter::EscapeHtml($source->Url) ?>" class="project-gutenberg">Transcription at Project Gutenberg Canada</a>
|
||||||
|
<? }elseif($source->Type == EbookSourceType::Wikisource){ ?>
|
||||||
|
<a href="<?= Formatter::EscapeHtml($source->Url) ?>" class="wikisource">Transcription at Wikisource</a>
|
||||||
|
<? }elseif($source->Type == EbookSourceType::FadedPage){ ?>
|
||||||
|
<a href="<?= Formatter::EscapeHtml($source->Url) ?>" class="globe">Transcription at Faded Page</a>
|
||||||
<? }else{?>
|
<? }else{?>
|
||||||
<a href="<?= Formatter::EscapeHtml($source->Url) ?>" class="globe">Transcription</a>
|
<a href="<?= Formatter::EscapeHtml($source->Url) ?>" class="globe">Transcription</a>
|
||||||
<? } ?>
|
<? } ?>
|
||||||
|
@ -361,10 +372,15 @@ catch(Exceptions\EbookNotFoundException){
|
||||||
<? foreach($scanSources as $source){ ?>
|
<? foreach($scanSources as $source){ ?>
|
||||||
<li>
|
<li>
|
||||||
<p>
|
<p>
|
||||||
<? if($source->Type == EbookSourceType::InternetArchive){ ?><a href="<?= Formatter::EscapeHtml($source->Url) ?>" class="internet-archive">Page scans at the Internet Archive</a>
|
<? if($source->Type == EbookSourceType::InternetArchive){ ?>
|
||||||
<? }elseif($source->Type == EbookSourceType::HathiTrust){ ?><a href="<?= Formatter::EscapeHtml($source->Url) ?>" class="hathitrust">Page scans at HathiTrust</a>
|
<a href="<?= Formatter::EscapeHtml($source->Url) ?>" class="internet-archive">Page scans at the Internet Archive</a>
|
||||||
<? }elseif($source->Type == EbookSourceType::GoogleBooks){ ?><a href="<?= Formatter::EscapeHtml($source->Url) ?>" class="google">Page scans at Google Books</a>
|
<? }elseif($source->Type == EbookSourceType::HathiTrust){ ?>
|
||||||
<? }else{ ?><a href="<?= Formatter::EscapeHtml($source->Url) ?>" class="globe">Page scans</a><? } ?>
|
<a href="<?= Formatter::EscapeHtml($source->Url) ?>" class="hathitrust">Page scans at HathiTrust</a>
|
||||||
|
<? }elseif($source->Type == EbookSourceType::GoogleBooks){ ?>
|
||||||
|
<a href="<?= Formatter::EscapeHtml($source->Url) ?>" class="google">Page scans at Google Books</a>
|
||||||
|
<? }else{ ?>
|
||||||
|
<a href="<?= Formatter::EscapeHtml($source->Url) ?>" class="globe">Page scans</a>
|
||||||
|
<? } ?>
|
||||||
</p>
|
</p>
|
||||||
</li>
|
</li>
|
||||||
<? } ?>
|
<? } ?>
|
||||||
|
@ -392,7 +408,9 @@ catch(Exceptions\EbookNotFoundException){
|
||||||
<h2>Improve this ebook</h2>
|
<h2>Improve this ebook</h2>
|
||||||
<p>Anyone can contribute to make a Standard Ebook better for everyone!</p>
|
<p>Anyone can contribute to make a Standard Ebook better for everyone!</p>
|
||||||
<p>To report typos, typography errors, or other corrections, see <a href="/contribute/report-errors">how to report errors</a>.</p>
|
<p>To report typos, typography errors, or other corrections, see <a href="/contribute/report-errors">how to report errors</a>.</p>
|
||||||
<? if($ebook->GitHubUrl !== null){ ?><p>If you’re comfortable with technology and want to contribute directly, check out <a href="<?= Formatter::EscapeHtml($ebook->GitHubUrl) ?>">this ebook’s GitHub repository</a> and our <a href="/contribute">contributors section</a>.</p><? } ?>
|
<? if($ebook->GitHubUrl !== null){ ?>
|
||||||
|
<p>If you’re comfortable with technology and want to contribute directly, check out <a href="<?= Formatter::EscapeHtml($ebook->GitHubUrl) ?>">this ebook’s GitHub repository</a> and our <a href="/contribute">contributors section</a>.</p>
|
||||||
|
<? } ?>
|
||||||
<p>You can also <a href="/donate">donate to Standard Ebooks</a> to help fund continuing improvement of this and other ebooks.</p>
|
<p>You can also <a href="/donate">donate to Standard Ebooks</a> to help fund continuing improvement of this and other ebooks.</p>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
|
|
@ -109,9 +109,9 @@ catch(Exceptions\AppException $ex){
|
||||||
<h1><?= $pageHeader ?></h1>
|
<h1><?= $pageHeader ?></h1>
|
||||||
<?= Template::DonationCounter() ?>
|
<?= Template::DonationCounter() ?>
|
||||||
<?= Template::DonationProgress() ?>
|
<?= Template::DonationProgress() ?>
|
||||||
<? if(!DONATION_DRIVE_ON && !DONATION_DRIVE_COUNTER_ON && DONATION_HOLIDAY_ALERT_ON){ ?>
|
|
||||||
<?= Template::DonationAlert() ?>
|
<?= 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){ ?>
|
<? 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>
|
<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>
|
||||||
|
@ -123,7 +123,9 @@ catch(Exceptions\AppException $ex){
|
||||||
<a<? if($page > 1){ ?> href="/ebooks?page=<?= $page - 1 ?><? if($queryStringWithoutPage != ''){ ?>&<?= Formatter::EscapeHtml($queryStringWithoutPage) ?><? } ?>" rel="prev"<? }else{ ?> aria-disabled="true"<? } ?>>Back</a>
|
<a<? if($page > 1){ ?> href="/ebooks?page=<?= $page - 1 ?><? if($queryStringWithoutPage != ''){ ?>&<?= Formatter::EscapeHtml($queryStringWithoutPage) ?><? } ?>" rel="prev"<? }else{ ?> aria-disabled="true"<? } ?>>Back</a>
|
||||||
<ol>
|
<ol>
|
||||||
<? for($i = 1; $i < $pages + 1; $i++){ ?>
|
<? for($i = 1; $i < $pages + 1; $i++){ ?>
|
||||||
<li<? if($page == $i){ ?> class="highlighted"<? } ?>><a href="/ebooks?page=<?= $i ?><? if($queryStringWithoutPage != ''){ ?>&<?= Formatter::EscapeHtml($queryStringWithoutPage) ?><? } ?>"><?= $i ?></a></li>
|
<li<? if($page == $i){ ?> class="highlighted"<? } ?>>
|
||||||
|
<a href="/ebooks?page=<?= $i ?><? if($queryStringWithoutPage != ''){ ?>&<?= Formatter::EscapeHtml($queryStringWithoutPage) ?><? } ?>"><?= $i ?></a>
|
||||||
|
</li>
|
||||||
<? } ?>
|
<? } ?>
|
||||||
</ol>
|
</ol>
|
||||||
<a<? if($page < ceil($totalEbooks / $perPage)){ ?> href="/ebooks?page=<?= $page + 1 ?><? if($queryStringWithoutPage != ''){ ?>&<?= Formatter::EscapeHtml($queryStringWithoutPage) ?><? } ?>" rel="next"<? }else{ ?> aria-disabled="true"<? } ?>>Next</a>
|
<a<? if($page < ceil($totalEbooks / $perPage)){ ?> href="/ebooks?page=<?= $page + 1 ?><? if($queryStringWithoutPage != ''){ ?>&<?= Formatter::EscapeHtml($queryStringWithoutPage) ?><? } ?>" rel="next"<? }else{ ?> aria-disabled="true"<? } ?>>Next</a>
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue