diff --git a/lib/Collection.php b/lib/Collection.php new file mode 100644 index 00000000..55e2cfcf --- /dev/null +++ b/lib/Collection.php @@ -0,0 +1,11 @@ +Name = $name; + $this->Url = '/collections/' . strtolower(str_replace(' ', '-', Formatter::ToPlainText($this->Name))) . '/'; + } +} +?> diff --git a/lib/Ebook.php b/lib/Ebook.php index be4158dd..89527915 100644 --- a/lib/Ebook.php +++ b/lib/Ebook.php @@ -12,6 +12,7 @@ class Ebook{ public $GitCommits = []; public $Tags = []; public $LocTags = []; + public $Collections = []; public $Identifier; public $UrlSafeIdentifier; public $HeroImageUrl; @@ -149,7 +150,12 @@ class Ebook{ // Get SE tags foreach($xml->xpath('/package/metadata/meta[@property="se:subject"]') ?: [] as $tag){ - $this->Tags[] = (string)$tag; + $this->Tags[] = new Tag($tag); + } + + // Get SE collections + foreach($xml->xpath('/package/metadata/meta[@property="se:collection"]') ?: [] as $collection){ + $this->Collections[] = new Collection($collection); } // Get LoC tags @@ -337,7 +343,7 @@ class Ebook{ } foreach($this->Tags as $tag){ - $searchString .= ' ' . $tag; + $searchString .= ' ' . $tag->Name; } foreach($this->LocTags as $tag){ @@ -502,5 +508,25 @@ class Ebook{ return null; } + + public function HasTag(string $tag): bool{ + foreach($this->Tags as $t){ + if(strtolower($t->Name) == strtolower($tag)){ + return true; + } + } + + return false; + } + + public function IsInCollection(string $collection): bool{ + foreach($this->Collections as $c){ + if(strtolower($c->Name) == strtolower($collection)){ + return true; + } + } + + return false; + } } ?> diff --git a/lib/Library.php b/lib/Library.php index 74bc83f1..57629152 100644 --- a/lib/Library.php +++ b/lib/Library.php @@ -100,6 +100,70 @@ class Library{ return $ebooks; } + public static function GetEbooksByTag(string $tag): array{ + // Do we have the tag's ebooks cached? + $ebooks = apcu_fetch('tag-' . $tag, $success); + + if(!$success){ + $ebooks = []; + + foreach(explode("\n", trim(shell_exec('find ' . SITE_ROOT . '/www/ebooks/ -name "content.opf"') ?? '')) as $filename){ + try{ + $ebookWwwFilesystemPath = preg_replace('|/src/.+|ius', '', $filename) ?? ''; + $ebook = apcu_fetch('ebook-' . $ebookWwwFilesystemPath, $success); + + if(!$success){ + $ebook = new Ebook($ebookWwwFilesystemPath); + apcu_store('ebook-' . $ebookWwwFilesystemPath, $ebook); + } + + if($ebook->HasTag($tag)){ + $ebooks[] = $ebook; + } + } + catch(\Exception $ex){ + // An error in a book isn't fatal; just carry on. + } + } + + apcu_store('tag-' . $tag, $ebooks); + } + + return $ebooks; + } + + public static function GetEbooksByCollection(string $collection): array{ + // Do we have the tag's ebooks cached? + $ebooks = apcu_fetch('collection-' . $collection, $success); + + if(!$success){ + $ebooks = []; + + foreach(explode("\n", trim(shell_exec('find ' . SITE_ROOT . '/www/ebooks/ -name "content.opf"') ?? '')) as $filename){ + try{ + $ebookWwwFilesystemPath = preg_replace('|/src/.+|ius', '', $filename) ?? ''; + $ebook = apcu_fetch('ebook-' . $ebookWwwFilesystemPath, $success); + + if(!$success){ + $ebook = new Ebook($ebookWwwFilesystemPath); + apcu_store('ebook-' . $ebookWwwFilesystemPath, $ebook); + } + + if($ebook->IsInCollection($collection)){ + $ebooks[] = $ebook; + } + } + catch(\Exception $ex){ + // An error in a book isn't fatal; just carry on. + } + } + + apcu_store('collection-' . $collection, $ebooks); + } + + return $ebooks; + } + public static function Search(string $query): array{ $ebooks = Library::GetEbooks(); $matches = []; diff --git a/lib/Tag.php b/lib/Tag.php new file mode 100644 index 00000000..5274a1fb --- /dev/null +++ b/lib/Tag.php @@ -0,0 +1,11 @@ +Name = $name; + $this->Url = '/tags/' . strtolower(str_replace(' ', '-', Formatter::ToPlainText($this->Name))) . '/'; + } +} +?> diff --git a/templates/SearchForm.php b/templates/SearchForm.php index 77f5e7da..acfa4191 100644 --- a/templates/SearchForm.php +++ b/templates/SearchForm.php @@ -1,5 +1,5 @@
diff --git a/www/css/core.css b/www/css/core.css index e4d5afaa..6324ad0a 100644 --- a/www/css/core.css +++ b/www/css/core.css @@ -1035,6 +1035,40 @@ figure{ margin-top: 1rem; } +ul.tags{ + list-style: none; + display: flex; + justify-content: center; + flex-wrap: wrap; +} + +ul.tags li{ + margin: 0; +} + +ul.tags li a{ + border: 1px solid #222; + border-radius: 5px; + padding: .25rem .5rem; + font-style: normal; + background: rgba(0, 0, 0, .1); + text-decoration: none; + display: block; + line-height: 1; + white-space: nowrap; + text-transform: lowercase; + box-shadow: 1px 1px 0px rgba(255, 255, 255, .5) inset; +} + +ul.tags li a:hover{ + color: #fff; + background: #288da4; +} + +ul.tags li + li{ + margin-left: .5rem; +} + code.tag, code.path, code.program, diff --git a/www/ebooks/ebook.php b/www/ebooks/ebook.php index 86b89d33..17e317d4 100644 --- a/www/ebooks/ebook.php +++ b/www/ebooks/ebook.php @@ -71,6 +71,12 @@ catch(\Exception $ex){ ContributorsHtml !== null){ ?>

ContributorsHtml ?>

+ Collections) > 0){ ?> + Collections as $collection){ ?> +

Part of the Name) ?> collection.

+ + +
diff --git a/www/ebooks/index.php b/www/ebooks/index.php index f4b627e1..246e904a 100644 --- a/www/ebooks/index.php +++ b/www/ebooks/index.php @@ -4,6 +4,8 @@ require_once('Core.php'); try{ $page = HttpInput::GetInt('page') ?? 1; $query = HttpInput::GetString('query', false); + $tag = HttpInput::GetString('tag', false); + $collection = HttpInput::GetString('collection', false); $sort = HttpInput::GetString('sort', false) ?? SORT_NEWEST; $pages = 0; $totalEbooks = 0; @@ -16,7 +18,29 @@ try{ $sort = SORT_NEWEST; } - if($query === null){ + if($query !== null){ + $ebooks = Library::Search($query); + $pageTitle = 'Search Standard Ebooks'; + $pageDescription = 'Search results'; + $pageHeader = 'Search Ebooks'; + } + elseif($tag !== null){ + $tag = strtolower(str_replace('-', ' ', $tag)); + $ebooks = Library::GetEbooksByTag($tag); + $pageTitle = 'Browse ebooks tagged “' . Formatter::ToPlainText($tag) . '”'; + $pageDescription = 'A list of ebooks tagged “' . Formatter::ToPlainText($tag) . '”'; + $pageHeader = 'Ebooks tagged “' . Formatter::ToPlainText($tag) . '”'; + } + elseif($collection !== null){ + $collection = strtolower(str_replace('-', ' ', $collection)); + $ebooks = Library::GetEbooksByCollection($collection); + $pageTitle = 'Browse ebooks in the ' . Formatter::ToPlainText(ucwords($collection)) . ' collection'; + $pageDescription = 'A list of ebooks in the ' . Formatter::ToPlainText(ucwords($collection)) . ' collection'; + $pageHeader = 'Ebooks in the ' . Formatter::ToPlainText(ucwords($collection)) . ' collection'; + } + else{ + $pageTitle = 'Browse Standard Ebooks'; + $pageHeader = 'Browse Ebooks'; $ebooks = Library::GetEbooks($sort); $pages = ceil(sizeof($ebooks) / EBOOKS_PER_PAGE); @@ -35,15 +59,6 @@ try{ break; } } - else{ - $ebooks = Library::Search($query); - $pageDescription = 'Search results'; - } - - $pageTitle = 'Browse Standard Ebooks'; - if($query !== null){ - $pageTitle = 'Search Standard Ebooks'; - } } catch(\Exception $ex){ http_response_code(404); @@ -52,7 +67,7 @@ catch(\Exception $ex){ } ?> $pageTitle, 'highlight' => 'ebooks', 'description' => $pageDescription]) ?>
-

BrowseSearch Ebooks

+

$query]) ?>

No ebooks matched your search. You can try different search terms, or browse all of our ebooks.