Skip to content

Commit

Permalink
Artwork db code tweaks, also remove 'in use' as a status
Browse files Browse the repository at this point in the history
  • Loading branch information
acabal committed Jan 17, 2024
1 parent 73bcae0 commit 5ef6d3a
Show file tree
Hide file tree
Showing 13 changed files with 249 additions and 174 deletions.
6 changes: 2 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
118 changes: 98 additions & 20 deletions lib/Artwork.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -252,15 +249,15 @@ protected function GetThumb2xUrl(): string{
}

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{
Expand Down Expand Up @@ -289,6 +286,55 @@ protected function GetEbook(): ?Ebook{
// *******
// 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
Expand Down Expand Up @@ -341,15 +387,8 @@ protected function Validate(array &$uploadedFile = []): void{
$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){
Expand Down Expand Up @@ -686,6 +725,13 @@ public function Save(array $uploadedFile = []): void{

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);
Expand All @@ -696,8 +742,15 @@ public function Save(array $uploadedFile = []): void{
}
$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
Expand All @@ -706,7 +759,7 @@ public function Save(array $uploadedFile = []): void{
UrlName = ?,
CompletedYear = ?,
CompletedYearIsCirca = ?,
Created = ?,
Updated = ?,
Status = ?,
SubmitterUserId = ?,
ReviewerUserId = ?,
Expand All @@ -723,24 +776,24 @@ public function Save(array $uploadedFile = []): void{
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]
);

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]);
}
Expand Down Expand Up @@ -840,4 +893,29 @@ public static function GetByUrl(?string $artistUrlName, ?string $artworkUrlName)

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;
}
}
1 change: 0 additions & 1 deletion lib/ArtworkStatus.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,4 @@ enum ArtworkStatus: string{
case Unverified = 'unverified';
case Declined = 'declined';
case Approved = 'approved';
case InUse = 'in_use';
}
22 changes: 18 additions & 4 deletions lib/Library.php
Original file line number Diff line number Diff line change
Expand Up @@ -162,39 +162,53 @@ public static function GetTags(): array{
* @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 = [];

if($status === null || $status == 'all'){
$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('
SELECT *
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 *
Expand Down
37 changes: 22 additions & 15 deletions templates/ArtworkCreateEditFields.php → templates/ArtworkForm.php
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -16,7 +22,7 @@
<? 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>
Expand All @@ -38,7 +44,7 @@
name="artist-year-of-death"
inputmode="numeric"
pattern="[0-9]+"
value="<?= $artwork->Artist->DeathYear ?>"
value="<?= Formatter::ToPlainText($artwork->Artist->DeathYear) ?>"
/>
</label>
</fieldset>
Expand All @@ -57,7 +63,7 @@
name="artwork-year"
inputmode="numeric"
pattern="[0-9]+"
value="<?= $artwork->CompletedYear ?>"
value="<?= Formatter::ToPlainText($artwork->CompletedYear) ?>"
/>
</label>
<label>
Expand All @@ -81,11 +87,11 @@
</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>
Expand Down Expand Up @@ -123,7 +129,7 @@
name="artwork-publication-year"
inputmode="numeric"
pattern="[0-9]+"
value="<?= $artwork->PublicationYear ?>"
value="<?= Formatter::ToPlainText($artwork->PublicationYear) ?>"
/>
</label>
<label>
Expand Down Expand Up @@ -174,27 +180,28 @@
<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>
<select name="artwork-status">
<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>
<? } ?>
<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>
<? 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">
Expand Down
2 changes: 1 addition & 1 deletion templates/ArtworkList.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
?>
<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"/>
Expand Down
Loading

0 comments on commit 5ef6d3a

Please sign in to comment.