Artwork db code tweaks, also remove 'in use' as a status

This commit is contained in:
Alex Cabal 2024-01-17 16:04:11 -06:00
parent 73bcae0c84
commit 5ef6d3aef8
13 changed files with 249 additions and 174 deletions

View file

@ -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/<artist-name>`, and link instances of artist name to that URL.
## PHP code style
- Indent with tabs.

View file

@ -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<mixed> $uploadedFile
* @throws \Exceptions\ValidationException
@ -341,16 +387,9 @@ 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());
}
}
if(count($this->Tags) > ARTWORK_MAX_TAGS){
$error->Add(new Exceptions\TooManyTagsException());
@ -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;
}
}

View file

@ -3,5 +3,4 @@ enum ArtworkStatus: string{
case Unverified = 'unverified';
case Declined = 'declined';
case Approved = 'approved';
case InUse = 'in_use';
}

View file

@ -162,12 +162,13 @@ class Library{
* @return array<Artwork>
*/
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 *

View file

@ -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
<? foreach(Library::GetAllArtists() as $artist){ ?>
<option value="<?= Formatter::ToPlainText($artist->Name) ?>"><?= Formatter::ToPlainText($artist->Name) ?>, d. <? if($artist->DeathYear !== null){ ?><?= $artist->DeathYear ?><? }else{ ?>unknown<? } ?></option>
<? foreach($artist->AlternateSpellings as $alternateSpelling){ ?>
<option value="<?= Formatter::ToPlainText($alternateSpelling) ?>"><?= Formatter::ToPlainText($alternateSpelling) ?>, d. <? if($artist->DeathYear !== null){ ?><?= $artist->DeathYear ?><? }else{ ?>unknown<? } ?></option>
<option value="<?= Formatter::ToPlainText($alternateSpelling) ?>"><?= Formatter::ToPlainText($alternateSpelling) ?>, d. <? if($artist->DeathYear !== null){ ?><?= Formatter::ToPlainText($artist->DeathYear) ?><? }else{ ?>unknown<? } ?></option>
<? } ?>
<? } ?>
</datalist>
@ -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="<?= $artwork->Artist->DeathYear ?>"
value="<?= Formatter::ToPlainText($artwork->Artist->DeathYear) ?>"
/>
</label>
</fieldset>
@ -57,7 +63,7 @@ $now = new DateTime('now', new DateTimeZone('America/Juneau')); // Latest contin
name="artwork-year"
inputmode="numeric"
pattern="[0-9]+"
value="<?= $artwork->CompletedYear ?>"
value="<?= Formatter::ToPlainText($artwork->CompletedYear) ?>"
/>
</label>
<label>
@ -81,11 +87,11 @@ $now = new DateTime('now', new DateTimeZone('America/Juneau')); // Latest contin
</label>
<label>
<span>High-resolution image</span>
<span>jpg, bmp, png, and tiff are accepted; <?= number_format(ARTWORK_IMAGE_MINIMUM_WIDTH) ?> × <?= number_format(ARTWORK_IMAGE_MINIMUM_HEIGHT) ?> minimum; 32MB max.</span>
<span>jpg, bmp, png, and tiff are accepted; <?= number_format(ARTWORK_IMAGE_MINIMUM_WIDTH) ?> × <?= number_format(ARTWORK_IMAGE_MINIMUM_HEIGHT) ?> minimum; 32MB max.<? if($isEditForm){ ?> Leave this blank to not change the image.<? } ?></span>
<input
type="file"
name="artwork-image"
<? if($imageRequired){ ?>required="required"<? } ?>
<? if(!$isEditForm){ ?>required="required"<? } ?>
accept="<?= implode(',', ImageMimeType::Values()) ?>"
/>
</label>
@ -123,7 +129,7 @@ $now = new DateTime('now', new DateTimeZone('America/Juneau')); // Latest contin
name="artwork-publication-year"
inputmode="numeric"
pattern="[0-9]+"
value="<?= $artwork->PublicationYear ?>"
value="<?= Formatter::ToPlainText($artwork->PublicationYear) ?>"
/>
</label>
<label>
@ -174,10 +180,10 @@ $now = new DateTime('now', new DateTimeZone('America/Juneau')); // Latest contin
<textarea maxlength="1024" name="artwork-notes"><?= Formatter::ToPlainText($artwork->Notes) ?></textarea>
</label>
</fieldset>
<? if($isAdminView){ ?>
<? if($artwork->CanStatusBeChangedBy($GLOBALS['User'] ?? null) || $artwork->CanEbookWwwFilesysemPathBeChangedBy($GLOBALS['User'] ?? null)){ ?>
<fieldset>
<legend>Editor options</legend>
<? if($GLOBALS['User']->Benefits->CanReviewOwnArtwork){ ?>
<? if($artwork->CanStatusBeChangedBy($GLOBALS['User'] ?? null)){ ?>
<label class="select">
<span>Artwork approval status</span>
<span>
@ -185,16 +191,17 @@ $now = new DateTime('now', new DateTimeZone('America/Juneau')); // Latest contin
<option value="<?= ArtworkStatus::Unverified->value ?>"<? if($artwork->Status == ArtworkStatus::Unverified){ ?> selected="selected"<? } ?>>Unverified</option>
<option value="<?= ArtworkStatus::Declined->value ?>"<? if($artwork->Status == ArtworkStatus::Declined){ ?> selected="selected"<? } ?>>Declined</option>
<option value="<?= ArtworkStatus::Approved->value ?>"<? if($artwork->Status == ArtworkStatus::Approved){ ?> selected="selected"<? } ?>>Approved</option>
<option value="<?= ArtworkStatus::InUse->value ?>"<? if($artwork->Status == ArtworkStatus::InUse){ ?> selected="selected"<? } ?>>In use</option>
</select>
</span>
</label>
<? } ?>
<? if($artwork->CanEbookWwwFilesysemPathBeChangedBy($GLOBALS['User'] ?? null)){ ?>
<label>
<span>In use by</span>
<span>Ebook file system slug, like <code>c-s-lewis_poetry</code>. If not in use, leave this blank.</span>
<input type="text" name="artwork-ebook-www-filesystem-path" value="<?= Formatter::ToPlainText($artwork->EbookWwwFilesystemPath) ?>"/>
</label>
<? } ?>
</fieldset>
<? } ?>
<div class="footer">

View file

@ -3,7 +3,7 @@ $artworks = $artworks ?? [];
?>
<ol class="artwork-list">
<? foreach($artworks as $artwork){ ?>
<li <? if($artwork->Status == ArtworkStatus::InUse){ ?> class="in-use"<? } ?>>
<li <? if($artwork->EbookWwwFilesystemPath !== null){ ?> class="in-use"<? } ?>>
<a href="<?= $artwork->Url ?>">
<picture>
<source srcset="<?= $artwork->Thumb2xUrl ?> 2x, <?= $artwork->ThumbUrl ?> 1x" type="image/jpg"/>

View file

@ -5,5 +5,5 @@ $artwork = $artwork ?? null;
<? if($artwork->Status == ArtworkStatus::Approved){ ?>Approved<? } ?>
<? if($artwork->Status == ArtworkStatus::Declined){ ?>Declined<? } ?>
<? if($artwork->Status == ArtworkStatus::Unverified){ ?>Unverified<? } ?>
<? if($artwork->Status == ArtworkStatus::InUse){ ?>In use<? if($artwork->EbookWwwFilesystemPath !== null){ ?> for <? if($artwork->Ebook !== null && $artwork->Ebook->Url !== null){ ?><i><a href="<?= $artwork->Ebook->Url ?>"><?= Formatter::ToPlainText($artwork->Ebook->Title) ?></a></i><? }else{ ?><code><?= Formatter::ToPlainText($artwork->EbookWwwFilesystemPath) ?></code> (Unreleased)<? } ?><? } ?><? } ?>
<? if($artwork->EbookWwwFilesystemPath !== null){ ?> — in use<? if($artwork->EbookWwwFilesystemPath !== null){ ?> by <? if($artwork->Ebook !== null && $artwork->Ebook->Url !== null){ ?><i><a href="<?= $artwork->Ebook->Url ?>"><?= Formatter::ToPlainText($artwork->Ebook->Title) ?></a></i><? }else{ ?><code><?= Formatter::ToPlainText($artwork->EbookWwwFilesystemPath) ?></code> (unreleased)<? } ?><? } ?><? } ?>
<? } ?>

View file

@ -4,7 +4,7 @@ use function Safe\session_unset;
session_start();
$exception = $_SESSION['exception'] ?? null;
/** @var Artwork $artwork */
/** @var ?Artwork $artwork */
$artwork = $_SESSION['artwork'] ?? null;
try{
@ -13,11 +13,10 @@ try{
}
if($artwork === null){
$artwork = Artwork::GetByUrl(HttpInput::Str(GET, 'artist-url-name') ?? '', HttpInput::Str(GET, 'artwork-url-name') ?? '');
$artwork = Artwork::GetByUrl(HttpInput::Str(GET, 'artist-url-name', false) ?? '', HttpInput::Str(GET, 'artwork-url-name', false) ?? '');
}
$isEditingAllowed = ($artwork->Status == ArtworkStatus::Unverified) && ($GLOBALS['User']->Benefits->CanReviewArtwork || ($artwork->SubmitterUserId == $GLOBALS['User']->UserId));
if(!$isEditingAllowed){
if(!$artwork->CanBeEditedBy($GLOBALS['User'])){
throw new Exceptions\InvalidPermissionsException();
}
@ -36,13 +35,13 @@ catch(Exceptions\LoginRequiredException){
Template::RedirectToLogin();
}
catch(Exceptions\InvalidPermissionsException){
Template::Emit403(); // No permissions to submit artwork
Template::Emit403(); // No permissions to edit artwork
}
?>
<?= Template::Header(
[
'title' => 'Edit Artwork',
'title' => 'Edit ' . $artwork->Name . ', by ' . $artwork->Artist->Name,
'artwork' => true,
'highlight' => '',
'description' => 'Edit public domain artwork to the database for use as cover art.'
@ -54,23 +53,14 @@ catch(Exceptions\InvalidPermissionsException){
<?= Template::Error(['exception' => $exception]) ?>
<a href="<?= $artwork->ImageUrl ?>">
<picture>
<source srcset="<?= $artwork->Thumb2xUrl ?> 2x, <?= $artwork->ThumbUrl ?> 1x" type="image/jpg"/>
<img src="<?= $artwork->ThumbUrl ?>" alt="" property="schema:image"/>
</picture>
</a>
<form class="create-update-artwork" method="post" action="<?= $artwork->Url ?>" enctype="multipart/form-data">
<input type="hidden" name="_method" value="PUT" />
<?= Template::ArtworkCreateEditFields(
[
'artwork' => $artwork,
'imageRequired' => false,
'isAdminView' => $isAdminView
]
) ?>
<?= Template::ArtworkForm(['artwork' => $artwork, 'isEditForm' => true,'isAdminView' => $isAdminView]) ?>
</form>
</section>
</main>

View file

@ -9,12 +9,14 @@ $exception = $_SESSION['exception'] ?? null;
try{
$artwork = Artwork::GetByUrl(HttpInput::Str(GET, 'artist-url-name') ?? '', HttpInput::Str(GET, 'artwork-url-name') ?? '');
$isAdminView = $GLOBALS['User']->Benefits->CanReviewArtwork ?? false;
$userId = $GLOBALS['User']->UserId ?? null;
$isEditingAllowed = ($artwork->Status == ArtworkStatus::Unverified) && ($isAdminView || ($userId !== null && $userId == $artwork->SubmitterUserId));
// If the artwork is not approved, and we're not an admin or the submitter when they can edit, don't show it.
if($artwork->Status != ArtworkStatus::Approved && $artwork->Status != ArtworkStatus::InUse && !$isAdminView && !$isEditingAllowed){
throw new Exceptions\ArtworkNotFoundException();
if(
($GLOBALS['User'] === null && $artwork->Status != ArtworkStatus::Approved)
||
($GLOBALS['User'] !== null && $artwork->SubmitterUserId != $GLOBALS['User']->UserId && !$isAdminView)
){
throw new Exceptions\InvalidPermissionsException();
}
// We got here because an artwork was successfully submitted
@ -22,16 +24,26 @@ try{
session_unset();
}
// We got here because an artwork submission had errors and the user has to try again
// We got here because an artwork PATCH operation had errors and the user has to try again
if($exception){
http_response_code(422);
// Before we overwrite the original artwork with our new one, restore the old status,
// because if the new status is 'approved' then it will hide the status form entirely,
// which will be confusing.
$oldStatus = $artwork->Status;
$artwork = $_SESSION['artwork'] ?? $artwork;
$artwork->Status = $oldStatus;
session_unset();
}
}
catch(Exceptions\ArtworkNotFoundException){
Template::Emit404();
}
catch(Exceptions\InvalidPermissionsException){
Template::Emit403();
}
?><?= Template::Header(['title' => $artwork->Name, 'artwork' => true]) ?>
<main class="artworks">
@ -130,17 +142,22 @@ catch(Exceptions\ArtworkNotFoundException){
<?= Formatter::EscapeMarkdown($artwork->Notes) ?>
<? } ?>
<? if($isEditingAllowed){ ?>
<? if($artwork->CanBeEditedBy($GLOBALS['User'] ?? null)){ ?>
<h2>Edit artwork</h2>
<p>Before approval, the editor and submitter may <a href="<?= $artwork->EditUrl ?>">edit <i><?= Formatter::ToPlainText($artwork->Name) ?></i></a>.</p>
<? } ?>
<? if($isAdminView){ ?>
<? if($artwork->CanStatusBeChangedBy($GLOBALS['User'] ?? null) || $artwork->CanEbookWwwFilesysemPathBeChangedBy($GLOBALS['User'] ?? null)){ ?>
<h2>Editor options</h2>
<p>Review the metadata and PD proof for this artwork submission. Approve to make it available for future producers.</p>
<? if($artwork->CanStatusBeChangedBy($GLOBALS['User'] ?? null)){ ?>
<p>Review the metadata and PD proof for this artwork submission. Approve to make it available for future producers. Once an artwork is approved, it can no longer be edited.</p>
<? } ?>
<? if($artwork->CanEbookWwwFilesysemPathBeChangedBy($GLOBALS['User'] ?? null)){ ?>
<p>Set a file system slug to mark this artwork as “in use.</p>
<? } ?>
<form method="post" action="<?= $artwork->Url ?>">
<input type="hidden" name="_method" value="PATCH" />
<? if(($artwork->SubmitterUserId != $GLOBALS['User']->UserId) || $GLOBALS['User']->Benefits->CanReviewOwnArtwork){ ?>
<? if($artwork->CanStatusBeChangedBy($GLOBALS['User'] ?? null)){ ?>
<label class="select">
<span>Artwork approval status</span>
<span>
@ -148,16 +165,21 @@ catch(Exceptions\ArtworkNotFoundException){
<option value="<?= ArtworkStatus::Unverified->value ?>"<? if($artwork->Status == ArtworkStatus::Unverified){ ?> selected="selected"<? } ?>>Unverified</option>
<option value="<?= ArtworkStatus::Declined->value ?>"<? if($artwork->Status == ArtworkStatus::Declined){ ?> selected="selected"<? } ?>>Declined</option>
<option value="<?= ArtworkStatus::Approved->value ?>"<? if($artwork->Status == ArtworkStatus::Approved){ ?> selected="selected"<? } ?>>Approved</option>
<option value="<?= ArtworkStatus::InUse->value ?>"<? if($artwork->Status == ArtworkStatus::InUse){ ?> selected="selected"<? } ?>>In use</option>
</select>
</span>
</label>
<? }else{ ?>
<input type="hidden" name="artwork-status" value="<?= Formatter::ToPlainText($artwork->Status->value ?? '') ?>" />
<? } ?>
<? if($artwork->CanEbookWwwFilesysemPathBeChangedBy($GLOBALS['User'] ?? null)){ ?>
<label>
<span>In use by</span>
<span>Ebook file system slug, like <code>c-s-lewis_poetry</code>. If not in use, leave this blank.</span>
<input type="text" name="artwork-ebook-www-filesystem-path" value="<?= Formatter::ToPlainText($artwork->EbookWwwFilesystemPath) ?>"/>
</label>
<? }else{ ?>
<input type="hidden" name="artwork-ebook-www-filesystem-path" value="<?= Formatter::ToPlainText($artwork->EbookWwwFilesystemPath) ?>" />
<? } ?>
<div class="footer">
<button>Save changes</button>
</div>

View file

@ -49,19 +49,19 @@ if($isSubmitterView){
}
}
if(!$isAdminView && !$isSubmitterView && !in_array($status, array('all', ArtworkStatus::Approved->value, ArtworkStatus::InUse->value))){
if(!$isAdminView && !$isSubmitterView && !in_array($status, array('all', ArtworkStatus::Approved->value, 'in-use'))){
$status = ArtworkStatus::Approved->value;
$filterArtworkStatus = $status;
}
if($isAdminView && !in_array($status, array('all', ArtworkStatus::Unverified->value, ArtworkStatus::Declined->value, ArtworkStatus::Approved->value, ArtworkStatus::InUse->value))
&& !in_array($filterArtworkStatus, array('all-admin', ArtworkStatus::Unverified->value, ArtworkStatus::Declined->value, ArtworkStatus::Approved->value, ArtworkStatus::InUse->value))){
if($isAdminView && !in_array($status, array('all', ArtworkStatus::Unverified->value, ArtworkStatus::Declined->value, ArtworkStatus::Approved->value, 'in-use'))
&& !in_array($filterArtworkStatus, array('all-admin', ArtworkStatus::Unverified->value, ArtworkStatus::Declined->value, ArtworkStatus::Approved->value, 'in-use'))){
$status = ArtworkStatus::Approved->value;
$filterArtworkStatus = $status;
}
if($isSubmitterView && !in_array($status, array('all', ArtworkStatus::Unverified->value, ArtworkStatus::Approved->value, ArtworkStatus::InUse->value))
&& !in_array($filterArtworkStatus, array('all-submitter', 'unverified-submitter', ArtworkStatus::Approved->value, ArtworkStatus::InUse->value))){
if($isSubmitterView && !in_array($status, array('all', ArtworkStatus::Unverified->value, ArtworkStatus::Approved->value, 'in-use'))
&& !in_array($filterArtworkStatus, array('all-submitter', 'unverified-submitter', ArtworkStatus::Approved->value, 'in-use'))){
$status = ArtworkStatus::Approved->value;
$filterArtworkStatus = $status;
}
@ -107,8 +107,8 @@ if($perPage !== ARTWORK_PER_PAGE){
<option value="all"<? if($status === null){ ?> selected="selected"<? } ?>>All</option>
<? if($isAdminView || $isSubmitterView){ ?><option value="<?= ArtworkStatus::Unverified->value ?>"<? if($status == ArtworkStatus::Unverified->value){ ?> selected="selected"<? } ?>>Unverified</option><? } ?>
<? if($isAdminView){ ?><option value="<?= ArtworkStatus::Declined->value ?>"<? if($status == ArtworkStatus::Declined->value){ ?> selected="selected"<? } ?>>Declined</option><? } ?>
<option value="<?= ArtworkStatus::Approved->value ?>"<? if($status == ArtworkStatus::Approved->value){ ?> selected="selected"<? } ?>>Approved</option>
<option value="<?= ArtworkStatus::InUse->value ?>"<? if($status == ArtworkStatus::InUse->value){ ?> selected="selected"<? } ?>>In use</option>
<option value="<?= ArtworkStatus::Approved->value ?>"<? if($status == ArtworkStatus::Approved->value){ ?> selected="selected"<? } ?>>Approved, not in use</option>
<option value="in-use"<? if($status == 'in-use'){ ?> selected="selected"<? } ?>>In use</option>
</select>
</span>
</label>

View file

@ -68,13 +68,7 @@ catch(Exceptions\InvalidPermissionsException){
<? } ?>
<form class="create-update-artwork" method="post" action="/artworks" enctype="multipart/form-data">
<?= Template::ArtworkCreateEditFields(
[
'artwork' => $artwork,
'imageRequired' => true,
'isAdminView' => $isAdminView
]
) ?>
<?= Template::ArtworkForm(['artwork' => $artwork, 'isAdminView' => $isAdminView]) ?>
</form>
</section>
</main>

View file

@ -1,39 +1,8 @@
<?
function populateArtworkFields(Artwork $artwork): void{
$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);
// Only approved reviewers can set the status to anything but unverified when uploading
// The submitter cannot review their own submissions unless they have special permission
if($artwork->Status !== ArtworkStatus::Unverified && !$GLOBALS['User']->Benefits->CanReviewOwnArtwork){
throw new Exceptions\InvalidPermissionsException();
}
// If the artwork is approved, set the reviewer
if($artwork->Status !== ArtworkStatus::Unverified){
$artwork->ReviewerUserId = $GLOBALS['User']->UserId;
}
}
try{
session_start();
$httpMethod =HttpInput::RequestMethod();
$exceptionRedirectUrl = '/artworks/new';
if($httpMethod != HTTP_POST && $httpMethod != HTTP_PATCH && $httpMethod != HTTP_PUT){
throw new Exceptions\InvalidRequestException();
@ -53,10 +22,20 @@ try{
throw new Exceptions\InvalidPermissionsException();
}
$artwork = new Artwork();
populateArtworkFields($artwork);
$artwork = Artwork::FromHttpPost();
$artwork->SubmitterUserId = $GLOBALS['User']->UserId ?? null;
// Only approved reviewers can set the status to anything but unverified when uploading.
// The submitter cannot review their own submissions unless they have special permission.
if($artwork->Status !== ArtworkStatus::Unverified && !$artwork->CanStatusBeChangedBy($GLOBALS['User'])){
throw new Exceptions\InvalidPermissionsException();
}
// If the artwork is approved, set the reviewer
if($artwork->Status !== ArtworkStatus::Unverified){
$artwork->ReviewerUserId = $GLOBALS['User']->UserId;
}
// Confirm that the files came from POST
if(!is_uploaded_file($_FILES['artwork-image']['tmp_name'])){
throw new Exceptions\InvalidImageUploadException();
@ -71,20 +50,31 @@ try{
header('Location: /artworks/new');
}
// PUTing a new artwork
// PUTing an artwork
if($httpMethod == HTTP_PUT){
$originalArtwork = Artwork::GetByUrl(HttpInput::Str(GET, 'artist-url-name', false), HttpInput::Str(GET, 'artwork-url-name', false));
if(!$GLOBALS['User']->Benefits->CanReviewArtwork && !($originalArtwork->SubmitterUserId == $GLOBALS['User']->UserId)){
if(!$originalArtwork->CanBeEditedBy($GLOBALS['User'])){
throw new Exceptions\InvalidPermissionsException();
}
$artwork = new Artwork();
populateArtworkFields($artwork);
$exceptionRedirectUrl = $originalArtwork->EditUrl;
$artwork = Artwork::FromHttpPost();
$artwork->ArtworkId = $originalArtwork->ArtworkId;
$artwork->Created = $originalArtwork->Created;
$artwork->SubmitterUserId = $originalArtwork->SubmitterUserId;
$newStatus = ArtworkStatus::tryFrom(HttpInput::Str(POST, 'artwork-status', false) ?? '');
if($newStatus !== null){
if($originalArtwork->Status != $newStatus && !$originalArtwork->CanStatusBeChangedBy($GLOBALS['User'])){
throw new Exceptions\InvalidPermissionsException();
}
$artwork->ReviewerUserId = $GLOBALS['User']->UserId;
$artwork->Status = $newStatus;
}
$uploadedFile = [];
$uploadError = $_FILES['artwork-image']['error'];
@ -107,43 +97,31 @@ try{
// PATCHing a new artwork
if($httpMethod == HTTP_PATCH){
if(!$GLOBALS['User']->Benefits->CanReviewArtwork){
throw new Exceptions\InvalidPermissionsException();
}
$artwork = Artwork::GetByUrl(HttpInput::Str(GET, 'artist-url-name', false), HttpInput::Str(GET, 'artwork-url-name', false));
$artwork->Artist->Name = HttpInput::Str(POST, 'artist-name', false) ?? $artwork->Artist->Name;
$artwork->Artist->DeathYear = HttpInput::Int(POST, 'artist-year-of-death') ?? $artwork->Artist->DeathYear;
$exceptionRedirectUrl = $artwork->Url;
$artwork->Name = HttpInput::Str(POST, 'artwork-name', false) ?? $artwork->Name;
$artwork->CompletedYear = HttpInput::Int(POST, 'artwork-year') ?? $artwork->CompletedYear;
$artwork->CompletedYearIsCirca = HttpInput::Bool(POST, 'artwork-year-is-circa', false) ?? $artwork->CompletedYearIsCirca;
$artwork->Tags = HttpInput::Str(POST, 'artwork-tags', false) ?? $artwork->Tags;
$artwork->EbookWwwFilesystemPath = HttpInput::Str(POST, 'artwork-ebook-www-filesystem-path', false) ?? $artwork->EbookWwwFilesystemPath;
$artwork->IsPublishedInUs = HttpInput::Bool(POST, 'artwork-is-published-in-us', false) ?? $artwork->IsPublishedInUs;
$artwork->PublicationYear = HttpInput::Int(POST, 'artwork-publication-year') ?? $artwork->PublicationYear;
$artwork->PublicationYearPageUrl = HttpInput::Str(POST, 'artwork-publication-year-page-url', false) ?? $artwork->PublicationYearPageUrl;
$artwork->CopyrightPageUrl = HttpInput::Str(POST, 'artwork-copyright-page-url', false) ?? $artwork->CopyrightPageUrl;
$artwork->ArtworkPageUrl = HttpInput::Str(POST, 'artwork-artwork-page-url', false) ?? $artwork->ArtworkPageUrl;
$artwork->MuseumUrl = HttpInput::Str(POST, 'artwork-museum-url', false) ?? $artwork->MuseumUrl;
$artwork->Exception = HttpInput::Str(POST, 'artwork-exception', false) ?? $artwork->Exception;
$artwork->Notes = HttpInput::Str(POST, 'artwork-notes', false) ?? $artwork->Notes;
$artwork->ReviewerUserId = $GLOBALS['User']->UserId;
// We can PATCH either the status or the ebook www filesystem path.
$newStatus = ArtworkStatus::tryFrom(HttpInput::Str(POST, 'artwork-status', false) ?? '');
if($newStatus !== null){
if($artwork->Status != $newStatus){
// Is the user attempting to review their own artwork?
if($artwork->Status != ArtworkStatus::Unverified && $GLOBALS['User']->UserId == $artwork->SubmitterUserId && !$GLOBALS['User']->Benefits->CanReviewOwnArtwork){
if($artwork->Status != $newStatus && !$artwork->CanStatusBeChangedBy($GLOBALS['User'])){
throw new Exceptions\InvalidPermissionsException();
}
}
$artwork->ReviewerUserId = $GLOBALS['User']->UserId;
$artwork->Status = $newStatus;
}
$newEbookWwwFilesystemPath = HttpInput::Str(POST, 'artwork-ebook-www-filesystem-path', false) ?? null;
if($artwork->EbookWwwFilesystemPath != $newEbookWwwFilesystemPath && !$artwork->CanEbookWwwFilesysemPathBeChangedBy($GLOBALS['User'])){
throw new Exceptions\InvalidPermissionsException();
}
$artwork->ReviewerUserId = $GLOBALS['User']->UserId;
$artwork->Status = $newStatus;
$artwork->EbookWwwFilesystemPath = $newEbookWwwFilesystemPath;
$artwork->Save();
$_SESSION['artwork'] = $artwork;
@ -172,15 +150,5 @@ catch(Exceptions\AppException $exception){
$_SESSION['exception'] = $exception;
http_response_code(303);
if($httpMethod == HTTP_PATCH && $artwork !== null){
header('Location: ' . $artwork->Url);
}
else if($httpMethod == HTTP_PUT && $artwork !== null){
header('Location: ' . $artwork->EditUrl);
}
else{
header('Location: /artworks/new');
}
header('Location: ' . $exceptionRedirectUrl);
}

View file

@ -145,9 +145,13 @@ form.create-update-artwork fieldset p:first-of-type{
}
form.create-update-artwork legend{
font-size: 1.2rem;
font-weight: bold;
margin: 0.5rem 0;
font-size: 1.4rem;
font-family: "League Spartan", Arial, sans-serif;
margin-top: 2rem;
margin-bottom: 1rem;
line-height: 1.2;
letter-spacing: 1px;
text-transform: uppercase;
}
form.create-update-artwork label{
@ -178,7 +182,8 @@ form div.footer{
}
main h1 ~ a[href^="/images/cover-uploads"],
.artworks h1 ~ a[href^="/images/cover-uploads"]{
.artworks h1 ~ a[href^="/images/cover-uploads"],
main section.narrow h1 + picture{
width: auto;
line-height: 0;
}