mirror of
https://github.com/standardebooks/web.git
synced 2025-07-06 06:40:33 -04:00
Restructure how incorrect ebook URLs are redirected
This commit is contained in:
parent
d6a2bdcbc8
commit
d4c7703cf0
6 changed files with 62 additions and 72 deletions
|
@ -106,20 +106,20 @@ const DONATION_DRIVE_COUNTER_END = new DateTimeImmutable('May 8, 2022 23:59:00 A
|
||||||
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;
|
||||||
const PD_DAY_EBOOKS = [
|
const PD_DAY_EBOOKS = [
|
||||||
'graham-greene/the-man-within' => ['author' => 'Graham Greene', 'title' => 'The Man Within'],
|
EBOOKS_IDENTIFIER_PREFIX . 'graham-greene/the-man-within' => ['author' => 'Graham Greene', 'title' => 'The Man Within'],
|
||||||
'c-s-forester/brown-on-resolution' => ['author' => 'C. S. Forester', 'title' => 'Brown on Resolution'],
|
EBOOKS_IDENTIFIER_PREFIX . 'c-s-forester/brown-on-resolution' => ['author' => 'C. S. Forester', 'title' => 'Brown on Resolution'],
|
||||||
'dashiell-hammett/red-harvest' => ['author' => 'Dashiell Hammett', 'title' => 'Red Harvest'],
|
EBOOKS_IDENTIFIER_PREFIX . 'dashiell-hammett/red-harvest' => ['author' => 'Dashiell Hammett', 'title' => 'Red Harvest'],
|
||||||
'dashiell-hammett/the-dain-curse' => ['author' => 'Dashiell Hammett', 'title' => 'The Dain Curse'],
|
EBOOKS_IDENTIFIER_PREFIX . 'dashiell-hammett/the-dain-curse' => ['author' => 'Dashiell Hammett', 'title' => 'The Dain Curse'],
|
||||||
'erich-maria-remarque/all-quiet-on-the-western-front/a-w-wheen' => ['author' => 'Erich Maria Remarque', 'title' => 'All Quiet on the Western Front', 'translator' => 'A. W. Wheen'],
|
EBOOKS_IDENTIFIER_PREFIX . 'erich-maria-remarque/all-quiet-on-the-western-front/a-w-wheen' => ['author' => 'Erich Maria Remarque', 'title' => 'All Quiet on the Western Front', 'translator' => 'A. W. Wheen'],
|
||||||
'ernest-hemingway/a-farewell-to-arms' => ['author' => 'Ernest Heminway', 'title' => 'A Farewell to Arms'],
|
EBOOKS_IDENTIFIER_PREFIX . 'ernest-hemingway/a-farewell-to-arms' => ['author' => 'Ernest Heminway', 'title' => 'A Farewell to Arms'],
|
||||||
'j-b-priestley/the-good-companions' => ['author' => 'J. B. Priestley', 'title' => 'The Good Companions'],
|
EBOOKS_IDENTIFIER_PREFIX . 'j-b-priestley/the-good-companions' => ['author' => 'J. B. Priestley', 'title' => 'The Good Companions'],
|
||||||
'john-steinbeck/cup-of-gold' => ['author' => 'John Steinbeck', 'title' => 'Cup of Gold'],
|
EBOOKS_IDENTIFIER_PREFIX . 'john-steinbeck/cup-of-gold' => ['author' => 'John Steinbeck', 'title' => 'Cup of Gold'],
|
||||||
'oliver-la-farge/laughing-boy' => ['author' => 'Oliver La Farge', 'title' => 'Laughing Boy'],
|
EBOOKS_IDENTIFIER_PREFIX . 'oliver-la-farge/laughing-boy' => ['author' => 'Oliver La Farge', 'title' => 'Laughing Boy'],
|
||||||
'william-faulkner/the-sound-and-the-fury' => ['author' => 'William Faulkner', 'title' => 'The Sound and the Fury'],
|
EBOOKS_IDENTIFIER_PREFIX . 'william-faulkner/the-sound-and-the-fury' => ['author' => 'William Faulkner', 'title' => 'The Sound and the Fury'],
|
||||||
'mahatma-gandhi/the-story-of-my-experiments-with-truth/mahadev-desai' => ['author' => 'Mahatma Gandhi', 'title' => 'The Story of My Experiments with Truth'],
|
EBOOKS_IDENTIFIER_PREFIX . 'mahatma-gandhi/the-story-of-my-experiments-with-truth/mahadev-desai' => ['author' => 'Mahatma Gandhi', 'title' => 'The Story of My Experiments with Truth'],
|
||||||
'arthur-conan-doyle/the-maracot-deep' => ['author' => 'Arthur Conan Doyle', 'title' => 'The Maracot Deep'],
|
EBOOKS_IDENTIFIER_PREFIX . 'arthur-conan-doyle/the-maracot-deep' => ['author' => 'Arthur Conan Doyle', 'title' => 'The Maracot Deep'],
|
||||||
'sinclair-lewis/dodsworth' => ['author' => 'Sinclair Lewis', 'title' => 'Dodsworth'],
|
EBOOKS_IDENTIFIER_PREFIX . 'sinclair-lewis/dodsworth' => ['author' => 'Sinclair Lewis', 'title' => 'Dodsworth'],
|
||||||
'thomas-wolfe/look-homeward-angel' => ['author' => 'Thomas Wolfe', 'title' => 'Look Homeward, Angel'],
|
EBOOKS_IDENTIFIER_PREFIX . 'thomas-wolfe/look-homeward-angel' => ['author' => 'Thomas Wolfe', 'title' => 'Look Homeward, Angel'],
|
||||||
'lloyd-c-douglas/magnificent-obsession' => ['author' => 'Lloyd C. Dougals', 'title' => 'Magnificent Obsession'],
|
EBOOKS_IDENTIFIER_PREFIX . 'lloyd-c-douglas/magnificent-obsession' => ['author' => 'Lloyd C. Dougals', 'title' => 'Magnificent Obsession'],
|
||||||
'edith-wharton/hudson-river-bracketed' => ['author' => 'Edith Wharton', 'title' => 'Hudson River Bracketed']
|
EBOOKS_IDENTIFIER_PREFIX . 'edith-wharton/hudson-river-bracketed' => ['author' => 'Edith Wharton', 'title' => 'Hudson River Bracketed']
|
||||||
];
|
];
|
||||||
|
|
|
@ -766,6 +766,7 @@ class Ebook{
|
||||||
foreach($xml->xpath('/package/metadata/meta[@property="se:subject"]') ?: [] as $tag){
|
foreach($xml->xpath('/package/metadata/meta[@property="se:subject"]') ?: [] as $tag){
|
||||||
$ebookTag = new EbookTag();
|
$ebookTag = new EbookTag();
|
||||||
$ebookTag->Name = $tag;
|
$ebookTag->Name = $tag;
|
||||||
|
$ebookTag->UrlName = Formatter::MakeUrlSafe($ebookTag->Name);
|
||||||
$tags[] = $ebookTag;
|
$tags[] = $ebookTag;
|
||||||
}
|
}
|
||||||
$ebook->Tags = $tags;
|
$ebook->Tags = $tags;
|
||||||
|
@ -1855,17 +1856,26 @@ class Ebook{
|
||||||
throw new Exceptions\EbookNotFoundException('Invalid identifier: ' . $identifier);
|
throw new Exceptions\EbookNotFoundException('Invalid identifier: ' . $identifier);
|
||||||
}
|
}
|
||||||
|
|
||||||
$result = Db::Query('
|
return Db::Query('
|
||||||
SELECT *
|
SELECT *
|
||||||
from Ebooks
|
from Ebooks
|
||||||
where Identifier = ?
|
where Identifier = ?
|
||||||
', [$identifier], Ebook::class);
|
', [$identifier], Ebook::class)[0] ?? throw new Exceptions\EbookNotFoundException('Invalid identifier: ' . $identifier);
|
||||||
|
}
|
||||||
|
|
||||||
if(sizeof($result) == 0){
|
/**
|
||||||
|
* @throws Exceptions\EbookNotFoundException
|
||||||
|
*/
|
||||||
|
public static function GetByIdentifierStartingWith(?string $identifier): Ebook{
|
||||||
|
if($identifier === null){
|
||||||
throw new Exceptions\EbookNotFoundException('Invalid identifier: ' . $identifier);
|
throw new Exceptions\EbookNotFoundException('Invalid identifier: ' . $identifier);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $result[0];
|
return Db::Query('
|
||||||
|
SELECT *
|
||||||
|
from Ebooks
|
||||||
|
where Identifier like concat(?, "%")
|
||||||
|
', [$identifier], Ebook::class)[0] ?? throw new Exceptions\EbookNotFoundException('Invalid identifier: ' . $identifier);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -1,11 +0,0 @@
|
||||||
<?
|
|
||||||
namespace Exceptions;
|
|
||||||
|
|
||||||
class SeeOtherEbookException extends AppException{
|
|
||||||
public string $Url;
|
|
||||||
|
|
||||||
public function __construct(string $url = ''){
|
|
||||||
$this->Url = $url;
|
|
||||||
parent::__construct('This ebook is at a different URL: ' . $url);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -37,7 +37,7 @@ $collection = $collection ?? null;
|
||||||
<? } ?>
|
<? } ?>
|
||||||
</div>
|
</div>
|
||||||
<div class="details">
|
<div class="details">
|
||||||
<? if($ebook->ContributorsHtml !== null){ ?>
|
<? if($ebook->ContributorsHtml != ''){ ?>
|
||||||
<div>
|
<div>
|
||||||
<p><?= rtrim($ebook->ContributorsHtml, '.') ?></p>
|
<p><?= rtrim($ebook->ContributorsHtml, '.') ?></p>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -15,30 +15,7 @@ $carouselTag = null;
|
||||||
$targetCarouselSize = 5;
|
$targetCarouselSize = 5;
|
||||||
|
|
||||||
try{
|
try{
|
||||||
$urlPath = trim(str_replace('.', '', HttpInput::Str(GET, 'url-path') ?? ''), '/'); // Contains the portion of the URL (without query string) that comes after `https://standardebooks.org/ebooks/`.
|
$identifier = EBOOKS_IDENTIFIER_PREFIX . trim(str_replace('.', '', HttpInput::Str(GET, 'url-path') ?? ''), '/'); // Contains the portion of the URL (without query string) that comes after `https://standardebooks.org/ebooks/`.
|
||||||
$wwwFilesystemPath = EBOOKS_DIST_PATH . $urlPath; // Path to the deployed WWW files for this ebook.
|
|
||||||
|
|
||||||
if($urlPath == '' || mb_stripos($wwwFilesystemPath, EBOOKS_DIST_PATH) !== 0){
|
|
||||||
// Ensure the path exists and that the root is in our www directory.
|
|
||||||
throw new Exceptions\EbookNotFoundException();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Were we passed the author and a work but not the translator?
|
|
||||||
// For example: <https://standardebooks.org/ebooks/omar-khayyam/the-rubaiyat-of-omar-khayyam>
|
|
||||||
// Instead of: <https://standardebooks.org/ebooks/omar-khayyam/the-rubaiyat-of-omar-khayyam/edward-fitzgerald/edmund-dulac>.
|
|
||||||
// We can tell because if so, the dir we are passed will exist, but there will be no `src` folder.
|
|
||||||
if(is_dir($wwwFilesystemPath) && !is_dir($wwwFilesystemPath . '/src')){
|
|
||||||
/** @var DirectoryIterator $file */
|
|
||||||
foreach(new RecursiveIteratorIterator(new RecursiveDirectoryIterator($wwwFilesystemPath)) as $file){
|
|
||||||
// This iterator will do a deep scan on the directory. When we hit another directory, the filename will be `.` and the path will contain the directory path.
|
|
||||||
// We want to find where the `src` directory is, and the directory directly below that will be the final web URL we're looking for.
|
|
||||||
if($file->getFilename() == '.' && preg_match('|/src$|ius', $file->getPath())){
|
|
||||||
throw new Exceptions\SeeOtherEbookException(preg_replace(['|' . WEB_ROOT . '|ius', '|/src$|ius'], '', $file->getPath()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$identifier = EBOOKS_IDENTIFIER_PREFIX . $urlPath;
|
|
||||||
|
|
||||||
$ebook = Ebook::GetByIdentifier($identifier);
|
$ebook = Ebook::GetByIdentifier($identifier);
|
||||||
|
|
||||||
|
@ -68,16 +45,27 @@ try{
|
||||||
if(sizeof($ebook->Tags) > 0){
|
if(sizeof($ebook->Tags) > 0){
|
||||||
$carouselTag = $ebook->Tags[rand(0, sizeof($ebook->Tags) - 1)];
|
$carouselTag = $ebook->Tags[rand(0, sizeof($ebook->Tags) - 1)];
|
||||||
}
|
}
|
||||||
|
|
||||||
$carousel = Ebook::GetAllByRelated($ebook, $targetCarouselSize, $carouselTag);
|
$carousel = Ebook::GetAllByRelated($ebook, $targetCarouselSize, $carouselTag);
|
||||||
}
|
}
|
||||||
catch(Exceptions\SeeOtherEbookException $ex){
|
|
||||||
http_response_code(301);
|
|
||||||
header('Location: ' . $ex->Url);
|
|
||||||
exit();
|
|
||||||
}
|
|
||||||
catch(Exceptions\EbookNotFoundException){
|
catch(Exceptions\EbookNotFoundException){
|
||||||
|
// Were we passed the author and a work but not the translator?
|
||||||
|
// For example: <https://standardebooks.org/ebooks/omar-khayyam/the-rubaiyat-of-omar-khayyam>
|
||||||
|
// Instead of: <https://standardebooks.org/ebooks/omar-khayyam/the-rubaiyat-of-omar-khayyam/edward-fitzgerald>.
|
||||||
|
try{
|
||||||
|
$ebook = Ebook::GetByIdentifierStartingWith($identifier);
|
||||||
|
|
||||||
|
// Found, redirect.
|
||||||
|
http_response_code(301);
|
||||||
|
header('Location: ' . $ebook->Url);
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
catch(Exceptions\EbookNotFoundException){
|
||||||
|
// Still not found, continue.
|
||||||
|
}
|
||||||
|
|
||||||
// Are we accessing a placeholder for a Public Domain Day book that is not yet released?
|
// Are we accessing a placeholder for a Public Domain Day book that is not yet released?
|
||||||
if(array_key_exists($urlPath, PD_DAY_EBOOKS)){
|
if(array_key_exists($identifier, PD_DAY_EBOOKS)){
|
||||||
require('/standardebooks.org/web/www/ebooks/public-domain-day-placeholder.php');
|
require('/standardebooks.org/web/www/ebooks/public-domain-day-placeholder.php');
|
||||||
exit();
|
exit();
|
||||||
}
|
}
|
||||||
|
@ -127,7 +115,7 @@ catch(Exceptions\EbookNotFoundException){
|
||||||
|
|
||||||
<aside id="reading-ease">
|
<aside id="reading-ease">
|
||||||
<p><?= number_format($ebook->WordCount) ?> words (<?= $ebook->ReadingTime ?>) with a reading ease of <?= $ebook->ReadingEase ?> (<?= $ebook->ReadingEaseDescription ?>)</p>
|
<p><?= number_format($ebook->WordCount) ?> words (<?= $ebook->ReadingTime ?>) with a reading ease of <?= $ebook->ReadingEase ?> (<?= $ebook->ReadingEaseDescription ?>)</p>
|
||||||
<? if($ebook->ContributorsHtml !== null){ ?>
|
<? if($ebook->ContributorsHtml != ''){ ?>
|
||||||
<p><?= $ebook->ContributorsHtml ?></p>
|
<p><?= $ebook->ContributorsHtml ?></p>
|
||||||
<? } ?>
|
<? } ?>
|
||||||
<? if(sizeof($ebook->CollectionMemberships) > 0){ ?>
|
<? if(sizeof($ebook->CollectionMemberships) > 0){ ?>
|
||||||
|
|
|
@ -1,33 +1,36 @@
|
||||||
<?
|
<?
|
||||||
use function Safe\preg_replace;
|
use function Safe\preg_replace;
|
||||||
|
|
||||||
/** @var string $urlPath Passed from script this is included from. */
|
/** @var string $identifier Passed from script this is included from. */
|
||||||
$ebook = null;
|
$ebook = null;
|
||||||
|
|
||||||
try{
|
try{
|
||||||
try{
|
try{
|
||||||
// Attempt to read a draft ebook repo from the filesystem.
|
// Attempt to read a draft ebook repo from the filesystem.
|
||||||
$ebook = Ebook::FromFilesystem(PD_DAY_DRAFT_PATH . '/' . str_replace('/', '_', $urlPath));
|
$ebook = Ebook::FromFilesystem(PD_DAY_DRAFT_PATH . '/' . str_replace('/', '_', preg_replace('|^' . EBOOKS_IDENTIFIER_PREFIX . '|', '', $identifier)));
|
||||||
}
|
}
|
||||||
catch(Exceptions\EbookNotFoundException $ex){
|
catch(Exceptions\EbookNotFoundException $ex){
|
||||||
// We may have ebooks listed as in progress, but no actual draft repos yet.
|
// We may have ebooks listed as in progress, but no actual draft repos yet.
|
||||||
// In that case, fill in the details from `PD_DAY_EBOOKS`.
|
// In that case, fill in the details from `PD_DAY_EBOOKS`.
|
||||||
if(array_key_exists($urlPath, PD_DAY_EBOOKS)){
|
if(array_key_exists($identifier, PD_DAY_EBOOKS)){
|
||||||
$ebook = new Ebook();
|
$ebook = new Ebook();
|
||||||
|
$ebook->EbookId = 0;
|
||||||
|
$ebook->Description = '';
|
||||||
|
$ebook->LongDescription = '';
|
||||||
|
|
||||||
$c = new Contributor();
|
$c = new Contributor();
|
||||||
$c->Name = PD_DAY_EBOOKS[$urlPath]['author'];
|
$c->Name = PD_DAY_EBOOKS[$identifier]['author'];
|
||||||
$ebook->Authors = [$c];
|
$ebook->Authors = [$c];
|
||||||
|
|
||||||
if(isset(PD_DAY_EBOOKS[$urlPath]['translator'])){
|
if(isset(PD_DAY_EBOOKS[$identifier]['translator'])){
|
||||||
$c = new Contributor();
|
$c = new Contributor();
|
||||||
$c->Name = PD_DAY_EBOOKS[$urlPath]['translator'];
|
$c->Name = PD_DAY_EBOOKS[$identifier]['translator'];
|
||||||
$ebook->Translators = [$c];
|
$ebook->Translators = [$c];
|
||||||
}
|
}
|
||||||
|
|
||||||
$ebook->Title = PD_DAY_EBOOKS[$urlPath]['title'];
|
$ebook->Title = PD_DAY_EBOOKS[$identifier]['title'];
|
||||||
$ebook->WwwFilesystemPath = '';
|
$ebook->WwwFilesystemPath = '';
|
||||||
$ebook->Identifier = 'url:https://standardebooks.org/ebooks/' . $urlPath;
|
$ebook->Identifier = 'url:https://standardebooks.org/ebooks/' . $identifier;
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
throw $ex;
|
throw $ex;
|
||||||
|
@ -59,7 +62,7 @@ catch(Exceptions\EbookNotFoundException){
|
||||||
|
|
||||||
<? if(sizeof($ebook->Tags) > 0){ ?>
|
<? if(sizeof($ebook->Tags) > 0){ ?>
|
||||||
<aside id="reading-ease">
|
<aside id="reading-ease">
|
||||||
<? if($ebook->ContributorsHtml !== null){ ?>
|
<? if($ebook->ContributorsHtml != ''){ ?>
|
||||||
<p><?= $ebook->ContributorsHtml ?></p>
|
<p><?= $ebook->ContributorsHtml ?></p>
|
||||||
<? } ?>
|
<? } ?>
|
||||||
<? if(sizeof($ebook->CollectionMemberships) > 0){ ?>
|
<? if(sizeof($ebook->CollectionMemberships) > 0){ ?>
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue