if($query === null){ ?>Browse }else{ ?>Search } ?> Ebooks
+= $pageHeader ?>
= Template::SearchForm(['query' => $query]) ?> if(sizeof($ebooks) == 0){ ?>No ebooks matched your search. You can try different search terms, or browse all of our ebooks.
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 @@ + +class Collection{ + public $Name; + public $Url; + + public function __construct(string $name){ + $this->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 @@ + +class Tag{ + public $Name; + public $Url; + + public function __construct(string $name){ + $this->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){ if($ebook->ContributorsHtml !== null){ ?>= $ebook->ContributorsHtml ?>
} ?> + if(sizeof($ebook->Collections) > 0){ ?> + foreach($ebook->Collections as $collection){ ?> +Part of the = Formatter::ToPlainText($collection->Name) ?> collection.
+ } ?> + } ?> +No ebooks matched your search. You can try different search terms, or browse all of our ebooks.