mirror of
https://github.com/standardebooks/web.git
synced 2025-07-06 14:50:39 -04:00
Add PropertyFromHttp trait and update codebase to use new pattern
This commit is contained in:
parent
c35c47b793
commit
acb30b897c
47 changed files with 851 additions and 527 deletions
|
@ -7,7 +7,7 @@ CREATE TABLE IF NOT EXISTS `Artworks` (
|
||||||
`CompletedYearIsCirca` boolean NOT NULL DEFAULT FALSE,
|
`CompletedYearIsCirca` boolean NOT NULL DEFAULT FALSE,
|
||||||
`Created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
`Created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
`Updated` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
`Updated` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||||
`Status` enum('unverified', 'approved', 'declined', 'in_use') DEFAULT 'unverified',
|
`Status` enum('unverified', 'approved', 'declined', 'in_use') NOT NULL DEFAULT 'unverified',
|
||||||
`SubmitterUserId` int(10) unsigned NULL,
|
`SubmitterUserId` int(10) unsigned NULL,
|
||||||
`ReviewerUserId` int(10) unsigned NULL,
|
`ReviewerUserId` int(10) unsigned NULL,
|
||||||
`MuseumUrl` varchar(255) NULL,
|
`MuseumUrl` varchar(255) NULL,
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
CREATE TABLE IF NOT EXISTS `EbookSources` (
|
CREATE TABLE IF NOT EXISTS `EbookSources` (
|
||||||
`EbookId` int(10) unsigned NOT NULL,
|
`EbookId` int(10) unsigned NOT NULL,
|
||||||
`Type` enum('project_gutenberg', 'project_gutenberg_australia', 'project_gutenberg_canada', 'internet_archive', 'hathi_trust', 'wikisource', 'google_books', 'faded_page', 'other') DEFAULT 'other',
|
`Type` enum('project_gutenberg', 'project_gutenberg_australia', 'project_gutenberg_canada', 'internet_archive', 'hathi_trust', 'wikisource', 'google_books', 'faded_page', 'other') NOT NULL DEFAULT 'other',
|
||||||
`Url` varchar(255) NOT NULL,
|
`Url` varchar(255) NOT NULL,
|
||||||
`SortOrder` tinyint(3) unsigned NOT NULL,
|
`SortOrder` tinyint(3) unsigned NOT NULL,
|
||||||
KEY `index1` (`EbookId`)
|
KEY `index1` (`EbookId`)
|
||||||
|
|
|
@ -9,49 +9,39 @@ use Safe\DateTimeImmutable;
|
||||||
*/
|
*/
|
||||||
class Artist{
|
class Artist{
|
||||||
use Traits\Accessor;
|
use Traits\Accessor;
|
||||||
|
use Traits\PropertyFromHttp;
|
||||||
|
|
||||||
public ?int $ArtistId = null;
|
public int $ArtistId;
|
||||||
public ?string $Name = null;
|
public string $Name = '';
|
||||||
public ?DateTimeImmutable $Created = null;
|
public DateTimeImmutable $Created;
|
||||||
public ?DateTimeImmutable $Updated = null;
|
public DateTimeImmutable $Updated;
|
||||||
|
public ?int $DeathYear = null;
|
||||||
|
|
||||||
protected ?int $_DeathYear = null;
|
protected string $_UrlName;
|
||||||
protected ?string $_UrlName = null;
|
protected string $_Url;
|
||||||
protected ?string $_Url = null;
|
/** @var array<string> $_AlternateNames */
|
||||||
/** @var ?array<string> $_AlternateNames */
|
protected array $_AlternateNames;
|
||||||
protected $_AlternateNames = null;
|
|
||||||
|
|
||||||
// *******
|
|
||||||
// SETTERS
|
|
||||||
// *******
|
|
||||||
|
|
||||||
protected function SetDeathYear(?int $deathYear): void{
|
|
||||||
if($this->Name == 'Anonymous'){
|
|
||||||
$this->_DeathYear = null;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
$this->_DeathYear = $deathYear;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// *******
|
// *******
|
||||||
// GETTERS
|
// GETTERS
|
||||||
// *******
|
// *******
|
||||||
|
|
||||||
protected function GetUrlName(): string{
|
protected function GetUrlName(): string{
|
||||||
if($this->Name === null || $this->Name == ''){
|
if(!isset($this->_UrlName)){
|
||||||
return '';
|
if(!isset($this->Name) || $this->Name == ''){
|
||||||
}
|
$this->_UrlName = '';
|
||||||
|
}
|
||||||
if($this->_UrlName === null){
|
else{
|
||||||
$this->_UrlName = Formatter::MakeUrlSafe($this->Name);
|
$this->_UrlName = Formatter::MakeUrlSafe($this->Name);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->_UrlName;
|
return $this->_UrlName;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function GetUrl(): string{
|
protected function GetUrl(): string{
|
||||||
if($this->_Url === null){
|
if(!isset($this->_Url)){
|
||||||
$this->_Url = '/artworks/' . $this->UrlName;
|
$this->_Url = '/artworks/' . $this->UrlName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -62,7 +52,7 @@ class Artist{
|
||||||
* @return array<string>
|
* @return array<string>
|
||||||
*/
|
*/
|
||||||
protected function GetAlternateNames(): array{
|
protected function GetAlternateNames(): array{
|
||||||
if($this->_AlternateNames === null){
|
if(!isset($this->_AlternateNames)){
|
||||||
$this->_AlternateNames = [];
|
$this->_AlternateNames = [];
|
||||||
|
|
||||||
$result = Db::Query('
|
$result = Db::Query('
|
||||||
|
@ -79,6 +69,7 @@ class Artist{
|
||||||
return $this->_AlternateNames;
|
return $this->_AlternateNames;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// *******
|
// *******
|
||||||
// METHODS
|
// METHODS
|
||||||
// *******
|
// *******
|
||||||
|
@ -91,16 +82,15 @@ class Artist{
|
||||||
|
|
||||||
$error = new Exceptions\InvalidArtistException();
|
$error = new Exceptions\InvalidArtistException();
|
||||||
|
|
||||||
if($this->Name === null || $this->Name == ''){
|
if(!isset($this->Name) || $this->Name == ''){
|
||||||
$error->Add(new Exceptions\ArtistNameRequiredException());
|
$error->Add(new Exceptions\ArtistNameRequiredException());
|
||||||
}
|
}
|
||||||
|
elseif(strlen($this->Name) > ARTWORK_MAX_STRING_LENGTH){
|
||||||
if($this->Name !== null && strlen($this->Name) > ARTWORK_MAX_STRING_LENGTH){
|
|
||||||
$error->Add(new Exceptions\StringTooLongException('Artist Name'));
|
$error->Add(new Exceptions\StringTooLongException('Artist Name'));
|
||||||
}
|
}
|
||||||
|
|
||||||
if($this->Name == 'Anonymous' && $this->DeathYear !== null){
|
if($this->Name == 'Anonymous' && $this->DeathYear !== null){
|
||||||
$this->_DeathYear = null;
|
$this->DeathYear = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if($this->DeathYear !== null && ($this->DeathYear <= 0 || $this->DeathYear > $thisYear + 50)){
|
if($this->DeathYear !== null && ($this->DeathYear <= 0 || $this->DeathYear > $thisYear + 50)){
|
||||||
|
@ -111,11 +101,16 @@ class Artist{
|
||||||
throw $error;
|
throw $error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function FillFromHttpPost(): void{
|
||||||
|
$this->PropertyFromHttp('Name');
|
||||||
|
$this->PropertyFromHttp('DeathYear');
|
||||||
|
}
|
||||||
|
|
||||||
// ***********
|
// ***********
|
||||||
// ORM METHODS
|
// ORM METHODS
|
||||||
// ***********
|
// ***********
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @throws Exceptions\ArtistNotFoundException
|
* @throws Exceptions\ArtistNotFoundException
|
||||||
*/
|
*/
|
||||||
|
@ -124,13 +119,11 @@ class Artist{
|
||||||
throw new Exceptions\ArtistNotFoundException();
|
throw new Exceptions\ArtistNotFoundException();
|
||||||
}
|
}
|
||||||
|
|
||||||
$result = Db::Query('
|
return Db::Query('
|
||||||
SELECT *
|
SELECT *
|
||||||
from Artists
|
from Artists
|
||||||
where ArtistId = ?
|
where ArtistId = ?
|
||||||
', [$artistId], Artist::class);
|
', [$artistId], Artist::class)[0] ?? throw new Exceptions\ArtistNotFoundException();
|
||||||
|
|
||||||
return $result[0] ?? throw new Exceptions\ArtistNotFoundException();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -141,15 +134,13 @@ class Artist{
|
||||||
throw new Exceptions\ArtistNotFoundException();
|
throw new Exceptions\ArtistNotFoundException();
|
||||||
}
|
}
|
||||||
|
|
||||||
$result = Db::Query('
|
return Db::Query('
|
||||||
SELECT a.*
|
SELECT a.*
|
||||||
from Artists a
|
from Artists a
|
||||||
left outer join ArtistAlternateNames aan using (ArtistId)
|
left outer join ArtistAlternateNames aan using (ArtistId)
|
||||||
where aan.UrlName = ?
|
where aan.UrlName = ?
|
||||||
limit 1
|
limit 1
|
||||||
', [$urlName], Artist::class);
|
', [$urlName], Artist::class)[0] ?? throw new Exceptions\ArtistNotFoundException();
|
||||||
|
|
||||||
return $result[0] ?? throw new Exceptions\ArtistNotFoundException();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
353
lib/Artwork.php
353
lib/Artwork.php
|
@ -7,6 +7,7 @@ use function Safe\getimagesize;
|
||||||
use function Safe\parse_url;
|
use function Safe\parse_url;
|
||||||
use function Safe\preg_match;
|
use function Safe\preg_match;
|
||||||
use function Safe\preg_replace;
|
use function Safe\preg_replace;
|
||||||
|
use function Safe\unlink;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @property string $UrlName
|
* @property string $UrlName
|
||||||
|
@ -24,19 +25,20 @@ use function Safe\preg_replace;
|
||||||
* @property string $Dimensions
|
* @property string $Dimensions
|
||||||
* @property Ebook $Ebook
|
* @property Ebook $Ebook
|
||||||
* @property Museum $Museum
|
* @property Museum $Museum
|
||||||
* @property User $Submitter
|
* @property ?User $Submitter
|
||||||
* @property User $Reviewer
|
* @property ?User $Reviewer
|
||||||
*/
|
*/
|
||||||
class Artwork{
|
class Artwork{
|
||||||
use Traits\Accessor;
|
use Traits\Accessor;
|
||||||
|
use Traits\PropertyFromHttp;
|
||||||
|
|
||||||
public ?string $Name = null;
|
public int $ArtworkId;
|
||||||
public ?int $ArtworkId = null;
|
public string $Name = '';
|
||||||
public ?int $ArtistId = null;
|
public int $ArtistId;
|
||||||
public ?int $CompletedYear = null;
|
public ?int $CompletedYear = null;
|
||||||
public bool $CompletedYearIsCirca = false;
|
public bool $CompletedYearIsCirca = false;
|
||||||
public ?DateTimeImmutable $Created = null;
|
public DateTimeImmutable $Created;
|
||||||
public ?DateTimeImmutable $Updated = null;
|
public DateTimeImmutable $Updated;
|
||||||
public ?string $EbookUrl = null;
|
public ?string $EbookUrl = null;
|
||||||
public ?int $SubmitterUserId = null;
|
public ?int $SubmitterUserId = null;
|
||||||
public ?int $ReviewerUserId = null;
|
public ?int $ReviewerUserId = null;
|
||||||
|
@ -45,26 +47,27 @@ class Artwork{
|
||||||
public ?string $PublicationYearPageUrl = null;
|
public ?string $PublicationYearPageUrl = null;
|
||||||
public ?string $CopyrightPageUrl = null;
|
public ?string $CopyrightPageUrl = null;
|
||||||
public ?string $ArtworkPageUrl = null;
|
public ?string $ArtworkPageUrl = null;
|
||||||
public ?bool $IsPublishedInUs = null;
|
public bool $IsPublishedInUs = false;
|
||||||
public ?string $Exception = null;
|
public ?string $Exception = null;
|
||||||
public ?string $Notes = null;
|
public ?string $Notes = null;
|
||||||
public ?Enums\ImageMimeType $MimeType = null;
|
public Enums\ImageMimeType $MimeType;
|
||||||
public ?Enums\ArtworkStatusType $Status = null;
|
public Enums\ArtworkStatusType $Status = Enums\ArtworkStatusType::Unverified;
|
||||||
|
|
||||||
|
protected string $_UrlName;
|
||||||
|
protected string $_Url;
|
||||||
|
protected string $_EditUrl;
|
||||||
|
/** @var array<ArtworkTag> $_Tags */
|
||||||
|
protected array $_Tags;
|
||||||
|
protected Artist $_Artist;
|
||||||
|
protected string $_ImageUrl;
|
||||||
|
protected string $_ThumbUrl;
|
||||||
|
protected string $_Thumb2xUrl;
|
||||||
|
protected string $_Dimensions ;
|
||||||
|
protected ?Ebook $_Ebook;
|
||||||
|
protected ?Museum $_Museum;
|
||||||
|
protected ?User $_Submitter;
|
||||||
|
protected ?User $_Reviewer;
|
||||||
|
|
||||||
protected ?string $_UrlName = null;
|
|
||||||
protected ?string $_Url = null;
|
|
||||||
protected ?string $_EditUrl = null;
|
|
||||||
/** @var ?array<ArtworkTag> $_Tags */
|
|
||||||
protected $_Tags = null;
|
|
||||||
protected ?Artist $_Artist = null;
|
|
||||||
protected ?string $_ImageUrl = null;
|
|
||||||
protected ?string $_ThumbUrl = null;
|
|
||||||
protected ?string $_Thumb2xUrl = null;
|
|
||||||
protected ?string $_Dimensions = null;
|
|
||||||
protected ?Ebook $_Ebook = null;
|
|
||||||
protected ?Museum $_Museum = null;
|
|
||||||
protected ?User $_Submitter = null;
|
|
||||||
protected ?User $_Reviewer = null;
|
|
||||||
|
|
||||||
// *******
|
// *******
|
||||||
// SETTERS
|
// SETTERS
|
||||||
|
@ -74,45 +77,54 @@ class Artwork{
|
||||||
* @param string|null|array<ArtworkTag> $tags
|
* @param string|null|array<ArtworkTag> $tags
|
||||||
*/
|
*/
|
||||||
protected function SetTags(null|string|array $tags): void{
|
protected function SetTags(null|string|array $tags): void{
|
||||||
if($tags === null || is_array($tags)){
|
if(is_array($tags)){
|
||||||
$this->_Tags = $tags;
|
$this->_Tags = $tags;
|
||||||
}
|
}
|
||||||
elseif(is_string($tags)){
|
else{
|
||||||
$tags = array_map('trim', explode(',', $tags));
|
$tags = trim($tags ?? '');
|
||||||
$tags = array_values(array_filter($tags));
|
|
||||||
$tags = array_unique($tags);
|
|
||||||
|
|
||||||
$this->_Tags = array_map(function ($str){
|
if($tags === ''){
|
||||||
$tag = new ArtworkTag();
|
$this->_Tags = [];
|
||||||
$tag->Name = $str;
|
}
|
||||||
return $tag;
|
else{
|
||||||
}, $tags);
|
$tags = array_map('trim', explode(',', $tags));
|
||||||
|
$tags = array_values(array_filter($tags));
|
||||||
|
$tags = array_unique($tags);
|
||||||
|
|
||||||
|
$this->_Tags = array_map(function ($str): ArtworkTag{
|
||||||
|
$tag = new ArtworkTag();
|
||||||
|
$tag->Name = $str;
|
||||||
|
return $tag;
|
||||||
|
}, $tags);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// *******
|
// *******
|
||||||
// GETTERS
|
// GETTERS
|
||||||
// *******
|
// *******
|
||||||
|
|
||||||
protected function GetUrlName(): string{
|
protected function GetUrlName(): string{
|
||||||
if($this->Name === null || $this->Name == ''){
|
if(!isset($this->_UrlName)){
|
||||||
return '';
|
if(!isset($this->Name) || $this->Name == ''){
|
||||||
}
|
$this->_UrlName = '';
|
||||||
|
}
|
||||||
if($this->_UrlName === null){
|
else{
|
||||||
$this->_UrlName = Formatter::MakeUrlSafe($this->Name);
|
$this->_UrlName = Formatter::MakeUrlSafe($this->Name);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->_UrlName;
|
return $this->_UrlName;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function GetSubmitter(): ?User{
|
protected function GetSubmitter(): ?User{
|
||||||
if($this->_Submitter === null){
|
if(!isset($this->_Submitter)){
|
||||||
try{
|
try{
|
||||||
$this->_Submitter = User::Get($this->SubmitterUserId);
|
$this->_Submitter = User::Get($this->SubmitterUserId);
|
||||||
}
|
}
|
||||||
catch(Exceptions\UserNotFoundException){
|
catch(Exceptions\UserNotFoundException){
|
||||||
// Return null
|
$this->Submitter = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -120,12 +132,12 @@ class Artwork{
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function GetReviewer(): ?User{
|
protected function GetReviewer(): ?User{
|
||||||
if($this->_Reviewer === null){
|
if(!isset($this->_Reviewer)){
|
||||||
try{
|
try{
|
||||||
$this->_Reviewer = User::Get($this->ReviewerUserId);
|
$this->_Reviewer = User::Get($this->ReviewerUserId);
|
||||||
}
|
}
|
||||||
catch(Exceptions\UserNotFoundException){
|
catch(Exceptions\UserNotFoundException){
|
||||||
// Return null
|
$this->_Reviewer = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -133,7 +145,7 @@ class Artwork{
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function GetUrl(): string{
|
protected function GetUrl(): string{
|
||||||
if($this->_Url === null){
|
if(!isset($this->_Url)){
|
||||||
$this->_Url = '/artworks/' . $this->Artist->UrlName . '/' . $this->UrlName;
|
$this->_Url = '/artworks/' . $this->Artist->UrlName . '/' . $this->UrlName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -141,7 +153,7 @@ class Artwork{
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function GetEditUrl(): string{
|
protected function GetEditUrl(): string{
|
||||||
if($this->_EditUrl === null){
|
if(!isset($this->_EditUrl)){
|
||||||
$this->_EditUrl = $this->Url . '/edit';
|
$this->_EditUrl = $this->Url . '/edit';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -152,7 +164,7 @@ class Artwork{
|
||||||
* @return array<ArtworkTag>
|
* @return array<ArtworkTag>
|
||||||
*/
|
*/
|
||||||
protected function GetTags(): array{
|
protected function GetTags(): array{
|
||||||
if($this->_Tags === null){
|
if(!isset($this->_Tags)){
|
||||||
$this->_Tags = Db::Query('
|
$this->_Tags = Db::Query('
|
||||||
SELECT t.*
|
SELECT t.*
|
||||||
from Tags t
|
from Tags t
|
||||||
|
@ -168,34 +180,28 @@ class Artwork{
|
||||||
* @throws Exceptions\InvalidUrlException
|
* @throws Exceptions\InvalidUrlException
|
||||||
*/
|
*/
|
||||||
public function GetMuseum(): ?Museum{
|
public function GetMuseum(): ?Museum{
|
||||||
if($this->_Museum === null){
|
if(!isset($this->_Museum)){
|
||||||
try{
|
try{
|
||||||
$this->_Museum = Museum::GetByUrl($this->MuseumUrl);
|
$this->_Museum = Museum::GetByUrl($this->MuseumUrl);
|
||||||
}
|
}
|
||||||
catch(Exceptions\MuseumNotFoundException){
|
catch(Exceptions\MuseumNotFoundException){
|
||||||
// Pass
|
// Pass.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->_Museum;
|
return $this->_Museum;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function ImplodeTags(): string{
|
|
||||||
$tags = $this->Tags ?? [];
|
|
||||||
$tags = array_column($tags, 'Name');
|
|
||||||
return trim(implode(', ', $tags));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @throws Exceptions\InvalidArtworkException
|
* @throws Exceptions\InvalidArtworkException
|
||||||
*/
|
*/
|
||||||
protected function GetImageUrl(): string{
|
protected function GetImageUrl(): string{
|
||||||
if($this->_ImageUrl === null){
|
if(!isset($this->_ImageUrl)){
|
||||||
if($this->ArtworkId === null || $this->MimeType === null){
|
if(!isset($this->ArtworkId) || !isset($this->MimeType)){
|
||||||
throw new Exceptions\InvalidArtworkException();
|
throw new Exceptions\InvalidArtworkException();
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->_ImageUrl = COVER_ART_UPLOAD_PATH . $this->ArtworkId . $this->MimeType->GetFileExtension() . '?ts=' . $this->Updated?->getTimestamp();
|
$this->_ImageUrl = COVER_ART_UPLOAD_PATH . $this->ArtworkId . $this->MimeType->GetFileExtension() . '?ts=' . $this->Updated->getTimestamp();
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->_ImageUrl;
|
return $this->_ImageUrl;
|
||||||
|
@ -205,12 +211,12 @@ class Artwork{
|
||||||
* @throws Exceptions\ArtworkNotFoundException
|
* @throws Exceptions\ArtworkNotFoundException
|
||||||
*/
|
*/
|
||||||
protected function GetThumbUrl(): string{
|
protected function GetThumbUrl(): string{
|
||||||
if($this->_ThumbUrl === null){
|
if(!isset($this->_ThumbUrl)){
|
||||||
if($this->ArtworkId === null){
|
if(!isset($this->ArtworkId)){
|
||||||
throw new Exceptions\ArtworkNotFoundException();
|
throw new Exceptions\ArtworkNotFoundException();
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->_ThumbUrl = COVER_ART_UPLOAD_PATH . $this->ArtworkId . '-thumb.jpg' . '?ts=' . $this->Updated?->getTimestamp();
|
$this->_ThumbUrl = COVER_ART_UPLOAD_PATH . $this->ArtworkId . '-thumb.jpg' . '?ts=' . $this->Updated->getTimestamp();
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->_ThumbUrl;
|
return $this->_ThumbUrl;
|
||||||
|
@ -220,12 +226,12 @@ class Artwork{
|
||||||
* @throws Exceptions\ArtworkNotFoundException
|
* @throws Exceptions\ArtworkNotFoundException
|
||||||
*/
|
*/
|
||||||
protected function GetThumb2xUrl(): string{
|
protected function GetThumb2xUrl(): string{
|
||||||
if($this->_Thumb2xUrl === null){
|
if(!isset($this->_Thumb2xUrl)){
|
||||||
if($this->ArtworkId === null){
|
if(!isset($this->ArtworkId)){
|
||||||
throw new Exceptions\ArtworkNotFoundException();
|
throw new Exceptions\ArtworkNotFoundException();
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->_Thumb2xUrl = COVER_ART_UPLOAD_PATH . $this->ArtworkId . '-thumb@2x.jpg' . '?ts=' . $this->Updated?->getTimestamp();
|
$this->_Thumb2xUrl = COVER_ART_UPLOAD_PATH . $this->ArtworkId . '-thumb@2x.jpg' . '?ts=' . $this->Updated->getTimestamp();
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->_Thumb2xUrl;
|
return $this->_Thumb2xUrl;
|
||||||
|
@ -244,22 +250,24 @@ class Artwork{
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function GetDimensions(): string{
|
protected function GetDimensions(): string{
|
||||||
$this->_Dimensions = '';
|
if(!isset($this->Dimensions)){
|
||||||
try{
|
$this->_Dimensions = '';
|
||||||
list($imageWidth, $imageHeight) = getimagesize($this->ImageFsPath);
|
try{
|
||||||
if($imageWidth && $imageHeight){
|
list($imageWidth, $imageHeight) = getimagesize($this->ImageFsPath);
|
||||||
$this->_Dimensions = number_format($imageWidth) . ' × ' . number_format($imageHeight);
|
if($imageWidth && $imageHeight){
|
||||||
|
$this->_Dimensions = number_format($imageWidth) . ' × ' . number_format($imageHeight);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch(Exception){
|
||||||
|
// Image doesn't exist, return a blank string.
|
||||||
}
|
}
|
||||||
}
|
|
||||||
catch(Exception){
|
|
||||||
// Image doesn't exist, return blank string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->_Dimensions;
|
return $this->_Dimensions;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function GetEbook(): ?Ebook{
|
protected function GetEbook(): ?Ebook{
|
||||||
if($this->_Ebook === null){
|
if(!isset($this->_Ebook)){
|
||||||
if($this->EbookUrl === null){
|
if($this->EbookUrl === null){
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -277,9 +285,11 @@ class Artwork{
|
||||||
return $this->_Ebook;
|
return $this->_Ebook;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// *******
|
// *******
|
||||||
// METHODS
|
// METHODS
|
||||||
// *******
|
// *******
|
||||||
|
|
||||||
public function CanBeEditedBy(?User $user): bool{
|
public function CanBeEditedBy(?User $user): bool{
|
||||||
if($user === null){
|
if($user === null){
|
||||||
return false;
|
return false;
|
||||||
|
@ -336,15 +346,16 @@ class Artwork{
|
||||||
$thisYear = intval(NOW->format('Y'));
|
$thisYear = intval(NOW->format('Y'));
|
||||||
$error = new Exceptions\InvalidArtworkException();
|
$error = new Exceptions\InvalidArtworkException();
|
||||||
|
|
||||||
if($this->Artist === null){
|
if(!isset($this->Artist)){
|
||||||
$error->Add(new Exceptions\InvalidArtistException());
|
$error->Add(new Exceptions\InvalidArtistException());
|
||||||
}
|
}
|
||||||
|
else{
|
||||||
try{
|
try{
|
||||||
$this->Artist->Validate();
|
$this->Artist->Validate();
|
||||||
}
|
}
|
||||||
catch(Exceptions\ValidationException $ex){
|
catch(Exceptions\ValidationException $ex){
|
||||||
$error->Add($ex);
|
$error->Add($ex);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if($this->Exception !== null && trim($this->Exception) == ''){
|
if($this->Exception !== null && trim($this->Exception) == ''){
|
||||||
|
@ -355,12 +366,17 @@ class Artwork{
|
||||||
$this->Notes = null;
|
$this->Notes = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if($this->Name === null || $this->Name == ''){
|
if(isset($this->Name)){
|
||||||
$error->Add(new Exceptions\ArtworkNameRequiredException());
|
if($this->Name == ''){
|
||||||
}
|
$error->Add(new Exceptions\ArtworkNameRequiredException());
|
||||||
|
}
|
||||||
|
|
||||||
if($this->Name !== null && strlen($this->Name) > ARTWORK_MAX_STRING_LENGTH){
|
if(strlen($this->Name) > ARTWORK_MAX_STRING_LENGTH){
|
||||||
$error->Add(new Exceptions\StringTooLongException('Artwork Name'));
|
$error->Add(new Exceptions\StringTooLongException('Artwork Name'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
$error->Add(new Exceptions\ArtworkNameRequiredException());
|
||||||
}
|
}
|
||||||
|
|
||||||
if($this->CompletedYear !== null && ($this->CompletedYear <= 0 || $this->CompletedYear > $thisYear)){
|
if($this->CompletedYear !== null && ($this->CompletedYear <= 0 || $this->CompletedYear > $thisYear)){
|
||||||
|
@ -375,27 +391,32 @@ class Artwork{
|
||||||
$error->Add(new Exceptions\InvalidPublicationYearException());
|
$error->Add(new Exceptions\InvalidPublicationYearException());
|
||||||
}
|
}
|
||||||
|
|
||||||
if($this->Status === null){
|
if(!isset($this->Status)){
|
||||||
$error->Add(new Exceptions\InvalidArtworkException('Invalid status.'));
|
$error->Add(new Exceptions\InvalidArtworkException('Invalid status.'));
|
||||||
}
|
}
|
||||||
|
|
||||||
if(count($this->Tags) == 0){
|
if(isset($this->Tags)){
|
||||||
|
if(count($this->Tags) == 0){
|
||||||
|
$error->Add(new Exceptions\TagsRequiredException());
|
||||||
|
}
|
||||||
|
|
||||||
|
if(count($this->Tags) > ARTWORK_MAX_TAGS){
|
||||||
|
$error->Add(new Exceptions\TooManyTagsException());
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach($this->Tags as $tag){
|
||||||
|
try{
|
||||||
|
$tag->Validate();
|
||||||
|
}
|
||||||
|
catch(Exceptions\ValidationException $ex){
|
||||||
|
$error->Add($ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else{
|
||||||
$error->Add(new Exceptions\TagsRequiredException());
|
$error->Add(new Exceptions\TagsRequiredException());
|
||||||
}
|
}
|
||||||
|
|
||||||
if(count($this->Tags) > ARTWORK_MAX_TAGS){
|
|
||||||
$error->Add(new Exceptions\TooManyTagsException());
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach($this->Tags as $tag){
|
|
||||||
try{
|
|
||||||
$tag->Validate();
|
|
||||||
}
|
|
||||||
catch(Exceptions\ValidationException $ex){
|
|
||||||
$error->Add($ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if($this->MuseumUrl !== null){
|
if($this->MuseumUrl !== null){
|
||||||
if(strlen($this->MuseumUrl) > ARTWORK_MAX_STRING_LENGTH){
|
if(strlen($this->MuseumUrl) > ARTWORK_MAX_STRING_LENGTH){
|
||||||
$error->Add(new Exceptions\StringTooLongException('Link to an approved museum page'));
|
$error->Add(new Exceptions\StringTooLongException('Link to an approved museum page'));
|
||||||
|
@ -482,36 +503,38 @@ class Artwork{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for existing Artwork objects with the same URL but different Artwork IDs.
|
// Check for existing `Artwork` objects with the same URL but different `ArtworkID`s.
|
||||||
try{
|
if(isset($this->ArtworkId)){
|
||||||
$existingArtwork = Artwork::GetByUrl($this->Artist->UrlName, $this->UrlName);
|
try{
|
||||||
if($existingArtwork->ArtworkId != $this->ArtworkId){
|
$existingArtwork = Artwork::GetByUrl($this->Artist->UrlName, $this->UrlName);
|
||||||
// Duplicate found, alert the user
|
if($existingArtwork->ArtworkId != $this->ArtworkId){
|
||||||
$error->Add(new Exceptions\ArtworkAlreadyExistsException());
|
// Duplicate found, alert the user.
|
||||||
|
$error->Add(new Exceptions\ArtworkAlreadyExistsException());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
catch(Exceptions\ArtworkNotFoundException){
|
||||||
catch(Exceptions\ArtworkNotFoundException){
|
// No duplicates found, continue.
|
||||||
// No duplicates found, continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if($isImageRequired && $imagePath === null){
|
|
||||||
$error->Add(new Exceptions\InvalidImageUploadException('An image is required.'));
|
|
||||||
}
|
|
||||||
|
|
||||||
if($imagePath !== null && $this->MimeType !== null){
|
|
||||||
if(!is_writable(WEB_ROOT . COVER_ART_UPLOAD_PATH)){
|
|
||||||
$error->Add(new Exceptions\InvalidImageUploadException('Upload path not writable.'));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check for minimum dimensions
|
|
||||||
list($imageWidth, $imageHeight) = getimagesize($imagePath);
|
|
||||||
if(!$imageWidth || !$imageHeight || $imageWidth < ARTWORK_IMAGE_MINIMUM_WIDTH || $imageHeight < ARTWORK_IMAGE_MINIMUM_HEIGHT){
|
|
||||||
$error->Add(new Exceptions\ArtworkImageDimensionsTooSmallException());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if($imagePath !== null && $this->MimeType === null && !$error->Has('Exceptions\InvalidImageUploadException')){
|
if($isImageRequired){
|
||||||
// Only notify of wrong mimetype if there are no other problem with the uploaded image
|
if($imagePath === null){
|
||||||
|
$error->Add(new Exceptions\InvalidImageUploadException('An image is required.'));
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
if(!is_writable(WEB_ROOT . COVER_ART_UPLOAD_PATH)){
|
||||||
|
$error->Add(new Exceptions\InvalidImageUploadException('Upload path not writable.'));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for minimum dimensions.
|
||||||
|
list($imageWidth, $imageHeight) = getimagesize($imagePath);
|
||||||
|
if(!$imageWidth || !$imageHeight || $imageWidth < ARTWORK_IMAGE_MINIMUM_WIDTH || $imageHeight < ARTWORK_IMAGE_MINIMUM_HEIGHT){
|
||||||
|
$error->Add(new Exceptions\ArtworkImageDimensionsTooSmallException());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!isset($this->MimeType)){
|
||||||
$error->Add(new Exceptions\InvalidMimeTypeException());
|
$error->Add(new Exceptions\InvalidMimeTypeException());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -520,6 +543,12 @@ class Artwork{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function ImplodeTags(): string{
|
||||||
|
$tags = $this->Tags ?? [];
|
||||||
|
$tags = array_column($tags, 'Name');
|
||||||
|
return trim(implode(', ', $tags));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @throws Exceptions\InvalidUrlException
|
* @throws Exceptions\InvalidUrlException
|
||||||
* @throws Exceptions\InvalidPageScanUrlException
|
* @throws Exceptions\InvalidPageScanUrlException
|
||||||
|
@ -661,11 +690,12 @@ class Artwork{
|
||||||
* @throws Exceptions\InvalidImageUploadException
|
* @throws Exceptions\InvalidImageUploadException
|
||||||
*/
|
*/
|
||||||
public function Create(?string $imagePath = null): void{
|
public function Create(?string $imagePath = null): void{
|
||||||
$this->MimeType = Enums\ImageMimeType::FromFile($imagePath);
|
$this->MimeType = Enums\ImageMimeType::FromFile($imagePath) ?? throw new Exceptions\InvalidImageUploadException();
|
||||||
|
|
||||||
$this->Validate($imagePath, true);
|
$this->Validate($imagePath, true);
|
||||||
|
|
||||||
$this->Created = NOW;
|
$this->Created = NOW;
|
||||||
|
$this->Updated = NOW;
|
||||||
|
|
||||||
$tags = [];
|
$tags = [];
|
||||||
foreach($this->Tags as $artworkTag){
|
foreach($this->Tags as $artworkTag){
|
||||||
|
@ -677,7 +707,7 @@ class Artwork{
|
||||||
|
|
||||||
Db::Query('
|
Db::Query('
|
||||||
INSERT into
|
INSERT into
|
||||||
Artworks (ArtistId, Name, UrlName, CompletedYear, CompletedYearIsCirca, Created, Status, SubmitterUserId, ReviewerUserId, MuseumUrl,
|
Artworks (ArtistId, Name, UrlName, CompletedYear, CompletedYearIsCirca, Created, Updated, Status, SubmitterUserId, ReviewerUserId, MuseumUrl,
|
||||||
PublicationYear, PublicationYearPageUrl, CopyrightPageUrl, ArtworkPageUrl, IsPublishedInUs,
|
PublicationYear, PublicationYearPageUrl, CopyrightPageUrl, ArtworkPageUrl, IsPublishedInUs,
|
||||||
EbookUrl, MimeType, Exception, Notes)
|
EbookUrl, MimeType, Exception, Notes)
|
||||||
values (?,
|
values (?,
|
||||||
|
@ -698,9 +728,10 @@ class Artwork{
|
||||||
?,
|
?,
|
||||||
?,
|
?,
|
||||||
?,
|
?,
|
||||||
|
?,
|
||||||
?)
|
?)
|
||||||
', [$this->Artist->ArtistId, $this->Name, $this->UrlName, $this->CompletedYear, $this->CompletedYearIsCirca,
|
', [$this->Artist->ArtistId, $this->Name, $this->UrlName, $this->CompletedYear, $this->CompletedYearIsCirca,
|
||||||
$this->Created, $this->Status, $this->SubmitterUserId, $this->ReviewerUserId, $this->MuseumUrl, $this->PublicationYear, $this->PublicationYearPageUrl,
|
$this->Created, $this->Updated, $this->Status, $this->SubmitterUserId, $this->ReviewerUserId, $this->MuseumUrl, $this->PublicationYear, $this->PublicationYearPageUrl,
|
||||||
$this->CopyrightPageUrl, $this->ArtworkPageUrl, $this->IsPublishedInUs, $this->EbookUrl, $this->MimeType, $this->Exception, $this->Notes]
|
$this->CopyrightPageUrl, $this->ArtworkPageUrl, $this->IsPublishedInUs, $this->EbookUrl, $this->MimeType, $this->Exception, $this->Notes]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -726,16 +757,16 @@ class Artwork{
|
||||||
* @throws Exceptions\InvalidImageUploadException
|
* @throws Exceptions\InvalidImageUploadException
|
||||||
*/
|
*/
|
||||||
public function Save(?string $imagePath = null): void{
|
public function Save(?string $imagePath = null): void{
|
||||||
$this->_UrlName = null;
|
unset($this->_UrlName);
|
||||||
|
|
||||||
if($imagePath !== null){
|
if($imagePath !== null){
|
||||||
$this->MimeType = Enums\ImageMimeType::FromFile($imagePath);
|
$this->MimeType = Enums\ImageMimeType::FromFile($imagePath) ?? throw new Exceptions\InvalidImageUploadException();
|
||||||
|
|
||||||
// Manually set the updated timestamp, because if we only update the image and nothing else, the row's updated timestamp won't change automatically.
|
// Manually set the updated timestamp, because if we only update the image and nothing else, the row's updated timestamp won't change automatically.
|
||||||
$this->Updated = NOW;
|
$this->Updated = NOW;
|
||||||
$this->_ImageUrl = null;
|
unset($this->_ImageUrl);
|
||||||
$this->_ThumbUrl = null;
|
unset($this->_ThumbUrl);
|
||||||
$this->_Thumb2xUrl = null;
|
unset($this->_Thumb2xUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->Validate($imagePath, false);
|
$this->Validate($imagePath, false);
|
||||||
|
@ -828,8 +859,30 @@ class Artwork{
|
||||||
from Artworks
|
from Artworks
|
||||||
where ArtworkId = ?
|
where ArtworkId = ?
|
||||||
', [$this->ArtworkId]);
|
', [$this->ArtworkId]);
|
||||||
|
|
||||||
|
try{
|
||||||
|
unlink($this->ImageFsPath);
|
||||||
|
}
|
||||||
|
catch(\Safe\Exceptions\FilesystemException){
|
||||||
|
// Pass.
|
||||||
|
}
|
||||||
|
|
||||||
|
try{
|
||||||
|
unlink($this->ThumbFsPath);
|
||||||
|
}
|
||||||
|
catch(\Safe\Exceptions\FilesystemException){
|
||||||
|
// Pass.
|
||||||
|
}
|
||||||
|
|
||||||
|
try{
|
||||||
|
unlink($this->Thumb2xFsPath);
|
||||||
|
}
|
||||||
|
catch(\Safe\Exceptions\FilesystemException){
|
||||||
|
// Pass.
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// ***********
|
// ***********
|
||||||
// ORM METHODS
|
// ORM METHODS
|
||||||
// ***********
|
// ***********
|
||||||
|
@ -871,14 +924,10 @@ class Artwork{
|
||||||
|
|
||||||
public static function FromHttpPost(): Artwork{
|
public static function FromHttpPost(): Artwork{
|
||||||
$artwork = new Artwork();
|
$artwork = new Artwork();
|
||||||
$artwork->Artist = new Artist();
|
|
||||||
|
|
||||||
$artwork->Artist->Name = HttpInput::Str(POST, 'artist-name');
|
$artwork->Name = HttpInput::Str(POST, 'artwork-name') ?? '';
|
||||||
$artwork->Artist->DeathYear = HttpInput::Int(POST, 'artist-year-of-death');
|
|
||||||
|
|
||||||
$artwork->Name = HttpInput::Str(POST, 'artwork-name');
|
|
||||||
$artwork->CompletedYear = HttpInput::Int(POST, 'artwork-year');
|
$artwork->CompletedYear = HttpInput::Int(POST, 'artwork-year');
|
||||||
$artwork->CompletedYearIsCirca = HttpInput::Bool(POST, 'artwork-year-is-circa') ?? false;
|
$artwork->CompletedYearIsCirca = HttpInput::Bool(POST, 'artwork-completed-year-is-circa') ?? false;
|
||||||
$artwork->Tags = HttpInput::Str(POST, 'artwork-tags') ?? [];
|
$artwork->Tags = HttpInput::Str(POST, 'artwork-tags') ?? [];
|
||||||
$artwork->Status = Enums\ArtworkStatusType::tryFrom(HttpInput::Str(POST, 'artwork-status') ?? '') ?? Enums\ArtworkStatusType::Unverified;
|
$artwork->Status = Enums\ArtworkStatusType::tryFrom(HttpInput::Str(POST, 'artwork-status') ?? '') ?? Enums\ArtworkStatusType::Unverified;
|
||||||
$artwork->EbookUrl = HttpInput::Str(POST, 'artwork-ebook-url');
|
$artwork->EbookUrl = HttpInput::Str(POST, 'artwork-ebook-url');
|
||||||
|
@ -893,4 +942,28 @@ class Artwork{
|
||||||
|
|
||||||
return $artwork;
|
return $artwork;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function FillFromHttpPost(): void{
|
||||||
|
if(!isset($this->Artist)){
|
||||||
|
$this->Artist = new Artist();
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->Artist->FillFromHttpPost();
|
||||||
|
|
||||||
|
$this->PropertyFromHttp('Name');
|
||||||
|
$this->PropertyFromHttp('CompletedYear');
|
||||||
|
$this->PropertyFromHttp('CompletedYearIsCirca');
|
||||||
|
$this->PropertyFromHttp('Status');
|
||||||
|
$this->PropertyFromHttp('EbookUrl');
|
||||||
|
$this->PropertyFromHttp('IsPublishedInUs');
|
||||||
|
$this->PropertyFromHttp('PublicationYear');
|
||||||
|
$this->PropertyFromHttp('PublicationYearPageUrl');
|
||||||
|
$this->PropertyFromHttp('CopyrightPageUrl');
|
||||||
|
$this->PropertyFromHttp('ArtworkPageUrl');
|
||||||
|
$this->PropertyFromHttp('MuseumUrl');
|
||||||
|
$this->PropertyFromHttp('Exception');
|
||||||
|
$this->PropertyFromHttp('Notes');
|
||||||
|
|
||||||
|
$this->Tags = HttpInput::Str(POST, 'artwork-tags') ?? ''; // Converted from a string to an array via a setter.
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,18 +7,20 @@ class ArtworkTag extends Tag{
|
||||||
$this->Type = Enums\TagType::Artwork;
|
$this->Type = Enums\TagType::Artwork;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// *******
|
// *******
|
||||||
// GETTERS
|
// GETTERS
|
||||||
// *******
|
// *******
|
||||||
|
|
||||||
protected function GetUrl(): string{
|
protected function GetUrl(): string{
|
||||||
if($this->_Url === null){
|
if(!isset($this->_Url)){
|
||||||
$this->_Url = '/artworks?query=' . Formatter::MakeUrlSafe($this->Name);
|
$this->_Url = '/artworks?query=' . Formatter::MakeUrlSafe($this->Name);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->_Url;
|
return $this->_Url;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// *******
|
// *******
|
||||||
// METHODS
|
// METHODS
|
||||||
// *******
|
// *******
|
||||||
|
@ -29,20 +31,27 @@ class ArtworkTag extends Tag{
|
||||||
public function Validate(): void{
|
public function Validate(): void{
|
||||||
$error = new Exceptions\InvalidArtworkTagException($this->Name);
|
$error = new Exceptions\InvalidArtworkTagException($this->Name);
|
||||||
|
|
||||||
$this->Name = mb_strtolower(trim($this->Name));
|
if(isset($this->Name)){
|
||||||
// Collapse spaces into one
|
$this->Name = mb_strtolower(trim($this->Name));
|
||||||
$this->Name = preg_replace('/[\s]+/ius', ' ', $this->Name);
|
// Collapse spaces into one
|
||||||
|
$this->Name = preg_replace('/[\s]+/ius', ' ', $this->Name);
|
||||||
|
|
||||||
if(strlen($this->Name) == 0){
|
if(strlen($this->Name) == 0){
|
||||||
$error->Add(new Exceptions\InvalidArtworkTagNameException());
|
$error->Add(new Exceptions\InvalidArtworkTagNameException());
|
||||||
|
}
|
||||||
|
|
||||||
|
if(strlen($this->Name) > ARTWORK_MAX_STRING_LENGTH){
|
||||||
|
$error->Add(new Exceptions\StringTooLongException('Artwork tag: '. $this->Name));
|
||||||
|
}
|
||||||
|
|
||||||
|
if(preg_match('/[^\sa-z0-9]/ius', $this->Name)){
|
||||||
|
$error->Add(new Exceptions\InvalidArtworkTagNameException());
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->UrlName = Formatter::MakeUrlSafe($this->Name);
|
||||||
}
|
}
|
||||||
|
else{
|
||||||
if(strlen($this->Name) > ARTWORK_MAX_STRING_LENGTH){
|
$error->Add(new Exceptions\ArtworkTagNameRequiredException());
|
||||||
$error->Add(new Exceptions\StringTooLongException('Artwork tag: '. $this->Name));
|
|
||||||
}
|
|
||||||
|
|
||||||
if(preg_match('/[^\sa-z0-9]/ius', $this->Name)){
|
|
||||||
$error->Add(new Exceptions\InvalidArtworkTagNameException());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if($this->Type != Enums\TagType::Artwork){
|
if($this->Type != Enums\TagType::Artwork){
|
||||||
|
@ -61,10 +70,11 @@ class ArtworkTag extends Tag{
|
||||||
$this->Validate();
|
$this->Validate();
|
||||||
|
|
||||||
Db::Query('
|
Db::Query('
|
||||||
INSERT into Tags (Name, Type)
|
INSERT into Tags (Name, UrlName, Type)
|
||||||
values (?,
|
values (?,
|
||||||
|
?,
|
||||||
?)
|
?)
|
||||||
', [$this->Name, $this->Type]);
|
', [$this->Name, $this->UrlName, $this->Type]);
|
||||||
$this->TagId = Db::GetLastInsertedId();
|
$this->TagId = Db::GetLastInsertedId();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -19,18 +19,19 @@ class AtomFeed extends Feed{
|
||||||
$this->Stylesheet = SITE_URL . '/feeds/atom/style';
|
$this->Stylesheet = SITE_URL . '/feeds/atom/style';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// *******
|
// *******
|
||||||
// METHODS
|
// METHODS
|
||||||
// *******
|
// *******
|
||||||
|
|
||||||
protected function GetXmlString(): string{
|
protected function GetXmlString(): string{
|
||||||
if($this->XmlString === null){
|
if(!isset($this->_XmlString)){
|
||||||
$feed = Template::AtomFeed(['id' => $this->Id, 'url' => $this->Url, 'title' => $this->Title, 'subtitle' => $this->Subtitle, 'updated' => $this->Updated, 'entries' => $this->Entries]);
|
$feed = Template::AtomFeed(['id' => $this->Id, 'url' => $this->Url, 'title' => $this->Title, 'subtitle' => $this->Subtitle, 'updated' => $this->Updated, 'entries' => $this->Entries]);
|
||||||
|
|
||||||
$this->XmlString = $this->CleanXmlString($feed);
|
$this->_XmlString = $this->CleanXmlString($feed);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->XmlString;
|
return $this->_XmlString;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function SaveIfChanged(): bool{
|
public function SaveIfChanged(): bool{
|
||||||
|
@ -58,7 +59,7 @@ class AtomFeed extends Feed{
|
||||||
$obj->Id = SITE_URL . $entry->Url;
|
$obj->Id = SITE_URL . $entry->Url;
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
$obj->Updated = $entry->Updated !== null ? $entry->Updated->format(Enums\DateTimeFormat::Iso->value) : '';
|
$obj->Updated = $entry->Updated->format(Enums\DateTimeFormat::Iso->value);
|
||||||
$obj->Id = $entry->Id;
|
$obj->Id = $entry->Id;
|
||||||
}
|
}
|
||||||
$currentEntries[] = $obj;
|
$currentEntries[] = $obj;
|
||||||
|
|
|
@ -13,6 +13,11 @@ class Collection{
|
||||||
public ?Enums\CollectionType $Type = null;
|
public ?Enums\CollectionType $Type = null;
|
||||||
protected ?string $_Url = null;
|
protected ?string $_Url = null;
|
||||||
|
|
||||||
|
|
||||||
|
// *******
|
||||||
|
// GETTERS
|
||||||
|
// *******
|
||||||
|
|
||||||
protected function GetUrl(): string{
|
protected function GetUrl(): string{
|
||||||
if($this->_Url === null){
|
if($this->_Url === null){
|
||||||
$this->_Url = '/collections/' . $this->UrlName;
|
$this->_Url = '/collections/' . $this->UrlName;
|
||||||
|
@ -21,6 +26,11 @@ class Collection{
|
||||||
return $this->_Url;
|
return $this->_Url;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// ***********
|
||||||
|
// ORM METHODS
|
||||||
|
// ***********
|
||||||
|
|
||||||
public static function FromName(string $name): Collection{
|
public static function FromName(string $name): Collection{
|
||||||
$instance = new Collection();
|
$instance = new Collection();
|
||||||
$instance->Name = $name;
|
$instance->Name = $name;
|
||||||
|
@ -45,6 +55,11 @@ class Collection{
|
||||||
return $result[0] ?? throw new Exceptions\CollectionNotFoundException();;
|
return $result[0] ?? throw new Exceptions\CollectionNotFoundException();;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// *******
|
||||||
|
// METHODS
|
||||||
|
// *******
|
||||||
|
|
||||||
public function GetSortedName(): string{
|
public function GetSortedName(): string{
|
||||||
return preg_replace('/^(the|and|a|)\s/ius', '', $this->Name);
|
return preg_replace('/^(the|and|a|)\s/ius', '', $this->Name);
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,9 +5,10 @@
|
||||||
class CollectionMembership{
|
class CollectionMembership{
|
||||||
use Traits\Accessor;
|
use Traits\Accessor;
|
||||||
|
|
||||||
public ?int $EbookId = null;
|
public int $EbookId;
|
||||||
public ?int $CollectionId = null;
|
public int $CollectionId;
|
||||||
public ?int $SequenceNumber = null;
|
public ?int $SequenceNumber = null;
|
||||||
public ?int $SortOrder = null;
|
public int $SortOrder;
|
||||||
protected ?Collection $_Collection = null;
|
|
||||||
|
protected Collection $_Collection;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,8 @@
|
||||||
<?
|
<?
|
||||||
|
|
||||||
use Safe\DateTimeImmutable;
|
|
||||||
|
|
||||||
use function Safe\preg_match;
|
use function Safe\preg_match;
|
||||||
|
|
||||||
class Contributor{
|
class Contributor{
|
||||||
public ?int $EbookId = null;
|
public int $EbookId;
|
||||||
public string $Name;
|
public string $Name;
|
||||||
public string $UrlName;
|
public string $UrlName;
|
||||||
public ?string $SortName = null;
|
public ?string $SortName = null;
|
||||||
|
@ -13,19 +10,11 @@ class Contributor{
|
||||||
public ?string $MarcRole = null;
|
public ?string $MarcRole = null;
|
||||||
public ?string $FullName = null;
|
public ?string $FullName = null;
|
||||||
public ?string $NacoafUrl = null;
|
public ?string $NacoafUrl = null;
|
||||||
public ?int $SortOrder = null;
|
public int $SortOrder;
|
||||||
|
|
||||||
public static function FromProperties(string $name, string $sortName = null, string $fullName = null, string $wikipediaUrl = null, string $marcRole = null, string $nacoafUrl = null): Contributor{
|
// *******
|
||||||
$instance = new Contributor();
|
// METHODS
|
||||||
$instance->Name = str_replace('\'', '’', $name);
|
// *******
|
||||||
$instance->UrlName = Formatter::MakeUrlSafe($name);
|
|
||||||
$instance->SortName = $sortName;
|
|
||||||
$instance->FullName = $fullName;
|
|
||||||
$instance->WikipediaUrl = $wikipediaUrl;
|
|
||||||
$instance->MarcRole = $marcRole;
|
|
||||||
$instance->NacoafUrl = $nacoafUrl;
|
|
||||||
return $instance;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @throws Exceptions\ValidationException
|
* @throws Exceptions\ValidationException
|
||||||
|
|
|
@ -18,15 +18,10 @@ class DonationDrive{
|
||||||
public function __construct(public string $Name, public DateTimeImmutable $Start, public DateTimeImmutable $End, public int $BaseTargetDonationCount, public int $StretchTargetDonationCount){
|
public function __construct(public string $Name, public DateTimeImmutable $Start, public DateTimeImmutable $End, public int $BaseTargetDonationCount, public int $StretchTargetDonationCount){
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function GetByIsRunning(): ?DonationDrive{
|
|
||||||
foreach(DONATION_DRIVE_DATES as $donationDrive){
|
|
||||||
if(NOW > $donationDrive->Start && NOW < $donationDrive->End){
|
|
||||||
return $donationDrive;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
// *******
|
||||||
}
|
// GETTERS
|
||||||
|
// *******
|
||||||
|
|
||||||
protected function GetDonationCount(): int{
|
protected function GetDonationCount(): int{
|
||||||
if(!isset($this->_DonationCount)){
|
if(!isset($this->_DonationCount)){
|
||||||
|
@ -92,4 +87,19 @@ class DonationDrive{
|
||||||
|
|
||||||
return $this->_IsStretchEnabled;
|
return $this->_IsStretchEnabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// ***********
|
||||||
|
// ORM METHODS
|
||||||
|
// ***********
|
||||||
|
|
||||||
|
public static function GetByIsRunning(): ?DonationDrive{
|
||||||
|
foreach(DONATION_DRIVE_DATES as $donationDrive){
|
||||||
|
if(NOW > $donationDrive->Start && NOW < $donationDrive->End){
|
||||||
|
return $donationDrive;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
258
lib/Ebook.php
258
lib/Ebook.php
|
@ -46,7 +46,7 @@ use function Safe\shell_exec;
|
||||||
class Ebook{
|
class Ebook{
|
||||||
use Traits\Accessor;
|
use Traits\Accessor;
|
||||||
|
|
||||||
public ?int $EbookId = null;
|
public int $EbookId;
|
||||||
public string $Identifier;
|
public string $Identifier;
|
||||||
public string $WwwFilesystemPath;
|
public string $WwwFilesystemPath;
|
||||||
public string $RepoFilesystemPath;
|
public string $RepoFilesystemPath;
|
||||||
|
@ -56,12 +56,12 @@ class Ebook{
|
||||||
public ?string $KepubUrl = null;
|
public ?string $KepubUrl = null;
|
||||||
public ?string $Azw3Url = null;
|
public ?string $Azw3Url = null;
|
||||||
public ?string $DistCoverUrl = null;
|
public ?string $DistCoverUrl = null;
|
||||||
public ?string $Title = null;
|
public string $Title;
|
||||||
public ?string $FullTitle = null;
|
public ?string $FullTitle = null;
|
||||||
public ?string $AlternateTitle = null;
|
public ?string $AlternateTitle = null;
|
||||||
public ?string $Description = null;
|
public string $Description;
|
||||||
public ?string $LongDescription = null;
|
public string $LongDescription;
|
||||||
public ?string $Language = null;
|
public string $Language;
|
||||||
public int $WordCount;
|
public int $WordCount;
|
||||||
public float $ReadingEase;
|
public float $ReadingEase;
|
||||||
public ?string $GitHubUrl = null;
|
public ?string $GitHubUrl = null;
|
||||||
|
@ -71,47 +71,48 @@ class Ebook{
|
||||||
public DateTimeImmutable $Created;
|
public DateTimeImmutable $Created;
|
||||||
public DateTimeImmutable $Updated;
|
public DateTimeImmutable $Updated;
|
||||||
public ?int $TextSinglePageByteCount = null;
|
public ?int $TextSinglePageByteCount = null;
|
||||||
|
|
||||||
/** @var array<GitCommit> $_GitCommits */
|
/** @var array<GitCommit> $_GitCommits */
|
||||||
protected $_GitCommits = null;
|
protected array $_GitCommits;
|
||||||
/** @var array<EbookTag> $_Tags */
|
/** @var array<EbookTag> $_Tags */
|
||||||
protected $_Tags = null;
|
protected array $_Tags;
|
||||||
/** @var array<LocSubject> $_LocSubjects */
|
/** @var array<LocSubject> $_LocSubjects */
|
||||||
protected $_LocSubjects = null;
|
protected array $_LocSubjects;
|
||||||
/** @var array<CollectionMembership> $_CollectionMemberships */
|
/** @var array<CollectionMembership> $_CollectionMemberships */
|
||||||
protected $_CollectionMemberships = null;
|
protected array $_CollectionMemberships;
|
||||||
/** @var array<EbookSource> $_Sources */
|
/** @var array<EbookSource> $_Sources */
|
||||||
protected $_Sources = null;
|
protected array $_Sources;
|
||||||
/** @var array<Contributor> $_Authors */
|
/** @var array<Contributor> $_Authors */
|
||||||
protected $_Authors = null;
|
protected array $_Authors;
|
||||||
/** @var array<Contributor> $_Illustrators */
|
/** @var array<Contributor> $_Illustrators */
|
||||||
protected $_Illustrators = null;
|
protected array $_Illustrators;
|
||||||
/** @var array<Contributor> $_Translators */
|
/** @var array<Contributor> $_Translators */
|
||||||
protected $_Translators = null;
|
protected array$_Translators;
|
||||||
/** @var array<Contributor> $_Contributors */
|
/** @var array<Contributor> $_Contributors */
|
||||||
protected $_Contributors = null;
|
protected array $_Contributors;
|
||||||
/** @var ?array<string> $_TocEntries */
|
/** @var ?array<string> $_TocEntries */
|
||||||
protected $_TocEntries = null; // A list of non-Roman ToC entries ONLY IF the work has the 'se:is-a-collection' metadata element, null otherwise.
|
protected ?array $_TocEntries = null; // A list of non-Roman ToC entries *only if* the work has the `se:is-a-collection` metadata element; `null` otherwise.
|
||||||
protected ?string $_Url = null;
|
protected string $_Url;
|
||||||
protected ?bool $_HasDownloads = null;
|
protected bool $_HasDownloads;
|
||||||
protected ?string $_UrlSafeIdentifier = null;
|
protected string $_UrlSafeIdentifier;
|
||||||
protected ?string $_HeroImageUrl = null;
|
protected string $_HeroImageUrl;
|
||||||
protected ?string $_HeroImageAvifUrl = null;
|
protected string $_HeroImageAvifUrl;
|
||||||
protected ?string $_HeroImage2xUrl = null;
|
protected string $_HeroImage2xUrl;
|
||||||
protected ?string $_HeroImage2xAvifUrl = null;
|
protected string $_HeroImage2xAvifUrl;
|
||||||
protected ?string $_CoverImageUrl = null;
|
protected string $_CoverImageUrl;
|
||||||
protected ?string $_CoverImageAvifUrl = null;
|
protected string $_CoverImageAvifUrl;
|
||||||
protected ?string $_CoverImage2xUrl = null;
|
protected string $_CoverImage2xUrl;
|
||||||
protected ?string $_CoverImage2xAvifUrl = null;
|
protected string $_CoverImage2xAvifUrl;
|
||||||
protected ?string $_ReadingEaseDescription = null;
|
protected string $_ReadingEaseDescription;
|
||||||
protected ?string $_ReadingTime = null;
|
protected string $_ReadingTime;
|
||||||
protected ?string $_AuthorsHtml = null;
|
protected string $_AuthorsHtml;
|
||||||
protected ?string $_AuthorsUrl = null; // This is a single URL even if there are multiple authors; for example, /ebooks/karl-marx_friedrich-engels/
|
protected string $_AuthorsUrl; // This is a single URL even if there are multiple authors; for example, `/ebooks/karl-marx_friedrich-engels/`.
|
||||||
protected ?string $_ContributorsHtml = null;
|
protected string $_ContributorsHtml;
|
||||||
protected ?string $_TitleWithCreditsHtml = null;
|
protected string $_TitleWithCreditsHtml;
|
||||||
protected ?string $_TextUrl = null;
|
protected string $_TextUrl;
|
||||||
protected ?string $_TextSinglePageUrl = null;
|
protected string $_TextSinglePageUrl;
|
||||||
protected ?string $_TextSinglePageSizeFormatted = null;
|
protected string $_TextSinglePageSizeFormatted;
|
||||||
protected ?string $_IndexableText = null;
|
protected string $_IndexableText;
|
||||||
|
|
||||||
// *******
|
// *******
|
||||||
// GETTERS
|
// GETTERS
|
||||||
|
@ -121,7 +122,7 @@ class Ebook{
|
||||||
* @return array<GitCommit>
|
* @return array<GitCommit>
|
||||||
*/
|
*/
|
||||||
protected function GetGitCommits(): array{
|
protected function GetGitCommits(): array{
|
||||||
if($this->_GitCommits === null){
|
if(!isset($this->_GitCommits)){
|
||||||
$this->_GitCommits = Db::Query('
|
$this->_GitCommits = Db::Query('
|
||||||
SELECT *
|
SELECT *
|
||||||
from GitCommits
|
from GitCommits
|
||||||
|
@ -137,7 +138,7 @@ class Ebook{
|
||||||
* @return array<EbookTag>
|
* @return array<EbookTag>
|
||||||
*/
|
*/
|
||||||
protected function GetTags(): array{
|
protected function GetTags(): array{
|
||||||
if($this->_Tags === null){
|
if(!isset($this->_Tags)){
|
||||||
$this->_Tags = Db::Query('
|
$this->_Tags = Db::Query('
|
||||||
SELECT t.*
|
SELECT t.*
|
||||||
from Tags t
|
from Tags t
|
||||||
|
@ -154,7 +155,7 @@ class Ebook{
|
||||||
* @return array<LocSubject>
|
* @return array<LocSubject>
|
||||||
*/
|
*/
|
||||||
protected function GetLocSubjects(): array{
|
protected function GetLocSubjects(): array{
|
||||||
if($this->_LocSubjects === null){
|
if(!isset($this->_LocSubjects)){
|
||||||
$this->_LocSubjects = Db::Query('
|
$this->_LocSubjects = Db::Query('
|
||||||
SELECT l.*
|
SELECT l.*
|
||||||
from LocSubjects l
|
from LocSubjects l
|
||||||
|
@ -171,7 +172,7 @@ class Ebook{
|
||||||
* @return array<CollectionMembership>
|
* @return array<CollectionMembership>
|
||||||
*/
|
*/
|
||||||
protected function GetCollectionMemberships(): array{
|
protected function GetCollectionMemberships(): array{
|
||||||
if($this->_CollectionMemberships === null){
|
if(!isset($this->_CollectionMemberships)){
|
||||||
$this->_CollectionMemberships = Db::Query('
|
$this->_CollectionMemberships = Db::Query('
|
||||||
SELECT *
|
SELECT *
|
||||||
from CollectionEbooks
|
from CollectionEbooks
|
||||||
|
@ -187,7 +188,7 @@ class Ebook{
|
||||||
* @return array<EbookSource>
|
* @return array<EbookSource>
|
||||||
*/
|
*/
|
||||||
protected function GetSources(): array{
|
protected function GetSources(): array{
|
||||||
if($this->_Sources === null){
|
if(!isset($this->_Sources)){
|
||||||
$this->_Sources = Db::Query('
|
$this->_Sources = Db::Query('
|
||||||
SELECT *
|
SELECT *
|
||||||
from EbookSources
|
from EbookSources
|
||||||
|
@ -203,7 +204,7 @@ class Ebook{
|
||||||
* @return array<Contributor>
|
* @return array<Contributor>
|
||||||
*/
|
*/
|
||||||
protected function GetAuthors(): array{
|
protected function GetAuthors(): array{
|
||||||
if($this->_Authors === null){
|
if(!isset($this->_Authors)){
|
||||||
$this->_Authors = Db::Query('
|
$this->_Authors = Db::Query('
|
||||||
SELECT *
|
SELECT *
|
||||||
from Contributors
|
from Contributors
|
||||||
|
@ -220,7 +221,7 @@ class Ebook{
|
||||||
* @return array<Contributor>
|
* @return array<Contributor>
|
||||||
*/
|
*/
|
||||||
protected function GetIllustrators(): array{
|
protected function GetIllustrators(): array{
|
||||||
if($this->_Illustrators === null){
|
if(!isset($this->_Illustrators)){
|
||||||
$this->_Illustrators = Db::Query('
|
$this->_Illustrators = Db::Query('
|
||||||
SELECT *
|
SELECT *
|
||||||
from Contributors
|
from Contributors
|
||||||
|
@ -237,7 +238,7 @@ class Ebook{
|
||||||
* @return array<Contributor>
|
* @return array<Contributor>
|
||||||
*/
|
*/
|
||||||
protected function GetTranslators(): array{
|
protected function GetTranslators(): array{
|
||||||
if($this->_Translators === null){
|
if(!isset($this->_Translators)){
|
||||||
$this->_Translators = Db::Query('
|
$this->_Translators = Db::Query('
|
||||||
SELECT *
|
SELECT *
|
||||||
from Contributors
|
from Contributors
|
||||||
|
@ -254,7 +255,7 @@ class Ebook{
|
||||||
* @return array<Contributor>
|
* @return array<Contributor>
|
||||||
*/
|
*/
|
||||||
protected function GetContributors(): array{
|
protected function GetContributors(): array{
|
||||||
if($this->_Contributors === null){
|
if(!isset($this->_Contributors)){
|
||||||
$this->_Contributors = Db::Query('
|
$this->_Contributors = Db::Query('
|
||||||
SELECT *
|
SELECT *
|
||||||
from Contributors
|
from Contributors
|
||||||
|
@ -268,10 +269,10 @@ class Ebook{
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return array<string>
|
* @return ?array<string>
|
||||||
*/
|
*/
|
||||||
protected function GetTocEntries(): array{
|
protected function GetTocEntries(): ?array{
|
||||||
if($this->_TocEntries === null){
|
if(!isset($this->_TocEntries)){
|
||||||
$this->_TocEntries = [];
|
$this->_TocEntries = [];
|
||||||
|
|
||||||
$result = Db::Query('
|
$result = Db::Query('
|
||||||
|
@ -279,18 +280,22 @@ class Ebook{
|
||||||
from TocEntries
|
from TocEntries
|
||||||
where EbookId = ?
|
where EbookId = ?
|
||||||
order by SortOrder asc
|
order by SortOrder asc
|
||||||
', [$this->EbookId], stdClass::class);
|
', [$this->EbookId]);
|
||||||
|
|
||||||
foreach($result as $row){
|
foreach($result as $row){
|
||||||
$this->_TocEntries[] = $row->TocEntry;
|
$this->_TocEntries[] = $row->TocEntry;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(sizeof($this->_TocEntries) == 0){
|
||||||
|
$this->_TocEntries = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->_TocEntries;
|
return $this->_TocEntries;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function GetUrl(): string{
|
protected function GetUrl(): string{
|
||||||
if($this->_Url === null){
|
if(!isset($this->_Url)){
|
||||||
$this->_Url = str_replace(WEB_ROOT, '', $this->WwwFilesystemPath);
|
$this->_Url = str_replace(WEB_ROOT, '', $this->WwwFilesystemPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -298,7 +303,7 @@ class Ebook{
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function GetHasDownloads(): bool{
|
protected function GetHasDownloads(): bool{
|
||||||
if($this->_HasDownloads === null){
|
if(!isset($this->_HasDownloads)){
|
||||||
$this->_HasDownloads = $this->EpubUrl || $this->AdvancedEpubUrl || $this->KepubUrl || $this->Azw3Url;
|
$this->_HasDownloads = $this->EpubUrl || $this->AdvancedEpubUrl || $this->KepubUrl || $this->Azw3Url;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -306,7 +311,7 @@ class Ebook{
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function GetUrlSafeIdentifier(): string{
|
protected function GetUrlSafeIdentifier(): string{
|
||||||
if($this->_UrlSafeIdentifier === null){
|
if(!isset($this->_UrlSafeIdentifier)){
|
||||||
$this->_UrlSafeIdentifier = str_replace(['url:https://standardebooks.org/ebooks/', '/'], ['', '_'], $this->Identifier);
|
$this->_UrlSafeIdentifier = str_replace(['url:https://standardebooks.org/ebooks/', '/'], ['', '_'], $this->Identifier);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -318,7 +323,7 @@ class Ebook{
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function GetHeroImageUrl(): string{
|
protected function GetHeroImageUrl(): string{
|
||||||
if($this->_HeroImageUrl === null){
|
if(!isset($this->_HeroImageUrl)){
|
||||||
$this->_HeroImageUrl = '/images/covers/' . $this->UrlSafeIdentifier . '-' . $this->GetLatestCommitHash() . '-hero.jpg';
|
$this->_HeroImageUrl = '/images/covers/' . $this->UrlSafeIdentifier . '-' . $this->GetLatestCommitHash() . '-hero.jpg';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -326,7 +331,7 @@ class Ebook{
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function GetHeroImageAvifUrl(): ?string{
|
protected function GetHeroImageAvifUrl(): ?string{
|
||||||
if($this->_HeroImageAvifUrl === null){
|
if(!isset($this->_HeroImageAvifUrl)){
|
||||||
if(file_exists(WEB_ROOT . '/images/covers/' . $this->UrlSafeIdentifier . '-hero.avif')){
|
if(file_exists(WEB_ROOT . '/images/covers/' . $this->UrlSafeIdentifier . '-hero.avif')){
|
||||||
$this->_HeroImageAvifUrl = '/images/covers/' . $this->UrlSafeIdentifier . '-' . $this->GetLatestCommitHash() . '-hero.avif';
|
$this->_HeroImageAvifUrl = '/images/covers/' . $this->UrlSafeIdentifier . '-' . $this->GetLatestCommitHash() . '-hero.avif';
|
||||||
}
|
}
|
||||||
|
@ -336,7 +341,7 @@ class Ebook{
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function GetHeroImage2xUrl(): string{
|
protected function GetHeroImage2xUrl(): string{
|
||||||
if($this->_HeroImage2xUrl === null){
|
if(!isset($this->_HeroImage2xUrl)){
|
||||||
$this->_HeroImage2xUrl = '/images/covers/' . $this->UrlSafeIdentifier . '-' . $this->GetLatestCommitHash() . '-hero@2x.jpg';
|
$this->_HeroImage2xUrl = '/images/covers/' . $this->UrlSafeIdentifier . '-' . $this->GetLatestCommitHash() . '-hero@2x.jpg';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -344,7 +349,7 @@ class Ebook{
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function GetHeroImage2xAvifUrl(): ?string{
|
protected function GetHeroImage2xAvifUrl(): ?string{
|
||||||
if($this->_HeroImage2xAvifUrl === null){
|
if(!isset($this->_HeroImage2xAvifUrl)){
|
||||||
if(file_exists(WEB_ROOT . '/images/covers/' . $this->UrlSafeIdentifier . '-hero@2x.avif')){
|
if(file_exists(WEB_ROOT . '/images/covers/' . $this->UrlSafeIdentifier . '-hero@2x.avif')){
|
||||||
$this->_HeroImage2xAvifUrl = '/images/covers/' . $this->UrlSafeIdentifier . '-' . $this->GetLatestCommitHash() . '-hero@2x.avif';
|
$this->_HeroImage2xAvifUrl = '/images/covers/' . $this->UrlSafeIdentifier . '-' . $this->GetLatestCommitHash() . '-hero@2x.avif';
|
||||||
}
|
}
|
||||||
|
@ -354,7 +359,7 @@ class Ebook{
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function GetCoverImageUrl(): string{
|
protected function GetCoverImageUrl(): string{
|
||||||
if($this->_CoverImageUrl === null){
|
if(!isset($this->_CoverImageUrl)){
|
||||||
$this->_CoverImageUrl = '/images/covers/' . $this->UrlSafeIdentifier . '-' . $this->GetLatestCommitHash() . '-cover.jpg';
|
$this->_CoverImageUrl = '/images/covers/' . $this->UrlSafeIdentifier . '-' . $this->GetLatestCommitHash() . '-cover.jpg';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -362,7 +367,7 @@ class Ebook{
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function GetCoverImageAvifUrl(): ?string{
|
protected function GetCoverImageAvifUrl(): ?string{
|
||||||
if($this->_CoverImageAvifUrl === null){
|
if(!isset($this->_CoverImageAvifUrl)){
|
||||||
if(file_exists(WEB_ROOT . '/images/covers/' . $this->UrlSafeIdentifier . '-cover.avif')){
|
if(file_exists(WEB_ROOT . '/images/covers/' . $this->UrlSafeIdentifier . '-cover.avif')){
|
||||||
$this->_CoverImageAvifUrl = '/images/covers/' . $this->UrlSafeIdentifier . '-' . $this->GetLatestCommitHash() . '-cover.avif';
|
$this->_CoverImageAvifUrl = '/images/covers/' . $this->UrlSafeIdentifier . '-' . $this->GetLatestCommitHash() . '-cover.avif';
|
||||||
}
|
}
|
||||||
|
@ -372,7 +377,7 @@ class Ebook{
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function GetCoverImage2xUrl(): string{
|
protected function GetCoverImage2xUrl(): string{
|
||||||
if($this->_CoverImage2xUrl === null){
|
if(!isset($this->_CoverImage2xUrl)){
|
||||||
$this->_CoverImage2xUrl = '/images/covers/' . $this->UrlSafeIdentifier . '-' . $this->GetLatestCommitHash() . '-cover@2x.jpg';
|
$this->_CoverImage2xUrl = '/images/covers/' . $this->UrlSafeIdentifier . '-' . $this->GetLatestCommitHash() . '-cover@2x.jpg';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -380,7 +385,7 @@ class Ebook{
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function GetCoverImage2xAvifUrl(): ?string{
|
protected function GetCoverImage2xAvifUrl(): ?string{
|
||||||
if($this->_CoverImage2xAvifUrl === null){
|
if(!isset($this->_CoverImage2xAvifUrl)){
|
||||||
if(file_exists(WEB_ROOT . '/images/covers/' . $this->UrlSafeIdentifier . '-cover@2x.avif')){
|
if(file_exists(WEB_ROOT . '/images/covers/' . $this->UrlSafeIdentifier . '-cover@2x.avif')){
|
||||||
$this->_CoverImage2xAvifUrl = '/images/covers/' . $this->UrlSafeIdentifier . '-' . $this->GetLatestCommitHash() . '-cover@2x.avif';
|
$this->_CoverImage2xAvifUrl = '/images/covers/' . $this->UrlSafeIdentifier . '-' . $this->GetLatestCommitHash() . '-cover@2x.avif';
|
||||||
}
|
}
|
||||||
|
@ -390,7 +395,7 @@ class Ebook{
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function GetReadingEaseDescription(): string{
|
protected function GetReadingEaseDescription(): string{
|
||||||
if($this->_ReadingEaseDescription === null){
|
if(!isset($this->_ReadingEaseDescription)){
|
||||||
if($this->ReadingEase > 89){
|
if($this->ReadingEase > 89){
|
||||||
$this->_ReadingEaseDescription = 'very easy';
|
$this->_ReadingEaseDescription = 'very easy';
|
||||||
}
|
}
|
||||||
|
@ -418,7 +423,7 @@ class Ebook{
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function GetReadingTime(): string{
|
protected function GetReadingTime(): string{
|
||||||
if($this->_ReadingTime === null){
|
if(!isset($this->_ReadingTime)){
|
||||||
$readingTime = ceil($this->WordCount / AVERAGE_READING_WORDS_PER_MINUTE);
|
$readingTime = ceil($this->WordCount / AVERAGE_READING_WORDS_PER_MINUTE);
|
||||||
$this->_ReadingTime = (string)$readingTime;
|
$this->_ReadingTime = (string)$readingTime;
|
||||||
|
|
||||||
|
@ -449,7 +454,7 @@ class Ebook{
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function GetAuthorsHtml(): string{
|
protected function GetAuthorsHtml(): string{
|
||||||
if($this->_AuthorsHtml === null){
|
if(!isset($this->_AuthorsHtml)){
|
||||||
$this->_AuthorsHtml = Ebook::GenerateContributorList($this->Authors, true);
|
$this->_AuthorsHtml = Ebook::GenerateContributorList($this->Authors, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -457,7 +462,7 @@ class Ebook{
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function GetAuthorsUrl(): string{
|
protected function GetAuthorsUrl(): string{
|
||||||
if($this->_AuthorsUrl === null){
|
if(!isset($this->_AuthorsUrl)){
|
||||||
$this->_AuthorsUrl = preg_replace('|url:https://standardebooks.org/ebooks/([^/]+)/.*|ius', '/ebooks/\1', $this->Identifier);
|
$this->_AuthorsUrl = preg_replace('|url:https://standardebooks.org/ebooks/([^/]+)/.*|ius', '/ebooks/\1', $this->Identifier);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -465,7 +470,7 @@ class Ebook{
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function GetContributorsHtml(): string{
|
protected function GetContributorsHtml(): string{
|
||||||
if($this->_ContributorsHtml === null){
|
if(!isset($this->_ContributorsHtml)){
|
||||||
$this->_ContributorsHtml = '';
|
$this->_ContributorsHtml = '';
|
||||||
if(sizeof($this->Contributors) > 0){
|
if(sizeof($this->Contributors) > 0){
|
||||||
$this->_ContributorsHtml .= ' with ' . Ebook::GenerateContributorList($this->Contributors, false) . ';';
|
$this->_ContributorsHtml .= ' with ' . Ebook::GenerateContributorList($this->Contributors, false) . ';';
|
||||||
|
@ -492,7 +497,7 @@ class Ebook{
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function GetTitleWithCreditsHtml(): string{
|
protected function GetTitleWithCreditsHtml(): string{
|
||||||
if($this->_TitleWithCreditsHtml === null){
|
if(!isset($this->_TitleWithCreditsHtml)){
|
||||||
$titleContributors = '';
|
$titleContributors = '';
|
||||||
if(sizeof($this->Contributors) > 0){
|
if(sizeof($this->Contributors) > 0){
|
||||||
$titleContributors .= '. With ' . Ebook::GenerateContributorList($this->Contributors, false);
|
$titleContributors .= '. With ' . Ebook::GenerateContributorList($this->Contributors, false);
|
||||||
|
@ -513,7 +518,7 @@ class Ebook{
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function GetTextUrl(): string{
|
protected function GetTextUrl(): string{
|
||||||
if($this->_TextUrl === null){
|
if(!isset($this->_TextUrl)){
|
||||||
$this->_TextUrl = $this->Url . '/text';
|
$this->_TextUrl = $this->Url . '/text';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -521,7 +526,7 @@ class Ebook{
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function GetTextSinglePageUrl(): string{
|
protected function GetTextSinglePageUrl(): string{
|
||||||
if($this->_TextSinglePageUrl === null){
|
if(!isset($this->_TextSinglePageUrl)){
|
||||||
$this->_TextSinglePageUrl = $this->Url . '/text/single-page';
|
$this->_TextSinglePageUrl = $this->Url . '/text/single-page';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -529,7 +534,7 @@ class Ebook{
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function GetTextSinglePageSizeFormatted(): string{
|
protected function GetTextSinglePageSizeFormatted(): string{
|
||||||
if($this->_TextSinglePageSizeFormatted === null){
|
if(!isset($this->_TextSinglePageSizeFormatted)){
|
||||||
$bytes = $this->TextSinglePageByteCount;
|
$bytes = $this->TextSinglePageByteCount;
|
||||||
$sizes = array('B', 'KB', 'MB', 'GB', 'TB', 'PB');
|
$sizes = array('B', 'KB', 'MB', 'GB', 'TB', 'PB');
|
||||||
|
|
||||||
|
@ -551,7 +556,7 @@ class Ebook{
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function GetIndexableText(): string{
|
protected function GetIndexableText(): string{
|
||||||
if($this->_IndexableText === null){
|
if(!isset($this->_IndexableText)){
|
||||||
$this->_IndexableText = $this->FullTitle ?? $this->Title;
|
$this->_IndexableText = $this->FullTitle ?? $this->Title;
|
||||||
|
|
||||||
$this->_IndexableText .= ' ' . $this->AlternateTitle;
|
$this->_IndexableText .= ' ' . $this->AlternateTitle;
|
||||||
|
@ -585,6 +590,11 @@ class Ebook{
|
||||||
return $this->_IndexableText;
|
return $this->_IndexableText;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// ***********
|
||||||
|
// ORM METHODS
|
||||||
|
// ***********
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Construct an Ebook from a filesystem path.
|
* Construct an Ebook from a filesystem path.
|
||||||
*
|
*
|
||||||
|
@ -714,8 +724,8 @@ class Ebook{
|
||||||
|
|
||||||
$xml->registerXPathNamespace('dc', 'http://purl.org/dc/elements/1.1/');
|
$xml->registerXPathNamespace('dc', 'http://purl.org/dc/elements/1.1/');
|
||||||
|
|
||||||
$ebook->Title = Ebook::NullIfEmpty($xml->xpath('/package/metadata/dc:title'));
|
$ebook->Title = trim(Ebook::NullIfEmpty($xml->xpath('/package/metadata/dc:title')) ?? '');
|
||||||
if($ebook->Title === null){
|
if($ebook->Title == ''){
|
||||||
throw new Exceptions\EbookParsingException('Invalid <dc:title> element.');
|
throw new Exceptions\EbookParsingException('Invalid <dc:title> element.');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -808,14 +818,16 @@ class Ebook{
|
||||||
$fileAs = (string)$author;
|
$fileAs = (string)$author;
|
||||||
}
|
}
|
||||||
|
|
||||||
$authors[] = Contributor::FromProperties(
|
$contributor = new Contributor();
|
||||||
(string)$author,
|
$contributor->Name = (string)$author;
|
||||||
$fileAs,
|
$contributor->UrlName = Formatter::MakeUrlSafe($contributor->Name);
|
||||||
Ebook::NullIfEmpty($xml->xpath('/package/metadata/meta[@property="se:name.person.full-name"][@refines="#' . $id . '"]')),
|
$contributor->SortName = $fileAs;
|
||||||
Ebook::NullIfEmpty($xml->xpath('/package/metadata/meta[@property="se:url.encyclopedia.wikipedia"][@refines="#' . $id . '"]')),
|
$contributor->FullName = Ebook::NullIfEmpty($xml->xpath('/package/metadata/meta[@property="se:name.person.full-name"][@refines="#' . $id . '"]'));
|
||||||
'aut',
|
$contributor->WikipediaUrl = Ebook::NullIfEmpty($xml->xpath('/package/metadata/meta[@property="se:url.encyclopedia.wikipedia"][@refines="#' . $id . '"]'));
|
||||||
Ebook::NullIfEmpty($xml->xpath('/package/metadata/meta[@property="se:url.authority.nacoaf"][@refines="#' . $id . '"]'))
|
$contributor->MarcRole = 'aut';
|
||||||
);
|
$contributor->NacoafUrl = Ebook::NullIfEmpty($xml->xpath('/package/metadata/meta[@property="se:url.authority.nacoaf"][@refines="#' . $id . '"]'));
|
||||||
|
|
||||||
|
$authors[] = $contributor;
|
||||||
}
|
}
|
||||||
if(sizeof($authors) == 0){
|
if(sizeof($authors) == 0){
|
||||||
throw new Exceptions\EbookParsingException('Invalid <dc:creator> element.');
|
throw new Exceptions\EbookParsingException('Invalid <dc:creator> element.');
|
||||||
|
@ -833,14 +845,14 @@ class Ebook{
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach($xml->xpath('/package/metadata/meta[ (@property="role" or @property="se:role") and @refines="#' . $id . '"]') ?: [] as $role){
|
foreach($xml->xpath('/package/metadata/meta[ (@property="role" or @property="se:role") and @refines="#' . $id . '"]') ?: [] as $role){
|
||||||
$c = Contributor::FromProperties(
|
$c = new Contributor();
|
||||||
(string)$contributor,
|
$c->Name = (string)$contributor;
|
||||||
Ebook::NullIfEmpty($xml->xpath('/package/metadata/meta[@property="file-as"][@refines="#' . $id . '"]')),
|
$c->UrlName = Formatter::MakeUrlSafe($contributor->Name);
|
||||||
Ebook::NullIfEmpty($xml->xpath('/package/metadata/meta[@property="se:name.person.full-name"][@refines="#' . $id . '"]')),
|
$c->SortName = Ebook::NullIfEmpty($xml->xpath('/package/metadata/meta[@property="file-as"][@refines="#' . $id . '"]'));
|
||||||
Ebook::NullIfEmpty($xml->xpath('/package/metadata/meta[@property="se:url.encyclopedia.wikipedia"][@refines="#' . $id . '"]')),
|
$c->FullName = Ebook::NullIfEmpty($xml->xpath('/package/metadata/meta[@property="se:name.person.full-name"][@refines="#' . $id . '"]'));
|
||||||
$role,
|
$c->WikipediaUrl = Ebook::NullIfEmpty($xml->xpath('/package/metadata/meta[@property="se:url.encyclopedia.wikipedia"][@refines="#' . $id . '"]'));
|
||||||
Ebook::NullIfEmpty($xml->xpath('/package/metadata/meta[@property="se:url.authority.nacoaf"][@refines="#' . $id . '"]'))
|
$c->MarcRole = $role;
|
||||||
);
|
$c->NacoafUrl = Ebook::NullIfEmpty($xml->xpath('/package/metadata/meta[@property="se:url.authority.nacoaf"][@refines="#' . $id . '"]'));
|
||||||
|
|
||||||
// A display-sequence of 0 indicates that we don't want to process this contributor.
|
// A display-sequence of 0 indicates that we don't want to process this contributor.
|
||||||
$displaySequence = Ebook::NullIfEmpty($xml->xpath('/package/metadata/meta[@property="display-seq"][@refines="#' . $id . '"]'));
|
$displaySequence = Ebook::NullIfEmpty($xml->xpath('/package/metadata/meta[@property="display-seq"][@refines="#' . $id . '"]'));
|
||||||
|
@ -875,9 +887,9 @@ class Ebook{
|
||||||
$ebook->Contributors = $contributors;
|
$ebook->Contributors = $contributors;
|
||||||
|
|
||||||
// Some basic data.
|
// Some basic data.
|
||||||
$ebook->Description = Ebook::NullIfEmpty($xml->xpath('/package/metadata/dc:description'));
|
$ebook->Description = Ebook::NullIfEmpty($xml->xpath('/package/metadata/dc:description')) ?? '';
|
||||||
$ebook->Language = Ebook::NullIfEmpty($xml->xpath('/package/metadata/dc:language'));
|
$ebook->LongDescription = Ebook::NullIfEmpty($xml->xpath('/package/metadata/meta[@property="se:long-description"]')) ?? '';
|
||||||
$ebook->LongDescription = Ebook::NullIfEmpty($xml->xpath('/package/metadata/meta[@property="se:long-description"]'));
|
$ebook->Language = Ebook::NullIfEmpty($xml->xpath('/package/metadata/dc:language')) ?? '';
|
||||||
|
|
||||||
$wordCount = 0;
|
$wordCount = 0;
|
||||||
$wordCountElement = $xml->xpath('/package/metadata/meta[@property="se:word-count"]');
|
$wordCountElement = $xml->xpath('/package/metadata/meta[@property="se:word-count"]');
|
||||||
|
@ -1048,7 +1060,6 @@ class Ebook{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
$this->KepubUrl = trim($this->KepubUrl ?? '');
|
$this->KepubUrl = trim($this->KepubUrl ?? '');
|
||||||
if($this->KepubUrl == ''){
|
if($this->KepubUrl == ''){
|
||||||
$this->KepubUrl = null;
|
$this->KepubUrl = null;
|
||||||
|
@ -1095,7 +1106,7 @@ class Ebook{
|
||||||
}
|
}
|
||||||
|
|
||||||
if(isset($this->Title)){
|
if(isset($this->Title)){
|
||||||
$this->Title = trim($this->Title ?? '');
|
$this->Title = trim($this->Title);
|
||||||
|
|
||||||
if($this->Title == ''){
|
if($this->Title == ''){
|
||||||
$error->Add(new Exceptions\EbookTitleRequiredException());
|
$error->Add(new Exceptions\EbookTitleRequiredException());
|
||||||
|
@ -1128,7 +1139,7 @@ class Ebook{
|
||||||
}
|
}
|
||||||
|
|
||||||
if(isset($this->Description)){
|
if(isset($this->Description)){
|
||||||
$this->Description = trim($this->Description ?? '');
|
$this->Description = trim($this->Description);
|
||||||
|
|
||||||
if($this->Description == ''){
|
if($this->Description == ''){
|
||||||
$error->Add(new Exceptions\EbookDescriptionRequiredException());
|
$error->Add(new Exceptions\EbookDescriptionRequiredException());
|
||||||
|
@ -1139,7 +1150,7 @@ class Ebook{
|
||||||
}
|
}
|
||||||
|
|
||||||
if(isset($this->LongDescription)){
|
if(isset($this->LongDescription)){
|
||||||
$this->LongDescription = trim($this->LongDescription ?? '');
|
$this->LongDescription = trim($this->LongDescription);
|
||||||
|
|
||||||
if($this->LongDescription == ''){
|
if($this->LongDescription == ''){
|
||||||
$error->Add(new Exceptions\EbookLongDescriptionRequiredException());
|
$error->Add(new Exceptions\EbookLongDescriptionRequiredException());
|
||||||
|
@ -1150,7 +1161,7 @@ class Ebook{
|
||||||
}
|
}
|
||||||
|
|
||||||
if(isset($this->Language)){
|
if(isset($this->Language)){
|
||||||
$this->Language = trim($this->Language ?? '');
|
$this->Language = trim($this->Language);
|
||||||
|
|
||||||
if($this->Language == ''){
|
if($this->Language == ''){
|
||||||
$error->Add(new Exceptions\EbookLanguageRequiredException());
|
$error->Add(new Exceptions\EbookLanguageRequiredException());
|
||||||
|
@ -1520,8 +1531,9 @@ class Ebook{
|
||||||
return $string;
|
return $string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* If the given list of elements has an element that is not `''`, return that value; otherwise, return `null`.
|
||||||
|
*
|
||||||
* @param array<SimpleXMLElement>|false|null $elements
|
* @param array<SimpleXMLElement>|false|null $elements
|
||||||
*/
|
*/
|
||||||
private static function NullIfEmpty($elements): ?string{
|
private static function NullIfEmpty($elements): ?string{
|
||||||
|
@ -1529,8 +1541,6 @@ class Ebook{
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Helper function when getting values from SimpleXml.
|
|
||||||
// Checks if the result is set, and returns the value if so; if the value is the empty string, return null.
|
|
||||||
if(isset($elements[0])){
|
if(isset($elements[0])){
|
||||||
$str = (string)$elements[0];
|
$str = (string)$elements[0];
|
||||||
if($str !== ''){
|
if($str !== ''){
|
||||||
|
@ -1541,31 +1551,6 @@ class Ebook{
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ***********
|
|
||||||
// ORM METHODS
|
|
||||||
// ***********
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @throws Exceptions\EbookNotFoundException
|
|
||||||
*/
|
|
||||||
public static function GetByIdentifier(?string $identifier): Ebook{
|
|
||||||
if($identifier === null){
|
|
||||||
throw new Exceptions\EbookNotFoundException('Invalid identifier: ' . $identifier);
|
|
||||||
}
|
|
||||||
|
|
||||||
$result = Db::Query('
|
|
||||||
SELECT *
|
|
||||||
from Ebooks
|
|
||||||
where Identifier = ?
|
|
||||||
', [$identifier], Ebook::class);
|
|
||||||
|
|
||||||
if(sizeof($result) == 0){
|
|
||||||
throw new Exceptions\EbookNotFoundException('Invalid identifier: ' . $identifier);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $result[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @throws Exceptions\ValidationException
|
* @throws Exceptions\ValidationException
|
||||||
*/
|
*/
|
||||||
|
@ -1843,4 +1828,29 @@ class Ebook{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ***********
|
||||||
|
// ORM METHODS
|
||||||
|
// ***********
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @throws Exceptions\EbookNotFoundException
|
||||||
|
*/
|
||||||
|
public static function GetByIdentifier(?string $identifier): Ebook{
|
||||||
|
if($identifier === null){
|
||||||
|
throw new Exceptions\EbookNotFoundException('Invalid identifier: ' . $identifier);
|
||||||
|
}
|
||||||
|
|
||||||
|
$result = Db::Query('
|
||||||
|
SELECT *
|
||||||
|
from Ebooks
|
||||||
|
where Identifier = ?
|
||||||
|
', [$identifier], Ebook::class);
|
||||||
|
|
||||||
|
if(sizeof($result) == 0){
|
||||||
|
throw new Exceptions\EbookNotFoundException('Invalid identifier: ' . $identifier);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $result[0];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,18 +3,20 @@
|
||||||
use Safe\DateTimeImmutable;
|
use Safe\DateTimeImmutable;
|
||||||
|
|
||||||
class EbookSource{
|
class EbookSource{
|
||||||
public ?int $EbookId = null;
|
public int $EbookId;
|
||||||
public Enums\EbookSourceType $Type;
|
public Enums\EbookSourceType $Type;
|
||||||
public string $Url;
|
public string $Url;
|
||||||
public ?int $SortOrder = null;
|
public int $SortOrder;
|
||||||
|
|
||||||
|
|
||||||
|
// *******
|
||||||
|
// METHODS
|
||||||
|
// *******
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @throws Exceptions\ValidationException
|
* @throws Exceptions\ValidationException
|
||||||
*/
|
*/
|
||||||
public function Validate(): void{
|
public function Validate(): void{
|
||||||
/** @throws void */
|
|
||||||
$now = new DateTimeImmutable();
|
|
||||||
|
|
||||||
$error = new Exceptions\ValidationException();
|
$error = new Exceptions\ValidationException();
|
||||||
|
|
||||||
if(!isset($this->EbookId)){
|
if(!isset($this->EbookId)){
|
||||||
|
|
|
@ -4,25 +4,20 @@ class EbookTag extends Tag{
|
||||||
$this->Type = Enums\TagType::Ebook;
|
$this->Type = Enums\TagType::Ebook;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// *******
|
// *******
|
||||||
// GETTERS
|
// GETTERS
|
||||||
// *******
|
// *******
|
||||||
protected function GetUrlName(): string{
|
|
||||||
if($this->_UrlName === null){
|
|
||||||
$this->_UrlName = Formatter::MakeUrlSafe($this->Name);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this->_UrlName;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function GetUrl(): string{
|
protected function GetUrl(): string{
|
||||||
if($this->_Url === null){
|
if(!isset($this->_Url)){
|
||||||
$this->_Url = '/subjects/' . $this->UrlName;
|
$this->_Url = '/subjects/' . $this->UrlName;
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->_Url;
|
return $this->_Url;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// *******
|
// *******
|
||||||
// METHODS
|
// METHODS
|
||||||
// *******
|
// *******
|
||||||
|
@ -43,6 +38,8 @@ class EbookTag extends Tag{
|
||||||
if(strlen($this->Name) > EBOOKS_MAX_STRING_LENGTH){
|
if(strlen($this->Name) > EBOOKS_MAX_STRING_LENGTH){
|
||||||
$error->Add(new Exceptions\StringTooLongException('Ebook tag: '. $this->Name));
|
$error->Add(new Exceptions\StringTooLongException('Ebook tag: '. $this->Name));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$this->UrlName = Formatter::MakeUrlSafe($this->Name);
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
$error->Add(new Exceptions\EbookTagNameRequiredException());
|
$error->Add(new Exceptions\EbookTagNameRequiredException());
|
||||||
|
@ -72,6 +69,11 @@ class EbookTag extends Tag{
|
||||||
$this->TagId = Db::GetLastInsertedId();
|
$this->TagId = Db::GetLastInsertedId();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// ***********
|
||||||
|
// ORM METHODS
|
||||||
|
// ***********
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @throws Exceptions\ValidationException
|
* @throws Exceptions\ValidationException
|
||||||
*/
|
*/
|
||||||
|
|
7
lib/Exceptions/ArtworkTagNameRequiredException.php
Normal file
7
lib/Exceptions/ArtworkTagNameRequiredException.php
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
<?
|
||||||
|
namespace Exceptions;
|
||||||
|
|
||||||
|
class ArtworkTagNameRequiredException extends AppException{
|
||||||
|
/** @var string $message */
|
||||||
|
protected $message = 'Tag name is required.';
|
||||||
|
}
|
16
lib/Feed.php
16
lib/Feed.php
|
@ -14,8 +14,8 @@ abstract class Feed{
|
||||||
public $Entries = [];
|
public $Entries = [];
|
||||||
public string $Path;
|
public string $Path;
|
||||||
public ?string $Stylesheet = null;
|
public ?string $Stylesheet = null;
|
||||||
protected ?string $XmlString = null;
|
protected string $_XmlString;
|
||||||
public ?DateTimeImmutable $Updated = null;
|
public DateTimeImmutable $Updated;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param string $title
|
* @param string $title
|
||||||
|
@ -31,6 +31,13 @@ abstract class Feed{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// *******
|
||||||
|
// GETTERS
|
||||||
|
// *******
|
||||||
|
|
||||||
|
abstract protected function GetXmlString(): string;
|
||||||
|
|
||||||
|
|
||||||
// *******
|
// *******
|
||||||
// METHODS
|
// METHODS
|
||||||
// *******
|
// *******
|
||||||
|
@ -52,11 +59,6 @@ abstract class Feed{
|
||||||
return $output;
|
return $output;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function GetXmlString(): string{
|
|
||||||
// Virtual function, meant to be implemented by subclass
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
|
|
||||||
public function Save(): void{
|
public function Save(): void{
|
||||||
$feed = $this->GetXmlString();
|
$feed = $this->GetXmlString();
|
||||||
|
|
||||||
|
|
|
@ -2,11 +2,16 @@
|
||||||
use Safe\DateTimeImmutable;
|
use Safe\DateTimeImmutable;
|
||||||
|
|
||||||
class GitCommit{
|
class GitCommit{
|
||||||
public ?int $EbookId = null;
|
public int $EbookId;
|
||||||
public DateTimeImmutable $Created;
|
public DateTimeImmutable $Created;
|
||||||
public string $Message;
|
public string $Message;
|
||||||
public string $Hash;
|
public string $Hash;
|
||||||
|
|
||||||
|
|
||||||
|
// ***********
|
||||||
|
// ORM METHODS
|
||||||
|
// ***********
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @throws Exceptions\InvalidGitCommitException
|
* @throws Exceptions\InvalidGitCommitException
|
||||||
*/
|
*/
|
||||||
|
@ -26,6 +31,11 @@ class GitCommit{
|
||||||
return $instance;
|
return $instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// *******
|
||||||
|
// METHODS
|
||||||
|
// *******
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @throws Exceptions\ValidationException
|
* @throws Exceptions\ValidationException
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -62,7 +62,7 @@ class Image{
|
||||||
unlink($tempFilename);
|
unlink($tempFilename);
|
||||||
}
|
}
|
||||||
catch(Exception){
|
catch(Exception){
|
||||||
// Pass if file doesn't exist
|
// Pass if file doesn't exist.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,15 +5,13 @@ use function Safe\exec;
|
||||||
use function Safe\filemtime;
|
use function Safe\filemtime;
|
||||||
use function Safe\filesize;
|
use function Safe\filesize;
|
||||||
use function Safe\glob;
|
use function Safe\glob;
|
||||||
use function Safe\ksort;
|
|
||||||
use function Safe\preg_replace;
|
use function Safe\preg_replace;
|
||||||
use function Safe\preg_split;
|
use function Safe\preg_split;
|
||||||
use function Safe\sprintf;
|
|
||||||
use function Safe\usort;
|
|
||||||
|
|
||||||
class Library{
|
class Library{
|
||||||
/**
|
/**
|
||||||
* @param array<string> $tags
|
* @param array<string> $tags
|
||||||
|
*
|
||||||
* @return array{ebooks: array<Ebook>, ebooksCount: int}
|
* @return array{ebooks: array<Ebook>, ebooksCount: int}
|
||||||
*/
|
*/
|
||||||
public static function FilterEbooks(string $query = null, array $tags = [], Enums\EbookSortType $sort = null, int $page = 1, int $perPage = EBOOKS_PER_PAGE): array{
|
public static function FilterEbooks(string $query = null, array $tags = [], Enums\EbookSortType $sort = null, int $page = 1, int $perPage = EBOOKS_PER_PAGE): array{
|
||||||
|
@ -102,11 +100,11 @@ class Library{
|
||||||
', [$urlPath], Ebook::class);
|
', [$urlPath], Ebook::class);
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
// Multiple authors, e.g., karl-marx_friedrich-engels
|
// Multiple authors, e.g., `karl-marx_friedrich-engels`.
|
||||||
$authors = explode('_', $urlPath);
|
$authors = explode('_', $urlPath);
|
||||||
|
|
||||||
$params = $authors;
|
$params = $authors;
|
||||||
$params[] = sizeof($authors); // The number of authors in the URL must match the number of Contributor records.
|
$params[] = sizeof($authors); // The number of authors in the URL must match the number of `Contributor` records.
|
||||||
|
|
||||||
return Db::Query('
|
return Db::Query('
|
||||||
SELECT e.*
|
SELECT e.*
|
||||||
|
@ -123,6 +121,7 @@ class Library{
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return array<Collection>
|
* @return array<Collection>
|
||||||
|
*
|
||||||
* @throws Exceptions\AppException
|
* @throws Exceptions\AppException
|
||||||
*/
|
*/
|
||||||
public static function GetEbookCollections(): array{
|
public static function GetEbookCollections(): array{
|
||||||
|
@ -135,7 +134,10 @@ class Library{
|
||||||
if($collator === null){
|
if($collator === null){
|
||||||
throw new Exceptions\AppException('Couldn\'t create collator object when getting collections.');
|
throw new Exceptions\AppException('Couldn\'t create collator object when getting collections.');
|
||||||
}
|
}
|
||||||
usort($collections, function($a, $b) use($collator){ return $collator->compare($a->GetSortedName(), $b->GetSortedName()); });
|
usort($collections, function(Collection $a, Collection $b) use($collator){
|
||||||
|
$result = $collator->compare($a->GetSortedName(), $b->GetSortedName());
|
||||||
|
return $result === false ? 0 : $result;
|
||||||
|
});
|
||||||
return $collections;
|
return $collections;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -344,6 +346,7 @@ class Library{
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return array<Artwork>
|
* @return array<Artwork>
|
||||||
|
*
|
||||||
* @throws Exceptions\ArtistNotFoundException
|
* @throws Exceptions\ArtistNotFoundException
|
||||||
*/
|
*/
|
||||||
public static function GetArtworksByArtist(?string $artistUrlName, ?string $status, ?int $submitterUserId): array{
|
public static function GetArtworksByArtist(?string $artistUrlName, ?string $status, ?int $submitterUserId): array{
|
||||||
|
@ -451,7 +454,8 @@ class Library{
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param array<int, stdClass> $items
|
* @param array<int, stdClass> $items
|
||||||
* @return array<string, array<int|string, array<int|string, mixed>>>
|
*
|
||||||
|
* @return array<int, stdClass>
|
||||||
*/
|
*/
|
||||||
private static function SortBulkDownloads(array $items): array{
|
private static function SortBulkDownloads(array $items): array{
|
||||||
// This sorts our items in a special order, epub first and advanced epub last
|
// This sorts our items in a special order, epub first and advanced epub last
|
||||||
|
@ -481,7 +485,8 @@ class Library{
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return array<string, array<int|string, array<int|string, stdClass>>>
|
* @return array{'months': array<string, array<string, stdClass>>, 'subjects': array<stdClass>, 'collections': array<stdClass>, 'authors': array<stdClass>}
|
||||||
|
*
|
||||||
* @throws Exceptions\AppException
|
* @throws Exceptions\AppException
|
||||||
*/
|
*/
|
||||||
public static function RebuildBulkDownloadsCache(): array{
|
public static function RebuildBulkDownloadsCache(): array{
|
||||||
|
@ -489,6 +494,7 @@ class Library{
|
||||||
if($collator === null){
|
if($collator === null){
|
||||||
throw new Exceptions\AppException('Couldn\'t create collator object when rebuilding bulk download cache.');
|
throw new Exceptions\AppException('Couldn\'t create collator object when rebuilding bulk download cache.');
|
||||||
}
|
}
|
||||||
|
/** @var array<string, array<string, stdClass>> $months */
|
||||||
$months = [];
|
$months = [];
|
||||||
$subjects = [];
|
$subjects = [];
|
||||||
$collections = [];
|
$collections = [];
|
||||||
|
@ -509,6 +515,8 @@ class Library{
|
||||||
catch(\Exception){
|
catch(\Exception){
|
||||||
throw new Exceptions\AppException('Couldn\'t parse date on bulk download object.');
|
throw new Exceptions\AppException('Couldn\'t parse date on bulk download object.');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @var string $year Required to satisfy PHPStan */
|
||||||
$year = $date->format('Y');
|
$year = $date->format('Y');
|
||||||
$month = $date->format('F');
|
$month = $date->format('F');
|
||||||
|
|
||||||
|
@ -533,7 +541,10 @@ class Library{
|
||||||
foreach(glob(WEB_ROOT . '/bulk-downloads/collections/*/', GLOB_NOSORT) as $dir){
|
foreach(glob(WEB_ROOT . '/bulk-downloads/collections/*/', GLOB_NOSORT) as $dir){
|
||||||
$collections[] = self::FillBulkDownloadObject($dir, 'collections', '/collections');
|
$collections[] = self::FillBulkDownloadObject($dir, 'collections', '/collections');
|
||||||
}
|
}
|
||||||
usort($collections, function($a, $b) use($collator){ return $collator->compare($a->LabelSort, $b->LabelSort); });
|
usort($collections, function(stdClass $a, stdClass $b) use($collator): int{
|
||||||
|
$result = $collator->compare($a->LabelSort, $b->LabelSort);
|
||||||
|
return $result === false ? 0 : $result;
|
||||||
|
});
|
||||||
|
|
||||||
apcu_store('bulk-downloads-collections', $collections, 43200); // 12 hours
|
apcu_store('bulk-downloads-collections', $collections, 43200); // 12 hours
|
||||||
|
|
||||||
|
@ -541,7 +552,10 @@ class Library{
|
||||||
foreach(glob(WEB_ROOT . '/bulk-downloads/authors/*/', GLOB_NOSORT) as $dir){
|
foreach(glob(WEB_ROOT . '/bulk-downloads/authors/*/', GLOB_NOSORT) as $dir){
|
||||||
$authors[] = self::FillBulkDownloadObject($dir, 'authors', '/ebooks');
|
$authors[] = self::FillBulkDownloadObject($dir, 'authors', '/ebooks');
|
||||||
}
|
}
|
||||||
usort($authors, function($a, $b) use($collator){ return $collator->compare($a->LabelSort, $b->LabelSort); });
|
usort($authors, function(stdClass $a, stdClass $b) use($collator): int{
|
||||||
|
$result = $collator->compare($a->LabelSort, $b->LabelSort);
|
||||||
|
return $result === false ? 0 : $result;
|
||||||
|
});
|
||||||
|
|
||||||
apcu_store('bulk-downloads-authors', $authors, 43200); // 12 hours
|
apcu_store('bulk-downloads-authors', $authors, 43200); // 12 hours
|
||||||
|
|
||||||
|
@ -549,7 +563,8 @@ class Library{
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return array<string, array<int|string, array<int|string, mixed>>>
|
* @return array<stdClass>
|
||||||
|
*
|
||||||
* @throws Exceptions\AppException
|
* @throws Exceptions\AppException
|
||||||
*/
|
*/
|
||||||
public static function RebuildFeedsCache(?string $returnType = null, ?string $returnClass = null): ?array{
|
public static function RebuildFeedsCache(?string $returnType = null, ?string $returnClass = null): ?array{
|
||||||
|
@ -584,7 +599,10 @@ class Library{
|
||||||
$feeds[] = $obj;
|
$feeds[] = $obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
usort($feeds, function($a, $b) use($collator){ return $collator->compare($a->LabelSort, $b->LabelSort); });
|
usort($feeds, function(stdClass $a, stdClass $b) use($collator): int{
|
||||||
|
$result = $collator->compare($a->LabelSort, $b->LabelSort);
|
||||||
|
return $result === false ? 0 : $result;
|
||||||
|
});
|
||||||
|
|
||||||
if($type == $returnType && $class == $returnClass){
|
if($type == $returnType && $class == $returnClass){
|
||||||
$retval = $feeds;
|
$retval = $feeds;
|
||||||
|
|
|
@ -3,6 +3,10 @@ class LocSubject{
|
||||||
public int $LocSubjectId;
|
public int $LocSubjectId;
|
||||||
public string $Name;
|
public string $Name;
|
||||||
|
|
||||||
|
// *******
|
||||||
|
// METHODS
|
||||||
|
// *******
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @throws Exceptions\ValidationException
|
* @throws Exceptions\ValidationException
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
<?
|
<?
|
||||||
use Safe\DateTimeImmutable;
|
|
||||||
use function Safe\fopen;
|
use function Safe\fopen;
|
||||||
use function Safe\fwrite;
|
use function Safe\fwrite;
|
||||||
use function Safe\fclose;
|
use function Safe\fclose;
|
||||||
|
|
|
@ -1,9 +1,12 @@
|
||||||
<?
|
<?
|
||||||
use function Safe\glob;
|
use function Safe\glob;
|
||||||
use function Safe\preg_match_all;
|
use function Safe\preg_match_all;
|
||||||
use function Safe\sort;
|
|
||||||
|
|
||||||
class Manual{
|
class Manual{
|
||||||
|
// *******
|
||||||
|
// METHODS
|
||||||
|
// *******
|
||||||
|
|
||||||
public static function GetLatestVersion(): string{
|
public static function GetLatestVersion(): string{
|
||||||
$dirs = glob(MANUAL_PATH . '/*', GLOB_ONLYDIR);
|
$dirs = glob(MANUAL_PATH . '/*', GLOB_ONLYDIR);
|
||||||
sort($dirs);
|
sort($dirs);
|
||||||
|
|
|
@ -10,6 +10,11 @@ class Museum{
|
||||||
public string $Name;
|
public string $Name;
|
||||||
public string $Domain;
|
public string $Domain;
|
||||||
|
|
||||||
|
|
||||||
|
// *******
|
||||||
|
// METHODS
|
||||||
|
// *******
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @throws Exceptions\InvalidUrlException
|
* @throws Exceptions\InvalidUrlException
|
||||||
* @throws Exceptions\InvalidMuseumUrlException
|
* @throws Exceptions\InvalidMuseumUrlException
|
||||||
|
@ -597,6 +602,10 @@ class Museum{
|
||||||
return $outputUrl;
|
return $outputUrl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ***********
|
||||||
|
// ORM METHODS
|
||||||
|
// ***********
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @throws Exceptions\MuseumNotFoundException
|
* @throws Exceptions\MuseumNotFoundException
|
||||||
* @throws Exceptions\InvalidUrlException
|
* @throws Exceptions\InvalidUrlException
|
||||||
|
|
|
@ -13,15 +13,17 @@ class NewsletterSubscription{
|
||||||
public bool $IsSubscribedToNewsletter = false;
|
public bool $IsSubscribedToNewsletter = false;
|
||||||
public ?int $UserId = null;
|
public ?int $UserId = null;
|
||||||
public DateTimeImmutable $Created;
|
public DateTimeImmutable $Created;
|
||||||
protected ?User $_User = null;
|
|
||||||
protected ?string $_Url = null;
|
protected ?User $_User;
|
||||||
|
protected string $_Url;
|
||||||
|
|
||||||
|
|
||||||
// *******
|
// *******
|
||||||
// GETTERS
|
// GETTERS
|
||||||
// *******
|
// *******
|
||||||
|
|
||||||
protected function GetUrl(): string{
|
protected function GetUrl(): string{
|
||||||
if($this->_Url === null){
|
if(!isset($this->_Url)){
|
||||||
$this->_Url = '/newsletter/subscriptions/' . $this->User->Uuid;
|
$this->_Url = '/newsletter/subscriptions/' . $this->User->Uuid;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -13,10 +13,10 @@ class OpdsAcquisitionFeed extends OpdsFeed{
|
||||||
// *******
|
// *******
|
||||||
|
|
||||||
protected function GetXmlString(): string{
|
protected function GetXmlString(): string{
|
||||||
if($this->XmlString === null){
|
if(!isset($this->_XmlString)){
|
||||||
$this->XmlString = $this->CleanXmlString(Template::OpdsAcquisitionFeed(['id' => $this->Id, 'url' => $this->Url, 'title' => $this->Title, 'parentUrl' => $this->Parent ? $this->Parent->Url : null, 'updated' => $this->Updated, 'isCrawlable' => $this->IsCrawlable, 'subtitle' => $this->Subtitle, 'entries' => $this->Entries]));
|
$this->_XmlString = $this->CleanXmlString(Template::OpdsAcquisitionFeed(['id' => $this->Id, 'url' => $this->Url, 'title' => $this->Title, 'parentUrl' => $this->Parent ? $this->Parent->Url : null, 'updated' => $this->Updated, 'isCrawlable' => $this->IsCrawlable, 'subtitle' => $this->Subtitle, 'entries' => $this->Entries]));
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->XmlString;
|
return $this->_XmlString;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,7 +36,7 @@ abstract class OpdsFeed extends AtomFeed{
|
||||||
|
|
||||||
$this->Updated = $updated;
|
$this->Updated = $updated;
|
||||||
|
|
||||||
$this->XmlString = null;
|
unset($this->_XmlString);
|
||||||
file_put_contents($this->Path, $this->GetXmlString());
|
file_put_contents($this->Path, $this->GetXmlString());
|
||||||
|
|
||||||
// Do we have any parents of our own to update?
|
// Do we have any parents of our own to update?
|
||||||
|
|
|
@ -6,12 +6,12 @@ class OpdsNavigationEntry{
|
||||||
public string $Url;
|
public string $Url;
|
||||||
public string $Rel;
|
public string $Rel;
|
||||||
public string $Type;
|
public string $Type;
|
||||||
public ?DateTimeImmutable $Updated = null;
|
public DateTimeImmutable $Updated;
|
||||||
public string $Description;
|
public string $Description;
|
||||||
public string $Title;
|
public string $Title;
|
||||||
public string $SortTitle;
|
public string $SortTitle;
|
||||||
|
|
||||||
public function __construct(string $title, string $description, string $url, ?DateTimeImmutable $updated, string $rel, string $type){
|
public function __construct(string $title, string $description, string $url, DateTimeImmutable $updated, string $rel, string $type){
|
||||||
$this->Id = SITE_URL . $url;
|
$this->Id = SITE_URL . $url;
|
||||||
$this->Url = $url;
|
$this->Url = $url;
|
||||||
$this->Rel = $rel;
|
$this->Rel = $rel;
|
||||||
|
|
|
@ -38,10 +38,10 @@ class OpdsNavigationFeed extends OpdsFeed{
|
||||||
// *******
|
// *******
|
||||||
|
|
||||||
protected function GetXmlString(): string{
|
protected function GetXmlString(): string{
|
||||||
if($this->XmlString === null){
|
if(!isset($this->_XmlString)){
|
||||||
$this->XmlString = $this->CleanXmlString(Template::OpdsNavigationFeed(['id' => $this->Id, 'url' => $this->Url, 'title' => $this->Title, 'parentUrl' => $this->Parent ? $this->Parent->Url : null, 'updated' => $this->Updated, 'subtitle' => $this->Subtitle, 'entries' => $this->Entries]));
|
$this->_XmlString = $this->CleanXmlString(Template::OpdsNavigationFeed(['id' => $this->Id, 'url' => $this->Url, 'title' => $this->Title, 'parentUrl' => $this->Parent ? $this->Parent->Url : null, 'updated' => $this->Updated, 'subtitle' => $this->Subtitle, 'entries' => $this->Entries]));
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->XmlString;
|
return $this->_XmlString;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,14 +7,15 @@ use Safe\DateTimeImmutable;
|
||||||
class Patron{
|
class Patron{
|
||||||
use Traits\Accessor;
|
use Traits\Accessor;
|
||||||
|
|
||||||
public ?int $UserId = null;
|
public int $UserId;
|
||||||
public bool $IsAnonymous;
|
public bool $IsAnonymous;
|
||||||
public ?string $AlternateName = null;
|
public ?string $AlternateName = null;
|
||||||
public bool $IsSubscribedToEmails;
|
public bool $IsSubscribedToEmails;
|
||||||
public ?DateTimeImmutable $Created = null;
|
public DateTimeImmutable $Created;
|
||||||
public ?DateTimeImmutable $Ended = null;
|
public ?DateTimeImmutable $Ended = null;
|
||||||
|
|
||||||
protected ?User $_User = null;
|
protected User $_User;
|
||||||
|
|
||||||
|
|
||||||
// *******
|
// *******
|
||||||
// METHODS
|
// METHODS
|
||||||
|
|
|
@ -19,6 +19,11 @@ class Payment{
|
||||||
|
|
||||||
protected ?User $_User = null;
|
protected ?User $_User = null;
|
||||||
|
|
||||||
|
|
||||||
|
// *******
|
||||||
|
// GETTERS
|
||||||
|
// *******
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @throws Exceptions\UserNotFoundException
|
* @throws Exceptions\UserNotFoundException
|
||||||
*/
|
*/
|
||||||
|
|
25
lib/Poll.php
25
lib/Poll.php
|
@ -1,6 +1,5 @@
|
||||||
<?
|
<?
|
||||||
use Safe\DateTimeImmutable;
|
use Safe\DateTimeImmutable;
|
||||||
use function Safe\usort;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @property string $Url
|
* @property string $Url
|
||||||
|
@ -18,12 +17,13 @@ class Poll{
|
||||||
public DateTimeImmutable $Created;
|
public DateTimeImmutable $Created;
|
||||||
public DateTimeImmutable $Start;
|
public DateTimeImmutable $Start;
|
||||||
public DateTimeImmutable $End;
|
public DateTimeImmutable $End;
|
||||||
protected ?string $_Url = null;
|
|
||||||
/** @var ?array<PollItem> $_PollItems */
|
protected string $_Url;
|
||||||
protected $_PollItems = null;
|
/** @var array<PollItem> $_PollItems */
|
||||||
/** @var ?array<PollItem> $_PollItemsByWinner */
|
protected array $_PollItems;
|
||||||
protected $_PollItemsByWinner = null;
|
/** @var array<PollItem> $_PollItemsByWinner */
|
||||||
protected ?int $_VoteCount = null;
|
protected array $_PollItemsByWinner;
|
||||||
|
protected int $_VoteCount;
|
||||||
|
|
||||||
|
|
||||||
// *******
|
// *******
|
||||||
|
@ -31,7 +31,7 @@ class Poll{
|
||||||
// *******
|
// *******
|
||||||
|
|
||||||
protected function GetUrl(): string{
|
protected function GetUrl(): string{
|
||||||
if($this->_Url === null){
|
if(!isset($this->_Url)){
|
||||||
$this->_Url = '/polls/' . $this->UrlName;
|
$this->_Url = '/polls/' . $this->UrlName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -39,7 +39,7 @@ class Poll{
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function GetVoteCount(): int{
|
protected function GetVoteCount(): int{
|
||||||
if($this->_VoteCount === null){
|
if(!isset($this->_VoteCount)){
|
||||||
$this->_VoteCount = Db::QueryInt('
|
$this->_VoteCount = Db::QueryInt('
|
||||||
SELECT count(*)
|
SELECT count(*)
|
||||||
from PollVotes pv
|
from PollVotes pv
|
||||||
|
@ -55,7 +55,7 @@ class Poll{
|
||||||
* @return array<PollItem>
|
* @return array<PollItem>
|
||||||
*/
|
*/
|
||||||
protected function GetPollItems(): array{
|
protected function GetPollItems(): array{
|
||||||
if($this->_PollItems === null){
|
if(!isset($this->_PollItems)){
|
||||||
$this->_PollItems = Db::Query('
|
$this->_PollItems = Db::Query('
|
||||||
SELECT *
|
SELECT *
|
||||||
from PollItems
|
from PollItems
|
||||||
|
@ -71,11 +71,11 @@ class Poll{
|
||||||
* @return array<PollItem>
|
* @return array<PollItem>
|
||||||
*/
|
*/
|
||||||
protected function GetPollItemsByWinner(): array{
|
protected function GetPollItemsByWinner(): array{
|
||||||
if($this->_PollItemsByWinner === null){
|
if(!isset($this->_PollItemsByWinner)){
|
||||||
$this->_PollItemsByWinner = $this->PollItems;
|
$this->_PollItemsByWinner = $this->PollItems;
|
||||||
usort($this->_PollItemsByWinner, function(PollItem $a, PollItem $b){ return $a->VoteCount <=> $b->VoteCount; });
|
usort($this->_PollItemsByWinner, function(PollItem $a, PollItem $b){ return $a->VoteCount <=> $b->VoteCount; });
|
||||||
|
|
||||||
$this->_PollItemsByWinner = array_reverse($this->_PollItemsByWinner ?? []);
|
$this->_PollItemsByWinner = array_reverse($this->_PollItemsByWinner);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->_PollItemsByWinner;
|
return $this->_PollItemsByWinner;
|
||||||
|
@ -87,7 +87,6 @@ class Poll{
|
||||||
// *******
|
// *******
|
||||||
|
|
||||||
public function IsActive(): bool{
|
public function IsActive(): bool{
|
||||||
/** @throws void */
|
|
||||||
if( ($this->Start !== null && $this->Start > NOW) || ($this->End !== null && $this->End < NOW)){
|
if( ($this->Start !== null && $this->Start > NOW) || ($this->End !== null && $this->End < NOW)){
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,4 @@
|
||||||
<?
|
<?
|
||||||
|
|
||||||
use Exceptions\PollItemNotFoundException;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @property int $VoteCount
|
* @property int $VoteCount
|
||||||
* @property Poll $Poll
|
* @property Poll $Poll
|
||||||
|
@ -13,8 +10,9 @@ class PollItem{
|
||||||
public int $PollId;
|
public int $PollId;
|
||||||
public string $Name;
|
public string $Name;
|
||||||
public string $Description;
|
public string $Description;
|
||||||
protected ?int $_VoteCount = null;
|
|
||||||
protected ?Poll $_Poll = null;
|
protected int $_VoteCount;
|
||||||
|
protected Poll $_Poll;
|
||||||
|
|
||||||
|
|
||||||
// *******
|
// *******
|
||||||
|
@ -22,7 +20,7 @@ class PollItem{
|
||||||
// *******
|
// *******
|
||||||
|
|
||||||
protected function GetVoteCount(): int{
|
protected function GetVoteCount(): int{
|
||||||
if($this->_VoteCount === null){
|
if(!isset($this->_VoteCount)){
|
||||||
$this->_VoteCount = Db::QueryInt('
|
$this->_VoteCount = Db::QueryInt('
|
||||||
SELECT count(*)
|
SELECT count(*)
|
||||||
from PollVotes pv
|
from PollVotes pv
|
||||||
|
|
|
@ -8,14 +8,15 @@ use Safe\DateTimeImmutable;
|
||||||
*/
|
*/
|
||||||
class PollVote{
|
class PollVote{
|
||||||
use Traits\Accessor;
|
use Traits\Accessor;
|
||||||
|
use Traits\PropertyFromHttp;
|
||||||
|
|
||||||
public ?int $UserId = null;
|
public int $UserId;
|
||||||
public DateTimeImmutable $Created;
|
public DateTimeImmutable $Created;
|
||||||
public ?int $PollItemId = null;
|
public int $PollItemId;
|
||||||
|
|
||||||
protected ?User $_User = null;
|
protected User $_User;
|
||||||
protected ?PollItem $_PollItem = null;
|
protected PollItem $_PollItem;
|
||||||
protected ?string $_Url = null;
|
protected string $_Url;
|
||||||
|
|
||||||
|
|
||||||
// *******
|
// *******
|
||||||
|
@ -23,7 +24,7 @@ class PollVote{
|
||||||
// *******
|
// *******
|
||||||
|
|
||||||
protected function GetUrl(): string{
|
protected function GetUrl(): string{
|
||||||
if($this->_Url === null){
|
if(!isset($this->_Url)){
|
||||||
$this->_Url = $this->PollItem->Poll->Url . '/votes/' . $this->UserId;
|
$this->_Url = $this->PollItem->Poll->Url . '/votes/' . $this->UserId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -41,26 +42,30 @@ class PollVote{
|
||||||
protected function Validate(): void{
|
protected function Validate(): void{
|
||||||
$error = new Exceptions\InvalidPollVoteException();
|
$error = new Exceptions\InvalidPollVoteException();
|
||||||
|
|
||||||
if($this->User === null){
|
if(!isset($this->UserId)){
|
||||||
$error->Add(new Exceptions\UserNotFoundException());
|
$error->Add(new Exceptions\UserNotFoundException());
|
||||||
}
|
}
|
||||||
|
else{
|
||||||
|
try{
|
||||||
|
// Attempt to get the `User`.
|
||||||
|
User::Get($this->UserId);
|
||||||
|
}
|
||||||
|
catch(Exceptions\UserNotFoundException $ex){
|
||||||
|
$error->Add($ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if($this->PollItemId === null){
|
if(!isset($this->PollItemId)){
|
||||||
$error->Add(new Exceptions\PollItemRequiredException());
|
$error->Add(new Exceptions\PollItemRequiredException());
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
if($this->PollItem === null){
|
try{
|
||||||
$error->Add(new Exceptions\PollNotFoundException());
|
if(!$this->PollItem->Poll->IsActive()){
|
||||||
|
$error->Add(new Exceptions\PollClosedException());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else{
|
catch(Exceptions\PollItemNotFoundException | Exceptions\PollNotFoundException){
|
||||||
if($this->PollItem->Poll === null){
|
$error->Add(new Exceptions\PollNotFoundException());
|
||||||
$error->Add(new Exceptions\PollNotFoundException());
|
|
||||||
}
|
|
||||||
else{
|
|
||||||
if(!$this->PollItem->Poll->IsActive()){
|
|
||||||
$error->Add(new Exceptions\PollClosedException());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -97,10 +102,8 @@ class PollVote{
|
||||||
$this->UserId = $this->User->UserId;
|
$this->UserId = $this->User->UserId;
|
||||||
}
|
}
|
||||||
catch(Exceptions\UserNotFoundException){
|
catch(Exceptions\UserNotFoundException){
|
||||||
// Can't validate patron email - do nothing for now,
|
// Can't validate patron email - do nothing for now, this will be caught later when we validate the vote during creation.
|
||||||
// this will be caught later when we validate the vote during creation.
|
// Save the email in the User object in case we want it later, for example prefilling the 'create' form after an error is returned.
|
||||||
// Save the email in the User object in case we want it later,
|
|
||||||
// for example prefilling the 'create' form after an error is returned.
|
|
||||||
$this->User = new User();
|
$this->User = new User();
|
||||||
$this->User->Email = $email;
|
$this->User->Email = $email;
|
||||||
}
|
}
|
||||||
|
@ -135,4 +138,8 @@ class PollVote{
|
||||||
|
|
||||||
return $result[0] ?? throw new Exceptions\PollVoteNotFoundException();
|
return $result[0] ?? throw new Exceptions\PollVoteNotFoundException();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function FillFromHttpPost(): void{
|
||||||
|
$this->PropertyFromHttp('PollItemId');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,13 +21,13 @@ class RssFeed extends Feed{
|
||||||
// *******
|
// *******
|
||||||
|
|
||||||
protected function GetXmlString(): string{
|
protected function GetXmlString(): string{
|
||||||
if($this->XmlString === null){
|
if(!isset($this->_XmlString)){
|
||||||
$feed = Template::RssFeed(['url' => $this->Url, 'description' => $this->Description, 'title' => $this->Title, 'entries' => $this->Entries, 'updated' => NOW]);
|
$feed = Template::RssFeed(['url' => $this->Url, 'description' => $this->Description, 'title' => $this->Title, 'entries' => $this->Entries, 'updated' => NOW]);
|
||||||
|
|
||||||
$this->XmlString = $this->CleanXmlString($feed);
|
$this->_XmlString = $this->CleanXmlString($feed);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->XmlString;
|
return $this->_XmlString;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function SaveIfChanged(): bool{
|
public function SaveIfChanged(): bool{
|
||||||
|
|
|
@ -13,8 +13,8 @@ class Session{
|
||||||
public DateTimeImmutable $Created;
|
public DateTimeImmutable $Created;
|
||||||
public string $SessionId;
|
public string $SessionId;
|
||||||
|
|
||||||
protected ?User $_User = null;
|
protected User $_User;
|
||||||
public ?string $_Url = null;
|
public string $_Url;
|
||||||
|
|
||||||
|
|
||||||
// *******
|
// *******
|
||||||
|
@ -22,7 +22,7 @@ class Session{
|
||||||
// *******
|
// *******
|
||||||
|
|
||||||
protected function GetUrl(): string{
|
protected function GetUrl(): string{
|
||||||
if($this->_Url === null){
|
if(!isset($this->_Url)){
|
||||||
$this->_Url = '/sessions/' . $this->SessionId;
|
$this->_Url = '/sessions/' . $this->SessionId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -101,6 +101,11 @@ class Session{
|
||||||
setcookie('sessionid', $sessionId, ['expires' => intval((new DateTimeImmutable('+1 week'))->format(Enums\DateTimeFormat::UnixTimestamp->value)), 'path' => '/', 'domain' => SITE_DOMAIN, 'secure' => true, 'httponly' => false, 'samesite' => 'Lax']); // Expires in two weeks
|
setcookie('sessionid', $sessionId, ['expires' => intval((new DateTimeImmutable('+1 week'))->format(Enums\DateTimeFormat::UnixTimestamp->value)), 'path' => '/', 'domain' => SITE_DOMAIN, 'secure' => true, 'httponly' => false, 'samesite' => 'Lax']); // Expires in two weeks
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// ***********
|
||||||
|
// ORM METHODS
|
||||||
|
// ***********
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @throws Exceptions\SessionNotFoundException
|
* @throws Exceptions\SessionNotFoundException
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
<?
|
<?
|
||||||
/**
|
/**
|
||||||
* @property string $Url
|
* @property string $Url
|
||||||
* @property string $UrlName
|
|
||||||
*/
|
*/
|
||||||
class Tag{
|
class Tag{
|
||||||
use Traits\Accessor;
|
use Traits\Accessor;
|
||||||
|
|
||||||
public int $TagId;
|
public int $TagId;
|
||||||
public string $Name;
|
public string $Name;
|
||||||
|
public string $UrlName;
|
||||||
public Enums\TagType $Type;
|
public Enums\TagType $Type;
|
||||||
protected ?string $_UrlName = null;
|
|
||||||
protected ?string $_Url = null;
|
protected string $_Url;
|
||||||
}
|
}
|
||||||
|
|
|
@ -117,6 +117,7 @@ trait Accessor{
|
||||||
* $t->User = new User();
|
* $t->User = new User();
|
||||||
*
|
*
|
||||||
* isset($t->User); // true
|
* isset($t->User); // true
|
||||||
|
* ```
|
||||||
*
|
*
|
||||||
* @see <https://reddit.com/r/PHPhelp/comments/x2avqu/anyone_know_the_rules_behind_null_coalescing/>
|
* @see <https://reddit.com/r/PHPhelp/comments/x2avqu/anyone_know_the_rules_behind_null_coalescing/>
|
||||||
*/
|
*/
|
||||||
|
|
154
lib/Traits/PropertyFromHttp.php
Normal file
154
lib/Traits/PropertyFromHttp.php
Normal file
|
@ -0,0 +1,154 @@
|
||||||
|
<?
|
||||||
|
namespace Traits;
|
||||||
|
|
||||||
|
use function Safe\preg_replace;
|
||||||
|
|
||||||
|
trait PropertyFromHttp{
|
||||||
|
/**
|
||||||
|
* Given the string name of a property, try to fill it from HTTP data (POST by default).
|
||||||
|
*
|
||||||
|
* This function will try to infer the type of the class property using reflection.
|
||||||
|
*
|
||||||
|
* - If a variable doesn't match a class property (either by name or by type), then the class property is unchanged.
|
||||||
|
*
|
||||||
|
* - If a variable matches a class property both by name and type, then the class property is set to the variable.
|
||||||
|
*
|
||||||
|
* - If the class property type is both nullable and not `string` (e.g., the class property is `?int`), then a matching but empty variable will set that class property to `null`; this includes `?string` class properties. (I.e., a matching variable of value `""` will set a `?string` class property to `null`.)
|
||||||
|
*
|
||||||
|
* For example, consider:
|
||||||
|
*
|
||||||
|
* ```
|
||||||
|
* class Test{
|
||||||
|
* use Traits\PropertyFromHttp;
|
||||||
|
*
|
||||||
|
* public int $Id;
|
||||||
|
* public string $Name;
|
||||||
|
* public ?string $Description;
|
||||||
|
* public ?int $ChapterNumber;
|
||||||
|
* }
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* - POST `['test-foo' => 'bar']`:
|
||||||
|
*
|
||||||
|
* No changes
|
||||||
|
*
|
||||||
|
* - POST `['test-id' => '123']`:
|
||||||
|
*
|
||||||
|
* `$Id`: set to `123`
|
||||||
|
*
|
||||||
|
* - POST `['test-id' => '']`:
|
||||||
|
*
|
||||||
|
* `$Id`: unchanged, because it is not nullable
|
||||||
|
*
|
||||||
|
* - POST `['test-name' => 'bob']`:
|
||||||
|
*
|
||||||
|
* `$Name`: set to `"bob"`
|
||||||
|
*
|
||||||
|
* - POST `['test-name' => '']`:
|
||||||
|
*
|
||||||
|
* `$Name`: set to `""`, because it is not nullable
|
||||||
|
*
|
||||||
|
* - POST `['test-description' => 'abc']`:
|
||||||
|
*
|
||||||
|
* `$Description`: set to `abc`
|
||||||
|
*
|
||||||
|
* - POST `['test-description' => '']`:
|
||||||
|
*
|
||||||
|
* `$Description`: set to `null`, because it is nullable
|
||||||
|
*
|
||||||
|
* - POST `['test-chapter-number' => '456']`:
|
||||||
|
*
|
||||||
|
* `$ChapterNumber`: set to `456`
|
||||||
|
*
|
||||||
|
* - POST `['test-chapter-number' => '']`:
|
||||||
|
*
|
||||||
|
* `$ChapterNumber`: set to `null`, because an empty string sets nullable properties to `null`.
|
||||||
|
*/
|
||||||
|
public function PropertyFromHttp(string $property, \Enums\HttpVariableSource $set = POST, ?string $httpName = null): void{
|
||||||
|
try{
|
||||||
|
$rp = new \ReflectionProperty($this, $property);
|
||||||
|
}
|
||||||
|
catch(\ReflectionException){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @var ?\ReflectionNamedType $propertyType */
|
||||||
|
$propertyType = $rp->getType();
|
||||||
|
if($propertyType !== null){
|
||||||
|
if($httpName === null){
|
||||||
|
$httpName = mb_strtolower(preg_replace('/([^^])([A-Z])/u', '\1-\2', $this::class . $property));
|
||||||
|
}
|
||||||
|
|
||||||
|
$vars = [];
|
||||||
|
|
||||||
|
switch($set){
|
||||||
|
case \Enums\HttpVariableSource::Get:
|
||||||
|
$vars = $_GET;
|
||||||
|
break;
|
||||||
|
case \Enums\HttpVariableSource::Post:
|
||||||
|
$vars = $_POST;
|
||||||
|
break;
|
||||||
|
case \Enums\HttpVariableSource::Cookie:
|
||||||
|
$vars = $_COOKIE;
|
||||||
|
break;
|
||||||
|
case \Enums\HttpVariableSource::Session:
|
||||||
|
$vars = $_SESSION;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the variable was not passed to us, don't change the property.
|
||||||
|
if(!isset($vars[$httpName])){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$type = $propertyType->getName();
|
||||||
|
$isPropertyNullable = $propertyType->allowsNull();
|
||||||
|
$isPropertyEnum = is_a($type, 'BackedEnum', true);
|
||||||
|
$postValue = null;
|
||||||
|
|
||||||
|
if($isPropertyEnum){
|
||||||
|
$postValue = $type::tryFrom(\HttpInput::Str($set, $httpName) ?? '');
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
switch($type){
|
||||||
|
case 'int':
|
||||||
|
$postValue = \HttpInput::Int($set, $httpName);
|
||||||
|
break;
|
||||||
|
case 'bool':
|
||||||
|
$postValue = \HttpInput::Bool($set, $httpName);
|
||||||
|
break;
|
||||||
|
case 'float':
|
||||||
|
$postValue = \HttpInput::Dec($set, $httpName);
|
||||||
|
break;
|
||||||
|
case 'DateTimeImmutable':
|
||||||
|
case 'Safe\DateTimeImmutable':
|
||||||
|
$postValue = \HttpInput::Date($set, $httpName);
|
||||||
|
break;
|
||||||
|
case 'array':
|
||||||
|
$postValue = \HttpInput::Array($set, $httpName);
|
||||||
|
break;
|
||||||
|
case 'string':
|
||||||
|
$postValue = \HttpInput::Str($set, $httpName, true);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if($type == 'string'){
|
||||||
|
if($isPropertyNullable){
|
||||||
|
if($postValue == ''){
|
||||||
|
$this->{$property} = null;
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
$this->{$property} = $postValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
elseif($postValue !== null){
|
||||||
|
$this->{$property} = $postValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
elseif($isPropertyNullable || $postValue !== null){
|
||||||
|
$this->{$property} = $postValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
15
lib/User.php
15
lib/User.php
|
@ -17,10 +17,11 @@ class User{
|
||||||
public string $Uuid;
|
public string $Uuid;
|
||||||
public ?string $PasswordHash = null;
|
public ?string $PasswordHash = null;
|
||||||
|
|
||||||
protected ?bool $_IsRegistered = null;
|
protected bool $_IsRegistered;
|
||||||
/** @var ?array<Payment> $_Payments */
|
/** @var array<Payment> $_Payments */
|
||||||
protected $_Payments = null;
|
protected array $_Payments;
|
||||||
protected ?Benefits $_Benefits = null;
|
protected Benefits $_Benefits;
|
||||||
|
|
||||||
|
|
||||||
// *******
|
// *******
|
||||||
// GETTERS
|
// GETTERS
|
||||||
|
@ -30,7 +31,7 @@ class User{
|
||||||
* @return array<Payment>
|
* @return array<Payment>
|
||||||
*/
|
*/
|
||||||
protected function GetPayments(): array{
|
protected function GetPayments(): array{
|
||||||
if($this->_Payments === null){
|
if(!isset($this->_Payments)){
|
||||||
$this->_Payments = Db::Query('
|
$this->_Payments = Db::Query('
|
||||||
SELECT *
|
SELECT *
|
||||||
from Payments
|
from Payments
|
||||||
|
@ -43,7 +44,7 @@ class User{
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function GetBenefits(): Benefits{
|
protected function GetBenefits(): Benefits{
|
||||||
if($this->_Benefits === null){
|
if(!isset($this->_Benefits)){
|
||||||
$result = Db::Query('
|
$result = Db::Query('
|
||||||
SELECT *
|
SELECT *
|
||||||
from Benefits
|
from Benefits
|
||||||
|
@ -64,7 +65,7 @@ class User{
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function GetIsRegistered(): ?bool{
|
protected function GetIsRegistered(): ?bool{
|
||||||
if($this->_IsRegistered === null){
|
if(!isset($this->_IsRegistered)){
|
||||||
// A user is "registered" if they have a benefits entry in the table.
|
// A user is "registered" if they have a benefits entry in the table.
|
||||||
// This function will fill it out for us.
|
// This function will fill it out for us.
|
||||||
$this->GetBenefits();
|
$this->GetBenefits();
|
||||||
|
|
|
@ -46,12 +46,7 @@ function CreateOpdsCollectionFeed(string $name, string $url, string $description
|
||||||
|
|
||||||
usort($collections, function($a, $b) use($collator){
|
usort($collections, function($a, $b) use($collator){
|
||||||
$result = $collator->compare($a['sortedname'], $b['sortedname']);
|
$result = $collator->compare($a['sortedname'], $b['sortedname']);
|
||||||
if($result === false){
|
return $result === false ? 0 : $result;
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
else{
|
|
||||||
return $result;
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Create the collections navigation document.
|
// Create the collections navigation document.
|
||||||
|
|
|
@ -208,7 +208,7 @@ try{
|
||||||
)
|
)
|
||||||
){
|
){
|
||||||
// This payment is eligible for the Patrons Circle!
|
// This payment is eligible for the Patrons Circle!
|
||||||
if($payment->User !== null){
|
if($payment->UserId !== null && $payment->User !== null){
|
||||||
// Are we already a patron?
|
// Are we already a patron?
|
||||||
if(!Db::QueryBool('
|
if(!Db::QueryBool('
|
||||||
SELECT exists(
|
SELECT exists(
|
||||||
|
|
|
@ -34,10 +34,10 @@ $isEditForm = $isEditForm ?? false;
|
||||||
<label class="year">
|
<label class="year">
|
||||||
<span>Year of death</span>
|
<span>Year of death</span>
|
||||||
<span>If circa or unknown, enter the latest possible year.</span>
|
<span>If circa or unknown, enter the latest possible year.</span>
|
||||||
<? /* Not using <input type="number"> for now, see https://technology.blog.gov.uk/2020/02/24/why-the-gov-uk-design-system-team-changed-the-input-type-for-numbers/ */ ?>
|
<? /* Not using `<input type="number">` for now, see <https://technology.blog.gov.uk/2020/02/24/why-the-gov-uk-design-system-team-changed-the-input-type-for-numbers/>. */ ?>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
name="artist-year-of-death"
|
name="artist-death-year"
|
||||||
inputmode="numeric"
|
inputmode="numeric"
|
||||||
pattern="[0-9]{1,4}"
|
pattern="[0-9]{1,4}"
|
||||||
value="<?= Formatter::EscapeHtml((string)$artwork->Artist->DeathYear) ?>"
|
value="<?= Formatter::EscapeHtml((string)$artwork->Artist->DeathYear) ?>"
|
||||||
|
@ -56,7 +56,7 @@ $isEditForm = $isEditForm ?? false;
|
||||||
Year of completion
|
Year of completion
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
name="artwork-year"
|
name="artwork-completed-year"
|
||||||
inputmode="numeric"
|
inputmode="numeric"
|
||||||
pattern="[0-9]{1,4}"
|
pattern="[0-9]{1,4}"
|
||||||
value="<?= Formatter::EscapeHtml((string)$artwork->CompletedYear) ?>"
|
value="<?= Formatter::EscapeHtml((string)$artwork->CompletedYear) ?>"
|
||||||
|
@ -65,7 +65,7 @@ $isEditForm = $isEditForm ?? false;
|
||||||
<label>
|
<label>
|
||||||
<input
|
<input
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
name="artwork-year-is-circa"
|
name="artwork-completed-year-is-circa"
|
||||||
<? if($artwork->CompletedYearIsCirca){ ?>checked="checked"<? } ?>
|
<? if($artwork->CompletedYearIsCirca){ ?>checked="checked"<? } ?>
|
||||||
/> Year is circa
|
/> Year is circa
|
||||||
</label>
|
</label>
|
||||||
|
@ -172,28 +172,28 @@ $isEditForm = $isEditForm ?? false;
|
||||||
</label>
|
</label>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
<? if($artwork->CanStatusBeChangedBy($GLOBALS['User'] ?? null) || $artwork->CanEbookUrlBeChangedBy($GLOBALS['User'] ?? null)){ ?>
|
<? if($artwork->CanStatusBeChangedBy($GLOBALS['User'] ?? null) || $artwork->CanEbookUrlBeChangedBy($GLOBALS['User'] ?? null)){ ?>
|
||||||
<fieldset>
|
<fieldset>
|
||||||
<legend>Editor options</legend>
|
<legend>Editor options</legend>
|
||||||
<? if($artwork->CanStatusBeChangedBy($GLOBALS['User'] ?? null)){ ?>
|
<? if($artwork->CanStatusBeChangedBy($GLOBALS['User'] ?? null)){ ?>
|
||||||
<label>
|
<label>
|
||||||
<span>Artwork approval status</span>
|
<span>Artwork approval status</span>
|
||||||
<span>
|
<span>
|
||||||
<select name="artwork-status">
|
<select name="artwork-status">
|
||||||
<option value="<?= Enums\ArtworkStatusType::Unverified->value ?>"<? if($artwork->Status == Enums\ArtworkStatusType::Unverified){ ?> selected="selected"<? } ?>>Unverified</option>
|
<option value="<?= Enums\ArtworkStatusType::Unverified->value ?>"<? if($artwork->Status == Enums\ArtworkStatusType::Unverified){ ?> selected="selected"<? } ?>>Unverified</option>
|
||||||
<option value="<?= Enums\ArtworkStatusType::Declined->value ?>"<? if($artwork->Status == Enums\ArtworkStatusType::Declined){ ?> selected="selected"<? } ?>>Declined</option>
|
<option value="<?= Enums\ArtworkStatusType::Declined->value ?>"<? if($artwork->Status == Enums\ArtworkStatusType::Declined){ ?> selected="selected"<? } ?>>Declined</option>
|
||||||
<option value="<?= Enums\ArtworkStatusType::Approved->value ?>"<? if($artwork->Status == Enums\ArtworkStatusType::Approved){ ?> selected="selected"<? } ?>>Approved</option>
|
<option value="<?= Enums\ArtworkStatusType::Approved->value ?>"<? if($artwork->Status == Enums\ArtworkStatusType::Approved){ ?> selected="selected"<? } ?>>Approved</option>
|
||||||
</select>
|
</select>
|
||||||
</span>
|
</span>
|
||||||
</label>
|
</label>
|
||||||
<? } ?>
|
<? } ?>
|
||||||
<? if($artwork->CanEbookUrlBeChangedBy($GLOBALS['User'] ?? null)){ ?>
|
<? if($artwork->CanEbookUrlBeChangedBy($GLOBALS['User'] ?? null)){ ?>
|
||||||
<label>
|
<label>
|
||||||
<span>In use by</span>
|
<span>In use by</span>
|
||||||
<span>The full S.E. ebook URL. If not in use, leave this blank.</span>
|
<span>The full S.E. ebook URL. If not in use, leave this blank.</span>
|
||||||
<input type="url" name="artwork-ebook-url" placeholder="https://standardebooks.org/ebooks/" pattern="^https:\/\/standardebooks\.org\/ebooks/[^\/]+(\/[^\/]+)+$" value="<?= Formatter::EscapeHtml($artwork->EbookUrl) ?>"/>
|
<input type="url" name="artwork-ebook-url" placeholder="https://standardebooks.org/ebooks/" pattern="^https:\/\/standardebooks\.org\/ebooks/[^\/]+(\/[^\/]+)+$" value="<?= Formatter::EscapeHtml($artwork->EbookUrl) ?>"/>
|
||||||
</label>
|
</label>
|
||||||
<? } ?>
|
<? } ?>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
<? } ?>
|
<? } ?>
|
||||||
<div class="footer">
|
<div class="footer">
|
||||||
<button><? if($isEditForm){ ?>Save changes<? }else{ ?>Submit<? } ?></button>
|
<button><? if($isEditForm){ ?>Save changes<? }else{ ?>Submit<? } ?></button>
|
||||||
|
|
|
@ -39,7 +39,7 @@ print("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n");
|
||||||
<entry>
|
<entry>
|
||||||
<title><?= Formatter::EscapeXml($entry->Title) ?></title>
|
<title><?= Formatter::EscapeXml($entry->Title) ?></title>
|
||||||
<link href="<?= SITE_URL . Formatter::EscapeXml($entry->Url) ?>" rel="<?= Formatter::EscapeXml($entry->Rel) ?>" type="application/atom+xml;profile=opds-catalog;kind=<?= $entry->Type ?>; charset=utf-8"/>
|
<link href="<?= SITE_URL . Formatter::EscapeXml($entry->Url) ?>" rel="<?= Formatter::EscapeXml($entry->Rel) ?>" type="application/atom+xml;profile=opds-catalog;kind=<?= $entry->Type ?>; charset=utf-8"/>
|
||||||
<updated><? if($entry->Updated !== null){ ?><?= $entry->Updated->format(Enums\DateTimeFormat::Iso->value) ?><? } ?></updated>
|
<updated><?= $entry->Updated->format(Enums\DateTimeFormat::Iso->value) ?></updated>
|
||||||
<id><?= Formatter::EscapeXml($entry->Id) ?></id>
|
<id><?= Formatter::EscapeXml($entry->Id) ?></id>
|
||||||
<content type="text"><?= Formatter::EscapeXml($entry->Description) ?></content>
|
<content type="text"><?= Formatter::EscapeXml($entry->Description) ?></content>
|
||||||
</entry>
|
</entry>
|
||||||
|
|
|
@ -19,7 +19,9 @@ try{
|
||||||
throw new Exceptions\InvalidPermissionsException();
|
throw new Exceptions\InvalidPermissionsException();
|
||||||
}
|
}
|
||||||
|
|
||||||
$artwork = Artwork::FromHttpPost();
|
$artwork = new Artwork();
|
||||||
|
$artwork->FillFromHttpPost();
|
||||||
|
|
||||||
$artwork->SubmitterUserId = $GLOBALS['User']->UserId ?? null;
|
$artwork->SubmitterUserId = $GLOBALS['User']->UserId ?? null;
|
||||||
|
|
||||||
// Only approved reviewers can set the status to anything but unverified when uploading.
|
// Only approved reviewers can set the status to anything but unverified when uploading.
|
||||||
|
@ -28,7 +30,7 @@ try{
|
||||||
throw new Exceptions\InvalidPermissionsException();
|
throw new Exceptions\InvalidPermissionsException();
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the artwork is approved, set the reviewer
|
// If the artwork is approved, set the reviewer.
|
||||||
if($artwork->Status !== Enums\ArtworkStatusType::Unverified){
|
if($artwork->Status !== Enums\ArtworkStatusType::Unverified){
|
||||||
$artwork->ReviewerUserId = $GLOBALS['User']->UserId;
|
$artwork->ReviewerUserId = $GLOBALS['User']->UserId;
|
||||||
}
|
}
|
||||||
|
@ -56,7 +58,7 @@ try{
|
||||||
$artwork->ArtworkId = $originalArtwork->ArtworkId;
|
$artwork->ArtworkId = $originalArtwork->ArtworkId;
|
||||||
$artwork->Created = $originalArtwork->Created;
|
$artwork->Created = $originalArtwork->Created;
|
||||||
$artwork->SubmitterUserId = $originalArtwork->SubmitterUserId;
|
$artwork->SubmitterUserId = $originalArtwork->SubmitterUserId;
|
||||||
$artwork->Status = $originalArtwork->Status; // Overwrite any value got from POST because we need permission to change the status
|
$artwork->Status = $originalArtwork->Status; // Overwrite any value got from POST because we need permission to change the status.
|
||||||
|
|
||||||
$newStatus = Enums\ArtworkStatusType::tryFrom(HttpInput::Str(POST, 'artwork-status') ?? '');
|
$newStatus = Enums\ArtworkStatusType::tryFrom(HttpInput::Str(POST, 'artwork-status') ?? '');
|
||||||
if($newStatus !== null){
|
if($newStatus !== null){
|
||||||
|
@ -96,9 +98,12 @@ try{
|
||||||
}
|
}
|
||||||
|
|
||||||
$artwork->ReviewerUserId = $GLOBALS['User']->UserId;
|
$artwork->ReviewerUserId = $GLOBALS['User']->UserId;
|
||||||
}
|
|
||||||
|
|
||||||
$artwork->Status = $newStatus;
|
$artwork->Status = $newStatus;
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
unset($artwork->Status);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(isset($_POST['artwork-ebook-url'])){
|
if(isset($_POST['artwork-ebook-url'])){
|
||||||
|
|
|
@ -162,13 +162,7 @@ catch(Exceptions\EbookNotFoundException){
|
||||||
|
|
||||||
<?= Template::DonationAlert() ?>
|
<?= Template::DonationAlert() ?>
|
||||||
|
|
||||||
<? if($ebook->LongDescription === null){ ?>
|
<?= $ebook->LongDescription ?>
|
||||||
<p>
|
|
||||||
<i>There’s no description for this ebook yet.</i>
|
|
||||||
</p>
|
|
||||||
<? }else{ ?>
|
|
||||||
<?= $ebook->LongDescription ?>
|
|
||||||
<? } ?>
|
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<? if($ebook->HasDownloads){ ?>
|
<? if($ebook->HasDownloads){ ?>
|
||||||
|
|
|
@ -30,7 +30,6 @@ try{
|
||||||
exit();
|
exit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
$subscription->User = new User();
|
$subscription->User = new User();
|
||||||
$subscription->User->Email = HttpInput::Str(POST, 'email');
|
$subscription->User->Email = HttpInput::Str(POST, 'email');
|
||||||
$subscription->IsSubscribedToNewsletter = HttpInput::Bool(POST, 'issubscribedtonewsletter') ?? false;
|
$subscription->IsSubscribedToNewsletter = HttpInput::Bool(POST, 'issubscribedtonewsletter') ?? false;
|
||||||
|
|
|
@ -17,7 +17,9 @@ try{
|
||||||
/** @var PollVote $vote */
|
/** @var PollVote $vote */
|
||||||
$vote = $_SESSION['vote'];
|
$vote = $_SESSION['vote'];
|
||||||
}
|
}
|
||||||
else{
|
|
||||||
|
if(!isset($vote->UserId)){
|
||||||
|
$vote->UserId = $GLOBALS['User']->UserId;
|
||||||
$vote->User = $GLOBALS['User'];
|
$vote->User = $GLOBALS['User'];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -59,23 +61,23 @@ catch(Exceptions\PollVoteExistsException $ex){
|
||||||
<h1>Vote in the <?= Formatter::EscapeHtml($poll->Name) ?> Poll</h1>
|
<h1>Vote in the <?= Formatter::EscapeHtml($poll->Name) ?> Poll</h1>
|
||||||
<?= Template::Error(['exception' => $exception]) ?>
|
<?= Template::Error(['exception' => $exception]) ?>
|
||||||
<form method="post" action="<?= Formatter::EscapeHtml($poll->Url) ?>/votes">
|
<form method="post" action="<?= Formatter::EscapeHtml($poll->Url) ?>/votes">
|
||||||
<input type="hidden" name="email" value="<? if($vote->User !== null){ ?><?= Formatter::EscapeHtml($vote->User->Email) ?><? } ?>" maxlength="80" required="required" />
|
<input type="hidden" name="email" value="<?= Formatter::EscapeHtml($vote->User->Email) ?>" maxlength="80" required="required" />
|
||||||
<fieldset>
|
<fieldset>
|
||||||
<p>Select one of these options.</p>
|
<p>Select one of these options.</p>
|
||||||
<ul>
|
<ul>
|
||||||
<? foreach($poll->PollItems as $pollItem){ ?>
|
<? foreach($poll->PollItems as $pollItem){ ?>
|
||||||
<li>
|
<li>
|
||||||
<label class="checkbox">
|
<label class="checkbox">
|
||||||
<input type="radio" value="<?= $pollItem->PollItemId ?>" name="pollitemid" required="required"<? if($vote->PollItemId == $pollItem->PollItemId){ ?> checked="checked"<? } ?>/>
|
<input type="radio" value="<?= $pollItem->PollItemId ?>" name="poll-vote-poll-item-id" required="required"<? if(isset($vote->PollItemId) && $vote->PollItemId == $pollItem->PollItemId){ ?> checked="checked"<? } ?>/>
|
||||||
<span>
|
<span>
|
||||||
<b><?= $pollItem->Name ?></b>
|
<b><?= $pollItem->Name ?></b>
|
||||||
<? if($pollItem->Description !== null){ ?>
|
<? if($pollItem->Description !== null){ ?>
|
||||||
<span><?= Formatter::EscapeHtml($pollItem->Description) ?></span>
|
<span><?= Formatter::EscapeHtml($pollItem->Description) ?></span>
|
||||||
<? } ?>
|
<? } ?>
|
||||||
</span>
|
</span>
|
||||||
</label>
|
</label>
|
||||||
</li>
|
</li>
|
||||||
<? } ?>
|
<? } ?>
|
||||||
</ul>
|
</ul>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
<button>Vote</button>
|
<button>Vote</button>
|
||||||
|
|
|
@ -10,7 +10,7 @@ try{
|
||||||
|
|
||||||
$vote = new PollVote();
|
$vote = new PollVote();
|
||||||
|
|
||||||
$vote->PollItemId = HttpInput::Int(POST, 'pollitemid');
|
$vote->FillFromHttpPost();
|
||||||
|
|
||||||
$vote->Create(HttpInput::Str(POST, 'email'));
|
$vote->Create(HttpInput::Str(POST, 'email'));
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue