mirror of
https://github.com/standardebooks/web.git
synced 2025-07-06 06:40:33 -04:00
work
This commit is contained in:
parent
d086ea59bd
commit
7f50f00b42
15 changed files with 303 additions and 130 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -16,3 +16,4 @@ www/manual/*
|
||||||
config/php/fpm/standardebooks.org-secrets.ini
|
config/php/fpm/standardebooks.org-secrets.ini
|
||||||
www/bulk-downloads/months
|
www/bulk-downloads/months
|
||||||
www/bulk-downloads/subjects
|
www/bulk-downloads/subjects
|
||||||
|
www/bulk-downloads/collections
|
||||||
|
|
|
@ -303,7 +303,7 @@ Define webroot /standardebooks.org/web
|
||||||
( \
|
( \
|
||||||
select Email, Uuid from Patrons p inner join Users u using (UserId) where p.Ended is null \
|
select Email, Uuid from Patrons p inner join Users u using (UserId) where p.Ended is null \
|
||||||
union \
|
union \
|
||||||
select Email, Uuid from FeedUsers fu inner join Users u using (UserId) where fu.Ended is null \
|
select Email, Uuid from ApiKeys fu inner join Users u using (UserId) where fu.Ended is null \
|
||||||
) x where %s in (Email, Uuid) limit 1 \
|
) x where %s in (Email, Uuid) limit 1 \
|
||||||
"
|
"
|
||||||
</DirectoryMatch>
|
</DirectoryMatch>
|
||||||
|
|
|
@ -229,6 +229,7 @@ Define webroot /standardebooks.org/web
|
||||||
RewriteRule ^/ebooks/([^\./]+?)$ /ebooks/author.php?url-path=$1 [QSA]
|
RewriteRule ^/ebooks/([^\./]+?)$ /ebooks/author.php?url-path=$1 [QSA]
|
||||||
RewriteRule ^/tags/([^\./]+?)$ /ebooks/index.php?tags[]=$1 [QSA]
|
RewriteRule ^/tags/([^\./]+?)$ /ebooks/index.php?tags[]=$1 [QSA]
|
||||||
RewriteRule ^/collections/([^\./]+?)$ /ebooks/index.php?collection=$1 [QSA]
|
RewriteRule ^/collections/([^\./]+?)$ /ebooks/index.php?collection=$1 [QSA]
|
||||||
|
RewriteRule ^/collections/([^/]+?)/download$ /bulk-downloads/get.php?collection=$1
|
||||||
|
|
||||||
# Prevent this rule from firing if we're getting a distribution file
|
# Prevent this rule from firing if we're getting a distribution file
|
||||||
RewriteCond %{REQUEST_FILENAME} !^/ebooks/.+?/downloads/.+$
|
RewriteCond %{REQUEST_FILENAME} !^/ebooks/.+?/downloads/.+$
|
||||||
|
@ -285,7 +286,7 @@ Define webroot /standardebooks.org/web
|
||||||
( \
|
( \
|
||||||
select Email, Uuid from Patrons p inner join Users u using (UserId) where p.Ended is null \
|
select Email, Uuid from Patrons p inner join Users u using (UserId) where p.Ended is null \
|
||||||
union \
|
union \
|
||||||
select Email, Uuid from FeedUsers fu inner join Users u using (UserId) where fu.Ended is null \
|
select Email, Uuid from ApiKeys fu inner join Users u using (UserId) where fu.Ended is null \
|
||||||
) x where %s in (Email, Uuid) limit 1 \
|
) x where %s in (Email, Uuid) limit 1 \
|
||||||
"
|
"
|
||||||
</DirectoryMatch>
|
</DirectoryMatch>
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
CREATE TABLE `FeedUsers` (
|
CREATE TABLE `ApiKeys` (
|
||||||
`UserId` int(10) unsigned NOT NULL,
|
`UserId` int(10) unsigned NOT NULL,
|
||||||
`Created` datetime NOT NULL,
|
`Created` datetime NOT NULL,
|
||||||
`Ended` datetime DEFAULT NULL,
|
`Ended` datetime DEFAULT NULL,
|
|
@ -54,13 +54,13 @@ class Formatter{
|
||||||
$output = number_format($bytes / 1024, 0) . 'KB';
|
$output = number_format($bytes / 1024, 0) . 'KB';
|
||||||
}
|
}
|
||||||
elseif($bytes > 1){
|
elseif($bytes > 1){
|
||||||
$output = $bytes . 'b';
|
$output = $bytes . 'B';
|
||||||
}
|
}
|
||||||
elseif($bytes == 1){
|
elseif($bytes == 1){
|
||||||
$output = $bytes . 'b';
|
$output = $bytes . 'B';
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
$output = '0b';
|
$output = '0B';
|
||||||
}
|
}
|
||||||
|
|
||||||
return $output;
|
return $output;
|
||||||
|
|
165
lib/Library.php
165
lib/Library.php
|
@ -225,6 +225,70 @@ class Library{
|
||||||
return $ebooks;
|
return $ebooks;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static function FillBulkDownloadObject(string $file, string $downloadType): stdClass{
|
||||||
|
$obj = new stdClass();
|
||||||
|
$obj->Size = Formatter::ToFileSize(filesize($file));
|
||||||
|
$obj->Updated = new DateTime('@' . filemtime($file));
|
||||||
|
|
||||||
|
// The count of ebooks in each file is stored as a filesystem attribute
|
||||||
|
$obj->Count = exec('attr -g se-ebook-count ' . escapeshellarg($file)) ?: null;
|
||||||
|
if($obj->Count !== null){
|
||||||
|
$obj->Count = intval($obj->Count);
|
||||||
|
}
|
||||||
|
|
||||||
|
// The subject of the batch is stored as a filesystem attribute
|
||||||
|
$obj->Label = exec('attr -g se-label ' . escapeshellarg($file)) ?: null;
|
||||||
|
if($obj->Label === null){
|
||||||
|
$obj->Label = str_replace('se-ebooks-', '', basename($file, '.zip'));
|
||||||
|
}
|
||||||
|
|
||||||
|
$obj->UrlLabel = Formatter::MakeUrlSafe($obj->Label);
|
||||||
|
|
||||||
|
$obj->Url = '/bulk-downloads/' . $downloadType . '/' . $obj->UrlLabel . '/' . basename($file);
|
||||||
|
|
||||||
|
// The type of ebook in the zip is stored as a filesystem attribute
|
||||||
|
$obj->Type = exec('attr -g se-ebook-type ' . escapeshellarg($file));
|
||||||
|
if($obj->Type == 'epub-advanced'){
|
||||||
|
$obj->Type = 'epub (advanced)';
|
||||||
|
}
|
||||||
|
|
||||||
|
$obj->UpdatedString = $obj->Updated->format('M j');
|
||||||
|
// Add a period to the abbreviated month, but not if it's May (the only 3-letter month)
|
||||||
|
$obj->UpdatedString = preg_replace('/^(.+?)(?<!May) /', '\1. ', $obj->UpdatedString);
|
||||||
|
if($obj->Updated->format('Y') != gmdate('Y')){
|
||||||
|
$obj->UpdatedString = $obj->Updated->format('M j, Y');
|
||||||
|
}
|
||||||
|
|
||||||
|
return $obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function SortBulkDownloads(array $items): array{
|
||||||
|
// This sorts our items in a special order, epub first and advanced epub last
|
||||||
|
$result = [];
|
||||||
|
|
||||||
|
foreach($items as $key => $item){
|
||||||
|
if($item->Type == 'epub'){
|
||||||
|
$result[0] = $item;
|
||||||
|
}
|
||||||
|
if($item->Type == 'azw3'){
|
||||||
|
$result[1] = $item;
|
||||||
|
}
|
||||||
|
if($item->Type == 'kepub'){
|
||||||
|
$result[2] = $item;
|
||||||
|
}
|
||||||
|
if($item->Type == 'xhtml'){
|
||||||
|
$result[3] = $item;
|
||||||
|
}
|
||||||
|
if($item->Type == 'epub (advanced)'){
|
||||||
|
$result[4] = $item;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ksort($result);
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return array<string, array<int|string, array<int|string, mixed>>>
|
* @return array<string, array<int|string, array<int|string, mixed>>>
|
||||||
*/
|
*/
|
||||||
|
@ -237,37 +301,9 @@ class Library{
|
||||||
rsort($files);
|
rsort($files);
|
||||||
|
|
||||||
foreach($files as $file){
|
foreach($files as $file){
|
||||||
$obj = new stdClass();
|
$obj = self::FillBulkDownloadObject($file, 'months');
|
||||||
$obj->Updated = new DateTime('@' . filemtime($file));
|
|
||||||
$date = new DateTime();
|
|
||||||
|
|
||||||
preg_match('/se-ebooks-(\d+-\d+)/ius', basename($file), $matches);
|
|
||||||
if(sizeof($matches) == 2){
|
|
||||||
$date = new DateTime($matches[1] . '-01');
|
|
||||||
}
|
|
||||||
|
|
||||||
// The type of zip is stored as a filesystem attribute
|
|
||||||
$obj->Type = exec('attr -g se-ebook-type ' . escapeshellarg($file));
|
|
||||||
if($obj->Type == 'epub-advanced'){
|
|
||||||
$obj->Type = 'epub (advanced)';
|
|
||||||
}
|
|
||||||
|
|
||||||
$obj->Month = $date->format('Y-m');
|
|
||||||
$obj->Url = '/bulk-downloads/months/' . $obj->Month . '/' . basename($file);
|
|
||||||
$obj->Size = Formatter::ToFileSize(filesize($file));
|
|
||||||
|
|
||||||
// The count of ebooks in each file is stored as a filesystem attribute
|
|
||||||
$obj->Count = exec('attr -g se-ebook-count ' . escapeshellarg($file)) ?: null;
|
|
||||||
if($obj->Count !== null){
|
|
||||||
$obj->Count = intval($obj->Count);
|
|
||||||
}
|
|
||||||
|
|
||||||
$obj->UpdatedString = $obj->Updated->format('M j');
|
|
||||||
$obj->UpdatedString = preg_replace('/^(.+?)(?<!May) /', '\1. ', $obj->UpdatedString);
|
|
||||||
if($obj->Updated->format('Y') != gmdate('Y')){
|
|
||||||
$obj->UpdatedString = $obj->Updated->format('M j, Y');
|
|
||||||
}
|
|
||||||
|
|
||||||
|
$date = new DateTime($obj->Label . '-01');
|
||||||
$year = $date->format('Y');
|
$year = $date->format('Y');
|
||||||
$month = $date->format('F');
|
$month = $date->format('F');
|
||||||
|
|
||||||
|
@ -285,61 +321,54 @@ class Library{
|
||||||
// Sort the downloads by filename extension
|
// Sort the downloads by filename extension
|
||||||
foreach($years as $year => $months){
|
foreach($years as $year => $months){
|
||||||
foreach($months as $month => $items){
|
foreach($months as $month => $items){
|
||||||
usort($items, function($a, $b){ return $a->Type <=> $b->Type; });
|
$years[$year][$month] = self::SortBulkDownloads($items);
|
||||||
|
|
||||||
// We have to reassign it because the foreach created a clone of the array
|
|
||||||
$years[$year][$month] = $items;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
apcu_store('bulk-downloads-years', $years);
|
apcu_store('bulk-downloads-years', $years);
|
||||||
|
|
||||||
// Generate bulk downloads by year
|
// Generate bulk downloads by subject
|
||||||
$files = glob(WEB_ROOT . '/bulk-downloads/subjects/*/*.zip');
|
$files = glob(WEB_ROOT . '/bulk-downloads/subjects/*/*.zip');
|
||||||
sort($files);
|
sort($files);
|
||||||
|
|
||||||
foreach($files as $file){
|
foreach($files as $file){
|
||||||
$obj = new stdClass();
|
$obj = self::FillBulkDownloadObject($file, 'subjects');
|
||||||
$obj->Url = '/bulk-downloads/' . basename($file);
|
|
||||||
$obj->Size = Formatter::ToFileSize(filesize($file));
|
|
||||||
$obj->Updated = new DateTime('@' . filemtime($file));
|
|
||||||
|
|
||||||
// The count of ebooks in each file is stored as a filesystem attribute
|
if(!isset($subjects[$obj->UrlLabel])){
|
||||||
$obj->Count = exec('attr -g se-ebook-count ' . escapeshellarg($file)) ?: null;
|
$subjects[$obj->UrlLabel] = [];
|
||||||
if($obj->Count !== null){
|
|
||||||
$obj->Count = intval($obj->Count);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// The subject of the batch is stored as a filesystem attribute
|
$subjects[$obj->UrlLabel][] = $obj;
|
||||||
$obj->Subject = exec('attr -g se-subject ' . escapeshellarg($file)) ?: null;
|
|
||||||
if($obj->Subject === null){
|
|
||||||
$obj->Subject = str_replace('se-ebooks-', '', basename($file, '.zip'));
|
|
||||||
}
|
|
||||||
|
|
||||||
// The type of zip is stored as a filesystem attribute
|
|
||||||
$obj->Type = exec('attr -g se-ebook-type ' . escapeshellarg($file));
|
|
||||||
if($obj->Type == 'epub-advanced'){
|
|
||||||
$obj->Type = 'epub (advanced)';
|
|
||||||
}
|
|
||||||
|
|
||||||
$obj->UpdatedString = $obj->Updated->format('M j');
|
|
||||||
$obj->UpdatedString = preg_replace('/^(.+?)(?<!May) /', '\1. ', $obj->UpdatedString);
|
|
||||||
if($obj->Updated->format('Y') != gmdate('Y')){
|
|
||||||
$obj->UpdatedString = $obj->Updated->format('M j, Y');
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!isset($subjects[$obj->Subject])){
|
|
||||||
$subjects[$obj->Subject] = [];
|
|
||||||
}
|
|
||||||
|
|
||||||
$subjects[$obj->Subject][] = $obj;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Subjects downloads are already correctly sorted
|
foreach($subjects as $subject => $items){
|
||||||
|
$subjects[$subject] = self::SortBulkDownloads($items);
|
||||||
|
}
|
||||||
|
|
||||||
apcu_store('bulk-downloads-subjects', $subjects);
|
apcu_store('bulk-downloads-subjects', $subjects);
|
||||||
|
|
||||||
return ['years' => $years, 'subjects' => $subjects];
|
|
||||||
|
// Generate bulk downloads by collection
|
||||||
|
$files = glob(WEB_ROOT . '/bulk-downloads/collections/*/*.zip');
|
||||||
|
sort($files);
|
||||||
|
|
||||||
|
foreach($files as $file){
|
||||||
|
$obj = self::FillBulkDownloadObject($file, 'collections');
|
||||||
|
|
||||||
|
if(!isset($collections[$obj->UrlLabel])){
|
||||||
|
$collections[$obj->UrlLabel] = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
$collections[$obj->UrlLabel][] = $obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach($collections as $collection => $items){
|
||||||
|
$collections[$collection] = self::SortBulkDownloads($items);
|
||||||
|
}
|
||||||
|
|
||||||
|
apcu_store('bulk-downloads-collections', $collections);
|
||||||
|
|
||||||
|
return ['years' => $years, 'subjects' => $subjects, 'collections' => $collections];
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function RebuildCache(): void{
|
public static function RebuildCache(): void{
|
||||||
|
|
17
lib/User.php
17
lib/User.php
|
@ -83,4 +83,21 @@ class User extends PropertiesBase{
|
||||||
|
|
||||||
return $result[0];
|
return $result[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get a user if by either email or uuid, ONLY IF they're either a patron or have a valid API key.
|
||||||
|
public static function GetByPatronIdentifier(?string $identifier): User{
|
||||||
|
if($identifier === null){
|
||||||
|
throw new Exceptions\InvalidUserException();
|
||||||
|
}
|
||||||
|
|
||||||
|
$result = Db::Query('SELECT u.* from Patrons p inner join Users u using (UserId) where p.Ended is null and (u.Email = ? or u.Uuid = ?)
|
||||||
|
union
|
||||||
|
select u.* from ApiKeys fu inner join Users u using (UserId) where fu.Ended is null and (u.Email = ? or u.Uuid = ?)', [$identifier, $identifier, $identifier, $identifier], 'User');
|
||||||
|
|
||||||
|
if(sizeof($result) == 0){
|
||||||
|
throw new Exceptions\InvalidUserException();
|
||||||
|
}
|
||||||
|
|
||||||
|
return $result[0];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,10 +11,12 @@ $webRoot = $options['webroot'] ?? WEB_ROOT;
|
||||||
$ebooksByMonth = [];
|
$ebooksByMonth = [];
|
||||||
$lastUpdatedTimestampsByMonth = [];
|
$lastUpdatedTimestampsByMonth = [];
|
||||||
$subjects = [];
|
$subjects = [];
|
||||||
|
$collections = [];
|
||||||
$ebooksBySubject = [];
|
$ebooksBySubject = [];
|
||||||
$lastUpdatedTimestampsBySubject = [];
|
$lastUpdatedTimestampsBySubject = [];
|
||||||
|
$lastUpdatedTimestampsByCollection = [];
|
||||||
|
|
||||||
function CreateZip(string $filePath, array $ebooks, string $type, string $webRoot, ?string $subject = null, ?string $month = null): void{
|
function CreateZip(string $filePath, array $ebooks, string $type, string $webRoot, string $label): void{
|
||||||
$tempFilename = tempnam(sys_get_temp_dir(), "se-ebooks");
|
$tempFilename = tempnam(sys_get_temp_dir(), "se-ebooks");
|
||||||
|
|
||||||
$zip = new ZipArchive();
|
$zip = new ZipArchive();
|
||||||
|
@ -70,14 +72,7 @@ function CreateZip(string $filePath, array $ebooks, string $type, string $webRoo
|
||||||
|
|
||||||
exec('attr -q -s se-ebook-type -V ' . escapeshellarg($type) . ' ' . escapeshellarg($filePath));
|
exec('attr -q -s se-ebook-type -V ' . escapeshellarg($type) . ' ' . escapeshellarg($filePath));
|
||||||
|
|
||||||
// If we're passed a subject, add it as a file attribute too
|
exec('attr -q -s se-label -V ' . escapeshellarg($label) . ' ' . escapeshellarg($filePath));
|
||||||
if($subject !== null){
|
|
||||||
exec('attr -q -s se-subject -V ' . escapeshellarg($subject) . ' ' . escapeshellarg($filePath));
|
|
||||||
}
|
|
||||||
|
|
||||||
if($month !== null){
|
|
||||||
exec('attr -q -s se-month -V ' . escapeshellarg($month) . ' ' . escapeshellarg($filePath));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Iterate over all ebooks and arrange them by publication month
|
// Iterate over all ebooks and arrange them by publication month
|
||||||
|
@ -112,6 +107,22 @@ foreach(Library::GetEbooksFromFilesystem($webRoot) as $ebook){
|
||||||
$lastUpdatedTimestampsBySubject[$tag->Name] = $updatedTimestamp;
|
$lastUpdatedTimestampsBySubject[$tag->Name] = $updatedTimestamp;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Add to the 'books by collection' list
|
||||||
|
foreach($ebook->Collections as $collection){
|
||||||
|
// Add the book's subjects to the main subjects list
|
||||||
|
if(!in_array($collection->Name, $collections)){
|
||||||
|
$collections[] = $collection->Name;
|
||||||
|
$lastUpdatedTimestampsByCollection[$collection->Name] = $updatedTimestamp;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sort this ebook by subject
|
||||||
|
$ebooksByCollection[$collection->Name][] = $ebook;
|
||||||
|
|
||||||
|
if($updatedTimestamp > $lastUpdatedTimestampsByCollection[$collection->Name]){
|
||||||
|
$lastUpdatedTimestampsByCollection[$collection->Name] = $updatedTimestamp;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$types = ['epub', 'epub-advanced', 'azw3', 'kepub', 'xhtml'];
|
$types = ['epub', 'epub-advanced', 'azw3', 'kepub', 'xhtml'];
|
||||||
|
@ -125,7 +136,7 @@ foreach($ebooksByMonth as $month => $ebooks){
|
||||||
if(!file_exists($filePath) || filemtime($filePath) < $lastUpdatedTimestampsByMonth[$month]){
|
if(!file_exists($filePath) || filemtime($filePath) < $lastUpdatedTimestampsByMonth[$month]){
|
||||||
print('Creating ' . $filePath . "\n");
|
print('Creating ' . $filePath . "\n");
|
||||||
|
|
||||||
CreateZip($filePath, $ebooks, $type, $webRoot, null, $month);
|
CreateZip($filePath, $ebooks, $type, $webRoot, $month);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -140,7 +151,22 @@ foreach($ebooksBySubject as $subject => $ebooks){
|
||||||
if(!file_exists($filePath) || filemtime($filePath) < $lastUpdatedTimestampsBySubject[$subject]){
|
if(!file_exists($filePath) || filemtime($filePath) < $lastUpdatedTimestampsBySubject[$subject]){
|
||||||
print('Creating ' . $filePath . "\n");
|
print('Creating ' . $filePath . "\n");
|
||||||
|
|
||||||
CreateZip($filePath, $ebooks, $type, $webRoot, $subject, null);
|
CreateZip($filePath, $ebooks, $type, $webRoot, $subject);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach($ebooksByCollection as $collection => $ebooks){
|
||||||
|
foreach($types as $type){
|
||||||
|
$urlSafeCollection = Formatter::MakeUrlSafe($collection);
|
||||||
|
$filename = 'se-ebooks-' . $urlSafeCollection . '-' . $type . '.zip';
|
||||||
|
$filePath = $webRoot . '/bulk-downloads/collections/' . $urlSafeCollection . '/'. $filename;
|
||||||
|
|
||||||
|
// If the file doesn't exist, or if the content.opf last updated time is newer than the file modification time
|
||||||
|
if(!file_exists($filePath) || filemtime($filePath) < $lastUpdatedTimestampsByCollection[$collection]){
|
||||||
|
print('Creating ' . $filePath . "\n");
|
||||||
|
|
||||||
|
CreateZip($filePath, $ebooks, $type, $webRoot, $collection);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
24
templates/BulkDownloadTable.php
Normal file
24
templates/BulkDownloadTable.php
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
<table class="download-list">
|
||||||
|
<thead>
|
||||||
|
<tr class="mid-header">
|
||||||
|
<th scope="col"><?= $label ?></th>
|
||||||
|
<th scope="col">Ebooks</th>
|
||||||
|
<th scope="col">Updated</th>
|
||||||
|
<th scope="col" colspan="10">Ebook format</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<? foreach($collections as $collection => $items){ ?>
|
||||||
|
<tr>
|
||||||
|
<td class="row-header"><a href="/collections/<?= Formatter::MakeUrlSafe($items[0]->Label) ?>"><?= Formatter::ToPlainText($items[0]->Label) ?></a></td>
|
||||||
|
<td class="number"><?= Formatter::ToPlainText(number_format($items[0]->Count)) ?></td>
|
||||||
|
<td class="number"><?= Formatter::ToPlainText($items[0]->UpdatedString) ?></td>
|
||||||
|
|
||||||
|
<? foreach($items as $item){ ?>
|
||||||
|
<td class="download"><a href="<?= $item->Url ?>" download=""><?= $item->Type ?></a></td>
|
||||||
|
<td>(<?= Formatter::ToPlainText($item->Size) ?>)</td>
|
||||||
|
<? } ?>
|
||||||
|
</tr>
|
||||||
|
<? } ?>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
57
www/bulk-downloads/get.php
Normal file
57
www/bulk-downloads/get.php
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
<?
|
||||||
|
require_once('Core.php');
|
||||||
|
|
||||||
|
use function Safe\apcu_fetch;
|
||||||
|
|
||||||
|
$bulkDownloadCollection = null;
|
||||||
|
$exception = null;
|
||||||
|
$user = null;
|
||||||
|
|
||||||
|
try{
|
||||||
|
if(isset($_SERVER['PHP_AUTH_USER'])){
|
||||||
|
$user = User::GetByPatronIdentifier($_SERVER['PHP_AUTH_USER']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch(Exceptions\InvalidUserException $ex){
|
||||||
|
$exception = new Exceptions\InvalidPatronException();
|
||||||
|
}
|
||||||
|
|
||||||
|
try{
|
||||||
|
|
||||||
|
$collection = HttpInput::Str(GET, 'collection', false) ?? '';
|
||||||
|
$collections = [];
|
||||||
|
|
||||||
|
try{
|
||||||
|
$collections = apcu_fetch('bulk-downloads-collections');
|
||||||
|
}
|
||||||
|
catch(Safe\Exceptions\ApcuException $ex){
|
||||||
|
$result = Library::RebuildBulkDownloadsCache();
|
||||||
|
$collections = $result['collections'];
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!isset($collections[$collection]) || sizeof($collections[$collection]) == 0){
|
||||||
|
throw new Exceptions\InvalidCollectionException();
|
||||||
|
}
|
||||||
|
|
||||||
|
$bulkDownloadCollection = $collections[$collection];
|
||||||
|
}
|
||||||
|
catch(Exceptions\InvalidCollectionException $ex){
|
||||||
|
Template::Emit404();
|
||||||
|
}
|
||||||
|
|
||||||
|
?><?= Template::Header(['title' => 'Download ', 'highlight' => '', 'description' => 'Download zip files containing all of the Standard Ebooks released in a given month.']) ?>
|
||||||
|
<main>
|
||||||
|
<section class="bulk-downloads">
|
||||||
|
<h1>Download the <?= $bulkDownloadCollection[0]->Label ?> Collection</h1>
|
||||||
|
<?= Template::Error(['exception' => $exception]) ?>
|
||||||
|
<? if($user === null){ ?>
|
||||||
|
<p><a href="/about#patrons-circle">Patrons circle members</a> can download zip files containing all of the ebooks in a collection. You can <a href="/donate#patrons-circle">join the Patrons Circle</a> with a small donation in support of our continuing mission to create free, beautiful digital literature.</p>
|
||||||
|
<p>If you’re a Patrons Circle member, when prompted enter your email address and leave the password field blank to download this collection.</p>
|
||||||
|
<? }else{ ?>
|
||||||
|
<p>Select the ebook format in which you’d like to download this collection.</p>
|
||||||
|
<p>You can also read about <a href="/help/how-to-use-our-ebooks#which-file-to-download">which ebook format to download</a>.</p>
|
||||||
|
<? } ?>
|
||||||
|
<?= Template::BulkDownloadTable(['label' => 'Collection', 'collections' => [$bulkDownloadCollection]]); ?>
|
||||||
|
</section>
|
||||||
|
</main>
|
||||||
|
<?= Template::Footer() ?>
|
|
@ -14,15 +14,18 @@ if(isset($_SERVER['PHP_AUTH_USER'])){
|
||||||
|
|
||||||
$years = [];
|
$years = [];
|
||||||
$subjects = [];
|
$subjects = [];
|
||||||
|
$collections = [];
|
||||||
|
|
||||||
try{
|
try{
|
||||||
$years = apcu_fetch('bulk-downloads-years');
|
$years = apcu_fetch('bulk-downloads-years');
|
||||||
$subjects = apcu_fetch('bulk-downloads-subjects');
|
$subjects = apcu_fetch('bulk-downloads-subjects');
|
||||||
|
$collections = apcu_fetch('bulk-downloads-collections');
|
||||||
}
|
}
|
||||||
catch(Safe\Exceptions\ApcuException $ex){
|
catch(Safe\Exceptions\ApcuException $ex){
|
||||||
$result = Library::RebuildBulkDownloadsCache();
|
$result = Library::RebuildBulkDownloadsCache();
|
||||||
$years = $result['years'];
|
$years = $result['years'];
|
||||||
$subjects = $result['subjects'];
|
$subjects = $result['subjects'];
|
||||||
|
$collections = $result['collections'];
|
||||||
}
|
}
|
||||||
|
|
||||||
?><?= Template::Header(['title' => 'Bulk Ebook Downloads', 'highlight' => '', 'description' => 'Download zip files containing all of the Standard Ebooks released in a given month.']) ?>
|
?><?= Template::Header(['title' => 'Bulk Ebook Downloads', 'highlight' => '', 'description' => 'Download zip files containing all of the Standard Ebooks released in a given month.']) ?>
|
||||||
|
@ -35,11 +38,7 @@ catch(Safe\Exceptions\ApcuException $ex){
|
||||||
<img src="/images/the-shop-of-the-bookdealer@2x.jpg" alt="A gentleman in regency-era dress buys books from a bookseller."/>
|
<img src="/images/the-shop-of-the-bookdealer@2x.jpg" alt="A gentleman in regency-era dress buys books from a bookseller."/>
|
||||||
</picture>
|
</picture>
|
||||||
<? if($forbiddenException !== null){ ?>
|
<? if($forbiddenException !== null){ ?>
|
||||||
<ul class="message error">
|
<?= Template::Error(['exception' => $forbiddenException]) ?>
|
||||||
<li>
|
|
||||||
<p><?= Formatter::ToPlainText($forbiddenException->getMessage()) ?></p>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
<? } ?>
|
<? } ?>
|
||||||
<p><a href="/about#patrons-circle">Patrons circle members</a> can download zip files containing all of the ebooks that were released in a given month of Standard Ebooks history. You can <a href="/donate#patrons-circle">join the Patrons Circle</a> with a small donation in support of our continuing mission to create free, beautiful digital literature.</p>
|
<p><a href="/about#patrons-circle">Patrons circle members</a> can download zip files containing all of the ebooks that were released in a given month of Standard Ebooks history. You can <a href="/donate#patrons-circle">join the Patrons Circle</a> with a small donation in support of our continuing mission to create free, beautiful digital literature.</p>
|
||||||
<p>These zip files contain each ebook in every format we offer, and are updated once daily with the latest versions of each ebook.</p>
|
<p>These zip files contain each ebook in every format we offer, and are updated once daily with the latest versions of each ebook.</p>
|
||||||
|
@ -47,54 +46,36 @@ catch(Safe\Exceptions\ApcuException $ex){
|
||||||
|
|
||||||
<section id="downloads-by-subject">
|
<section id="downloads-by-subject">
|
||||||
<h2>Downloads by subject</h2>
|
<h2>Downloads by subject</h2>
|
||||||
<table class="download-list">
|
<?= Template::BulkDownloadTable(['label' => 'Subject', 'collections' => $subjects]); ?>
|
||||||
<thead>
|
</section>
|
||||||
<tr class="mid-header">
|
|
||||||
<td></td>
|
|
||||||
<th scope="col">Ebooks</th>
|
|
||||||
<th scope="col">Updated</th>
|
|
||||||
<th scope="col" colspan="10">Download</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
<? foreach($subjects as $subject => $items){ ?>
|
|
||||||
<tr>
|
|
||||||
<td class="row-header"><?= Formatter::ToPlainText($subject) ?></td>
|
|
||||||
<td class="number"><?= Formatter::ToPlainText(number_format($items[0]->Count)) ?></td>
|
|
||||||
<td class="number"><?= Formatter::ToPlainText($items[0]->UpdatedString) ?></td>
|
|
||||||
|
|
||||||
<? foreach($items as $item){ ?>
|
<section id="downloads-by-collection">
|
||||||
<td class="download"><a href="<?= $item->Url ?>" download=""><?= $item->Type ?></a></td>
|
<h2>Downloads by collection</h2>
|
||||||
<td>(<?= Formatter::ToPlainText($item->Size) ?>)</td>
|
<?= Template::BulkDownloadTable(['label' => 'Collection', 'collections' => $collections]); ?>
|
||||||
<? } ?>
|
|
||||||
</tr>
|
|
||||||
<? } ?>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section id="downloads-by-year">
|
<section id="downloads-by-year">
|
||||||
<h2>Downloads by year</h2>
|
<h2>Downloads by month</h2>
|
||||||
<table class="download-list">
|
<table class="download-list">
|
||||||
<tbody>
|
<tbody>
|
||||||
<? foreach($years as $year => $months){
|
<? foreach($years as $year => $months){
|
||||||
$yearHeader = Formatter::ToPlainText((string)$year);
|
$yearHeader = Formatter::ToPlainText($year);
|
||||||
?>
|
?>
|
||||||
<tr class="year-header">
|
<tr class="year-header">
|
||||||
<th colspan="13" scope="colgroup" id="<?= $yearHeader ?>"><?= Formatter::ToPlainText((string)$year) ?></th>
|
<th colspan="13" scope="colgroup" id="<?= $yearHeader ?>"><?= Formatter::ToPlainText((string)$year) ?></th>
|
||||||
</tr>
|
</tr>
|
||||||
<tr class="mid-header">
|
<tr class="mid-header">
|
||||||
<td></td>
|
<th id="<?= $yearHeader?>-type" scope="col">Month</th>
|
||||||
<th id="<?= $yearHeader ?>-ebooks" scope="col">Ebooks</th>
|
<th id="<?= $yearHeader ?>-ebooks" scope="col">Ebooks</th>
|
||||||
<th id="<?= $yearHeader ?>-updated" scope="col">Updated</th>
|
<th id="<?= $yearHeader ?>-updated" scope="col">Updated</th>
|
||||||
<th id="<?= $yearHeader ?>-download" colspan="10" scope="col">Download</th>
|
<th id="<?= $yearHeader ?>-download" colspan="10" scope="col">Ebook format</th>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
<? foreach($months as $month => $items){
|
<? foreach($months as $month => $items){
|
||||||
$monthHeader = $items[0]->Month;
|
$monthHeader = Formatter::ToPlainText($month);
|
||||||
?>
|
?>
|
||||||
<tr>
|
<tr>
|
||||||
<th class="row-header" headers="<?= $yearHeader ?>" id="<?= $monthHeader ?>"><?= Formatter::ToPlainText($month) ?></th>
|
<th class="row-header" headers="<?= $yearHeader ?> <?= $monthHeader ?> <?= $yearHeader ?>-type" id="<?= $monthHeader ?>"><?= Formatter::ToPlainText($month) ?></th>
|
||||||
<td class="number" headers="<?= $yearHeader ?> <?= $monthHeader ?> <?= $yearHeader ?>-ebooks"><?= Formatter::ToPlainText(number_format($items[0]->Count)) ?></td>
|
<td class="number" headers="<?= $yearHeader ?> <?= $monthHeader ?> <?= $yearHeader ?>-ebooks"><?= Formatter::ToPlainText(number_format($items[0]->Count)) ?></td>
|
||||||
<td class="number" headers="<?= $yearHeader ?> <?= $monthHeader ?> <?= $yearHeader ?>-updated"><?= Formatter::ToPlainText($items[0]->UpdatedString) ?></td>
|
<td class="number" headers="<?= $yearHeader ?> <?= $monthHeader ?> <?= $yearHeader ?>-updated"><?= Formatter::ToPlainText($items[0]->UpdatedString) ?></td>
|
||||||
<? foreach($items as $item){ ?>
|
<? foreach($items as $item){ ?>
|
||||||
|
|
|
@ -63,6 +63,7 @@
|
||||||
--light-input-hover: #000;
|
--light-input-hover: #000;
|
||||||
--light-input-border: #777;
|
--light-input-border: #777;
|
||||||
--light-input-outline: #000;
|
--light-input-outline: #000;
|
||||||
|
--light-table-row-hover: #dddbd5;
|
||||||
|
|
||||||
--dark-body-bg: #2c3035;
|
--dark-body-bg: #2c3035;
|
||||||
--dark-body-text: #fff;
|
--dark-body-text: #fff;
|
||||||
|
@ -74,6 +75,7 @@
|
||||||
--dark-input-border: #aaa;
|
--dark-input-border: #aaa;
|
||||||
--dark-input-hover: #118460;
|
--dark-input-hover: #118460;
|
||||||
--dark-input-outline: #fff;
|
--dark-input-outline: #fff;
|
||||||
|
--dark-table-row-hover: #373b3f;
|
||||||
|
|
||||||
--body-text: var(--light-body-text);
|
--body-text: var(--light-body-text);
|
||||||
--header: var(--light-header);
|
--header: var(--light-header);
|
||||||
|
@ -87,6 +89,7 @@
|
||||||
--input-border: var(--light-input-border);
|
--input-border: var(--light-input-border);
|
||||||
--input-outline: var(--light-input-outline);
|
--input-outline: var(--light-input-outline);
|
||||||
--link-highlight: var(--button);
|
--link-highlight: var(--button);
|
||||||
|
--table-row-hover: var(--light-table-row-hover);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Start CSS reset */
|
/* Start CSS reset */
|
||||||
|
@ -676,13 +679,17 @@ ul.message.error li:only-child{
|
||||||
}
|
}
|
||||||
|
|
||||||
.bulk-downloads > p,
|
.bulk-downloads > p,
|
||||||
.bulk-downloads > section > h2{
|
.bulk-downloads h2{
|
||||||
width: 100%;
|
width: 100%;
|
||||||
max-width: 40rem;
|
max-width: 40rem;
|
||||||
margin-left: auto;
|
margin-left: auto;
|
||||||
margin-right: auto;
|
margin-right: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.bulk-downloads h2{
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
.download-list{
|
.download-list{
|
||||||
margin: auto;
|
margin: auto;
|
||||||
}
|
}
|
||||||
|
@ -692,9 +699,11 @@ ul.message.error li:only-child{
|
||||||
}
|
}
|
||||||
|
|
||||||
.download-list thead tr.mid-header:first-child > *{
|
.download-list thead tr.mid-header:first-child > *{
|
||||||
padding-top: 1rem;
|
padding-top: 2rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.download-list th.row-header,
|
||||||
|
.download-list .mid-header th:first-child,
|
||||||
.download-list .mid-header th:last-child{
|
.download-list .mid-header th:last-child{
|
||||||
text-align: left;
|
text-align: left;
|
||||||
}
|
}
|
||||||
|
@ -728,6 +737,7 @@ ul.message.error li:only-child{
|
||||||
|
|
||||||
.download-list tbody .row-header{
|
.download-list tbody .row-header{
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
|
white-space: normal;
|
||||||
}
|
}
|
||||||
|
|
||||||
.download-list tbody tr td,
|
.download-list tbody tr td,
|
||||||
|
@ -735,6 +745,7 @@ ul.message.error li:only-child{
|
||||||
border-top: 1px dashed var(--table-border);
|
border-top: 1px dashed var(--table-border);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.download-list tbody tr:first-child > *,
|
||||||
.download-list tbody tr.year-header > *,
|
.download-list tbody tr.year-header > *,
|
||||||
.download-list tbody tr.year-header + tr > *,
|
.download-list tbody tr.year-header + tr > *,
|
||||||
.download-list tbody tr.mid-header tr > *,
|
.download-list tbody tr.mid-header tr > *,
|
||||||
|
@ -743,11 +754,22 @@ ul.message.error li:only-child{
|
||||||
border: none;
|
border: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.download-list tbody tr:not([class]):hover > *{
|
||||||
|
background: var(--table-row-hover);
|
||||||
|
}
|
||||||
|
|
||||||
|
.download-list tbody tr:only-child:not([class]):hover > *{
|
||||||
|
background: unset; /* Don't highlight on hover if there's only one row */
|
||||||
|
}
|
||||||
|
|
||||||
|
h2 + .download-list tr.year-header:first-child th{
|
||||||
|
padding-top: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
.download-list .year-header th{
|
.download-list .year-header th{
|
||||||
padding-top: 4rem;
|
padding-top: 4rem;
|
||||||
font-size: 1.4rem;
|
font-size: 1.4rem;
|
||||||
font-family: "League Spartan", Arial, sans-serif;
|
font-family: "League Spartan", Arial, sans-serif;
|
||||||
margin-top: 4rem;
|
|
||||||
line-height: 1.2;
|
line-height: 1.2;
|
||||||
letter-spacing: 1px;
|
letter-spacing: 1px;
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
|
@ -2278,6 +2300,12 @@ article.step-by-step-guide ol ol{
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.download-collection{
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
margin-bottom: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
abbr.acronym{
|
abbr.acronym{
|
||||||
font-variant: all-small-caps;
|
font-variant: all-small-caps;
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
--input-border: var(--dark-input-border);
|
--input-border: var(--dark-input-border);
|
||||||
--input-outline: var(--dark-input-outline);
|
--input-outline: var(--dark-input-outline);
|
||||||
--link-highlight: var(--button-highlight); /* lighter looks better in dark mode */
|
--link-highlight: var(--button-highlight); /* lighter looks better in dark mode */
|
||||||
|
--table-row-hover: var(--dark-table-row-hover);
|
||||||
}
|
}
|
||||||
|
|
||||||
main.front-page > section > section figure img{
|
main.front-page > section > section figure img{
|
||||||
|
|
|
@ -338,10 +338,14 @@ catch(Exceptions\InvalidEbookException $ex){
|
||||||
<? foreach($transcriptionSources as $source){ ?>
|
<? foreach($transcriptionSources as $source){ ?>
|
||||||
<li>
|
<li>
|
||||||
<p>
|
<p>
|
||||||
<? if($source->Type == SOURCE_PROJECT_GUTENBERG){ ?><a href="<?= Formatter::ToPlainText($source->Url) ?>" class="project-gutenberg">Transcription at Project Gutenberg</a><? } ?>
|
<? if($source->Type == SOURCE_PROJECT_GUTENBERG){ ?><a href="<?= Formatter::ToPlainText($source->Url) ?>" class="project-gutenberg">Transcription at Project Gutenberg</a>
|
||||||
<? if($source->Type == SOURCE_PROJECT_GUTENBERG_AUSTRALIA){ ?><a href="<?= Formatter::ToPlainText($source->Url) ?>" class="project-gutenberg">Transcription at Project Gutenberg Australia</a><? } ?>
|
<? }elseif($source->Type == SOURCE_PROJECT_GUTENBERG_AUSTRALIA){ ?><a href="<?= Formatter::ToPlainText($source->Url) ?>" class="project-gutenberg">Transcription at Project Gutenberg Australia</a>
|
||||||
<? if($source->Type == SOURCE_PROJECT_GUTENBERG_CANADA){ ?><a href="<?= Formatter::ToPlainText($source->Url) ?>" class="project-gutenberg">Transcription at Project Gutenberg Canada</a><? } ?>
|
<? }elseif($source->Type == SOURCE_PROJECT_GUTENBERG_CANADA){ ?><a href="<?= Formatter::ToPlainText($source->Url) ?>" class="project-gutenberg">Transcription at Project Gutenberg Canada</a>
|
||||||
<? if($source->Type == SOURCE_WIKISOURCE){ ?><a href="<?= Formatter::ToPlainText($source->Url) ?>" class="wikisource">Transcription at Wikisource</a><? } ?>
|
<? }elseif($source->Type == SOURCE_WIKISOURCE){ ?><a href="<?= Formatter::ToPlainText($source->Url) ?>" class="wikisource">Transcription at Wikisource</a>
|
||||||
|
<? }elseif($source->Type == SOURCE_FADED_PAGE){ ?><a href="<?= Formatter::ToPlainText($source->Url) ?>" class="globe">Transcription at Faded Page</a>
|
||||||
|
<? }else{?>
|
||||||
|
<a href="<?= Formatter::ToPlainText($source->Url) ?>" class="globe">Transcription</a>
|
||||||
|
<? } ?>
|
||||||
</p>
|
</p>
|
||||||
</li>
|
</li>
|
||||||
<? } ?>
|
<? } ?>
|
||||||
|
@ -355,10 +359,10 @@ catch(Exceptions\InvalidEbookException $ex){
|
||||||
<? foreach($scanSources as $source){ ?>
|
<? foreach($scanSources as $source){ ?>
|
||||||
<li>
|
<li>
|
||||||
<p>
|
<p>
|
||||||
<? if($source->Type == SOURCE_INTERNET_ARCHIVE){ ?><a href="<?= Formatter::ToPlainText($source->Url) ?>" class="internet-archive">Page scans at the Internet Archive</a><? } ?>
|
<? if($source->Type == SOURCE_INTERNET_ARCHIVE){ ?><a href="<?= Formatter::ToPlainText($source->Url) ?>" class="internet-archive">Page scans at the Internet Archive</a>
|
||||||
<? if($source->Type == SOURCE_HATHI_TRUST){ ?><a href="<?= Formatter::ToPlainText($source->Url) ?>" class="hathitrust">Page scans at HathiTrust</a><? } ?>
|
<? }elseif($source->Type == SOURCE_HATHI_TRUST){ ?><a href="<?= Formatter::ToPlainText($source->Url) ?>" class="hathitrust">Page scans at HathiTrust</a>
|
||||||
<? if($source->Type == SOURCE_GOOGLE_BOOKS){ ?><a href="<?= Formatter::ToPlainText($source->Url) ?>" class="google">Page scans at Google Books</a><? } ?>
|
<? }elseif($source->Type == SOURCE_GOOGLE_BOOKS){ ?><a href="<?= Formatter::ToPlainText($source->Url) ?>" class="google">Page scans at Google Books</a>
|
||||||
<? if($source->Type == SOURCE_FADED_PAGE){ ?><a href="<?= Formatter::ToPlainText($source->Url) ?>" class="globe">Transcription at Faded Page</a><? } ?>
|
<? }else{ ?><a href="<?= Formatter::ToPlainText($source->Url) ?>" class="globe">Page scans</a><? } ?>
|
||||||
</p>
|
</p>
|
||||||
</li>
|
</li>
|
||||||
<? } ?>
|
<? } ?>
|
||||||
|
|
|
@ -135,6 +135,9 @@ catch(Exceptions\InvalidCollectionException $ex){
|
||||||
<? if($collection === null){ ?>
|
<? if($collection === null){ ?>
|
||||||
<?= 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($collection !== null && sizeof($ebooks) > 1){ ?>
|
||||||
|
<p class="download-collection"><a class="button" href="/collections/<?= Formatter::ToPlainText($collection) ?>/download">Download entire collection</a></p>
|
||||||
|
<? } ?>
|
||||||
<? 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>
|
||||||
<? }else{ ?>
|
<? }else{ ?>
|
||||||
|
@ -151,6 +154,7 @@ catch(Exceptions\InvalidCollectionException $ex){
|
||||||
<a<? if($page < ceil($totalEbooks / $perPage)){ ?> href="/ebooks/?page=<?= $page + 1 ?><? if($queryString != ''){ ?>&<?= $queryString ?><? } ?>" rel="next"<? }else{ ?> aria-disabled="true"<? } ?>>Next</a>
|
<a<? if($page < ceil($totalEbooks / $perPage)){ ?> href="/ebooks/?page=<?= $page + 1 ?><? if($queryString != ''){ ?>&<?= $queryString ?><? } ?>" rel="next"<? }else{ ?> aria-disabled="true"<? } ?>>Next</a>
|
||||||
</nav>
|
</nav>
|
||||||
<? } ?>
|
<? } ?>
|
||||||
|
|
||||||
<p class="feeds-alert">We also have <a href="/bulk-downloads">bulk ebook downloads</a> available, as well as <a href="/feeds">ebook catalog feeds</a> for use directly in your ereader app or RSS reader.</p>
|
<p class="feeds-alert">We also have <a href="/bulk-downloads">bulk ebook downloads</a> available, as well as <a href="/feeds">ebook catalog feeds</a> for use directly in your ereader app or RSS reader.</p>
|
||||||
<? if(sizeof($ebooks) > 0 && $query == '' && sizeof($tags) == 0 && $collection === null && $page == 1){ ?>
|
<? if(sizeof($ebooks) > 0 && $query == '' && sizeof($tags) == 0 && $collection === null && $page == 1){ ?>
|
||||||
<?= Template::ContributeAlert() ?>
|
<?= Template::ContributeAlert() ?>
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue