mirror of
https://github.com/standardebooks/web.git
synced 2025-07-12 01:22:23 -04:00
Add public domain day banner
This commit is contained in:
parent
1b604ca97f
commit
c65035630f
16 changed files with 293 additions and 105 deletions
|
@ -1,9 +1,10 @@
|
||||||
<?
|
<?
|
||||||
// Auto-included by Composer in composer.json to satisfy PHPStan.
|
// Auto-included by Composer in `composer.json` to satisfy PHPStan.
|
||||||
use Safe\DateTimeImmutable;
|
use Safe\DateTimeImmutable;
|
||||||
use function Safe\define;
|
use function Safe\define;
|
||||||
|
|
||||||
const NOW = new DateTimeImmutable();
|
const NOW = new DateTimeImmutable();
|
||||||
|
const LATEST_CONTINENTAL_US_TZ = new DateTimeZone('America/Juneau');
|
||||||
|
|
||||||
const SITE_STATUS_LIVE = 'live';
|
const SITE_STATUS_LIVE = 'live';
|
||||||
const SITE_STATUS_DEV = 'dev';
|
const SITE_STATUS_DEV = 'dev';
|
||||||
|
@ -76,7 +77,7 @@ const ZOHO_WEBHOOK_LOG_FILE_PATH = '/var/log/local/webhooks-zoho.log'; // Must b
|
||||||
const DONATIONS_LOG_FILE_PATH = '/var/log/local/donations.log'; // Must be writable by `www-data` Unix user.
|
const DONATIONS_LOG_FILE_PATH = '/var/log/local/donations.log'; // Must be writable by `www-data` Unix user.
|
||||||
const ARTWORK_UPLOADS_LOG_FILE_PATH = '/var/log/local/artwork-uploads.log'; // Must be writable by `www-data` Unix user.
|
const ARTWORK_UPLOADS_LOG_FILE_PATH = '/var/log/local/artwork-uploads.log'; // Must be writable by `www-data` Unix user.
|
||||||
|
|
||||||
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', LATEST_CONTINENTAL_US_TZ))->format('Y')) - 96);
|
||||||
define('PD_STRING', 'January 1, ' . (PD_YEAR + 1));
|
define('PD_STRING', 'January 1, ' . (PD_YEAR + 1));
|
||||||
|
|
||||||
// Controls the progress bar donation dialog.
|
// Controls the progress bar donation dialog.
|
||||||
|
@ -100,8 +101,8 @@ const DONATION_DRIVE_DATES = [
|
||||||
|
|
||||||
// Controls the countdown donation dialog, basically unused right now.
|
// Controls the countdown donation dialog, basically unused right now.
|
||||||
const DONATION_DRIVE_COUNTER_ENABLED = false;
|
const DONATION_DRIVE_COUNTER_ENABLED = 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', new DateTimeZone('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', new DateTimeZone('America/New_York'));
|
||||||
|
|
||||||
const PD_DAY_YEAR = 2025;
|
const PD_DAY_YEAR = 2025;
|
||||||
const PD_DAY_DRAFT_PATH = '/standardebooks.org/drafts/' . PD_DAY_YEAR;
|
const PD_DAY_DRAFT_PATH = '/standardebooks.org/drafts/' . PD_DAY_YEAR;
|
||||||
|
|
8
lib/Enums/ColorSchemeType.php
Normal file
8
lib/Enums/ColorSchemeType.php
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
<?
|
||||||
|
namespace Enums;
|
||||||
|
|
||||||
|
enum ColorSchemeType: string{
|
||||||
|
case Auto = 'auto';
|
||||||
|
case Dark = 'dark';
|
||||||
|
case Light = 'light';
|
||||||
|
}
|
|
@ -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_ENABLED || ($autoHide ?? $_COOKIE['hide-donation-alert'] ?? false) || NOW > DONATION_DRIVE_COUNTER_END){
|
if(!DONATION_DRIVE_COUNTER_ENABLED || ($autoHide ?? (HttpInput::Bool(COOKIE, 'hide-donation-alert') ?? false)) || NOW > DONATION_DRIVE_COUNTER_END){
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -42,6 +42,7 @@ $digits = str_split(str_pad((string)$current, 3, "0", STR_PAD_LEFT))
|
||||||
<aside class="donation counter closable">
|
<aside class="donation counter closable">
|
||||||
<? if($autoHide){ ?>
|
<? if($autoHide){ ?>
|
||||||
<form action="/settings" method="<?= Enums\HttpMethod::Post->value ?>">
|
<form action="/settings" method="<?= Enums\HttpMethod::Post->value ?>">
|
||||||
|
<input type="hidden" name="_method" value="<?= Enums\HttpMethod::Patch->value ?>" />
|
||||||
<input type="hidden" name="hide-donation-alert" value="true" />
|
<input type="hidden" name="hide-donation-alert" value="true" />
|
||||||
<button class="close" title="Close this box">Close this box</button>
|
<button class="close" title="Close this box">Close this box</button>
|
||||||
</form>
|
</form>
|
||||||
|
|
|
@ -4,7 +4,7 @@ $donationDrive = DonationDrive::GetByIsRunning();
|
||||||
if(
|
if(
|
||||||
!DONATION_DRIVES_ENABLED // Drives aren't enabled.
|
!DONATION_DRIVES_ENABLED // Drives aren't enabled.
|
||||||
||
|
||
|
||||||
($autoHide ?? $_COOKIE['hide-donation-alert'] ?? false) // If the user has hidden the box.
|
($autoHide ?? (HttpInput::Bool(COOKIE, 'hide-donation-alert') ?? false)) // If the user has hidden the box.
|
||||||
||
|
||
|
||||||
Session::$User !== null // If a user is logged in.
|
Session::$User !== null // If a user is logged in.
|
||||||
||
|
||
|
||||||
|
@ -44,6 +44,7 @@ else{
|
||||||
<aside class="donation closable">
|
<aside class="donation closable">
|
||||||
<? if($autoHide){ ?>
|
<? if($autoHide){ ?>
|
||||||
<form action="/settings" method="<?= Enums\HttpMethod::Post->value ?>">
|
<form action="/settings" method="<?= Enums\HttpMethod::Post->value ?>">
|
||||||
|
<input type="hidden" name="_method" value="<?= Enums\HttpMethod::Patch->value ?>" />
|
||||||
<input type="hidden" name="hide-donation-alert" value="true" />
|
<input type="hidden" name="hide-donation-alert" value="true" />
|
||||||
<button class="close" title="Close this box">Close this box</button>
|
<button class="close" title="Close this box">Close this box</button>
|
||||||
</form>
|
</form>
|
||||||
|
|
|
@ -1,11 +1,12 @@
|
||||||
<?
|
<?
|
||||||
|
use Safe\DateTimeImmutable;
|
||||||
use function Safe\filemtime;
|
use function Safe\filemtime;
|
||||||
|
|
||||||
$title = $title ?? '';
|
$title = $title ?? '';
|
||||||
$highlight = $highlight ?? '';
|
$highlight = $highlight ?? '';
|
||||||
$description = $description ?? '';
|
$description = $description ?? '';
|
||||||
$manual = $manual ?? false;
|
$manual = $manual ?? false;
|
||||||
$colorScheme = $_COOKIE['color-scheme'] ?? 'auto';
|
$colorScheme = Enums\ColorSchemeType::tryFrom(HttpInput::Str(COOKIE, 'color-scheme') ?? Enums\ColorSchemeType::Auto->value);
|
||||||
$isXslt = $isXslt ?? false;
|
$isXslt = $isXslt ?? false;
|
||||||
$feedUrl = $feedUrl ?? null;
|
$feedUrl = $feedUrl ?? null;
|
||||||
$feedTitle = $feedTitle ?? '';
|
$feedTitle = $feedTitle ?? '';
|
||||||
|
@ -13,6 +14,7 @@ $isErrorPage = $isErrorPage ?? false;
|
||||||
$downloadUrl = $downloadUrl ?? null;
|
$downloadUrl = $downloadUrl ?? null;
|
||||||
$canonicalUrl = $canonicalUrl ?? null;
|
$canonicalUrl = $canonicalUrl ?? null;
|
||||||
$css = $css ?? [];
|
$css = $css ?? [];
|
||||||
|
$showPublicDomainDayBanner = new DateTimeImmutable('now', LATEST_CONTINENTAL_US_TZ) < 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.
|
// 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.
|
||||||
// So, we try to detect Safari here, and don't use multiple `<source>` if we find Safari.
|
// So, we try to detect Safari here, and don't use multiple `<source>` if we find Safari.
|
||||||
|
@ -45,14 +47,14 @@ if(!$isXslt){
|
||||||
<link href="/css/ereader.css?version=<?= filemtime(WEB_ROOT . '/css/ereader.css') ?>" media="screen" rel="stylesheet" type="text/css"/>
|
<link href="/css/ereader.css?version=<?= filemtime(WEB_ROOT . '/css/ereader.css') ?>" media="screen" rel="stylesheet" type="text/css"/>
|
||||||
<? }else{ ?>
|
<? }else{ ?>
|
||||||
<link href="/css/core.css?version=<?= filemtime(WEB_ROOT . '/css/core.css') ?>" media="screen" rel="stylesheet" type="text/css"/>
|
<link href="/css/core.css?version=<?= filemtime(WEB_ROOT . '/css/core.css') ?>" media="screen" rel="stylesheet" type="text/css"/>
|
||||||
<? if($colorScheme == 'auto' || $colorScheme == 'dark'){ ?>
|
<? if($colorScheme == Enums\ColorSchemeType::Auto || $colorScheme == Enums\ColorSchemeType::Dark){ ?>
|
||||||
<link href="/css/dark.css?version=<?= filemtime(WEB_ROOT . '/css/dark.css') ?>" media="screen<? if($colorScheme == 'auto'){ ?> and (prefers-color-scheme: dark)<? } ?>" rel="stylesheet" type="text/css"/>
|
<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($manual){ ?>
|
||||||
<link href="/css/manual.css?version=<?= filemtime(WEB_ROOT . '/css/manual.css') ?>" media="screen" rel="stylesheet" type="text/css"/>
|
<link href="/css/manual.css?version=<?= filemtime(WEB_ROOT . '/css/manual.css') ?>" media="screen" rel="stylesheet" type="text/css"/>
|
||||||
<? if($colorScheme == 'auto' || $colorScheme == 'dark'){ ?>
|
<? 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 == 'auto'){ ?> and (prefers-color-scheme: dark)<? } ?>" rel="stylesheet" type="text/css"/>
|
<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"/>
|
||||||
<? } ?>
|
<? } ?>
|
||||||
<? } ?>
|
<? } ?>
|
||||||
<? foreach($css as $url){ ?>
|
<? foreach($css as $url){ ?>
|
||||||
|
@ -95,6 +97,27 @@ if(!$isXslt){
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<header>
|
<header>
|
||||||
|
<? if($showPublicDomainDayBanner){ ?>
|
||||||
|
<div class="public-domain-day-banner">
|
||||||
|
<div id="confettis">
|
||||||
|
<div class="confetti"></div>
|
||||||
|
<div class="confetti"></div>
|
||||||
|
<div class="confetti"></div>
|
||||||
|
<div class="confetti"></div>
|
||||||
|
<div class="confetti"></div>
|
||||||
|
<div class="confetti"></div>
|
||||||
|
<div class="confetti"></div>
|
||||||
|
<div class="confetti"></div>
|
||||||
|
<div class="confetti"></div>
|
||||||
|
</div>
|
||||||
|
<strong>Happy Public Domain Day <?= PD_DAY_YEAR ?>!</strong> <a href="/blog/public-domain-day-<?= PD_DAY_YEAR ?>">See what new literature is free to read starting January 1.</a>
|
||||||
|
<form action="/settings" method="<?= Enums\HttpMethod::Post->value ?>">
|
||||||
|
<input type="hidden" name="_method" value="<?= Enums\HttpMethod::Patch->value ?>" />
|
||||||
|
<input type="hidden" name="hide-public-domain-day-banner" value="true" />
|
||||||
|
<button class="close" title="Hide this banner">Hide this banner</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<? } ?>
|
||||||
<a href="/">Standard Ebooks</a>
|
<a href="/">Standard Ebooks</a>
|
||||||
<nav>
|
<nav>
|
||||||
<ul>
|
<ul>
|
||||||
|
|
|
@ -26,99 +26,86 @@ $ebooksWithDescriptions = [];
|
||||||
|
|
||||||
foreach($ebooks as $ebook){
|
foreach($ebooks as $ebook){
|
||||||
$description = '';
|
$description = '';
|
||||||
$order = 0;
|
|
||||||
|
|
||||||
switch($ebook->Identifier){
|
switch($ebook->Identifier){
|
||||||
case 'url:https://standardebooks.org/ebooks/william-faulkner/the-sound-and-the-fury':
|
case 'url:https://standardebooks.org/ebooks/william-faulkner/the-sound-and-the-fury':
|
||||||
$description = '<p>Faulkner’s widely-acclaimed masterpiece is well-known in America, as it’s taught in high schools across the country. In it we follow the Compson family, an aristocratic family in Mississippi, and their slow decline into poverty and ruin. What makes the novel so special—and what lends it its reputation as a challenging read—is its stream-of-consciousness style, in which Faulkner attempts to narrate the characters’ thoughts directly to the reader.</p><p><i>The Sound and the Fury</i> was an essential step in developing that modernist prose style, and is still considered to be one of the greatest works of American literature.</p>';
|
$description = '<p>Faulkner’s widely-acclaimed masterpiece is well-known in America, as it’s taught in high schools across the country. In it we follow the Compson family, an aristocratic family in Mississippi, and their slow decline into poverty and ruin. What makes the novel so special—and what lends it its reputation as a challenging read—is its stream-of-consciousness style, in which Faulkner attempts to narrate the characters’ thoughts directly to the reader.</p><p><i>The Sound and the Fury</i> was an essential step in developing that modernist prose style, and is still considered to be one of the greatest works of American literature.</p>';
|
||||||
$order = 0;
|
|
||||||
break;
|
break;
|
||||||
case 'url:https://standardebooks.org/ebooks/erich-maria-remarque/all-quiet-on-the-western-front/a-w-wheen':
|
case 'url:https://standardebooks.org/ebooks/erich-maria-remarque/all-quiet-on-the-western-front/a-w-wheen':
|
||||||
$description = '<p>One of the greatest war novels to be born from any conflict, Erich Maria Remarque’s grisly tale of the brutality and horror of the German trenches during the Great War was so powerful that it earned him nominations for the Nobel Prizes in <em>both</em> Literature and Peace, and was soon widely banned in a Europe that was preparing for a second cataclysmic conflict.</p>';
|
$description = '<p>One of the greatest war novels to come from any conflict, Erich Maria Remarque’s grisly tale of the brutality and horror of the German trenches during the Great War was so powerful that it earned him nominations for the Nobel Prizes in <em>both</em> Literature and Peace, and was soon widely banned in a Europe that was preparing for a second cataclysmic conflict.</p>';
|
||||||
$order = 1;
|
|
||||||
break;
|
break;
|
||||||
case 'url:https://standardebooks.org/ebooks/ernest-hemingway/a-farewell-to-arms':
|
case 'url:https://standardebooks.org/ebooks/ernest-hemingway/a-farewell-to-arms':
|
||||||
$description = '<p>Called “the premier American war novel from World War I,” Hemingway’s semi-autobiographical story of an American ambulance driver serving in the Italian front, and a bright and cynical British nurse, cemented his reputation as one of the generation’s foremost literary figures. Unlike Remarque, whose <i>All Quite on the Western Front</i> paints the war’s destruction in full color, Hemingway’s story is one of the mundanity of war—of the quotidien smallness that underpins even the most horrific of events.</p>';
|
$description = '<p>Called “the premier American war novel from World War I,” Hemingway’s semi-autobiographical story of an American ambulance driver serving in the Italian front, and a bright and cynical British nurse, cemented his reputation as one of the generation’s foremost literary figures. Unlike Remarque, whose <i>All Quite on the Western Front</i> paints the war’s destruction in full color, Hemingway’s story is one of the mundanity of war—of the quotidian smallness that underpins even the most horrific of events.</p>';
|
||||||
$order = 2;
|
|
||||||
break;
|
break;
|
||||||
case 'url:https://standardebooks.org/ebooks/john-steinbeck/cup-of-gold':
|
case 'url:https://standardebooks.org/ebooks/john-steinbeck/cup-of-gold':
|
||||||
$description = '<p><i>Cup of Gold</i> is John Steinbeck’s first novel, the swashbuckling story of the real-life Captain Morgan, legendary pirate. Little of Morgan’s actual life is known, but Steinbeck fills in the blanks of this rich historical fiction with rich portraits of Caribbean ports and flourishes of magical realism.</p><p>While one might expect a rollicking tale of adventure, the novel is actually a deep meditation on the pursuit of—and inability to find—true happiness, and its skillful craft deftly foreshadows Steinbeck’s later ascenion to literary titanhood.</p>';
|
$description = '<p><i>Cup of Gold</i> is John Steinbeck’s first novel, the swashbuckling story of the real-life Captain Morgan, legendary pirate. Little of Morgan’s actual life is known, but Steinbeck fills in the blanks of this rich historical fiction with rich portraits of Caribbean ports and flourishes of magical realism.</p><p>While one might expect a rollicking tale of adventure, the novel is actually a deep meditation on the pursuit of—and inability to find—true happiness, and its skillful craft deftly foreshadows Steinbeck’s later ascension to literary titanhood.</p>';
|
||||||
$order = 3;
|
|
||||||
break;
|
break;
|
||||||
case 'url:https://standardebooks.org/ebooks/dashiell-hammett/red-harvest':
|
case 'url:https://standardebooks.org/ebooks/dashiell-hammett/red-harvest':
|
||||||
$description = '<p><i>Red Harvest</i> is Dashiell Hammet’s first full-length novel to feature the <a href="/collections/continental-op">Continental Op</a>, the nameless, hard-drinking, cynical private eye that single-handedly created the archetype of the hard-boiled detective. The novel is a fast-paced, tightly-written murder thriller that simultaneously touches all the bases of, and <em>defines</em>, the classic noir style that was much-imitated, and later much-spoofed, in the following century.</p>';
|
$description = '<p><i>Red Harvest</i> is Dashiell Hammet’s first full-length novel to feature the <a href="/collections/continental-op">Continental Op</a>, the nameless, hard-drinking, cynical private eye that single-handedly created the archetype of the hard-boiled detective. The novel is a fast-paced, tightly-written murder thriller that simultaneously touches all the bases of, and <em>defines</em>, the classic noir style that was much-imitated, and later much-spoofed, in the following century.</p>';
|
||||||
$order = 4;
|
|
||||||
break;
|
break;
|
||||||
case 'url:https://standardebooks.org/ebooks/sinclair-lewis/dodsworth':
|
case 'url:https://standardebooks.org/ebooks/sinclair-lewis/dodsworth':
|
||||||
$description = '<p>Samuel Dodsworth is a successful automobile executive who decides to retire early. His younger wife Fran wants to tour Europe, so the two embark on a trip. But soon after they arrive, Fran becomes enchanted by the whirlwind of culture and old-world society, while Samuel, a mild-mannered, down-to-earth Midwesterner, yearns to escape the pretentiousness and return to the quiet stability of home. The novel explores the slow breakdown of their marriage while deftly satirizing American midde-class mores and the wide gulf between them and European culture.</p>';
|
$description = '<p>Samuel Dodsworth is a successful automobile executive who decides to retire early. His younger wife Fran wants to tour Europe, so the two embark on a trip. But soon after they arrive, Fran becomes enchanted by the whirlwind of culture and old-world society, while Samuel, a mild-mannered, down-to-earth Midwesterner, yearns to escape the pretentiousness and return to the quiet stability of home. The novel explores the slow breakdown of their marriage while deftly satirizing American middle-class mores and the wide gulf between them and European culture.</p>';
|
||||||
$order = 5;
|
|
||||||
break;
|
break;
|
||||||
case 'url:https://standardebooks.org/ebooks/oliver-la-farge/laughing-boy':
|
case 'url:https://standardebooks.org/ebooks/oliver-la-farge/laughing-boy':
|
||||||
$description = '<p><i>Laughing Boy</i> is the story of the titular main character, a young Navajo man living in the American Southwest around the turn of the 20th century. He meets the fiery young Slim Girl at a tribal meet, but her reputation precedes her, and the tribe disapproves of their union. Ignoring the advice of the tribe, the two start a life together as they try to keep ancient traditions alive in the face of the rapidly-encroaching modernization of the American Southwest.</p><p><i>Laughing Boy</i> was the recipient of the 1930 <a href="/collections/pulitzer-prize-for-fiction-winners">Pulitzer Prize for Fiction</a>.</p>';
|
$description = '<p><i>Laughing Boy</i>, the winner of the 1930 <a href="/collections/pulitzer-prize-for-fiction-winners">Pulitzer Prize for Fiction</a>, is the story of the titular main character, a young Navajo man living in the American Southwest around the turn of the 20th century. He meets the fiery young Slim Girl at a tribal meet, but her reputation precedes her, and the tribe disapproves of their union. Ignoring the advice of the tribe, the two start a life together as they try to keep ancient traditions alive in the face of the rapidly-encroaching modernization of the American Southwest.</p>';
|
||||||
$order = 6;
|
|
||||||
break;
|
break;
|
||||||
case 'url:https://standardebooks.org/ebooks/graham-greene/the-man-within':
|
case 'url:https://standardebooks.org/ebooks/graham-greene/the-man-within':
|
||||||
$description = '<p><i>The Man Within</i> is acclaimed novelist Graham Greene’s first novel. Set against the backdrop of the English countryside,, the novel explores themes of guilt, redemption, the nature of courage and cowardice, and the complex relationship between one’s inner beliefs and outward actions.</p>';
|
$description = '<p><i>The Man Within</i> is acclaimed novelist Graham Greene’s first novel. Set against the backdrop of the English countryside,, the novel explores themes of guilt, redemption, the nature of courage and cowardice, and the complex relationship between one’s inner beliefs and outward actions.</p>';
|
||||||
$order = 7;
|
|
||||||
break;
|
break;
|
||||||
case 'url:https://standardebooks.org/ebooks/calvin-coolidge/the-autobiography-of-calvin-coolidge':
|
case 'url:https://standardebooks.org/ebooks/calvin-coolidge/the-autobiography-of-calvin-coolidge':
|
||||||
$description = '<p>Calvin Coolidge was the 30th president of the United States, entering the office as vice president when president Warren G. Harding suddenly passed, and winning a reelection term. Even though he was hugely popular, he declined running for a second full term, opting to retire instead. In this autobiography—which is as brief as “Silent Cal’s” legend suggests it might be—we follow the former president from his idyllic boyhood in Vermont, to a career in the law, to the governership of Massachussetts, to the presidency and beyond.</p>';
|
$description = '<p>Calvin Coolidge was the 30th president of the United States, entering the office as vice president when president Warren G. Harding suddenly passed, and winning a reelection term. Even though he was hugely popular, he declined running for a second full term, opting to retire instead. In this autobiography—which is as brief as “Silent Cal’s” legend suggests it might be—we follow the former president from his idyllic boyhood in Vermont, to a career in the law, to the governorship of Massachusetts, to the presidency and beyond.</p>';
|
||||||
$order = 8;
|
|
||||||
break;
|
break;
|
||||||
case 'url:https://standardebooks.org/ebooks/lloyd-c-douglas/magnificent-obsession':
|
case 'url:https://standardebooks.org/ebooks/lloyd-c-douglas/magnificent-obsession':
|
||||||
$description = '<p>Robert Merrick, a young man from a wealthy family, accidentally causes the death of an esteemed neurosurgeon. Wracked by guilt, Robert decides to devote his own life to improving the life of others.</p><p><i>Magnificent Obsession</i> was a hugely popular work in its time, inspiring a blockbuster 1935 film of the same name.</p>';
|
$description = '<p>Robert Merrick, a young man from a wealthy family, accidentally causes the death of an esteemed neurosurgeon. Wracked by guilt, Robert decides to devote his own life to improving the life of others.</p><p><i>Magnificent Obsession</i> was a hugely popular work in its time, inspiring a blockbuster 1935 film of the same name.</p>';
|
||||||
$order = 9;
|
|
||||||
break;
|
break;
|
||||||
case 'url:https://standardebooks.org/ebooks/josephine-tey/the-man-in-the-queue':
|
case 'url:https://standardebooks.org/ebooks/josephine-tey/the-man-in-the-queue':
|
||||||
$description = '<p>Standing in line in a long queue for a show at a theater, a young man is stabbed in the back. <a href="/collections/inspector-grant">Inspector Alan Grant</a> of the Metropolitan Police is soon on the case, though he finds it deeply puzzling—not least because the identity of the victim is itself a mystery.</p><p><i>The Man in the Queue</i> was the first in a series of hugely successful detective novels by Josephine Tey.</p>';
|
$description = '<p>Standing in line in a long queue for a show at a theater, a young man is stabbed in the back. <a href="/collections/inspector-grant">Inspector Alan Grant</a> of the Metropolitan Police is soon on the case, though he finds it deeply puzzling—not least because the identity of the victim is itself a mystery.</p><p><i>The Man in the Queue</i> was the first in a series of hugely successful detective novels by Josephine Tey.</p>';
|
||||||
$order = 10;
|
|
||||||
break;
|
break;
|
||||||
case 'url:https://standardebooks.org/ebooks/john-buchan/the-courts-of-the-morning':
|
case 'url:https://standardebooks.org/ebooks/john-buchan/the-courts-of-the-morning':
|
||||||
$description = '<p><i>The Courts of the Morning</i> opens with <a href="/collections/richard-hannay">Major-General Richard Hannay</a> being approached by American diplomats regarding the disappearance of a wealthy industrialist. He in turn seeks the help of his friend Sandy Arbuthnot—but Arbuthnot himself quickly goes missing. We soon head to the South American country of Olifa, where a powerful head of a mining company is gradually enslaving the populace. It seems that only guerrilla warfare will save the country from rule under a ruthless tyrant.</p>';
|
$description = '<p><i>The Courts of the Morning</i> opens with <a href="/collections/richard-hannay">Major-General Richard Hannay</a> being approached by American diplomats regarding the disappearance of a wealthy industrialist. He in turn seeks the help of his friend Sandy Arbuthnot—but Arbuthnot himself quickly goes missing. We soon head to the South American country of Olifa, where a powerful head of a mining company is gradually enslaving the populace. It seems that only guerrilla warfare will save the country from rule under a ruthless tyrant.</p>';
|
||||||
$order = 11;
|
|
||||||
break;
|
break;
|
||||||
case 'url:https://standardebooks.org/ebooks/j-b-priestley/the-good-companions':
|
case 'url:https://standardebooks.org/ebooks/j-b-priestley/the-good-companions':
|
||||||
$description = '<p>The recipient of the <a href="/collections/james-tait-black-memorial-fiction-prize-winners">James Tait Black Memorial Fiction Prize</a>, <i>The Good Companions</i> was a blockbuster novel that made J. B. Priestley’s reputation. In it we follow three protagonists from different walks of life who, looking for a change of pace, strike out from home. They eventually cross paths with each other and with a group of “concert players,” a type of traveling vaudeville troupe common in the day. They decide to join forces and form the “Good Companions,” a musical act that takes them on a series of cozy adventures.</p>';
|
$description = '<p>The recipient of the <a href="/collections/james-tait-black-memorial-fiction-prize-winners">James Tait Black Memorial Fiction Prize</a>, <i>The Good Companions</i> was a blockbuster novel that made J. B. Priestley’s reputation. In it we follow three protagonists from different walks of life who, looking for a change of pace, strike out from home. They eventually cross paths with each other and with a group of “concert players,” a type of traveling vaudeville troupe common in the day. They decide to join forces and form the “Good Companions,” a musical act that takes them on a series of cozy adventures.</p>';
|
||||||
$order = 12;
|
|
||||||
break;
|
break;
|
||||||
case 'url:https://standardebooks.org/ebooks/dashiell-hammett/the-dain-curse':
|
case 'url:https://standardebooks.org/ebooks/dashiell-hammett/the-dain-curse':
|
||||||
$description = '<p>In <i>The Dain Curse</i>, the second <a href="/collections/continental-op">Continental Op</a> novel, the legendary but nameless hard-drinking and quick-shooting detective is sent to investigate the theft of diamonds from a San Franciso family. The fast-paced noir thriller quickly veers from car chases to cultists to the supernatural, but the unflappable Continental Op is relentless in his pursuit of truth.</p>';
|
$description = '<p>In <i>The Dain Curse</i>, the second <a href="/collections/continental-op">Continental Op</a> novel, the legendary but nameless hard-drinking and quick-shooting detective is sent to investigate the theft of diamonds from a San Francisco family. The fast-paced noir thriller quickly veers from car chases to cultists to the supernatural, but the unflappable Continental Op is relentless in his pursuit of truth.</p>';
|
||||||
$order = 13;
|
|
||||||
break;
|
break;
|
||||||
case 'url:https://standardebooks.org/ebooks/c-s-forester/brown-on-resolution':
|
case 'url:https://standardebooks.org/ebooks/c-s-forester/brown-on-resolution':
|
||||||
$description = '<p>While on operations in the Pacific during the first World War, the sailor Albert Brown’s ship is sunk—but he survives, and is taken on board the German cruiser that sank them. It too has suffered damage, and heads to some nearby islands for repairs. In this unlikely and hostile setting, Brown, alone, pits himself against the German ship and its crew, seeking to delay its progress while British naval reinforcements rush to his rescue.</p><p>Forester’s careful historical research adds an unimpeachable air of versimilitude to the novel, and indeed, the plot is loosely based on real events.</p>';
|
$description = '<p>While on operations in the Pacific during the first World War, the sailor Albert Brown’s ship is sunk—but he survives, and is taken on board the German cruiser that sank them. It too has suffered damage, and heads to some nearby islands for repairs. In this unlikely and hostile setting, Brown, alone, pits himself against the German ship and its crew, seeking to delay its progress while British naval reinforcements rush to his rescue.</p><p>Forester’s careful historical research adds an unimpeachable air of verisimilitude to the novel, and indeed, the plot is loosely based on real events.</p>';
|
||||||
$order = 14;
|
|
||||||
break;
|
break;
|
||||||
case 'url:https://standardebooks.org/ebooks/arthur-conan-doyle/the-maracot-deep':
|
case 'url:https://standardebooks.org/ebooks/arthur-conan-doyle/the-maracot-deep':
|
||||||
$description = '<p>While investigating the deepest part of the Atlantic Ocean, a team led by Dr. Maracot is cut off from their ship and hurled to the bottom of the ocean. There, they find themselves in the remnants of the ancient civilization of Atlantis.</p><p>Though Doyle is most famous for his <a href="/collections/sherlock-holmes">Sherlock Holmes</a> stories, in which a brilliant logician uses reason and deduction to solve crime, in later years he became deeply spiritual. This novel, written just a year before his death, combines his interest in science and reason with his new spiritual outlook.</p>';
|
$description = '<p>While investigating the deepest part of the Atlantic Ocean, a team led by Dr. Maracot is cut off from their ship and hurled to the bottom of the ocean. There, they find themselves in the remnants of the ancient civilization of Atlantis.</p><p>Though Doyle is most famous for his <a href="/collections/sherlock-holmes">Sherlock Holmes</a> stories, in which a brilliant logician uses reason and deduction to solve crime, in later years he became deeply spiritual. This novel, written just a year before his death, combines his interest in science and reason with his new spiritual outlook.</p>';
|
||||||
$order = 15;
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$ebooksWithDescriptions[$order] = ['ebook' => $ebook, 'description' => $description];
|
$ebooksWithDescriptions[array_search($ebook->Identifier, $identifiers)] = ['ebook' => $ebook, 'description' => $description];
|
||||||
}
|
}
|
||||||
|
|
||||||
ksort($ebooksWithDescriptions);
|
ksort($ebooksWithDescriptions);
|
||||||
|
|
||||||
?><?= Template::Header(['title' => 'Public Domain Day 2025 - 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' => 'Happy Public Domain Day 2025! - Blog', 'highlight' => '', 'description' => 'Read about the new ebooks Standard Ebooks is releasing for Public Domain Day 2025!', 'css' => ['/css/public-domain-day.css']]) ?>
|
||||||
<main>
|
<main>
|
||||||
<section class="narrow">
|
<section class="narrow">
|
||||||
<h1>Public Domain Day 2025</h1>
|
<h1>Happy Public Domain Day 2025!</h1>
|
||||||
<?= Template::DonationCounter() ?>
|
<?= Template::DonationCounter() ?>
|
||||||
<?= Template::DonationProgress() ?>
|
<?= Template::DonationProgress() ?>
|
||||||
<p>Happy Public Domain Day!</p>
|
<p>Happy Public Domain Day!</p>
|
||||||
<p>Around the world, people celebrate Public Domain Day on January 1, the day in which copyright expires on some older works and they enter the public domain in many different countries.</p>
|
<p>Around the world, people celebrate Public Domain Day on January 1, the day in which copyright expires on some older works, putting them in the public domain in many different countries.</p>
|
||||||
<p>Some countries have a copyright term of the life of the author plus seventy years. These countries have been celebrating public domain day for some time now.</p>
|
<p>Some countries have a copyright term of the life of the author plus seventy years. These countries have been celebrating public domain day for some time now.</p>
|
||||||
<p>But in the U.S., powerful corporations have continuously extended the length of copyright for decades so they could continue to profit. In the process, they locked <em>all</em> of our cultural output away from us for nearly a century.</p>
|
<p>In the U.S. Constitution, copyright terms were limited to up to twenty-eight years, in order to “promote the Progress of Science and useful Arts.” But since the Constitution was drafted, powerful corporations have continuously extended the length of copyright so they could continue to profit. The result is that today in the U.S., work only enters the public domain ninety-five years after publication. This has locked <em>all</em> of our cultural output away from us for <em>nearly a century</em>.</p>
|
||||||
<p>2019 was the year in which this extremely long, corporate-dictated copyright term was finally going to end. As that year approached, we had every reason to assume that these powerful corporations would once again lobby to extend copyright even further.</p>
|
<p>2019 was the year in which the first works from almost a hundred years ago were finally scheduled to enter the public domain, ending this long, corporate-dictated cultural winter. As that year approached, we had every reason to assume that these powerful corporations would once again lobby to extend copyright even further.</p>
|
||||||
<p>But as 2019 grew closer, it became clear that these corporations <em>wouldn’t</em> work to extend copyright yet again—making 2019 the first year in almost a century in which a signifcant amount of literature once again entered the public domain in the U.S., free for anyone in the U.S. to read, use, and share.</p>
|
<p>But as 2019 grew closer, it became clear that these corporations <em>wouldn’t</em> work to extend copyright yet again—making 2019 the first year in almost a century in which a significant amount of art and literature once again entered the public domain in the U.S., free for anyone in the U.S. to read, use, and share.</p>
|
||||||
<p>Ever since then, each year we’ve been joining our international friends in celebrating Public Domain Day by preparing some of the biggest literary hits of the year for you to read on January 1.</p>
|
<p>Ever since then, each year we’ve been joining our international friends in celebrating Public Domain Day by preparing some of the biggest literary hits of the year for you to read on January 1.</p>
|
||||||
<hr/>
|
<hr/>
|
||||||
<p><strong>In 2025, books published in 1929 enter the U.S. public domain.</strong> And 1929 was a literary doozy!</p>
|
<p><strong>In 2025, books published in 1929 enter the U.S. public domain.</strong></p>
|
||||||
<p>Books by William Faulkner, Ernest Hemingway, Mahatma Gandhi, and John Steinbeck entered the U.S. public domain. We also have the first novel featuring the Continental Op, the archetype for every hard-boiled noir detective to grace page and screen since; the English translation of <i>All Quiet on the Western Front</i>; and much more.</p>
|
<p>And 1929 was a literary doozy!</p>
|
||||||
<p>Our friends at Public Domain Review have written about some <a href="">other things besides literature that entered the public domain this year</a>.</p>
|
<p>Books by William Faulkner, Ernest Hemingway, Mahatma Gandhi, and John Steinbeck enter the U.S. public domain. Joining these esteemed names is the first novel featuring the Continental Op, the nameless hard-boiled noir detective who created the archetype for every hard-drinking, fedora-wearing private eye to grace page and screen since; the English translation of <i>All Quiet on the Western Front</i>; and much more.</p>
|
||||||
<p>At Standard Ebooks, our volunteers have been working hard for the past few months to prepare the following ebooks to be ready for Public Domain Day. Read more about them and download them for free:</p>
|
<p>Our friends at Public Domain Review have written about some <a href="">other things that enter the public domain this year, too</a>.</p>
|
||||||
|
<p>At Standard Ebooks, our volunteers have been working hard for the past few months to prepare the following ebooks to be ready for Public Domain Day. Join us in celebrating the freeing of our cultural heritage by downloading these ebooks and reading them for free:</p>
|
||||||
|
<? if(sizeof($ebooksWithDescriptions) == 0){ ?>
|
||||||
|
<p class="empty">We’re still preparing these free ebooks for Public Domain Day. Check back after January 1!</p>
|
||||||
|
<? }else{ ?>
|
||||||
<ul class="public-domain-day">
|
<ul class="public-domain-day">
|
||||||
<? foreach($ebooksWithDescriptions as $ebookGroup){ ?>
|
<? foreach($ebooksWithDescriptions as $ebookGroup){ ?>
|
||||||
<li>
|
<li>
|
||||||
|
@ -128,13 +115,21 @@ ksort($ebooksWithDescriptions);
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<h2><a href="<?= $ebookGroup['ebook']->Url ?>"><?= Formatter::EscapeHtml($ebookGroup['ebook']->Title) ?></a></h2>
|
<h2>
|
||||||
|
<a href="<?= $ebookGroup['ebook']->Url ?>"><?= Formatter::EscapeHtml($ebookGroup['ebook']->Title) ?></a>
|
||||||
|
</h2>
|
||||||
<p class="byline">by <a href="<?= $ebookGroup['ebook']->AuthorsUrl ?>"><?= Formatter::EscapeHtml($ebookGroup['ebook']->Authors[0]->Name) ?></a></p>
|
<p class="byline">by <a href="<?= $ebookGroup['ebook']->AuthorsUrl ?>"><?= Formatter::EscapeHtml($ebookGroup['ebook']->Authors[0]->Name) ?></a></p>
|
||||||
<p><?= $ebookGroup['description'] ?></p>
|
<p>
|
||||||
|
<?= $ebookGroup['description'] ?>
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<a href="<?= $ebookGroup['ebook']->Url ?>">Download and read for free →</a>
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
<? } ?>
|
<? } ?>
|
||||||
</ul>
|
</ul>
|
||||||
|
<? } ?>
|
||||||
</section>
|
</section>
|
||||||
</main>
|
</main>
|
||||||
<?= Template::Footer() ?>
|
<?= Template::Footer() ?>
|
||||||
|
|
133
www/css/core.css
133
www/css/core.css
|
@ -2799,12 +2799,11 @@ progress.stretch::-moz-progress-bar{
|
||||||
border-left: 5px solid #E9C91F;
|
border-left: 5px solid #E9C91F;
|
||||||
}
|
}
|
||||||
|
|
||||||
aside button.close{
|
button.close{
|
||||||
display: block;
|
display: block;
|
||||||
font-size: 0;
|
font-size: 0;
|
||||||
border: none;
|
border: none;
|
||||||
box-shadow: none;
|
box-shadow: none;
|
||||||
padding: 0;
|
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
top: 0;
|
||||||
right: 0;
|
right: 0;
|
||||||
|
@ -2819,11 +2818,12 @@ aside button.close{
|
||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
aside button.close:hover{
|
button.close:hover{
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
|
background-color: transparent;
|
||||||
}
|
}
|
||||||
|
|
||||||
aside button.close:active{
|
button.close:active{
|
||||||
left: auto;
|
left: auto;
|
||||||
top: 1px;
|
top: 1px;
|
||||||
right: -1px;
|
right: -1px;
|
||||||
|
@ -3071,6 +3071,35 @@ hr + p{
|
||||||
margin-top: 0;
|
margin-top: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.public-domain-day-banner{
|
||||||
|
width: 100%;
|
||||||
|
text-align: center;
|
||||||
|
padding: 1rem;
|
||||||
|
background: #d37a71;
|
||||||
|
margin-bottom: 1.5rem;
|
||||||
|
color: #fff;
|
||||||
|
text-shadow: 1px 1px 0px rgba(0, 0, 0, .75);
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
box-sizing: border-box;
|
||||||
|
box-shadow: 0 0 1rem rgba(0, 0, 0, .75);
|
||||||
|
}
|
||||||
|
|
||||||
|
.public-domain-day-banner button.close{
|
||||||
|
background-image: url('/images/close-dark.svg');
|
||||||
|
top: 2px;
|
||||||
|
right: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
body > header:has(.public-domain-day-banner){
|
||||||
|
flex-wrap: wrap;
|
||||||
|
padding-top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
form[action="/settings"] label{
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
@media (hover: none) and (pointer: coarse){ /* target ipads and smartphones without a mouse */
|
@media (hover: none) and (pointer: coarse){ /* target ipads and smartphones without a mouse */
|
||||||
/* For iPad, unset the height so it matches the other elements */
|
/* For iPad, unset the height so it matches the other elements */
|
||||||
select[multiple]{
|
select[multiple]{
|
||||||
|
@ -3267,6 +3296,10 @@ hr + p{
|
||||||
.downloads-container figure + div{
|
.downloads-container figure + div{
|
||||||
padding-left: 0;
|
padding-left: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.public-domain-day-banner strong{
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media(max-width: 900px){
|
@media(max-width: 900px){
|
||||||
|
@ -3845,24 +3878,116 @@ hr + p{
|
||||||
box-shadow: none;
|
box-shadow: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
figure.realistic-ebook:hover img,
|
||||||
.ebook .downloads-container:hover figure img{ /* cover */
|
.ebook .downloads-container:hover figure img{ /* cover */
|
||||||
filter: brightness(1.15);
|
filter: brightness(1.15);
|
||||||
transform: translateY(-.5rem);
|
transform: translateY(-.5rem);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
figure.realistic-ebook:hover picture::after,
|
||||||
.ebook .downloads-container:hover figure picture::after{ /* back board */
|
.ebook .downloads-container:hover figure picture::after{ /* back board */
|
||||||
transform: translateY(calc(-1 * (var(--size) + .5rem)));
|
transform: translateY(calc(-1 * (var(--size) + .5rem)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.realistic-ebook:hover picture::before,
|
||||||
.ebook .downloads-container:hover picture::before{ /* pages */
|
.ebook .downloads-container:hover picture::before{ /* pages */
|
||||||
transform: rotate(90deg) skewy(-45deg) translateX(-.5rem) translateY(-.5rem);
|
transform: rotate(90deg) skewy(-45deg) translateX(-.5rem) translateY(-.5rem);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
figure.realistic-ebook:hover::after,
|
||||||
.ebook .downloads-container:hover figure::after{ /* spine */
|
.ebook .downloads-container:hover figure::after{ /* spine */
|
||||||
transform: skewY(45deg) translateY(-.5rem);
|
transform: skewY(45deg) translateY(-.5rem);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
figure.realistic-ebook:hover::before
|
||||||
.ebook .downloads-container:hover figure::before{ /* shadow */
|
.ebook .downloads-container:hover figure::before{ /* shadow */
|
||||||
transform: skewX(45deg) scale(1.05);
|
transform: skewX(45deg) scale(1.05);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#confettis{
|
||||||
|
top: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
position: absolute;
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.confetti{
|
||||||
|
left: 50%;
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
position: absolute;
|
||||||
|
transform-origin: left top;
|
||||||
|
animation: confetti 5s ease-in-out -2s infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes confetti{
|
||||||
|
0%{
|
||||||
|
transform: rotateZ(15deg) rotateY(0deg) translate(0, 0);
|
||||||
|
}
|
||||||
|
25%{
|
||||||
|
transform: rotateZ(5deg) rotateY(360deg) translate(-5vw, 20vh);
|
||||||
|
}
|
||||||
|
50%{
|
||||||
|
transform: rotateZ(15deg) rotateY(720deg) translate(5vw, 60vh);
|
||||||
|
}
|
||||||
|
75%{
|
||||||
|
transform: rotateZ(5deg) rotateY(1080deg) translate(-10vw, 80vh);
|
||||||
|
}
|
||||||
|
100%{
|
||||||
|
transform: rotateZ(15deg) rotateY(1440deg) translate(10vw, 110vh);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.confetti:nth-child(1){
|
||||||
|
left: 10%;
|
||||||
|
animation-delay: 0;
|
||||||
|
background-color: #fc0120;
|
||||||
|
}
|
||||||
|
.confetti:nth-child(2){
|
||||||
|
left: 20%;
|
||||||
|
animation-delay: -5s;
|
||||||
|
background-color: #8257e6;
|
||||||
|
}
|
||||||
|
.confetti:nth-child(3){
|
||||||
|
left: 30%;
|
||||||
|
animation-delay: -3s;
|
||||||
|
background-color: #ffbf4d;
|
||||||
|
}
|
||||||
|
.confetti:nth-child(4){
|
||||||
|
left: 40%;
|
||||||
|
animation-delay: -2.5s;
|
||||||
|
background-color: #fe5d7a;
|
||||||
|
}
|
||||||
|
.confetti:nth-child(5){
|
||||||
|
left: 50%;
|
||||||
|
animation-delay: -4s;
|
||||||
|
background-color: #45ec9c;
|
||||||
|
}
|
||||||
|
.confetti:nth-child(6){
|
||||||
|
left: 60%;
|
||||||
|
animation-delay: -6s;
|
||||||
|
background-color: #f6e327;
|
||||||
|
}
|
||||||
|
.confetti:nth-child(7){
|
||||||
|
left: 70%;
|
||||||
|
animation-delay: -1.5s;
|
||||||
|
background-color: #f769ce;
|
||||||
|
}
|
||||||
|
.confetti:nth-child(8){
|
||||||
|
left: 80%;
|
||||||
|
animation-delay: -2s;
|
||||||
|
background-color: #007de7;
|
||||||
|
}
|
||||||
|
.confetti:nth-child(9){
|
||||||
|
left: 90%;
|
||||||
|
animation-delay: -3.5s;
|
||||||
|
background-color: #63b4fc;
|
||||||
|
}
|
||||||
|
.confetti:nth-child(10){
|
||||||
|
left: 100%;
|
||||||
|
animation-delay: -2.5s;
|
||||||
|
background-color: #f9c4ea;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -90,8 +90,9 @@ h1,h2,h3,h4,h5,h6{
|
||||||
text-shadow: 2px 2px 0 rgba(0, 0, 0, .75);
|
text-shadow: 2px 2px 0 rgba(0, 0, 0, .75);
|
||||||
}
|
}
|
||||||
|
|
||||||
aside button.close{
|
button.close{
|
||||||
filter: invert(1);
|
/* Can't use `filter: invert()` because that also inverts the focus outline. */
|
||||||
|
background-image: url("/images/close-dark.svg");
|
||||||
}
|
}
|
||||||
|
|
||||||
.donation .flipboard span{
|
.donation .flipboard span{
|
||||||
|
|
|
@ -11,6 +11,11 @@
|
||||||
margin-left: 1rem;
|
margin-left: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.public-domain-day div + div > p:last-child{
|
||||||
|
text-align: right;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
.public-domain-day h2{
|
.public-domain-day h2{
|
||||||
margin-top: 0;
|
margin-top: 0;
|
||||||
}
|
}
|
||||||
|
@ -31,6 +36,12 @@
|
||||||
margin-top: -1rem;
|
margin-top: -1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.empty{
|
||||||
|
text-align: center;
|
||||||
|
font-style: italic;
|
||||||
|
margin-top: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
@media(max-width: 1000px){
|
@media(max-width: 1000px){
|
||||||
.public-domain-day div + div{
|
.public-domain-day div + div{
|
||||||
margin-left: 0;
|
margin-left: 0;
|
||||||
|
|
|
@ -4,7 +4,7 @@ use Safe\DateTimeImmutable;
|
||||||
// If the user is not logged in, or has less than some amount of downloads, show a thank-you page.
|
// If the user is not logged in, or has less than some amount of downloads, show a thank-you page.
|
||||||
|
|
||||||
$ebook = null;
|
$ebook = null;
|
||||||
$downloadCount = $_COOKIE['download-count'] ?? 0;
|
$downloadCount = HttpInput::Int(COOKIE, 'download-count') ?? 0;
|
||||||
$showThankYouPage = Session::$User === null && $downloadCount < 5;
|
$showThankYouPage = Session::$User === null && $downloadCount < 5;
|
||||||
$downloadUrl = null;
|
$downloadUrl = null;
|
||||||
|
|
||||||
|
|
16
www/images/close-dark.svg
Normal file
16
www/images/close-dark.svg
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<svg
|
||||||
|
width="10"
|
||||||
|
height="10"
|
||||||
|
viewBox="0 0 10 10"
|
||||||
|
version="1.1"
|
||||||
|
xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path
|
||||||
|
style="fill:none;stroke:#ffffff;stroke-width:1.7522;"
|
||||||
|
d="M 0.61949671,0.61949 9.3805033,9.3805"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
style="fill:none;stroke:#ffffff;stroke-width:1.7522;"
|
||||||
|
d="M 9.3805033,0.61949 0.61949672,9.3805"
|
||||||
|
/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 418 B |
|
@ -31,7 +31,7 @@ if($exception){
|
||||||
|
|
||||||
<form action="/newsletter/subscriptions" method="<?= Enums\HttpMethod::Post->value ?>">
|
<form action="/newsletter/subscriptions" method="<?= Enums\HttpMethod::Post->value ?>">
|
||||||
<label class="automation-test"><? /* Test for spam bots filling out all fields */ ?>
|
<label class="automation-test"><? /* Test for spam bots filling out all fields */ ?>
|
||||||
<input type="text" name="automationtest" value="" maxlength="80" />
|
<input type="text" name="automation-test" value="" maxlength="80" />
|
||||||
</label>
|
</label>
|
||||||
<label>Your email address
|
<label>Your email address
|
||||||
<input type="email" name="email" value="<?= Formatter::EscapeHtml($subscription->User->Email ?? '') ?>" maxlength="80" required="required" />
|
<input type="email" name="email" value="<?= Formatter::EscapeHtml($subscription->User->Email ?? '') ?>" maxlength="80" required="required" />
|
||||||
|
|
|
@ -11,7 +11,7 @@ try{
|
||||||
|
|
||||||
$subscription = new NewsletterSubscription();
|
$subscription = new NewsletterSubscription();
|
||||||
|
|
||||||
if(HttpInput::Str(POST, 'automationtest')){
|
if(HttpInput::Str(POST, 'automation-test')){
|
||||||
// A bot filled out this form field, which should always be empty. Pretend like we succeeded.
|
// A bot filled out this form field, which should always be empty. Pretend like we succeeded.
|
||||||
if($requestType == Enums\HttpRequestType::Web){
|
if($requestType == Enums\HttpRequestType::Web){
|
||||||
http_response_code(Enums\HttpCode::SeeOther->value);
|
http_response_code(Enums\HttpCode::SeeOther->value);
|
||||||
|
|
|
@ -1,17 +1,18 @@
|
||||||
<?
|
<?
|
||||||
$colorScheme = $_COOKIE['color-scheme'] ?? 'auto';
|
$colorScheme = Enums\ColorSchemeType::tryFrom(HttpInput::Str(COOKIE, 'color-scheme') ?? Enums\ColorSchemeType::Auto->value);
|
||||||
|
|
||||||
?><?= Template::Header(['title' => 'Website Settings', 'description' => 'Adjust your settings for viewing the Standard Ebooks website.']) ?>
|
?><?= Template::Header(['title' => 'Website Settings', 'description' => 'Adjust your settings for viewing the Standard Ebooks website.']) ?>
|
||||||
<main>
|
<main>
|
||||||
<h1>Website Settings</h1>
|
<h1>Website Settings</h1>
|
||||||
<form action="/settings" method="<?= Enums\HttpMethod::Post->value ?>">
|
<form action="/settings" method="<?= Enums\HttpMethod::Post->value ?>">
|
||||||
|
<input type="hidden" name="_method" value="<?= Enums\HttpMethod::Patch->value ?>" />
|
||||||
<label>
|
<label>
|
||||||
<span>Color scheme</span>
|
<span>Color scheme</span>
|
||||||
<span>
|
<span>
|
||||||
<select name="color-scheme">
|
<select name="color-scheme">
|
||||||
<option value="auto"<? if($colorScheme == 'auto'){ ?> selected="selected"<? } ?>>Automatic</option>
|
<option value="<?= Enums\ColorSchemeType::Auto->value ?>"<? if($colorScheme == Enums\ColorSchemeType::Auto){ ?> selected="selected"<? } ?>>Automatic</option>
|
||||||
<option value="light"<? if($colorScheme == 'light'){ ?> selected="selected"<? } ?>>Light</option>
|
<option value="<?= Enums\ColorSchemeType::Light->value ?>"<? if($colorScheme == Enums\ColorSchemeType::Light){ ?> selected="selected"<? } ?>>Light</option>
|
||||||
<option value="dark"<? if($colorScheme == 'dark'){ ?> selected="selected"<? } ?>>Dark</option>
|
<option value="<?= Enums\ColorSchemeType::Dark->value ?>"<? if($colorScheme == Enums\ColorSchemeType::Dark){ ?> selected="selected"<? } ?>>Dark</option>
|
||||||
</select>
|
</select>
|
||||||
</span>
|
</span>
|
||||||
</label>
|
</label>
|
||||||
|
|
|
@ -1,30 +1,35 @@
|
||||||
<?
|
<?
|
||||||
use Safe\DateTimeImmutable;
|
use Safe\DateTimeImmutable;
|
||||||
|
|
||||||
$hideDonationAlert = HttpInput::Bool(POST, 'hide-donation-alert');
|
$httpMethod = HttpInput::ValidateRequestMethod([Enums\HttpMethod::Patch]);
|
||||||
$colorScheme = HttpInput::Str(POST, 'color-scheme');
|
|
||||||
|
|
||||||
if($hideDonationAlert !== null){
|
// PATCHing settings.
|
||||||
|
if($httpMethod == Enums\HttpMethod::Patch){
|
||||||
|
$hideDonationAlert = HttpInput::Bool(POST, 'hide-donation-alert');
|
||||||
|
$hidePublicDomainDayBanner = HttpInput::Bool(POST, 'hide-public-domain-day-banner');
|
||||||
|
$colorScheme = Enums\ColorSchemeType::tryFrom(HttpInput::Str(POST, 'color-scheme') ?? '');
|
||||||
|
|
||||||
|
if($hideDonationAlert !== null){
|
||||||
setcookie('hide-donation-alert', $hideDonationAlert ? 'true' : 'false', ['expires' => intval((new DateTimeImmutable('+1 month'))->format(Enums\DateTimeFormat::UnixTimestamp->value)), 'path' => '/', 'domain' => SITE_DOMAIN, 'secure' => true, 'httponly' => true, 'samesite' => 'Lax']);
|
setcookie('hide-donation-alert', $hideDonationAlert ? 'true' : 'false', ['expires' => intval((new DateTimeImmutable('+1 month'))->format(Enums\DateTimeFormat::UnixTimestamp->value)), 'path' => '/', 'domain' => SITE_DOMAIN, 'secure' => true, 'httponly' => true, 'samesite' => 'Lax']);
|
||||||
}
|
|
||||||
|
|
||||||
if($colorScheme !== null){
|
|
||||||
if($colorScheme !== 'dark' && $colorScheme !== 'light' && $colorScheme !== 'auto'){
|
|
||||||
$colorScheme = 'auto';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if($colorScheme == 'auto'){
|
if($hidePublicDomainDayBanner !== null){
|
||||||
|
setcookie('hide-public-domain-day-banner', $hidePublicDomainDayBanner ? 'true' : 'false', ['expires' => intval((new DateTimeImmutable('+1 month'))->format(Enums\DateTimeFormat::UnixTimestamp->value)), 'path' => '/', 'domain' => SITE_DOMAIN, 'secure' => true, 'httponly' => true, 'samesite' => 'Lax']);
|
||||||
|
}
|
||||||
|
|
||||||
|
if($colorScheme !== null){
|
||||||
|
if($colorScheme == Enums\ColorSchemeType::Auto){
|
||||||
// Delete the cookie; auto is the default
|
// Delete the cookie; auto is the default
|
||||||
setcookie('color-scheme', '', ['expires' => 0, 'path' => '/', 'domain' => SITE_DOMAIN, 'secure' => true, 'httponly' => true, 'samesite' => 'Lax']);
|
setcookie('color-scheme', '', ['expires' => 0, 'path' => '/', 'domain' => SITE_DOMAIN, 'secure' => true, 'httponly' => true, 'samesite' => 'Lax']);
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
setcookie('color-scheme', $colorScheme, ['expires' => intval((new DateTimeImmutable('+1 year'))->format(Enums\DateTimeFormat::UnixTimestamp->value)), 'path' => '/', 'domain' => SITE_DOMAIN, 'secure' => true, 'httponly' => true, 'samesite' => 'Lax']);
|
setcookie('color-scheme', $colorScheme->value, ['expires' => intval((new DateTimeImmutable('+1 year'))->format(Enums\DateTimeFormat::UnixTimestamp->value)), 'path' => '/', 'domain' => SITE_DOMAIN, 'secure' => true, 'httponly' => true, 'samesite' => 'Lax']);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// HTTP 303, See other
|
||||||
|
http_response_code(Enums\HttpCode::SeeOther->value);
|
||||||
|
|
||||||
|
$redirect = $_SERVER['HTTP_REFERER'] ?? '/';
|
||||||
|
header('Location: ' . $redirect);
|
||||||
}
|
}
|
||||||
|
|
||||||
// HTTP 303, See other
|
|
||||||
http_response_code(Enums\HttpCode::SeeOther->value);
|
|
||||||
|
|
||||||
$redirect = $_SERVER['HTTP_REFERER'] ?? '/';
|
|
||||||
header('Location: ' . $redirect);
|
|
||||||
?>
|
|
||||||
|
|
|
@ -20,12 +20,12 @@ print("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
|
||||||
<url>
|
<url>
|
||||||
<loc><?= SITE_URL ?>/authors</loc>
|
<loc><?= SITE_URL ?>/authors</loc>
|
||||||
</url>
|
</url>
|
||||||
<!-- <url>
|
<url>
|
||||||
<loc><?= SITE_URL ?>/blog</loc>
|
<loc><?= SITE_URL ?>/blog</loc>
|
||||||
</url> -->
|
</url>
|
||||||
<!-- <url>
|
<url>
|
||||||
<loc><?= SITE_URL ?>/blog/public-domain-day-2025</loc>
|
<loc><?= SITE_URL ?>/blog/public-domain-day-2025</loc>
|
||||||
</url> -->
|
</url>
|
||||||
<? foreach($authors as $author){ ?>
|
<? foreach($authors as $author){ ?>
|
||||||
<url>
|
<url>
|
||||||
<loc><?= SITE_URL ?><?= $author->Url ?></loc>
|
<loc><?= SITE_URL ?><?= $author->Url ?></loc>
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue