mirror of
https://github.com/standardebooks/web.git
synced 2025-07-07 15:20:32 -04:00
Add collections and authors RSS/Atom/OPDS feeds
This commit is contained in:
parent
e19847adac
commit
05e0f77b45
19 changed files with 280 additions and 142 deletions
6
.gitignore
vendored
6
.gitignore
vendored
|
@ -2,11 +2,11 @@ ebooks/*
|
||||||
www/ebooks/*
|
www/ebooks/*
|
||||||
www/images/covers/*
|
www/images/covers/*
|
||||||
www/feeds/opds/*.xml
|
www/feeds/opds/*.xml
|
||||||
www/feeds/opds/subjects/*.xml
|
www/feeds/opds/*/*.xml
|
||||||
www/feeds/rss/*.xml
|
www/feeds/rss/*.xml
|
||||||
www/feeds/rss/subjects/*.xml
|
www/feeds/rss/*/*.xml
|
||||||
www/feeds/atom/*.xml
|
www/feeds/atom/*.xml
|
||||||
www/feeds/atom/subjects/*.xml
|
www/feeds/atom/*/*.xml
|
||||||
vendor/
|
vendor/
|
||||||
composer.lock
|
composer.lock
|
||||||
.vagrant/
|
.vagrant/
|
||||||
|
|
|
@ -279,6 +279,8 @@ Define webroot /standardebooks.org/web
|
||||||
RewriteCond %{QUERY_STRING} \bquery=
|
RewriteCond %{QUERY_STRING} \bquery=
|
||||||
RewriteRule ^/feeds/(opds|atom|rss)/all.xml$ /feeds/$1/search.php [QSA]
|
RewriteRule ^/feeds/(opds|atom|rss)/all.xml$ /feeds/$1/search.php [QSA]
|
||||||
|
|
||||||
|
RewriteRule ^/feeds/(atom|rss)/([^/\.]+)$ /feeds/collection.php?type=$1&name=$2
|
||||||
|
|
||||||
# Rewrite rules for bulk downloads
|
# Rewrite rules for bulk downloads
|
||||||
RewriteRule ^/bulk-downloads/(.+\.zip)$ /bulk-downloads/download.php?path=$1
|
RewriteRule ^/bulk-downloads/(.+\.zip)$ /bulk-downloads/download.php?path=$1
|
||||||
RewriteRule ^/bulk-downloads/([^/\.]+)$ /bulk-downloads/collection.php?name=$1
|
RewriteRule ^/bulk-downloads/([^/\.]+)$ /bulk-downloads/collection.php?name=$1
|
||||||
|
|
|
@ -261,6 +261,8 @@ Define webroot /standardebooks.org/web
|
||||||
RewriteCond %{QUERY_STRING} \bquery=
|
RewriteCond %{QUERY_STRING} \bquery=
|
||||||
RewriteRule ^/feeds/(opds|atom|rss)/all.xml$ /feeds/$1/search.php [QSA]
|
RewriteRule ^/feeds/(opds|atom|rss)/all.xml$ /feeds/$1/search.php [QSA]
|
||||||
|
|
||||||
|
RewriteRule ^/feeds/(atom|rss)/([^/\.]+)$ /feeds/collection.php?type=$1&name=$2
|
||||||
|
|
||||||
# Rewrite rules for bulk downloads
|
# Rewrite rules for bulk downloads
|
||||||
RewriteRule ^/bulk-downloads/(.+\.zip)$ /bulk-downloads/download.php?path=$1
|
RewriteRule ^/bulk-downloads/(.+\.zip)$ /bulk-downloads/download.php?path=$1
|
||||||
RewriteRule ^/bulk-downloads/([^/\.]+)$ /bulk-downloads/collection.php?name=$1
|
RewriteRule ^/bulk-downloads/([^/\.]+)$ /bulk-downloads/collection.php?name=$1
|
||||||
|
|
|
@ -23,7 +23,7 @@ class AtomFeed extends Feed{
|
||||||
parent::__construct($title, $url, $path, $entries);
|
parent::__construct($title, $url, $path, $entries);
|
||||||
$this->Subtitle = $subtitle;
|
$this->Subtitle = $subtitle;
|
||||||
$this->Id = $url;
|
$this->Id = $url;
|
||||||
$this->Stylesheet = '/feeds/atom/style';
|
$this->Stylesheet = SITE_URL . '/feeds/atom/style';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -41,13 +41,16 @@ class AtomFeed extends Feed{
|
||||||
return $this->XmlString;
|
return $this->XmlString;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function SaveIfChanged(): void{
|
public function SaveIfChanged(): bool{
|
||||||
// Did we actually update the feed? If so, write to file and update the index
|
// Did we actually update the feed? If so, write to file and update the index
|
||||||
if($this->HasChanged($this->Path)){
|
if($this->HasChanged($this->Path)){
|
||||||
// Files don't match, save the file
|
// Files don't match, save the file
|
||||||
$this->Updated = new DateTime();
|
$this->Updated = new DateTime();
|
||||||
$this->Save();
|
$this->Save();
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function HasChanged(string $path): bool{
|
protected function HasChanged(string $path): bool{
|
||||||
|
|
|
@ -322,7 +322,10 @@ class Library{
|
||||||
* @return array<string, array<int|string, array<int|string, mixed>>>
|
* @return array<string, array<int|string, array<int|string, mixed>>>
|
||||||
*/
|
*/
|
||||||
public static function RebuildBulkDownloadsCache(): array{
|
public static function RebuildBulkDownloadsCache(): array{
|
||||||
$collator = collator_create( 'en_US' ); // Used for sorting letters with diacritics like in author names
|
$collator = Collator::create('en_US'); // Used for sorting letters with diacritics like in author names
|
||||||
|
if($collator === null){
|
||||||
|
throw new Exceptions\SeException('Couldn\'t create collator object when rebuilding bulk download cache.');
|
||||||
|
}
|
||||||
$months = [];
|
$months = [];
|
||||||
$subjects = [];
|
$subjects = [];
|
||||||
$collections = [];
|
$collections = [];
|
||||||
|
|
|
@ -16,7 +16,7 @@ class OpdsFeed extends AtomFeed{
|
||||||
public function __construct(string $title, string $subtitle, string $url, string $path, array $entries, ?OpdsNavigationFeed $parent){
|
public function __construct(string $title, string $subtitle, string $url, string $path, array $entries, ?OpdsNavigationFeed $parent){
|
||||||
parent::__construct($title, $subtitle, $url, $path, $entries);
|
parent::__construct($title, $subtitle, $url, $path, $entries);
|
||||||
$this->Parent = $parent;
|
$this->Parent = $parent;
|
||||||
$this->Stylesheet = '/feeds/opds/style';
|
$this->Stylesheet = SITE_URL . '/feeds/opds/style';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -45,7 +45,7 @@ class OpdsFeed extends AtomFeed{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function SaveIfChanged(): void{
|
public function SaveIfChanged(): bool{
|
||||||
// Did we actually update the feed? If so, write to file and update the index
|
// Did we actually update the feed? If so, write to file and update the index
|
||||||
if($this->HasChanged($this->Path)){
|
if($this->HasChanged($this->Path)){
|
||||||
// Files don't match, save the file and update the parent navigation feed with the last updated timestamp
|
// Files don't match, save the file and update the parent navigation feed with the last updated timestamp
|
||||||
|
@ -58,6 +58,9 @@ class OpdsFeed extends AtomFeed{
|
||||||
|
|
||||||
// Save our own file
|
// Save our own file
|
||||||
$this->Save();
|
$this->Save();
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,7 +17,7 @@ class RssFeed extends Feed{
|
||||||
public function __construct(string $title, string $description, string $url, string $path, array $entries){
|
public function __construct(string $title, string $description, string $url, string $path, array $entries){
|
||||||
parent::__construct($title, $url, $path, $entries);
|
parent::__construct($title, $url, $path, $entries);
|
||||||
$this->Description = $description;
|
$this->Description = $description;
|
||||||
$this->Stylesheet = '/feeds/rss/style';
|
$this->Stylesheet = SITE_URL . '/feeds/rss/style';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -35,12 +35,15 @@ class RssFeed extends Feed{
|
||||||
return $this->XmlString;
|
return $this->XmlString;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function SaveIfChanged(): void{
|
public function SaveIfChanged(): bool{
|
||||||
// Did we actually update the feed? If so, write to file and update the index
|
// Did we actually update the feed? If so, write to file and update the index
|
||||||
if($this->HasChanged($this->Path)){
|
if($this->HasChanged($this->Path)){
|
||||||
// Files don't match, save the file
|
// Files don't match, save the file
|
||||||
$this->Save();
|
$this->Save();
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function HasChanged(string $path): bool{
|
protected function HasChanged(string $path): bool{
|
||||||
|
|
|
@ -2,24 +2,58 @@
|
||||||
<?
|
<?
|
||||||
require_once('/standardebooks.org/web/lib/Core.php');
|
require_once('/standardebooks.org/web/lib/Core.php');
|
||||||
|
|
||||||
use function Safe\krsort;
|
use Safe\DateTime;
|
||||||
use function Safe\getopt;
|
use function Safe\getopt;
|
||||||
use function Safe\mkdir;
|
use function Safe\mkdir;
|
||||||
use function Safe\preg_replace;
|
use function Safe\preg_replace;
|
||||||
use function Safe\sort;
|
|
||||||
|
|
||||||
function SaveFeed($feed, $force, $now = null){
|
function SortByUpdatedDesc($a, $b){
|
||||||
|
return $b->Updated <=> $a->Updated;
|
||||||
|
}
|
||||||
|
|
||||||
|
function SaveFeed(Feed $feed, bool $force, ?string $label = null, ?string $labelSort = null, DateTime $now = null): void{
|
||||||
|
$updateAttrs = false;
|
||||||
|
|
||||||
if($force){
|
if($force){
|
||||||
if($now !== null){
|
if($now !== null){
|
||||||
$feed->Updated = $now;
|
$feed->Updated = $now;
|
||||||
}
|
}
|
||||||
$feed->Save();
|
$feed->Save();
|
||||||
|
$updateAttrs = true;
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
$feed->SaveIfChanged();
|
$updateAttrs = $feed->SaveIfChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
if($updateAttrs && $label !== null && $labelSort !== null){
|
||||||
|
exec('attr -q -s se-label -V ' . escapeshellarg($label) . ' ' . escapeshellarg($feed->Path));
|
||||||
|
exec('attr -q -s se-label-sort -V ' . escapeshellarg($labelSort) . ' ' . escapeshellarg($feed->Path));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function CreateOpdsCollectionFeed(string $name, string $url, string $description, array $collections, array $ebooks, DateTime $now, string $webRoot, OpdsNavigationFeed $opdsRoot, bool $force): void{
|
||||||
|
$collator = collator_create('en_US'); // Used for sorting letters with diacritics like in author names
|
||||||
|
usort($collections, function($a, $b) use($collator){ return $collator->compare($a['sortedname'], $b['sortedname']); });
|
||||||
|
|
||||||
|
// Create the collections navigation document
|
||||||
|
$collectionNavigationEntries = [];
|
||||||
|
foreach($collections as $collection){
|
||||||
|
$collectionNavigationEntries[] = new OpdsNavigationEntry($collection['name'], str_replace('%s', $collection['name'], $description), $url . '/' . $collection['id'], $now, 'subsection', 'navigation');
|
||||||
|
}
|
||||||
|
$collectionsFeed = new OpdsNavigationFeed('Standard Ebooks by ' . ucfirst($name), 'Browse Standard Ebooks by ' . $name . '.', $url, $webRoot . $url . '/index.xml', $collectionNavigationEntries, $opdsRoot);
|
||||||
|
$collectionsFeed->Subtitle = 'Browse Standard Ebooks by collection.';
|
||||||
|
SaveFeed($collectionsFeed, $force, null, null, $now);
|
||||||
|
|
||||||
|
// Now generate each individual collection feed
|
||||||
|
foreach($collectionNavigationEntries as $collectionNavigationEntry){
|
||||||
|
$id = basename($collectionNavigationEntry->Id);
|
||||||
|
usort($ebooks[$id], 'SortByUpdatedDesc');
|
||||||
|
$collectionFeed = new OpdsAcquisitionFeed($collectionNavigationEntry->Title . ' Ebooks', $collectionNavigationEntry->Description, $url . '/' . $id, $webRoot . $url . '/' . $id . '.xml', $ebooks[$id], $collectionsFeed);
|
||||||
|
SaveFeed($collectionFeed, $force, null, null, $now);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
$longopts = ['webroot:', 'force'];
|
$longopts = ['webroot:', 'force'];
|
||||||
$options = getopt('', $longopts);
|
$options = getopt('', $longopts);
|
||||||
$webRoot = $options['webroot'] ?? WEB_ROOT;
|
$webRoot = $options['webroot'] ?? WEB_ROOT;
|
||||||
|
@ -29,37 +63,47 @@ $allEbooks = [];
|
||||||
$newestEbooks = [];
|
$newestEbooks = [];
|
||||||
$subjects = [];
|
$subjects = [];
|
||||||
$ebooksBySubject = [];
|
$ebooksBySubject = [];
|
||||||
|
$collections = [];
|
||||||
|
$ebooksByCollection = [];
|
||||||
|
$authors = [];
|
||||||
|
$ebooksByAuthor = [];
|
||||||
$ebooksPerNewestEbooksFeed = 15;
|
$ebooksPerNewestEbooksFeed = 15;
|
||||||
|
|
||||||
if(!is_dir($webRoot . '/feeds/opds/subjects')){
|
$dirs = [ '/feeds/opds/subjects', '/feeds/rss/subjects', '/feeds/atom/subjects',
|
||||||
mkdir($webRoot . '/feeds/opds/subjects');
|
'/feeds/opds/collections', '/feeds/rss/collections', '/feeds/atom/collections',
|
||||||
}
|
'/feeds/opds/authors', '/feeds/rss/authors', '/feeds/atom/authors'
|
||||||
|
];
|
||||||
|
|
||||||
if(!is_dir($webRoot . '/feeds/rss/subjects')){
|
foreach($dirs as $dir){
|
||||||
mkdir($webRoot . '/feeds/rss/subjects');
|
if(!is_dir($webRoot . $dir)){
|
||||||
}
|
mkdir($webRoot . $dir);
|
||||||
|
}
|
||||||
if(!is_dir($webRoot . '/feeds/atom/subjects')){
|
|
||||||
mkdir($webRoot . '/feeds/atom/subjects');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Iterate over all ebooks to build the various feeds
|
// Iterate over all ebooks to build the various feeds
|
||||||
foreach(Library::GetEbooksFromFilesystem($webRoot) as $ebook){
|
foreach(Library::GetEbooksFromFilesystem($webRoot) as $ebook){
|
||||||
$allEbooks[$ebook->Updated->format('Y-m-d\TH:i:s\Z') . ' ' . $ebook->Identifier] = $ebook;
|
$allEbooks[] = $ebook;
|
||||||
$newestEbooks[$ebook->Created->format('Y-m-d\TH:i:s\Z') . ' ' . $ebook->Identifier] = $ebook;
|
$newestEbooks[] = $ebook;
|
||||||
|
|
||||||
foreach($ebook->Tags as $tag){
|
foreach($ebook->Tags as $tag){
|
||||||
// Add the book's subjects to the main subjects list
|
$urlName = Formatter::MakeUrlSafe($tag->Name);
|
||||||
if(!in_array($tag->Name, $subjects)){
|
$ebooksBySubject[$urlName][] = $ebook;
|
||||||
$subjects[] = $tag->Name;
|
$subjects[$urlName] = ['id' => $urlName, 'name' => $tag->Name, 'sortedname' => $tag->Name];
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sort this ebook by subject
|
foreach($ebook->Collections as $collection){
|
||||||
$ebooksBySubject[$tag->Name][$ebook->Created->format('Y-m-d\TH:i:s\Z') . ' ' . $ebook->Identifier] = $ebook;
|
$urlName = Formatter::MakeUrlSafe($collection->Name);
|
||||||
|
$ebooksByCollection[$urlName][] = $ebook;
|
||||||
|
$collections[$urlName] = ['id' => $urlName, 'name' => $collection->Name, 'sortedname' => $collection->GetSortedName()];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$authorsUrl = preg_replace('|^/ebooks/|', '', $ebook->AuthorsUrl);
|
||||||
|
$ebooksByAuthor[$authorsUrl][] = $ebook;
|
||||||
|
$authors[$authorsUrl] = ['id' => $authorsUrl, 'name' => strip_tags($ebook->AuthorsHtml), 'sortedname' => $ebook->Authors[0]->SortName];
|
||||||
}
|
}
|
||||||
|
|
||||||
krsort($newestEbooks);
|
usort($allEbooks, 'SortByUpdatedDesc');
|
||||||
|
usort($newestEbooks, function($a, $b){ return $b->Created <=> $a->Created; });
|
||||||
$newestEbooks = array_slice($newestEbooks, 0, $ebooksPerNewestEbooksFeed);
|
$newestEbooks = array_slice($newestEbooks, 0, $ebooksPerNewestEbooksFeed);
|
||||||
|
|
||||||
$now = new DateTime();
|
$now = new DateTime();
|
||||||
|
@ -81,6 +125,20 @@ $opdsRootEntries = [
|
||||||
$now,
|
$now,
|
||||||
'subsection',
|
'subsection',
|
||||||
'navigation'),
|
'navigation'),
|
||||||
|
new OpdsNavigationEntry(
|
||||||
|
'Standard Ebooks by Collection',
|
||||||
|
'Browse Standard Ebooks by collection.',
|
||||||
|
'/feeds/opds/collections',
|
||||||
|
$now,
|
||||||
|
'subsection',
|
||||||
|
'navigation'),
|
||||||
|
new OpdsNavigationEntry(
|
||||||
|
'Standard Ebooks by Author',
|
||||||
|
'Browse Standard Ebooks by author.',
|
||||||
|
'/feeds/opds/authors',
|
||||||
|
$now,
|
||||||
|
'subsection',
|
||||||
|
'navigation'),
|
||||||
new OpdsNavigationEntry(
|
new OpdsNavigationEntry(
|
||||||
'All Standard Ebooks',
|
'All Standard Ebooks',
|
||||||
'All Standard Ebooks, most-recently-updated first. This is a Complete Acquisition Feed as defined in OPDS 1.2 §2.5.',
|
'All Standard Ebooks, most-recently-updated first. This is a Complete Acquisition Feed as defined in OPDS 1.2 §2.5.',
|
||||||
|
@ -91,64 +149,85 @@ $opdsRootEntries = [
|
||||||
];
|
];
|
||||||
|
|
||||||
$opdsRoot = new OpdsNavigationFeed('Standard Ebooks', 'The Standard Ebooks catalog.', '/feeds/opds', $webRoot . '/feeds/opds/index.xml', $opdsRootEntries, null);
|
$opdsRoot = new OpdsNavigationFeed('Standard Ebooks', 'The Standard Ebooks catalog.', '/feeds/opds', $webRoot . '/feeds/opds/index.xml', $opdsRootEntries, null);
|
||||||
SaveFeed($opdsRoot, $force, $now);
|
SaveFeed($opdsRoot, $force, null, null, $now);
|
||||||
|
|
||||||
// Create the subjects navigation document
|
// Create the Subjects feeds
|
||||||
sort($subjects);
|
CreateOpdsCollectionFeed('subject', '/feeds/opds/subjects', 'Standard Ebooks in the “%s” subject, most-recently-released first.', $subjects, $ebooksBySubject, $now, $webRoot, $opdsRoot, $force);
|
||||||
$subjectNavigationEntries = [];
|
|
||||||
foreach($subjects as $subject){
|
|
||||||
$subjectNavigationEntries[] = new OpdsNavigationEntry($subject, 'Standard Ebooks tagged with “' . strtolower($subject) . ',” most-recently-released first.', '/feeds/opds/subjects/' . Formatter::MakeUrlSafe($subject), $now, 'subsection', 'navigation');
|
|
||||||
}
|
|
||||||
$subjectsFeed = new OpdsNavigationFeed('Standard Ebooks by Subject', 'Browse Standard Ebooks by subject.', '/feeds/opds/subjects', $webRoot . '/feeds/opds/subjects/index.xml', $subjectNavigationEntries, $opdsRoot);
|
|
||||||
$subjectsFeed->Subtitle = 'Browse Standard Ebooks by subject.';
|
|
||||||
SaveFeed($subjectsFeed, $force, $now);
|
|
||||||
|
|
||||||
// Now generate each individual subject feed
|
// Create the Collections feeds
|
||||||
foreach($subjectNavigationEntries as $subjectNavigationEntry){
|
CreateOpdsCollectionFeed('collection', '/feeds/opds/collections', 'Standard Ebooks in the “%s” collection, most-recently-released first.', $collections, $ebooksByCollection, $now, $webRoot, $opdsRoot, $force);
|
||||||
krsort($ebooksBySubject[$subjectNavigationEntry->Title]);
|
|
||||||
$subjectFeed = new OpdsAcquisitionFeed($subjectNavigationEntry->Title . ' Ebooks', $subjectNavigationEntry->Description, '/feeds/opds/subjects/' . Formatter::MakeUrlSafe($subjectNavigationEntry->Title), $webRoot . '/feeds/opds/subjects/' . Formatter::MakeUrlSafe($subjectNavigationEntry->Title) . '.xml', $ebooksBySubject[$subjectNavigationEntry->Title], $subjectsFeed);
|
|
||||||
SaveFeed($subjectFeed, $force, $now);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create the 'all' feed
|
// Create the Author feeds
|
||||||
krsort($allEbooks);
|
CreateOpdsCollectionFeed('author', '/feeds/opds/authors', 'Standard Ebooks by %s, most-recently-released first.', $authors, $ebooksByAuthor, $now, $webRoot, $opdsRoot, $force);
|
||||||
|
|
||||||
|
// Create the All feed
|
||||||
$allFeed = new OpdsAcquisitionFeed('All Standard Ebooks', 'All Standard Ebooks, most-recently-updated first. This is a Complete Acquisition Feed as defined in OPDS 1.2 §2.5.', '/feeds/opds/all', $webRoot . '/feeds/opds/all.xml', $allEbooks, $opdsRoot, true);
|
$allFeed = new OpdsAcquisitionFeed('All Standard Ebooks', 'All Standard Ebooks, most-recently-updated first. This is a Complete Acquisition Feed as defined in OPDS 1.2 §2.5.', '/feeds/opds/all', $webRoot . '/feeds/opds/all.xml', $allEbooks, $opdsRoot, true);
|
||||||
SaveFeed($allFeed, $force, $now);
|
SaveFeed($allFeed, $force, null, null, $now);
|
||||||
|
|
||||||
// Create the 'newest' feed
|
// Create the Newest feed
|
||||||
$newestFeed = new OpdsAcquisitionFeed('Newest Standard Ebooks', 'The ' . number_format($ebooksPerNewestEbooksFeed) . ' latest Standard Ebooks, most-recently-released first.', '/feeds/opds/new-releases', $webRoot . '/feeds/opds/new-releases.xml', $newestEbooks, $opdsRoot);
|
$newestFeed = new OpdsAcquisitionFeed('Newest Standard Ebooks', 'The ' . number_format($ebooksPerNewestEbooksFeed) . ' latest Standard Ebooks, most-recently-released first.', '/feeds/opds/new-releases', $webRoot . '/feeds/opds/new-releases.xml', $newestEbooks, $opdsRoot);
|
||||||
SaveFeed($newestFeed, $force, $now);
|
SaveFeed($newestFeed, $force, null, null, $now);
|
||||||
|
|
||||||
// Now create RSS feeds
|
|
||||||
|
|
||||||
// Create the 'newest' feed
|
|
||||||
$newestRssFeed = new RssFeed('Standard Ebooks - Newest Ebooks', 'The ' . number_format($ebooksPerNewestEbooksFeed) . ' latest Standard Ebooks, most-recently-released first.', '/feeds/rss/new-releases', $webRoot . '/feeds/rss/new-releases.xml', $newestEbooks);
|
|
||||||
SaveFeed($newestRssFeed, $force);
|
|
||||||
|
|
||||||
// Create the 'all' feed
|
// Create RSS/Atom feeds
|
||||||
|
|
||||||
|
// Create the RSS All feed
|
||||||
$allRssFeed = new RssFeed('Standard Ebooks - All Ebooks', 'All Standard Ebooks, most-recently-released first.', '/feeds/rss/all', $webRoot . '/feeds/rss/all.xml', $allEbooks);
|
$allRssFeed = new RssFeed('Standard Ebooks - All Ebooks', 'All Standard Ebooks, most-recently-released first.', '/feeds/rss/all', $webRoot . '/feeds/rss/all.xml', $allEbooks);
|
||||||
SaveFeed($allRssFeed, $force);
|
SaveFeed($allRssFeed, $force, null, null);
|
||||||
|
|
||||||
// Generate each individual subject feed
|
// Create the RSS Newest feed
|
||||||
foreach($ebooksBySubject as $subject => $ebooks){
|
$newestRssFeed = new RssFeed('Standard Ebooks - Newest Ebooks', 'The ' . number_format($ebooksPerNewestEbooksFeed) . ' latest Standard Ebooks, most-recently-released first.', '/feeds/rss/new-releases', $webRoot . '/feeds/rss/new-releases.xml', $newestEbooks);
|
||||||
krsort($ebooks);
|
SaveFeed($newestRssFeed, $force, null, null);
|
||||||
$subjectRssFeed = new RssFeed('Standard Ebooks - ' . (string)$subject . ' Ebooks', 'Standard Ebooks tagged with “' . strtolower($subject) . ',” most-recently-released first.', '/feeds/rss/subjects/' . Formatter::MakeUrlSafe((string)$subject), $webRoot . '/feeds/rss/subjects/' . Formatter::MakeUrlSafe((string)$subject) . '.xml', $ebooks);
|
|
||||||
SaveFeed($subjectRssFeed, $force);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Now create the Atom feeds
|
// Create the Atom All feed
|
||||||
// Create the 'newest' feed
|
|
||||||
$newestAtomFeed = new AtomFeed('Standard Ebooks - Newest Ebooks', 'The ' . number_format($ebooksPerNewestEbooksFeed) . ' latest Standard Ebooks, most-recently-released first.', '/feeds/atom/new-releases', $webRoot . '/feeds/atom/new-releases.xml', $newestEbooks);
|
|
||||||
SaveFeed($newestAtomFeed, $force, $now);
|
|
||||||
|
|
||||||
// Create the 'all' feed
|
|
||||||
$allAtomFeed = new AtomFeed('Standard Ebooks - All Ebooks', 'All Standard Ebooks, most-recently-released first.', '/feeds/atom/all', $webRoot . '/feeds/atom/all.xml', $allEbooks);
|
$allAtomFeed = new AtomFeed('Standard Ebooks - All Ebooks', 'All Standard Ebooks, most-recently-released first.', '/feeds/atom/all', $webRoot . '/feeds/atom/all.xml', $allEbooks);
|
||||||
SaveFeed($allAtomFeed, $force, $now);
|
SaveFeed($allAtomFeed, $force, null, null, $now);
|
||||||
|
|
||||||
|
// Create the Atom Newest feed
|
||||||
|
$newestAtomFeed = new AtomFeed('Standard Ebooks - Newest Ebooks', 'The ' . number_format($ebooksPerNewestEbooksFeed) . ' latest Standard Ebooks, most-recently-released first.', '/feeds/atom/new-releases', $webRoot . '/feeds/atom/new-releases.xml', $newestEbooks);
|
||||||
|
SaveFeed($newestAtomFeed, $force, null, null, $now);
|
||||||
|
|
||||||
// Generate each individual subject feed
|
// Generate each individual subject feed
|
||||||
foreach($ebooksBySubject as $subject => $ebooks){
|
foreach($ebooksBySubject as $subject => $ebooks){
|
||||||
krsort($ebooks);
|
usort($ebooks, 'SortByUpdatedDesc');
|
||||||
$subjectAtomFeed = new AtomFeed('Standard Ebooks - ' . (string)$subject . ' Ebooks', 'Standard Ebooks tagged with “' . strtolower($subject) . ',” most-recently-released first.', '/feeds/atom/subjects/' . Formatter::MakeUrlSafe((string)$subject), $webRoot . '/feeds/atom/subjects/' . Formatter::MakeUrlSafe((string)$subject) . '.xml', $ebooks);
|
|
||||||
SaveFeed($subjectAtomFeed, $force, $now);
|
$title = 'Standard Ebooks - ' . $subjects[$subject]['name'] . ' Ebooks';
|
||||||
|
$subtitle = 'Standard Ebooks in the “' . strtolower($subjects[$subject]['name']) . '” subject, most-recently-released first.';
|
||||||
|
|
||||||
|
$subjectRssFeed = new RssFeed($title, $subtitle, '/feeds/rss/subjects/' . Formatter::MakeUrlSafe($subject), $webRoot . '/feeds/rss/subjects/' . Formatter::MakeUrlSafe($subject) . '.xml', $ebooks);
|
||||||
|
SaveFeed($subjectRssFeed, $force, $subjects[$subject]['name'], $subjects[$subject]['sortedname']);
|
||||||
|
|
||||||
|
$subjectAtomFeed = new AtomFeed($title, $subtitle, '/feeds/atom/subjects/' . Formatter::MakeUrlSafe($subject), $webRoot . '/feeds/atom/subjects/' . Formatter::MakeUrlSafe($subject) . '.xml', $ebooks);
|
||||||
|
SaveFeed($subjectAtomFeed, $force, $subjects[$subject]['name'], $subjects[$subject]['sortedname'], $now);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate each individual collection feed
|
||||||
|
foreach($ebooksByCollection as $collection => $ebooks){
|
||||||
|
usort($ebooks, 'SortByUpdatedDesc');
|
||||||
|
|
||||||
|
$titleName = preg_replace('/^The /ius', '', $collections[$collection]['name']);
|
||||||
|
|
||||||
|
$title ='Standard Ebooks - Ebooks in the ' . $titleName . ' collection';
|
||||||
|
$subtitle = 'Standard Ebooks in the ' . $titleName . ' collection, most-recently-released first.';
|
||||||
|
|
||||||
|
$collectionRssFeed = new RssFeed($title, $subtitle, '/feeds/rss/collections/' . Formatter::MakeUrlSafe($collection), $webRoot . '/feeds/rss/collections/' . Formatter::MakeUrlSafe($collection) . '.xml', $ebooks);
|
||||||
|
SaveFeed($collectionRssFeed, $force, $collections[$collection]['name'], $collections[$collection]['sortedname']);
|
||||||
|
|
||||||
|
$collectionAtomFeed = new AtomFeed($title, $subtitle, '/feeds/atom/collections/' . Formatter::MakeUrlSafe($collection), $webRoot . '/feeds/atom/collections/' . Formatter::MakeUrlSafe($collection) . '.xml', $ebooks);
|
||||||
|
SaveFeed($collectionAtomFeed, $force, $collections[$collection]['name'], $collections[$collection]['sortedname'], $now);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate each individual author feed
|
||||||
|
foreach($ebooksByAuthor as $collection => $ebooks){
|
||||||
|
usort($ebooks, 'SortByUpdatedDesc');
|
||||||
|
|
||||||
|
$title = 'Standard Ebooks - Ebooks by ' . $authors[$collection]['name'];
|
||||||
|
$subtitle = 'Standard Ebooks by ' . $authors[$collection]['name'] . ', most-recently-released first.';
|
||||||
|
|
||||||
|
$collectionRssFeed = new RssFeed($title, $subtitle, '/feeds/rss/authors/' . $authors[$collection]['id'], $webRoot . '/feeds/rss/authors/' . $authors[$collection]['id'] . '.xml', $ebooks);
|
||||||
|
SaveFeed($collectionRssFeed, $force, $authors[$collection]['name'], $authors[$collection]['sortedname']);
|
||||||
|
|
||||||
|
$collectionAtomFeed = new AtomFeed($title, $subtitle, '/feeds/atom/authors/' . $authors[$collection]['id'], $webRoot . '/feeds/atom/authors/' . $authors[$collection]['id'] . '.xml', $ebooks);
|
||||||
|
SaveFeed($collectionAtomFeed, $force, $authors[$collection]['name'], $authors[$collection]['sortedname'], $now);
|
||||||
}
|
}
|
||||||
?>
|
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
<h2>Accessing the feeds</h2>
|
<h2>Accessing the feeds</h2>
|
||||||
<p>Our New Releases feeds are accessible by the public. Access to our other, more detailed feeds is available to our <a href="/about#patrons-circle">Patrons Circle supporters</a> and our <a href="/about#corporate-sponsors">corporate sponsors</a>.</p>
|
<p>Our New Releases feeds are accessible by the public. Access to our other, more detailed feeds is available to our <a href="/about#patrons-circle">Patrons Circle supporters</a> and our <a href="/about#corporate-sponsors">corporate sponsors</a>.</p>
|
||||||
<p><i>If you’re a Patrons Circle member, when prompted enter your email address and leave the password field blank to access a feed.</i></p>
|
<p><i>If you’re a Patrons Circle member, when prompted enter your email address and leave the password field blank to access a feed.</i></p>
|
||||||
|
<? if($GLOBALS['User'] === null){ ?>
|
||||||
<section id="individuals">
|
<section id="individuals">
|
||||||
<h3>Access for individuals</h3>
|
<h3>Access for individuals</h3>
|
||||||
<ul>
|
<ul>
|
||||||
|
@ -16,4 +17,5 @@
|
||||||
<li><p>Software projects that are open-source and not-for-profit may be granted free access to our feeds. <a href="/about#editor-in-chief">Contact us</a> to inquire.</p></li>
|
<li><p>Software projects that are open-source and not-for-profit may be granted free access to our feeds. <a href="/about#editor-in-chief">Contact us</a> to inquire.</p></li>
|
||||||
</ul>
|
</ul>
|
||||||
</section>
|
</section>
|
||||||
|
<? } ?>
|
||||||
</section>
|
</section>
|
||||||
|
|
|
@ -8,7 +8,7 @@ $canDownload = false;
|
||||||
$name = HttpInput::Str(GET, 'name', false) ?? '';
|
$name = HttpInput::Str(GET, 'name', false) ?? '';
|
||||||
|
|
||||||
if($name != 'authors' && $name != 'collections' && $name != 'subjects' && $name != 'months'){
|
if($name != 'authors' && $name != 'collections' && $name != 'subjects' && $name != 'months'){
|
||||||
$name = 'subjects';
|
Template::Emit404();
|
||||||
}
|
}
|
||||||
|
|
||||||
if($GLOBALS['User'] !== null && $GLOBALS['User']->Benefits->CanBulkDownload){
|
if($GLOBALS['User'] !== null && $GLOBALS['User']->Benefits->CanBulkDownload){
|
||||||
|
|
|
@ -17,21 +17,24 @@ require_once('Core.php');
|
||||||
<p>The fifteen latest Standard Ebooks, most-recently-released first.</p>
|
<p>The fifteen latest Standard Ebooks, most-recently-released first.</p>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<p><a href="/feeds/atom/all">All ebooks</a></p>
|
<p><a href="<? if($GLOBALS['User'] !== null){ ?>https://<?= rawurlencode($GLOBALS['User']->Email) ?>@<?= SITE_DOMAIN ?><? } ?>/feeds/atom/all">All ebooks</a></p>
|
||||||
<p class="url"><?= SITE_URL ?>/feeds/atom/all</p>
|
<p class="url"><? if($GLOBALS['User'] !== null){ ?>https://<?= rawurlencode($GLOBALS['User']->Email) ?>@<?= SITE_DOMAIN ?><? }else{ ?><?= SITE_URL ?><? } ?>/feeds/atom/all</p>
|
||||||
<p>All Standard Ebooks, most-recently-released first.</p>
|
<p>All Standard Ebooks, most-recently-released first.</p>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</section>
|
</section>
|
||||||
<section id="ebooks-by-subject">
|
<section id="feeds-by-topic">
|
||||||
<h2>Ebooks by subject</h2>
|
<h2>Feeds by topic</h2>
|
||||||
<ul class="feed">
|
<ul class="feed">
|
||||||
<? foreach(SE_SUBJECTS as $subject){ ?>
|
|
||||||
<li>
|
<li>
|
||||||
<p><a href="/feeds/atom/subjects/<?= Formatter::MakeUrlSafe($subject) ?>"><?= Formatter::ToPlainText($subject) ?></a></p>
|
<p><a href="/feeds/atom/authors">Feeds by author</a></p>
|
||||||
<p class="url"><?= SITE_URL ?>/feeds/atom/subjects/<?= Formatter::MakeUrlSafe($subject) ?></p>
|
</li>
|
||||||
|
<li>
|
||||||
|
<p><a href="/feeds/atom/collections">Feeds by collection</a></p>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p><a href="/feeds/atom/subjects">Feeds by subject</a></p>
|
||||||
</li>
|
</li>
|
||||||
<? } ?>
|
|
||||||
</ul>
|
</ul>
|
||||||
</section>
|
</section>
|
||||||
</section>
|
</section>
|
||||||
|
|
|
@ -16,7 +16,7 @@ catch(\Exception $ex){
|
||||||
include(WEB_ROOT . '/404.php');
|
include(WEB_ROOT . '/404.php');
|
||||||
exit();
|
exit();
|
||||||
}
|
}
|
||||||
print("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<?xml-stylesheet href=\"/feeds/atom/style\" type=\"text/xsl\"?>\n");
|
print("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<?xml-stylesheet href=\"" . SITE_URL . "/feeds/atom/style\" type=\"text/xsl\"?>\n");
|
||||||
?>
|
?>
|
||||||
<feed xmlns="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/" xmlns:opensearch="http://a9.com/-/spec/opensearch/1.1/">
|
<feed xmlns="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/" xmlns:opensearch="http://a9.com/-/spec/opensearch/1.1/">
|
||||||
<id><?= SITE_URL ?>/feeds/atom/all?query=<?= urlencode($query) ?></id>
|
<id><?= SITE_URL ?>/feeds/atom/all?query=<?= urlencode($query) ?></id>
|
||||||
|
|
|
@ -1,22 +0,0 @@
|
||||||
<?
|
|
||||||
require_once('Core.php');
|
|
||||||
|
|
||||||
?><?= Template::Header(['title' => 'Atom 1.0 Ebook Feeds by Subject', 'description' => 'A list of available Atom 1.0 feeds of Standard Ebooks ebooks by subject.']) ?>
|
|
||||||
<main>
|
|
||||||
<article>
|
|
||||||
<h1>Atom 1.0 Ebook Feeds by Subject</h1>
|
|
||||||
<?= Template::FeedHowTo() ?>
|
|
||||||
<section id="ebooks-by-subject">
|
|
||||||
<h2>Ebooks by subject</h2>
|
|
||||||
<ul class="feed">
|
|
||||||
<? foreach(SE_SUBJECTS as $subject){ ?>
|
|
||||||
<li>
|
|
||||||
<p><a href="/feeds/atom/subjects/<?= Formatter::MakeUrlSafe($subject) ?>"><?= Formatter::ToPlainText($subject) ?></a></p>
|
|
||||||
<p class="url"><?= SITE_URL ?>/feeds/atom/subjects/<?= Formatter::MakeUrlSafe($subject) ?></p>
|
|
||||||
</li>
|
|
||||||
<? } ?>
|
|
||||||
</ul>
|
|
||||||
</section>
|
|
||||||
</article>
|
|
||||||
</main>
|
|
||||||
<?= Template::Footer() ?>
|
|
79
www/feeds/collection.php
Normal file
79
www/feeds/collection.php
Normal file
|
@ -0,0 +1,79 @@
|
||||||
|
<?
|
||||||
|
require_once('Core.php');
|
||||||
|
|
||||||
|
use function Safe\apcu_fetch;
|
||||||
|
use function Safe\glob;
|
||||||
|
use function Safe\preg_replace;
|
||||||
|
use function Safe\usort;
|
||||||
|
|
||||||
|
$name = HttpInput::Str(GET, 'name', false) ?? '';
|
||||||
|
$type = HttpInput::Str(GET, 'type', false) ?? '';
|
||||||
|
|
||||||
|
if($name != 'authors' && $name != 'collections' && $name != 'subjects'){
|
||||||
|
Template::Emit404();
|
||||||
|
}
|
||||||
|
|
||||||
|
if($type != 'rss' && $type != 'atom'){
|
||||||
|
Template::Emit404();
|
||||||
|
}
|
||||||
|
|
||||||
|
$feeds = [];
|
||||||
|
|
||||||
|
$lcTitle = preg_replace('/s$/', '', $name);
|
||||||
|
$ucTitle = ucfirst($lcTitle);
|
||||||
|
$ucType = 'RSS 2.0';
|
||||||
|
if($type === 'atom'){
|
||||||
|
$ucType = 'Atom 1.0';
|
||||||
|
}
|
||||||
|
|
||||||
|
try{
|
||||||
|
$feeds = apcu_fetch('feeds-index-' . $type . '-' . $name);
|
||||||
|
}
|
||||||
|
catch(Safe\Exceptions\ApcuException $ex){
|
||||||
|
$files = glob(WEB_ROOT . '/feeds/' . $type . '/' . $name . '/*.xml');
|
||||||
|
|
||||||
|
$feeds = [];
|
||||||
|
|
||||||
|
foreach($files as $file){
|
||||||
|
$obj = new stdClass();
|
||||||
|
$obj->Url = '/feeds/' . $type . '/' . $name . '/' . basename($file, '.xml');
|
||||||
|
|
||||||
|
$obj->Label = exec('attr -g se-label ' . escapeshellarg($file)) ?: null;
|
||||||
|
if($obj->Label == null){
|
||||||
|
$obj->Label = basename($file, '.xml');
|
||||||
|
}
|
||||||
|
|
||||||
|
$obj->LabelSort = exec('attr -g se-label-sort ' . escapeshellarg($file)) ?: null;
|
||||||
|
if($obj->LabelSort == null){
|
||||||
|
$obj->LabelSort = basename($file, '.xml');
|
||||||
|
}
|
||||||
|
|
||||||
|
$feeds[] = $obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
$collator = Collator::create('en_US'); // Used for sorting letters with diacritics like in author names
|
||||||
|
if($collator !== null){
|
||||||
|
usort($feeds, function($a, $b) use($collator){ return $collator->compare($a->LabelSort, $b->LabelSort); });
|
||||||
|
}
|
||||||
|
|
||||||
|
apcu_store('feeds-index-' . $type . '-' . $name, $feeds, 43200); // 12 hours
|
||||||
|
}
|
||||||
|
?><?= Template::Header(['title' => $ucType . ' Ebook Feeds by ' . $ucTitle, 'description' => 'A list of available ' . $ucType . ' feeds of Standard Ebooks ebooks by ' . $lcTitle . '.']) ?>
|
||||||
|
<main>
|
||||||
|
<article>
|
||||||
|
<h1><?= $ucType ?> Ebook Feeds by <?= $ucTitle ?></h1>
|
||||||
|
<?= Template::FeedHowTo() ?>
|
||||||
|
<section id="ebooks-by-<?= $lcTitle ?>">
|
||||||
|
<h2>Ebooks by <?= $lcTitle ?></h2>
|
||||||
|
<ul class="feed">
|
||||||
|
<? foreach($feeds as $feed){ ?>
|
||||||
|
<li>
|
||||||
|
<p><a href="<? if($GLOBALS['User'] !== null){ ?>https://<?= rawurlencode($GLOBALS['User']->Email) ?>@<?= SITE_DOMAIN ?><? } ?><?= Formatter::ToPlainText($feed->Url) ?>"><?= Formatter::ToPlainText($feed->Label) ?></a></p>
|
||||||
|
<p class="url"><? if($GLOBALS['User'] !== null){ ?>https://<?= rawurlencode($GLOBALS['User']->Email) ?>@<?= SITE_DOMAIN ?><? }else{ ?><?= SITE_URL ?><? } ?><?= Formatter::ToPlainText($feed->Url) ?></p>
|
||||||
|
</li>
|
||||||
|
<? } ?>
|
||||||
|
</ul>
|
||||||
|
</section>
|
||||||
|
</article>
|
||||||
|
</main>
|
||||||
|
<?= Template::Footer() ?>
|
|
@ -18,8 +18,8 @@ require_once('Core.php');
|
||||||
<p>They’re also perfect for scripting, or for libraries or other organizations who wish to download, process, and keep up to date with our catalog of ebooks.</p>
|
<p>They’re also perfect for scripting, or for libraries or other organizations who wish to download, process, and keep up to date with our catalog of ebooks.</p>
|
||||||
<ul class="feed">
|
<ul class="feed">
|
||||||
<li>
|
<li>
|
||||||
<p><a href="/feeds/opds">The Standard Ebooks OPDS feed</a></p>
|
<p><a href="<? if($GLOBALS['User'] !== null){ ?>https://<?= rawurlencode($GLOBALS['User']->Email) ?>@<?= SITE_DOMAIN ?><? } ?>/feeds/opds">The Standard Ebooks OPDS feed</a></p>
|
||||||
<p class="url"><?= SITE_URL ?>/feeds/opds</p>
|
<p class="url"><? if($GLOBALS['User'] !== null){ ?>https://<?= rawurlencode($GLOBALS['User']->Email) ?>@<?= SITE_DOMAIN ?><? }else{ ?><?= SITE_URL ?><? } ?>/feeds/opds</p>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</section>
|
</section>
|
||||||
|
|
|
@ -15,7 +15,7 @@ catch(\Exception $ex){
|
||||||
http_response_code(500);
|
http_response_code(500);
|
||||||
exit();
|
exit();
|
||||||
}
|
}
|
||||||
print("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<?xml-stylesheet href=\"/feeds/opds/style\" type=\"text/xsl\"?>\n");
|
print("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<?xml-stylesheet href=\"". SITE_URL . "/feeds/opds/style\" type=\"text/xsl\"?>\n");
|
||||||
?>
|
?>
|
||||||
<feed xmlns="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:schema="http://schema.org/" xmlns:opensearch="http://a9.com/-/spec/opensearch/1.1/">
|
<feed xmlns="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:schema="http://schema.org/" xmlns:opensearch="http://a9.com/-/spec/opensearch/1.1/">
|
||||||
<id><?= SITE_URL ?>/feeds/opds/all?query=<?= urlencode($query) ?></id>
|
<id><?= SITE_URL ?>/feeds/opds/all?query=<?= urlencode($query) ?></id>
|
||||||
|
|
|
@ -8,7 +8,7 @@ require_once('Core.php');
|
||||||
<p>RSS feeds are the predecessors of <a href="/feeds/atom">Atom feeds</a>. They contain less information than Atom feeds, but might be better supported by some news readers.</p>
|
<p>RSS feeds are the predecessors of <a href="/feeds/atom">Atom feeds</a>. They contain less information than Atom feeds, but might be better supported by some news readers.</p>
|
||||||
<?= Template::FeedHowTo() ?>
|
<?= Template::FeedHowTo() ?>
|
||||||
<section id="general-feeds">
|
<section id="general-feeds">
|
||||||
<h2>General Feeds</h2>
|
<h2>General feeds</h2>
|
||||||
<ul class="feed">
|
<ul class="feed">
|
||||||
<li>
|
<li>
|
||||||
<p><a href="/feeds/rss/new-releases">New releases</a> (Public)</p>
|
<p><a href="/feeds/rss/new-releases">New releases</a> (Public)</p>
|
||||||
|
@ -22,15 +22,18 @@ require_once('Core.php');
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</section>
|
</section>
|
||||||
<section id="ebooks-by-subject">
|
<section id="ebooks-by-collection">
|
||||||
<h2>Ebooks by subject</h2>
|
<h2>Feeds by topic</h2>
|
||||||
<ul class="feed">
|
<ul class="feed">
|
||||||
<? foreach(SE_SUBJECTS as $subject){ ?>
|
|
||||||
<li>
|
<li>
|
||||||
<p><a href="/feeds/rss/subjects/<?= Formatter::MakeUrlSafe($subject) ?>"><?= Formatter::ToPlainText($subject) ?></a></p>
|
<p><a href="/feeds/rss/authors">Feeds by author</a></p>
|
||||||
<p class="url"><?= SITE_URL ?>/feeds/rss/subjects/<?= Formatter::MakeUrlSafe($subject) ?></p>
|
</li>
|
||||||
|
<li>
|
||||||
|
<p><a href="/feeds/rss/collections">Feeds by collection</a></p>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p><a href="/feeds/rss/subjects">Feeds by subject</a></p>
|
||||||
</li>
|
</li>
|
||||||
<? } ?>
|
|
||||||
</ul>
|
</ul>
|
||||||
</section>
|
</section>
|
||||||
</section>
|
</section>
|
||||||
|
|
|
@ -16,7 +16,7 @@ catch(\Exception $ex){
|
||||||
include(WEB_ROOT . '/404.php');
|
include(WEB_ROOT . '/404.php');
|
||||||
exit();
|
exit();
|
||||||
}
|
}
|
||||||
print("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<?xml-stylesheet href=\"/feeds/rss/style\" type=\"text/xsl\"?>\n");
|
print("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<?xml-stylesheet href=\"" . SITE_URL . "/feeds/rss/style\" type=\"text/xsl\"?>\n");
|
||||||
?>
|
?>
|
||||||
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/" xmlns:opensearch="http://a9.com/-/spec/opensearch/1.1/">
|
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/" xmlns:opensearch="http://a9.com/-/spec/opensearch/1.1/">
|
||||||
<channel>
|
<channel>
|
||||||
|
|
|
@ -1,22 +0,0 @@
|
||||||
<?
|
|
||||||
require_once('Core.php');
|
|
||||||
|
|
||||||
?><?= Template::Header(['title' => 'RSS 2.0 Ebook Feeds by Subject', 'description' => 'A list of available RSS 2.0 feeds of Standard Ebooks ebooks by subject.']) ?>
|
|
||||||
<main>
|
|
||||||
<article>
|
|
||||||
<h1>RSS 2.0 Ebook Feeds by Subject</h1>
|
|
||||||
<?= Template::FeedHowTo() ?>
|
|
||||||
<section id="ebooks-by-subject">
|
|
||||||
<h2>Ebooks by subject</h2>
|
|
||||||
<ul class="feed">
|
|
||||||
<? foreach(SE_SUBJECTS as $subject){ ?>
|
|
||||||
<li>
|
|
||||||
<p><a href="/feeds/rss/subjects/<?= Formatter::MakeUrlSafe($subject) ?>"><?= Formatter::ToPlainText($subject) ?></a></p>
|
|
||||||
<p class="url"><?= SITE_URL ?>/feeds/rss/subjects/<?= Formatter::MakeUrlSafe($subject) ?></p>
|
|
||||||
</li>
|
|
||||||
<? } ?>
|
|
||||||
</ul>
|
|
||||||
</section>
|
|
||||||
</article>
|
|
||||||
</main>
|
|
||||||
<?= Template::Footer() ?>
|
|
Loading…
Add table
Add a link
Reference in a new issue