diff --git a/README.md b/README.md index d8e7d5f6..bf963add 100644 --- a/README.md +++ b/README.md @@ -138,16 +138,14 @@ Before submitting design contributions, please discuss them with the Standard Eb ### Artwork database -- Allow submitter or admins to edit unapproved artwork submissions. Approved/in use submissions should not be editable by anyone. - - Tags should be searched as whole words. For example a search for `male` should not return items tagged as `female`. - Include in-use ebook slug as a search parameter when searching for artwork by keyword. -- Remove `in_use` status for an artwork; instead, an artwork with an `EbookWwwFilesystemPath` that is not `null` should be considered to be "in use" regardless of its `Status`. - - Artwork searching/filtering should be done in pure SQL, no after-sql filtering in PHP. +- Allow listing artwork by artist by visiting `/artworks/`, and link instances of artist name to that URL. + ## PHP code style - Indent with tabs. diff --git a/lib/Artwork.php b/lib/Artwork.php index ddac91d0..91c24e3b 100644 --- a/lib/Artwork.php +++ b/lib/Artwork.php @@ -58,9 +58,6 @@ class Artwork extends PropertiesBase{ protected ?string $_ImageUrl = null; protected ?string $_ThumbUrl = null; protected ?string $_Thumb2xUrl = null; - protected ?string $_ImageFsPath = null; - protected ?string $_ThumbFsPath = null; - protected ?string $_Thumb2xFsPath = null; protected ?string $_Dimensions = null; protected ?Ebook $_Ebook = null; protected ?Museum $_Museum = null; @@ -252,15 +249,15 @@ class Artwork extends PropertiesBase{ } protected function GetImageFsPath(): string{ - return WEB_ROOT . rtrim($this->ImageUrl, '?ts=0123456789'); + return WEB_ROOT . preg_replace('/\?[^\?]*$/ius', '', $this->ImageUrl); } protected function GetThumbFsPath(): string{ - return WEB_ROOT . rtrim($this->ThumbUrl, '?ts=0123456789'); + return WEB_ROOT . preg_replace('/\?[^\?]*$/ius', '', $this->ThumbUrl); } protected function GetThumb2xFsPath(): string{ - return WEB_ROOT . rtrim($this->Thumb2xUrl, '?ts=0123456789'); + return WEB_ROOT . preg_replace('/\?[^\?]*$/ius', '', $this->Thumb2xUrl); } protected function GetDimensions(): string{ @@ -289,6 +286,55 @@ class Artwork extends PropertiesBase{ // ******* // METHODS // ******* + public function CanBeEditedBy(?User $user): bool{ + if($user === null){ + return false; + } + + if($user->Benefits->CanReviewOwnArtwork){ + // Admins can edit all artwork. + return true; + } + + if(($user->Benefits->CanReviewArtwork || $user->UserId == $this->SubmitterUserId) && ($this->Status == ArtworkStatus::Unverified || $this->Status == ArtworkStatus::Declined)){ + // Editors can edit an artwork, and submitters can edit their own artwork, if it's not yet approved. + return true; + } + + return false; + } + + public function CanStatusBeChangedBy(?User $user): bool{ + if($user === null){ + return false; + } + + if($user->Benefits->CanReviewOwnArtwork){ + // Admins can change the status of all artwork. + return true; + } + + if($user->Benefits->CanReviewArtwork && $user->UserId != $this->SubmitterUserId && ($this->Status == ArtworkStatus::Unverified || $this->Status == ArtworkStatus::Declined)){ + // Editors can change the status of artwork they did not submit themselves, and that is not yet approved. + return true; + } + + return false; + } + + public function CanEbookWwwFilesysemPathBeChangedBy(?User $user): bool{ + if($user === null){ + return false; + } + + if($user->Benefits->CanReviewArtwork || $user->Benefits->CanReviewOwnArtwork){ + // Admins and editors can change the file system path of all artwork. + return true; + } + + return false; + } + /** * @param array $uploadedFile * @throws \Exceptions\ValidationException @@ -341,15 +387,8 @@ class Artwork extends PropertiesBase{ $error->Add(new Exceptions\InvalidArtworkException('Invalid status.')); } - if($this->Status == ArtworkStatus::InUse && $this->EbookWwwFilesystemPath === null){ - $error->Add(new Exceptions\MissingEbookException()); - } - if(count($this->Tags) == 0){ - // In-use artwork doesn't have user-provided tags. - if($this->Status != ArtworkStatus::InUse){ - $error->Add(new Exceptions\TagsRequiredException()); - } + $error->Add(new Exceptions\TagsRequiredException()); } if(count($this->Tags) > ARTWORK_MAX_TAGS){ @@ -686,6 +725,13 @@ class Artwork extends PropertiesBase{ if(!empty($uploadedFile) && $uploadedFile['error'] == UPLOAD_ERR_OK){ $this->MimeType = ImageMimeType::FromFile($uploadedFile['tmp_name'] ?? null); + + // 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 = new DateTime('now', new DateTimeZone('UTC')); + $this->_ImageUrl = null; + $this->_ThumbUrl = null; + $this->_Thumb2xUrl = null; } $this->Validate($uploadedFile); @@ -696,8 +742,15 @@ class Artwork extends PropertiesBase{ } $this->Tags = $tags; + $newDeathYear = $this->Artist->DeathYear; $this->Artist = Artist::GetOrCreate($this->Artist); + // Save the artist death year in case we changed it + if($newDeathYear != $this->Artist->DeathYear){ + Db::Query('UPDATE Artists set DeathYear = ? where ArtistId = ?', [$newDeathYear , $this->Artist->ArtistId]); + } + + // Save the artwork Db::Query(' UPDATE Artworks set @@ -706,7 +759,7 @@ class Artwork extends PropertiesBase{ UrlName = ?, CompletedYear = ?, CompletedYearIsCirca = ?, - Created = ?, + Updated = ?, Status = ?, SubmitterUserId = ?, ReviewerUserId = ?, @@ -723,7 +776,7 @@ class Artwork extends PropertiesBase{ where ArtworkId = ? ', [$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->Updated, $this->Status, $this->SubmitterUserId, $this->ReviewerUserId, $this->MuseumUrl, $this->PublicationYear, $this->PublicationYearPageUrl, $this->CopyrightPageUrl, $this->ArtworkPageUrl, $this->IsPublishedInUs, $this->EbookWwwFilesystemPath, $this->MimeType, $this->Exception, $this->Notes, $this->ArtworkId] ); @@ -731,16 +784,16 @@ class Artwork extends PropertiesBase{ Artist::DeleteUnreferencedArtists(); Db::Query(' - DELETE FROM ArtworkTags - WHERE + DELETE from ArtworkTags + where ArtworkId = ? ', [$this->ArtworkId] ); foreach($this->Tags as $tag){ Db::Query(' - INSERT INTO ArtworkTags (ArtworkId, TagId) - VALUES (?, + INSERT into ArtworkTags (ArtworkId, TagId) + values (?, ?) ', [$this->ArtworkId, $tag->TagId]); } @@ -840,4 +893,29 @@ class Artwork extends PropertiesBase{ return $result[0]; } + + public static function FromHttpPost(): Artwork{ + $artwork = new Artwork(); + $artwork->Artist = new Artist(); + + $artwork->Artist->Name = HttpInput::Str(POST, 'artist-name', false); + $artwork->Artist->DeathYear = HttpInput::Int(POST, 'artist-year-of-death'); + + $artwork->Name = HttpInput::Str(POST, 'artwork-name', false); + $artwork->CompletedYear = HttpInput::Int(POST, 'artwork-year'); + $artwork->CompletedYearIsCirca = HttpInput::Bool(POST, 'artwork-year-is-circa', false) ?? false; + $artwork->Tags = HttpInput::Str(POST, 'artwork-tags', false) ?? []; + $artwork->Status = HttpInput::Str(POST, 'artwork-status', false) ?? ArtworkStatus::Unverified; + $artwork->EbookWwwFilesystemPath = HttpInput::Str(POST, 'artwork-ebook-www-filesystem-path', false); + $artwork->IsPublishedInUs = HttpInput::Bool(POST, 'artwork-is-published-in-us', false); + $artwork->PublicationYear = HttpInput::Int(POST, 'artwork-publication-year'); + $artwork->PublicationYearPageUrl = HttpInput::Str(POST, 'artwork-publication-year-page-url', false); + $artwork->CopyrightPageUrl = HttpInput::Str(POST, 'artwork-copyright-page-url', false); + $artwork->ArtworkPageUrl = HttpInput::Str(POST, 'artwork-artwork-page-url', false); + $artwork->MuseumUrl = HttpInput::Str(POST, 'artwork-museum-url', false); + $artwork->Exception = HttpInput::Str(POST, 'artwork-exception', false); + $artwork->Notes = HttpInput::Str(POST, 'artwork-notes', false); + + return $artwork; + } } diff --git a/lib/ArtworkStatus.php b/lib/ArtworkStatus.php index 2bcfe8cc..6d9b058c 100644 --- a/lib/ArtworkStatus.php +++ b/lib/ArtworkStatus.php @@ -3,5 +3,4 @@ enum ArtworkStatus: string{ case Unverified = 'unverified'; case Declined = 'declined'; case Approved = 'approved'; - case InUse = 'in_use'; } diff --git a/lib/Library.php b/lib/Library.php index e0435837..681e7beb 100644 --- a/lib/Library.php +++ b/lib/Library.php @@ -162,12 +162,13 @@ class Library{ * @return array */ public static function FilterArtwork(string $query = null, string $status = null, string $sort = null, int $submitterUserId = null): array{ - // Possible special statuses: + // $status is either the string value of an ArtworkStatus enum, or one of these special statuses: // null: same as "all" // "all": Show all approved and in use artwork // "all-admin": Show all artwork regardless of status // "all-submitter": Show all approved and in use artwork, plus unverified artwork from the submitter // "unverified-submitter": Show unverified artwork from the submitter + // "in-use": Show only in-use artwork $artworks = []; @@ -175,19 +176,26 @@ class Library{ $artworks = Db::Query(' SELECT * from Artworks - where Status in (?, ?)', [ArtworkStatus::Approved->value, ArtworkStatus::InUse->value], 'Artwork'); + where Status in (?)', [ArtworkStatus::Approved->value], 'Artwork'); } elseif($status == 'all-admin'){ $artworks = Db::Query(' SELECT * from Artworks', [], 'Artwork'); } + elseif($status == 'in-use'){ + $artworks = Db::Query(' + SELECT * + from Artworks + where Status = ? and EbookWwwFilesystemPath is not null + ', [ArtworkStatus::Approved->value], 'Artwork'); + } elseif($status == 'all-submitter' && $submitterUserId !== null){ $artworks = Db::Query(' SELECT * from Artworks - where Status in (?, ?) - or (Status = ? and SubmitterUserId = ?)', [ArtworkStatus::Approved->value, ArtworkStatus::InUse->value, ArtworkStatus::Unverified->value, $submitterUserId], 'Artwork'); + where Status = ? + or (Status = ? and SubmitterUserId = ?)', [ArtworkStatus::Approved->value, ArtworkStatus::Unverified->value, $submitterUserId], 'Artwork'); } elseif($status == 'unverified-submitter' && $submitterUserId !== null){ $artworks = Db::Query(' @@ -195,6 +203,12 @@ class Library{ from Artworks where Status = ? and SubmitterUserId = ?', [ArtworkStatus::Unverified->value, $submitterUserId], 'Artwork'); } + elseif($status == ArtworkStatus::Approved->value){ + $artworks = Db::Query(' + SELECT * + from Artworks + where Status = ? and EbookWwwFilesystemPath is null', [ArtworkStatus::Approved->value], 'Artwork'); + } else{ $artworks = Db::Query(' SELECT * diff --git a/templates/ArtworkCreateEditFields.php b/templates/ArtworkForm.php similarity index 84% rename from templates/ArtworkCreateEditFields.php rename to templates/ArtworkForm.php index 9c0de8c0..fa41e691 100644 --- a/templates/ArtworkCreateEditFields.php +++ b/templates/ArtworkForm.php @@ -2,7 +2,13 @@ use Safe\DateTime; $artwork = $artwork ?? null; -$imageRequired = $imageRequired ?? true; + +if($artwork === null){ + $artwork = new Artwork(); + $artwork->Artist = new Artist(); +} + +$isEditForm = $isEditForm ?? false; $isAdminView = $isAdminView ?? false; $now = new DateTime('now', new DateTimeZone('America/Juneau')); // Latest continental US time zone @@ -16,7 +22,7 @@ $now = new DateTime('now', new DateTimeZone('America/Juneau')); // Latest contin AlternateSpellings as $alternateSpelling){ ?> - + @@ -38,7 +44,7 @@ $now = new DateTime('now', new DateTimeZone('America/Juneau')); // Latest contin name="artist-year-of-death" inputmode="numeric" pattern="[0-9]+" - value="Artist->DeathYear ?>" + value="Artist->DeathYear) ?>" /> @@ -57,7 +63,7 @@ $now = new DateTime('now', new DateTimeZone('America/Juneau')); // Latest contin name="artwork-year" inputmode="numeric" pattern="[0-9]+" - value="CompletedYear ?>" + value="CompletedYear) ?>" /> @@ -123,7 +129,7 @@ $now = new DateTime('now', new DateTimeZone('America/Juneau')); // Latest contin name="artwork-publication-year" inputmode="numeric" pattern="[0-9]+" - value="PublicationYear ?>" + value="PublicationYear) ?>" /> - +CanStatusBeChangedBy($GLOBALS['User'] ?? null) || $artwork->CanEbookWwwFilesysemPathBeChangedBy($GLOBALS['User'] ?? null)){ ?>
Editor options - Benefits->CanReviewOwnArtwork){ ?> + CanStatusBeChangedBy($GLOBALS['User'] ?? null)){ ?> - + CanEbookWwwFilesysemPathBeChangedBy($GLOBALS['User'] ?? null)){ ?> + +