mirror of
https://github.com/standardebooks/web.git
synced 2025-07-13 01:52:02 -04:00
Add support for tag browsing and collection browsing
This commit is contained in:
parent
f5d7d4e02a
commit
cfa54122d6
8 changed files with 181 additions and 14 deletions
11
lib/Collection.php
Normal file
11
lib/Collection.php
Normal 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))) . '/';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
?>
|
|
@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
?>
|
?>
|
||||||
|
|
|
@ -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
11
lib/Tag.php
Normal 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))) . '/';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
?>
|
|
@ -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>
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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">
|
||||||
|
|
|
@ -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>
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue