From b7b63a4be5a2b9ae21bf7444c43781a716db8751 Mon Sep 17 00:00:00 2001 From: Alex Cabal Date: Sun, 10 Nov 2024 22:59:57 -0600 Subject: [PATCH] Refactor functions out of Library --- lib/Artist.php | 10 + lib/Artwork.php | 204 +++++++++++++++-- lib/Collection.php | 11 + lib/Ebook.php | 155 ++++++++++++- lib/EbookTag.php | 14 ++ lib/Enums/FeedType.php | 8 + lib/Library.php | 391 -------------------------------- scripts/generate-bulk-downloads | 2 +- scripts/generate-feeds | 4 +- templates/ArtworkForm.php | 2 +- templates/SearchForm.php | 2 +- www/artists/get.php | 2 +- www/artworks/index.php | 2 +- www/artworks/post.php | 3 +- www/collections/get.php | 2 +- www/collections/index.php | 2 +- www/ebooks/author.php | 2 +- www/ebooks/ebook.php | 2 +- www/ebooks/index.php | 2 +- www/feeds/401.php | 16 +- www/feeds/atom/search.php | 2 +- www/feeds/get.php | 21 +- www/feeds/opds/search.php | 2 +- www/feeds/rss/search.php | 2 +- 24 files changed, 419 insertions(+), 444 deletions(-) create mode 100644 lib/Enums/FeedType.php diff --git a/lib/Artist.php b/lib/Artist.php index ec849f97..444a2f93 100644 --- a/lib/Artist.php +++ b/lib/Artist.php @@ -126,6 +126,16 @@ class Artist{ ', [$artistId], Artist::class)[0] ?? throw new Exceptions\ArtistNotFoundException(); } + /** + * @return array + */ + public static function GetAll(): array{ + return Db::Query(' + SELECT * + from Artists + order by Name asc', [], Artist::class); + } + /** * @throws Exceptions\ArtistNotFoundException */ diff --git a/lib/Artwork.php b/lib/Artwork.php index a3e8ee9b..f6b91f9c 100644 --- a/lib/Artwork.php +++ b/lib/Artwork.php @@ -7,6 +7,7 @@ use function Safe\getimagesize; use function Safe\parse_url; use function Safe\preg_match; use function Safe\preg_replace; +use function Safe\preg_split; use function Safe\unlink; /** @@ -922,25 +923,194 @@ class Artwork{ return $result[0] ?? throw new Exceptions\ArtworkNotFoundException(); } - public static function FromHttpPost(): Artwork{ - $artwork = new Artwork(); + /** + * @return array + * + * @throws Exceptions\ArtistNotFoundException + */ + public static function GetAllByArtist(?string $artistUrlName, ?string $status, ?int $submitterUserId): array{ + if($artistUrlName === null){ + throw new Exceptions\ArtistNotFoundException(); + } - $artwork->Name = HttpInput::Str(POST, 'artwork-name') ?? ''; - $artwork->CompletedYear = HttpInput::Int(POST, 'artwork-year'); - $artwork->CompletedYearIsCirca = HttpInput::Bool(POST, 'artwork-completed-year-is-circa') ?? false; - $artwork->Tags = HttpInput::Str(POST, 'artwork-tags') ?? []; - $artwork->Status = Enums\ArtworkStatusType::tryFrom(HttpInput::Str(POST, 'artwork-status') ?? '') ?? Enums\ArtworkStatusType::Unverified; - $artwork->EbookUrl = HttpInput::Str(POST, 'artwork-ebook-url'); - $artwork->IsPublishedInUs = HttpInput::Bool(POST, 'artwork-is-published-in-us') ?? false; - $artwork->PublicationYear = HttpInput::Int(POST, 'artwork-publication-year'); - $artwork->PublicationYearPageUrl = HttpInput::Str(POST, 'artwork-publication-year-page-url'); - $artwork->CopyrightPageUrl = HttpInput::Str(POST, 'artwork-copyright-page-url'); - $artwork->ArtworkPageUrl = HttpInput::Str(POST, 'artwork-artwork-page-url'); - $artwork->MuseumUrl = HttpInput::Str(POST, 'artwork-museum-url'); - $artwork->Exception = HttpInput::Str(POST, 'artwork-exception'); - $artwork->Notes = HttpInput::Str(POST, 'artwork-notes'); + // $status is only one of three special statuses, which are a subset of FilterArtwork() above: + // null: same as "all" + // "all": Show all approved and in use artwork + // "all-admin": Show all artwork regardless of status + // "all-submitter": Show all approved and in use artwork, plus unverified artwork from the submitter + $statusCondition = ''; + $params = []; - return $artwork; + if($status == 'all-admin'){ + $statusCondition = 'true'; + } + elseif($status == 'all-submitter' && $submitterUserId !== null){ + $statusCondition = '(Status = ? or (Status = ? and SubmitterUserId = ?))'; + $params[] = Enums\ArtworkStatusType::Approved->value; + $params[] = Enums\ArtworkStatusType::Unverified->value; + $params[] = $submitterUserId; + } + else{ + $statusCondition = 'Status = ?'; + $params[] = Enums\ArtworkStatusType::Approved->value; + } + + $params[] = $artistUrlName; // a.UrlName + + $artworks = Db::Query(' + SELECT art.* + from Artworks art + inner join Artists a using (ArtistId) + where ' . $statusCondition . ' + and a.UrlName = ? + order by art.Created desc', $params, Artwork::class); + + return $artworks; + } + + /** + * @return array{artworks: array, artworksCount: int} + */ + public static function GetAllByFilter(?string $query = null, ?string $status = null, ?Enums\ArtworkSortType $sort = null, ?int $submitterUserId = null, int $page = 1, int $perPage = ARTWORK_PER_PAGE): array{ + // $status is either the string value of an ArtworkStatus enum, or one of these special statuses: + // null: same as "all" + // "all": Show all approved and in use artwork + // "all-admin": Show all artwork regardless of status + // "all-submitter": Show all approved and in use artwork, plus unverified artwork from the submitter + // "unverified-submitter": Show unverified artwork from the submitter + // "in-use": Show only in-use artwork + + $statusCondition = ''; + $params = []; + + if($status === null || $status == 'all'){ + $statusCondition = 'Status = ?'; + $params[] = Enums\ArtworkStatusType::Approved->value; + } + elseif($status == 'all-admin'){ + $statusCondition = 'true'; + } + elseif($status == 'all-submitter' && $submitterUserId !== null){ + $statusCondition = '(Status = ? or (Status = ? and SubmitterUserId = ?))'; + $params[] = Enums\ArtworkStatusType::Approved->value; + $params[] = Enums\ArtworkStatusType::Unverified->value; + $params[] = $submitterUserId; + } + elseif($status == 'unverified-submitter' && $submitterUserId !== null){ + $statusCondition = 'Status = ? and SubmitterUserId = ?'; + $params[] = Enums\ArtworkStatusType::Unverified->value; + $params[] = $submitterUserId; + } + elseif($status == 'in-use'){ + $statusCondition = 'Status = ? and EbookUrl is not null'; + $params[] = Enums\ArtworkStatusType::Approved->value; + } + elseif($status == Enums\ArtworkStatusType::Approved->value){ + $statusCondition = 'Status = ? and EbookUrl is null'; + $params[] = Enums\ArtworkStatusType::Approved->value; + } + else{ + $statusCondition = 'Status = ?'; + $params[] = $status; + } + + $orderBy = 'art.Created desc'; + if($sort == Enums\ArtworkSortType::ArtistAlpha){ + $orderBy = 'a.Name'; + } + elseif($sort == Enums\ArtworkSortType::CompletedNewest){ + $orderBy = 'art.CompletedYear desc'; + } + + // Remove diacritics and non-alphanumeric characters, but preserve apostrophes + if($query !== null && $query != ''){ + $query = trim(preg_replace('|[^a-zA-Z0-9\'’ ]|ius', ' ', Formatter::RemoveDiacritics($query))); + } + else{ + $query = ''; + } + + // We use replace() below because if there's multiple contributors separated by an underscore, + // the underscore won't count as word boundary and we won't get a match. + // See https://github.com/standardebooks/web/pull/325 + $limit = $perPage; + $offset = (($page - 1) * $perPage); + + if($query == ''){ + $artworksCount = Db::QueryInt(' + SELECT count(*) + from Artworks art + where ' . $statusCondition, $params); + + $params[] = $limit; + $params[] = $offset; + + $artworks = Db::Query(' + SELECT art.* + from Artworks art + inner join Artists a USING (ArtistId) + where ' . $statusCondition . ' + order by ' . $orderBy . ' + limit ? + offset ?', $params, Artwork::class); + } + else{ + // Split the query on word boundaries followed by spaces. This keeps words with apostrophes intact. + $tokenArray = preg_split('/\b\s+/', $query, -1, PREG_SPLIT_NO_EMPTY); + + // Join the tokens with '|' to search on any token, but add word boundaries to force the full token to match + $tokenizedQuery = '\b(' . implode('|', $tokenArray) . ')\b'; + + $params[] = $tokenizedQuery; // art.Name + $params[] = $tokenizedQuery; // art.EbookUrl + $params[] = $tokenizedQuery; // a.Name + $params[] = $tokenizedQuery; // aan.Name + $params[] = $tokenizedQuery; // t.Name + + $artworksCount = Db::QueryInt(' + SELECT + count(*) + from + (SELECT distinct + ArtworkId + from + Artworks art + inner join Artists a USING (ArtistId) + left join ArtistAlternateNames aan USING (ArtistId) + left join ArtworkTags at USING (ArtworkId) + left join Tags t USING (TagId) + where + ' . $statusCondition . ' + and (art.Name regexp ? + or replace(art.EbookUrl, "_", " ") regexp ? + or a.Name regexp ? + or aan.Name regexp ? + or t.Name regexp ?) + group by art.ArtworkId) x', $params); + + $params[] = $limit; + $params[] = $offset; + + $artworks = Db::Query(' + SELECT art.* + from Artworks art + inner join Artists a using (ArtistId) + left join ArtistAlternateNames aan using (ArtistId) + left join ArtworkTags at using (ArtworkId) + left join Tags t using (TagId) + where ' . $statusCondition . ' + and (art.Name regexp ? + or replace(art.EbookUrl, "_", " ") regexp ? + or a.Name regexp ? + or aan.Name regexp ? + or t.Name regexp ?) + group by art.ArtworkId + order by ' . $orderBy . ' + limit ? + offset ?', $params, Artwork::class); + } + + return ['artworks' => $artworks, 'artworksCount' => $artworksCount]; } public function FillFromHttpPost(): void{ diff --git a/lib/Collection.php b/lib/Collection.php index faf8bfbe..63418fee 100644 --- a/lib/Collection.php +++ b/lib/Collection.php @@ -55,6 +55,17 @@ class Collection{ return $result[0] ?? throw new Exceptions\CollectionNotFoundException();; } + /** + * @return array + */ + public static function GetAll(): array{ + return Db::Query(' + SELECT * + from Collections + order by Name asc + ', [], Collection::class); + } + // ******* // METHODS diff --git a/lib/Ebook.php b/lib/Ebook.php index 46105bf1..19dabc21 100644 --- a/lib/Ebook.php +++ b/lib/Ebook.php @@ -1,5 +1,4 @@ + */ + public static function GetAll(): array{ + // Get all ebooks, unsorted. + return Db::Query(' + SELECT * + from Ebooks + ', [], Ebook::class); + } + + /** + * @return array + */ + public static function GetAllByAuthor(string $urlPath): array{ + if(mb_strpos($urlPath, '_') === false){ + // Single author + return Db::Query(' + SELECT e.* + from Ebooks e + inner join Contributors con using (EbookId) + where con.MarcRole = "aut" + and con.UrlName = ? + order by e.EbookCreated desc + ', [$urlPath], Ebook::class); + } + else{ + // Multiple authors, e.g., `karl-marx_friedrich-engels`. + $authors = explode('_', $urlPath); + + $params = $authors; + $params[] = sizeof($authors); // The number of authors in the URL must match the number of `Contributor` records. + + return Db::Query(' + SELECT e.* + from Ebooks e + inner join Contributors con using (EbookId) + where con.MarcRole = "aut" + and con.UrlName in ' . Db::CreateSetSql($authors) . ' + group by e.EbookId + having count(distinct con.UrlName) = ? + order by e.EbookCreated desc + ', $params, Ebook::class); + } + } + + /** + * @return array + */ + public static function GetAllByCollection(string $collection): array{ + $ebooks = Db::Query(' + SELECT e.* + from Ebooks e + inner join CollectionEbooks ce using (EbookId) + inner join Collections c using (CollectionId) + where c.UrlName = ? + order by ce.SequenceNumber, e.EbookCreated desc + ', [$collection], Ebook::class); + + return $ebooks; + } + + /** + * @return array + */ + public static function GetAllByRelated(Ebook $ebook, int $count, ?EbookTag $relatedTag): array{ + if($relatedTag !== null){ + $relatedEbooks = Db::Query(' + SELECT e.* + from Ebooks e + inner join EbookTags et using (EbookId) + where et.TagId = ? + and et.EbookId != ? + order by RAND() + limit ? + ', [$relatedTag->TagId, $ebook->EbookId, $count], Ebook::class); + } + else{ + $relatedEbooks = Db::Query(' + SELECT * + from Ebooks + where EbookId != ? + order by RAND() + limit ? + ', [$ebook->EbookId, $count], Ebook::class); + } + + return $relatedEbooks; + } + + /** + * @param array $tags + * + * @return array{ebooks: array, ebooksCount: int} + */ + public static function GetAllByFilter(string $query = null, array $tags = [], Enums\EbookSortType $sort = null, int $page = 1, int $perPage = EBOOKS_PER_PAGE): array{ + $limit = $perPage; + $offset = (($page - 1) * $perPage); + $joinContributors = ''; + $joinTags = ''; + $params = []; + $whereCondition = 'where true'; + + $orderBy = 'e.EbookCreated desc'; + if($sort == Enums\EbookSortType::AuthorAlpha){ + $joinContributors = 'inner join Contributors con using (EbookId)'; + $whereCondition .= ' AND con.MarcRole = "aut"'; + $orderBy = 'con.SortName, e.EbookCreated desc'; + } + elseif($sort == Enums\EbookSortType::ReadingEase){ + $orderBy = 'e.ReadingEase desc'; + } + elseif($sort == Enums\EbookSortType::Length){ + $orderBy = 'e.WordCount'; + } + + if(sizeof($tags) > 0 && !in_array('all', $tags)){ // 0 tags means "all ebooks" + $joinTags = 'inner join EbookTags et using (EbookId) + inner join Tags t using (TagId)'; + $whereCondition .= ' AND t.UrlName in ' . Db::CreateSetSql($tags) . ' '; + $params = $tags; + } + + if($query !== null && $query != ''){ + $query = trim(preg_replace('|[^a-zA-Z0-9 ]|ius', ' ', Formatter::RemoveDiacritics($query))); + $query = sprintf('"%s"', $query); // Require an exact match via double quotes. + $whereCondition .= ' AND match(e.IndexableText) against(? IN BOOLEAN MODE) '; + $params[] = $query; + } + + $ebooksCount = Db::QueryInt(' + SELECT count(distinct e.EbookId) + from Ebooks e + ' . $joinContributors . ' + ' . $joinTags . ' + ' . $whereCondition . ' + ', $params); + + $params[] = $limit; + $params[] = $offset; + + $ebooks = Db::Query(' + SELECT distinct e.* + from Ebooks e + ' . $joinContributors . ' + ' . $joinTags . ' + ' . $whereCondition . ' + order by ' . $orderBy . ' + limit ? + offset ?', $params, Ebook::class); + + return ['ebooks' => $ebooks, 'ebooksCount' => $ebooksCount]; + } } diff --git a/lib/EbookTag.php b/lib/EbookTag.php index 703e869b..67a21726 100644 --- a/lib/EbookTag.php +++ b/lib/EbookTag.php @@ -93,4 +93,18 @@ class EbookTag extends Tag{ return $this; } } + + /** + * @return array + */ + public static function GetAll(): array{ + $tags = Db::Query(' + SELECT * + from Tags t + where Type = ? + order by Name + ', [Enums\TagType::Ebook], EbookTag::class); + + return $tags; + } } diff --git a/lib/Enums/FeedType.php b/lib/Enums/FeedType.php new file mode 100644 index 00000000..058e0bb9 --- /dev/null +++ b/lib/Enums/FeedType.php @@ -0,0 +1,8 @@ + $tags - * - * @return array{ebooks: array, ebooksCount: int} - */ - public static function FilterEbooks(string $query = null, array $tags = [], Enums\EbookSortType $sort = null, int $page = 1, int $perPage = EBOOKS_PER_PAGE): array{ - $limit = $perPage; - $offset = (($page - 1) * $perPage); - $joinContributors = ''; - $joinTags = ''; - $params = []; - $whereCondition = 'where true'; - - $orderBy = 'e.EbookCreated desc'; - if($sort == Enums\EbookSortType::AuthorAlpha){ - $joinContributors = 'inner join Contributors con using (EbookId)'; - $whereCondition .= ' AND con.MarcRole = "aut"'; - $orderBy = 'con.SortName, e.EbookCreated desc'; - } - elseif($sort == Enums\EbookSortType::ReadingEase){ - $orderBy = 'e.ReadingEase desc'; - } - elseif($sort == Enums\EbookSortType::Length){ - $orderBy = 'e.WordCount'; - } - - if(sizeof($tags) > 0 && !in_array('all', $tags)){ // 0 tags means "all ebooks" - $joinTags = 'inner join EbookTags et using (EbookId) - inner join Tags t using (TagId)'; - $whereCondition .= ' AND t.UrlName in ' . Db::CreateSetSql($tags) . ' '; - $params = $tags; - } - - if($query !== null && $query != ''){ - $query = trim(preg_replace('|[^a-zA-Z0-9 ]|ius', ' ', Formatter::RemoveDiacritics($query))); - $query = sprintf('"%s"', $query); // Require an exact match via double quotes. - $whereCondition .= ' AND match(e.IndexableText) against(? IN BOOLEAN MODE) '; - $params[] = $query; - } - - $ebooksCount = Db::QueryInt(' - SELECT count(distinct e.EbookId) - from Ebooks e - ' . $joinContributors . ' - ' . $joinTags . ' - ' . $whereCondition . ' - ', $params); - - $params[] = $limit; - $params[] = $offset; - - $ebooks = Db::Query(' - SELECT distinct e.* - from Ebooks e - ' . $joinContributors . ' - ' . $joinTags . ' - ' . $whereCondition . ' - order by ' . $orderBy . ' - limit ? - offset ?', $params, Ebook::class); - - return ['ebooks' => $ebooks, 'ebooksCount' => $ebooksCount]; - } - - /** - * @return array - */ - public static function GetEbooks(): array{ - // Get all ebooks, unsorted. - return Db::Query(' - SELECT * - from Ebooks - ', [], Ebook::class); - } - - /** - * @return array - */ - public static function GetEbooksByAuthor(string $urlPath): array{ - if(mb_strpos($urlPath, '_') === false){ - // Single author - return Db::Query(' - SELECT e.* - from Ebooks e - inner join Contributors con using (EbookId) - where con.MarcRole = "aut" - and con.UrlName = ? - order by e.EbookCreated desc - ', [$urlPath], Ebook::class); - } - else{ - // Multiple authors, e.g., `karl-marx_friedrich-engels`. - $authors = explode('_', $urlPath); - - $params = $authors; - $params[] = sizeof($authors); // The number of authors in the URL must match the number of `Contributor` records. - - return Db::Query(' - SELECT e.* - from Ebooks e - inner join Contributors con using (EbookId) - where con.MarcRole = "aut" - and con.UrlName in ' . Db::CreateSetSql($authors) . ' - group by e.EbookId - having count(distinct con.UrlName) = ? - order by e.EbookCreated desc - ', $params, Ebook::class); - } - } - - /** - * @return array - * - * @throws Exceptions\AppException - */ - public static function GetEbookCollections(): array{ - $collections = Db::Query(' - SELECT * - from Collections - ', [], Collection::class); - - $collator = Collator::create('en_US'); - if($collator === null){ - throw new Exceptions\AppException('Couldn\'t create collator object when getting collections.'); - } - usort($collections, function(Collection $a, Collection $b) use($collator){ - $result = $collator->compare($a->GetSortedName(), $b->GetSortedName()); - return $result === false ? 0 : $result; - }); - return $collections; - } - - /** - * @return array - */ - public static function GetEbooksByCollection(string $collection): array{ - $ebooks = Db::Query(' - SELECT e.* - from Ebooks e - inner join CollectionEbooks ce using (EbookId) - inner join Collections c using (CollectionId) - where c.UrlName = ? - order by ce.SequenceNumber, e.EbookCreated desc - ', [$collection], Ebook::class); - - return $ebooks; - } - - /** - * @return array - */ - public static function GetRelatedEbooks(Ebook $ebook, int $count, ?EbookTag $relatedTag): array{ - if($relatedTag !== null){ - $relatedEbooks = Db::Query(' - SELECT e.* - from Ebooks e - inner join EbookTags et using (EbookId) - where et.TagId = ? - and et.EbookId != ? - order by RAND() - limit ? - ', [$relatedTag->TagId, $ebook->EbookId, $count], Ebook::class); - } - else{ - $relatedEbooks = Db::Query(' - SELECT * - from Ebooks - where EbookId != ? - order by RAND() - limit ? - ', [$ebook->EbookId, $count], Ebook::class); - } - - return $relatedEbooks; - } - - /** - * @return array - */ - public static function GetTags(): array{ - $tags = Db::Query(' - SELECT * - from Tags t - where Type = "ebook" - order by Name - ', [], EbookTag::class); - - return $tags; - } - - /** - * @return array{artworks: array, artworksCount: int} - */ - public static function FilterArtwork(?string $query = null, ?string $status = null, ?Enums\ArtworkSortType $sort = null, ?int $submitterUserId = null, int $page = 1, int $perPage = ARTWORK_PER_PAGE): array{ - // $status is either the string value of an ArtworkStatus enum, or one of these special statuses: - // null: same as "all" - // "all": Show all approved and in use artwork - // "all-admin": Show all artwork regardless of status - // "all-submitter": Show all approved and in use artwork, plus unverified artwork from the submitter - // "unverified-submitter": Show unverified artwork from the submitter - // "in-use": Show only in-use artwork - - $statusCondition = ''; - $params = []; - - if($status === null || $status == 'all'){ - $statusCondition = 'Status = ?'; - $params[] = Enums\ArtworkStatusType::Approved->value; - } - elseif($status == 'all-admin'){ - $statusCondition = 'true'; - } - elseif($status == 'all-submitter' && $submitterUserId !== null){ - $statusCondition = '(Status = ? or (Status = ? and SubmitterUserId = ?))'; - $params[] = Enums\ArtworkStatusType::Approved->value; - $params[] = Enums\ArtworkStatusType::Unverified->value; - $params[] = $submitterUserId; - } - elseif($status == 'unverified-submitter' && $submitterUserId !== null){ - $statusCondition = 'Status = ? and SubmitterUserId = ?'; - $params[] = Enums\ArtworkStatusType::Unverified->value; - $params[] = $submitterUserId; - } - elseif($status == 'in-use'){ - $statusCondition = 'Status = ? and EbookUrl is not null'; - $params[] = Enums\ArtworkStatusType::Approved->value; - } - elseif($status == Enums\ArtworkStatusType::Approved->value){ - $statusCondition = 'Status = ? and EbookUrl is null'; - $params[] = Enums\ArtworkStatusType::Approved->value; - } - else{ - $statusCondition = 'Status = ?'; - $params[] = $status; - } - - $orderBy = 'art.Created desc'; - if($sort == Enums\ArtworkSortType::ArtistAlpha){ - $orderBy = 'a.Name'; - } - elseif($sort == Enums\ArtworkSortType::CompletedNewest){ - $orderBy = 'art.CompletedYear desc'; - } - - // Remove diacritics and non-alphanumeric characters, but preserve apostrophes - if($query !== null && $query != ''){ - $query = trim(preg_replace('|[^a-zA-Z0-9\'’ ]|ius', ' ', Formatter::RemoveDiacritics($query))); - } - else{ - $query = ''; - } - - // We use replace() below because if there's multiple contributors separated by an underscore, - // the underscore won't count as word boundary and we won't get a match. - // See https://github.com/standardebooks/web/pull/325 - $limit = $perPage; - $offset = (($page - 1) * $perPage); - - if($query == ''){ - $artworksCount = Db::QueryInt(' - SELECT count(*) - from Artworks art - where ' . $statusCondition, $params); - - $params[] = $limit; - $params[] = $offset; - - $artworks = Db::Query(' - SELECT art.* - from Artworks art - inner join Artists a USING (ArtistId) - where ' . $statusCondition . ' - order by ' . $orderBy . ' - limit ? - offset ?', $params, Artwork::class); - } - else{ - // Split the query on word boundaries followed by spaces. This keeps words with apostrophes intact. - $tokenArray = preg_split('/\b\s+/', $query, -1, PREG_SPLIT_NO_EMPTY); - - // Join the tokens with '|' to search on any token, but add word boundaries to force the full token to match - $tokenizedQuery = '\b(' . implode('|', $tokenArray) . ')\b'; - - $params[] = $tokenizedQuery; // art.Name - $params[] = $tokenizedQuery; // art.EbookUrl - $params[] = $tokenizedQuery; // a.Name - $params[] = $tokenizedQuery; // aan.Name - $params[] = $tokenizedQuery; // t.Name - - $artworksCount = Db::QueryInt(' - SELECT - count(*) - from - (SELECT distinct - ArtworkId - from - Artworks art - inner join Artists a USING (ArtistId) - left join ArtistAlternateNames aan USING (ArtistId) - left join ArtworkTags at USING (ArtworkId) - left join Tags t USING (TagId) - where - ' . $statusCondition . ' - and (art.Name regexp ? - or replace(art.EbookUrl, "_", " ") regexp ? - or a.Name regexp ? - or aan.Name regexp ? - or t.Name regexp ?) - group by art.ArtworkId) x', $params); - - $params[] = $limit; - $params[] = $offset; - - $artworks = Db::Query(' - SELECT art.* - from Artworks art - inner join Artists a using (ArtistId) - left join ArtistAlternateNames aan using (ArtistId) - left join ArtworkTags at using (ArtworkId) - left join Tags t using (TagId) - where ' . $statusCondition . ' - and (art.Name regexp ? - or replace(art.EbookUrl, "_", " ") regexp ? - or a.Name regexp ? - or aan.Name regexp ? - or t.Name regexp ?) - group by art.ArtworkId - order by ' . $orderBy . ' - limit ? - offset ?', $params, Artwork::class); - } - - return ['artworks' => $artworks, 'artworksCount' => $artworksCount]; - } - - /** - * @return array - * - * @throws Exceptions\ArtistNotFoundException - */ - public static function GetArtworksByArtist(?string $artistUrlName, ?string $status, ?int $submitterUserId): array{ - if($artistUrlName === null){ - throw new Exceptions\ArtistNotFoundException(); - } - - // $status is only one of three special statuses, which are a subset of FilterArtwork() above: - // null: same as "all" - // "all": Show all approved and in use artwork - // "all-admin": Show all artwork regardless of status - // "all-submitter": Show all approved and in use artwork, plus unverified artwork from the submitter - $statusCondition = ''; - $params = []; - - if($status == 'all-admin'){ - $statusCondition = 'true'; - } - elseif($status == 'all-submitter' && $submitterUserId !== null){ - $statusCondition = '(Status = ? or (Status = ? and SubmitterUserId = ?))'; - $params[] = Enums\ArtworkStatusType::Approved->value; - $params[] = Enums\ArtworkStatusType::Unverified->value; - $params[] = $submitterUserId; - } - else{ - $statusCondition = 'Status = ?'; - $params[] = Enums\ArtworkStatusType::Approved->value; - } - - $params[] = $artistUrlName; // a.UrlName - - $artworks = Db::Query(' - SELECT art.* - from Artworks art - inner join Artists a using (ArtistId) - where ' . $statusCondition . ' - and a.UrlName = ? - order by art.Created desc', $params, Artwork::class); - - return $artworks; - } - private static function FillBulkDownloadObject(string $dir, string $downloadType, string $urlRoot): stdClass{ $obj = new stdClass(); @@ -614,14 +233,4 @@ class Library{ return $retval; } - - /** - * @return array - */ - public static function GetArtists(): array{ - return Db::Query(' - SELECT * - from Artists - order by Name asc', [], Artist::class); - } } diff --git a/scripts/generate-bulk-downloads b/scripts/generate-bulk-downloads index cc97d688..bf0af4f7 100755 --- a/scripts/generate-bulk-downloads +++ b/scripts/generate-bulk-downloads @@ -103,7 +103,7 @@ function CreateZip(string $filePath, array $ebooks, string $type, string $webRoo } // Iterate over all ebooks and arrange them by publication month. -foreach(Library::GetEbooks() as $ebook){ +foreach(Ebook::GetAll() as $ebook){ $timestamp = $ebook->EbookCreated->format('Y-m'); $updatedTimestamp = $ebook->EbookUpdated->getTimestamp(); diff --git a/scripts/generate-feeds b/scripts/generate-feeds index aefc2a19..4bafec85 100755 --- a/scripts/generate-feeds +++ b/scripts/generate-feeds @@ -38,7 +38,7 @@ function SaveFeed(Feed $feed, bool $force, ?string $label = null, ?string $label * @param array> $ebooks */ function CreateOpdsCollectionFeed(string $name, string $url, string $description, array $collections, array $ebooks, DateTimeImmutable $now, string $webRoot, OpdsNavigationFeed $opdsRoot, bool $force): void{ - $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){ return; @@ -97,7 +97,7 @@ foreach($dirs as $dir){ } // Iterate over all ebooks to build the various feeds. -foreach(Library::GetEbooks() as $ebook){ +foreach(Ebook::GetAll() as $ebook){ $allEbooks[] = $ebook; $newestEbooks[] = $ebook; diff --git a/templates/ArtworkForm.php b/templates/ArtworkForm.php index 4d6be444..63d87d5b 100644 --- a/templates/ArtworkForm.php +++ b/templates/ArtworkForm.php @@ -16,7 +16,7 @@ $isEditForm = $isEditForm ?? false; Name For existing artists, leave the year of death blank. - + AlternateNames ?? []) as $alternateName){ ?> diff --git a/templates/SearchForm.php b/templates/SearchForm.php index 884d84f8..31bd1605 100644 --- a/templates/SearchForm.php +++ b/templates/SearchForm.php @@ -12,7 +12,7 @@ $isAllSelected = sizeof($tags) == 0 || in_array('all', $tags);