Add support for tag browsing and collection browsing

This commit is contained in:
Alex Cabal 2019-02-26 17:05:23 -06:00
parent f5d7d4e02a
commit cfa54122d6
8 changed files with 181 additions and 14 deletions

11
lib/Collection.php Normal file
View file

@ -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))) . '/';
}
}
?>

View file

@ -12,6 +12,7 @@ class Ebook{
public $GitCommits = []; public $GitCommits = [];
public $Tags = []; public $Tags = [];
public $LocTags = []; public $LocTags = [];
public $Collections = [];
public $Identifier; public $Identifier;
public $UrlSafeIdentifier; public $UrlSafeIdentifier;
public $HeroImageUrl; public $HeroImageUrl;
@ -149,7 +150,12 @@ class Ebook{
// Get SE tags // Get SE tags
foreach($xml->xpath('/package/metadata/meta[@property="se:subject"]') ?: [] as $tag){ 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 // Get LoC tags
@ -337,7 +343,7 @@ class Ebook{
} }
foreach($this->Tags as $tag){ foreach($this->Tags as $tag){
$searchString .= ' ' . $tag; $searchString .= ' ' . $tag->Name;
} }
foreach($this->LocTags as $tag){ foreach($this->LocTags as $tag){
@ -502,5 +508,25 @@ class Ebook{
return null; 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;
}
} }
?> ?>

View file

@ -100,6 +100,70 @@ class Library{
return $ebooks; 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{ public static function Search(string $query): array{
$ebooks = Library::GetEbooks(); $ebooks = Library::GetEbooks();
$matches = []; $matches = [];

11
lib/Tag.php Normal file
View file

@ -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))) . '/';
}
}
?>

View file

@ -1,5 +1,5 @@
<form action="/ebooks/" method="get"> <form action="/ebooks/" method="get">
<label class="search"> <label class="search">
Search ebooks: <input type="search" name="query" placeholder="Search ebooks..." value="<?= Formatter::ToPlainText($query ?? '') ?>" /> Search ebooks: <input type="search" name="query" placeholder="Search all ebooks..." value="<?= Formatter::ToPlainText($query ?? '') ?>" />
</label> </label>
</form> </form>

View file

@ -1035,6 +1035,40 @@ figure{
margin-top: 1rem; 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.tag,
code.path, code.path,
code.program, code.program,

View file

@ -71,6 +71,12 @@ catch(\Exception $ex){
<? if($ebook->ContributorsHtml !== null){ ?> <? if($ebook->ContributorsHtml !== null){ ?>
<p><?= $ebook->ContributorsHtml ?></p> <p><?= $ebook->ContributorsHtml ?></p>
<? } ?> <? } ?>
<? if(sizeof($ebook->Collections) > 0){ ?>
<? foreach($ebook->Collections as $collection){ ?>
<p>Part of the <a href="<?= $collection->Url ?>"><?= Formatter::ToPlainText($collection->Name) ?> collection</a>.</p>
<? } ?>
<? } ?>
<ul class="tags"><? foreach($ebook->Tags as $tag){ ?><li><a href="<?= $tag->Url ?>"><?= Formatter::ToPlainText($tag->Name) ?></a></li><? } ?></ul>
</aside> </aside>
<section id="description"> <section id="description">

View file

@ -4,6 +4,8 @@ require_once('Core.php');
try{ try{
$page = HttpInput::GetInt('page') ?? 1; $page = HttpInput::GetInt('page') ?? 1;
$query = HttpInput::GetString('query', false); $query = HttpInput::GetString('query', false);
$tag = HttpInput::GetString('tag', false);
$collection = HttpInput::GetString('collection', false);
$sort = HttpInput::GetString('sort', false) ?? SORT_NEWEST; $sort = HttpInput::GetString('sort', false) ?? SORT_NEWEST;
$pages = 0; $pages = 0;
$totalEbooks = 0; $totalEbooks = 0;
@ -16,7 +18,29 @@ try{
$sort = SORT_NEWEST; $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); $ebooks = Library::GetEbooks($sort);
$pages = ceil(sizeof($ebooks) / EBOOKS_PER_PAGE); $pages = ceil(sizeof($ebooks) / EBOOKS_PER_PAGE);
@ -35,15 +59,6 @@ try{
break; break;
} }
} }
else{
$ebooks = Library::Search($query);
$pageDescription = 'Search results';
}
$pageTitle = 'Browse Standard Ebooks';
if($query !== null){
$pageTitle = 'Search Standard Ebooks';
}
} }
catch(\Exception $ex){ catch(\Exception $ex){
http_response_code(404); http_response_code(404);
@ -52,7 +67,7 @@ catch(\Exception $ex){
} }
?><?= Template::Header(['title' => $pageTitle, 'highlight' => 'ebooks', 'description' => $pageDescription]) ?> ?><?= Template::Header(['title' => $pageTitle, 'highlight' => 'ebooks', 'description' => $pageDescription]) ?>
<main class="ebooks"> <main class="ebooks">
<h1><? if($query === null){ ?>Browse<? }else{ ?>Search<? } ?> Ebooks</h1> <h1><?= $pageHeader ?></h1>
<?= Template::SearchForm(['query' => $query]) ?> <?= Template::SearchForm(['query' => $query]) ?>
<? if(sizeof($ebooks) == 0){ ?> <? if(sizeof($ebooks) == 0){ ?>
<p class="no-results">No ebooks matched your search. You can try different search terms, or <a href="/ebooks/">browse all of our ebooks</a>.</p> <p class="no-results">No ebooks matched your search. You can try different search terms, or <a href="/ebooks/">browse all of our ebooks</a>.</p>