Skip to content

Commit

Permalink
added the review shelf
Browse files Browse the repository at this point in the history
  • Loading branch information
RocketMan committed Jan 7, 2025
1 parent cfebdbf commit aaa91a9
Show file tree
Hide file tree
Showing 5 changed files with 302 additions and 7 deletions.
15 changes: 15 additions & 0 deletions css/zoostyle.css
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,15 @@ table.recent-reviews th {
text-align: left;
}

/* review shelf */
table.review-shelf tbody td {
border-color: rgba(0,0,0,0.08);
}

table.review-shelf tbody tr {
line-height: 30px;
}

/* home page */
.top-albums {
margin-top: 4px;
Expand Down Expand Up @@ -1001,11 +1010,17 @@ li.ui-menu-item .ui-state-active,
.form-entry button.edit-mode.default:active:not(:disabled) {
background-color: #5897ee;
}
.form-entry button.edit-delete,
.form-entry button#edit-delete {
color: #e33437;
border-color: #e33437;
}

.form-entry button.edit-alert {
color: #8a2be2;
border-color: #8a2be2;
}

.form-entry label {
display:inline-block;
font-weight: bold;
Expand Down
14 changes: 13 additions & 1 deletion engine/IReview.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
* Zookeeper Online
*
* @author Jim Mason <[email protected]>
* @copyright Copyright (C) 1997-2024 Jim Mason <[email protected]>
* @copyright Copyright (C) 1997-2025 Jim Mason <[email protected]>
* @link https://zookeeper.ibinx.com/
* @license GPL-3.0
*
Expand Down Expand Up @@ -52,4 +52,16 @@ function insertReview($tag, $private, $airname, $review, $user);
function updateReview($tag, $private, $airname, $review, $user);
function deleteReview($tag, $user);
function setExportId($tag, $user, $exportId);
function getReviewShelf();
/**
* update review shelf status
*
* @param int $tag target album
* @param ?string $user check album out to user or null (see below)
* @return ?array old status or null if none
*
* If $user is null and album is pending approval, it is moved
* to the library; otherwise, it is returned to the review shelf.
*/
function updateReviewShelf(int $tag, ?string $user = null): ?array;
}
62 changes: 57 additions & 5 deletions engine/impl/ReviewImpl.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
* Zookeeper Online
*
* @author Jim Mason <[email protected]>
* @copyright Copyright (C) 1997-2024 Jim Mason <[email protected]>
* @copyright Copyright (C) 1997-2025 Jim Mason <[email protected]>
* @link https://zookeeper.ibinx.com/
* @license GPL-3.0
*
Expand All @@ -29,6 +29,18 @@
* Music review operations
*/
class ReviewImpl extends DBO implements IReview {
// these codes are defined in ILibrary::LOCATIONS
private const ALBUM_AWAITING_REVIEW = 'E'; // Review Shelf
private const ALBUM_IN_REVIEW = 'F'; // Out for Review
private const ALBUM_REVIEWED = 'H'; // Pending Approval
private const ALBUM_LIBRARY = 'L'; // Library

private const REVIEW_SHELF = [
self::ALBUM_AWAITING_REVIEW,
self::ALBUM_IN_REVIEW,
self::ALBUM_REVIEWED
];

private function getRecentSubquery($user = "", $weeks = 0, $loggedIn = 0) {
// IMPORTANT: If columns change, revisit getRecentReviews below
$query = "SELECT a.airname, r.user, DATE_FORMAT(r.created, GET_FORMAT(DATE, 'ISO')) reviewed, r.id as rid, u.realname, r.tag, v.category, v.album, v.artist, v.iscoll FROM reviews r ";
Expand Down Expand Up @@ -191,8 +203,9 @@ protected function syncHashtags(int $tag, string $user, ?string $review = null)
}

public function insertReview($tag, $private, $airname, $review, $user) {
// we must do this first, as caller depends on lastInsertId from INSERT
// we must do these first as caller depends on lastInsertId from INSERT
$this->syncHashtags($tag, $user, $private ? null : $review);
$prev = $this->updateReviewShelf($tag, null, self::ALBUM_REVIEWED);

$query = "INSERT INTO reviews " .
"(tag, user, created, private, review, airname) VALUES (" .
Expand All @@ -209,10 +222,14 @@ public function insertReview($tag, $private, $airname, $review, $user) {
$stmt->bindValue(5, $airname);
$count = $stmt->execute() ? $stmt->rowCount() : 0;

// back out hashtags on failure
if(!$count)
// back out hashtags and review shelf on failure
if(!$count) {
$this->syncHashtags($tag, $user);

if($prev)
$this->updateReviewShelf($tag, $prev['user'], $prev['status']);
}

return $count;
}

Expand Down Expand Up @@ -247,8 +264,10 @@ public function deleteReview($tag, $user) {
$count = $stmt->execute() ? $stmt->rowCount() : 0;

// delete any associated hashtags
if($count)
if($count) {
$this->syncHashtags($tag, $user);
$this->updateReviewShelf($tag, null, self::ALBUM_AWAITING_REVIEW);
}

return $count;
}
Expand All @@ -262,4 +281,37 @@ public function setExportId($tag, $user, $exportId) {
$stmt->bindValue(3, $user);
return $stmt->execute()?$stmt->rowCount():0;
}

public function getReviewShelf() {
$n = count(self::REVIEW_SHELF);
$query = "SELECT a.*, u.realname FROM albumvol a LEFT JOIN users u ON a.bin = u.name WHERE location IN ( ?" . str_repeat(', ?', $n - 1) . " ) ORDER BY artist, album";
$stmt = $this->prepare($query);
foreach(self::REVIEW_SHELF as $status)
$stmt->bindValue($n--, $status);

return $stmt->executeAndFetchAll();
}

function updateReviewShelf(int $tag, ?string $user = null, ?string $status = null): ?array {
$query = "SELECT location status, bin user FROM albumvol WHERE tag = ?";
$stmt = $this->prepare($query);
$stmt->bindValue(1, $tag);
$result = $stmt->executeAndFetch() ?: null;

// if album is not in a review status, nothing to do
$ostatus = $result['status'] ?? '';
if(!in_array($ostatus, self::REVIEW_SHELF))
return null;

$location = $ostatus == self::ALBUM_REVIEWED ?
self::ALBUM_LIBRARY : self::ALBUM_AWAITING_REVIEW;

$query = "UPDATE albumvol SET location = ?, bin = ?, updated = NOW() WHERE tag = ?";
$stmt = $this->prepare($query);
$stmt->bindValue(1, $status ??
($user ? self::ALBUM_IN_REVIEW : $location));
$stmt->bindValue(2, $user);
$stmt->bindValue(3, $tag);
return $stmt->execute() ? $result : null;
}
}
119 changes: 118 additions & 1 deletion ui/Reviews.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
* Zookeeper Online
*
* @author Jim Mason <[email protected]>
* @copyright Copyright (C) 1997-2024 Jim Mason <[email protected]>
* @copyright Copyright (C) 1997-2025 Jim Mason <[email protected]>
* @link https://zookeeper.ibinx.com/
* @license GPL-3.0
*
Expand Down Expand Up @@ -55,6 +55,8 @@ class Reviews extends MenuItem {
[ "a", "viewDJ", "By DJ", "reviewsByDJ" ],
[ "a", "viewHashtag", "Trending", "viewTrending" ],
[ "a", "trendingData", 0, "getTrendingData" ],
[ "u", "viewReviewShelf", "Review Shelf", "viewReviewShelf" ],
[ "u", "updateReviewShelf", 0, "updateReviewShelf" ],
];

private $subaction;
Expand Down Expand Up @@ -148,6 +150,49 @@ public function getTrendingData() {
echo json_encode($data);
}

public function viewReviewShelf() {
$albums = Engine::api(IReview::class)->getReviewShelf();
$this->addVar('GENRES', ILibrary::GENRES);
$this->addVar('albums', $albums);
$this->setTemplate("review.shelf.html");
}

public function updateReviewShelf() {
$op = $_REQUEST['op'] ?? false;
$tag = $_REQUEST['tag'] ?? false;
if(!$op || !$tag) {
http_response_code(400); // bad request
return;
}

switch($op) {
case 'claim':
Engine::api(IReview::class)->updateReviewShelf($tag, $this->session->getUser());
break;
case 'release':
// fall through...
case 'xdtm':
Engine::api(IReview::class)->updateReviewShelf($tag, null);
break;
}

$album = Engine::api(ILibrary::class)->search(ILibrary::ALBUM_KEY, 0, 1, $tag)[0];
if($album['bin']) {
$user = Engine::api(ILibrary::class)->search(ILibrary::PASSWD_NAME, 0, 1, $album['bin']);
if(count($user))
$album['realname'] = $user[0]['realname'];
}

$this->claimReview($tag, $op);

$this->addVar('GENRES', ILibrary::GENRES);
$this->addVar('album', $album);
$this->setTemplate("review.shelf.html");
$html = $this->render('album');

echo json_encode(['html' => $html]);
}

public function viewRecentReviews() {
$isAuthorized = $this->session->isAuth('u');
$author = $isAuthorized && ($_GET['dj'] ?? '') == 'Me' ? $this->session->getUser() : '';
Expand All @@ -164,6 +209,78 @@ public function viewReview() {
$this->newEntity(Search::class)->searchByAlbumKey($_REQUEST["tag"]);
}

private function claimReview($tag, $op) {
// nothing to do if Slack is not configured
$config = Engine::param('slack');
if($op == 'dtm' ||
!$config || !($token = $config['token']) ||
!($channel = $config['review_channel'])) {
return;
}

// find the album
$libAPI = Engine::api(ILibrary::class);
$albums = $libAPI->search(ILibrary::ALBUM_KEY, 0, 1, $tag);
if(!count($albums))
return;

$artist = $albums[0]["iscoll"] ? "Various Artists" : $albums[0]["artist"];
$album = $albums[0]["album"];

$user = $libAPI->search(ILibrary::PASSWD_NAME, 0, 1, $this->session->getUser());
if(!count($user))
return;

$reviewer = $user[0]["realname"];
$unclaim = $op == 'release';
$action = $unclaim ? "has returned" : "is reviewing";
$verb = $unclaim ? "returned" : "claimed";

$base = Engine::getBaseUrl();
$title = Engine::param('station_title');

// compose the message
$body = [
'type' => 'section',
'text' => [
'type' => 'mrkdwn',
'text' => ":lower_left_fountain_pen: $reviewer $action <$base?s=byAlbumKey&amp;n=$tag&amp;action=search|$artist / $album>"
],
];

$client = new Client([
'base_uri' => self::SLACK_BASE,
RequestOptions::HEADERS => [
'User-Agent' => Engine::UA,
'Authorization' => 'Bearer ' . $token
]
]);

try {
$options = [
'channel' => $channel,
'text' => "$reviewer $verb $artist / $album",
'blocks' => [
$body,
],
];

$method = 'chat.postMessage';

$response = $client->post($method, [
RequestOptions::JSON => $options
]);

// Slack returns success/failure in 'ok' property
$body = $response->getBody()->getContents();
$json = json_decode($body);
if(!$json->ok)
error_log("postReview: $body");
} catch(\Exception $e) {
error_log("postReview: " . $e->getMessage());
}
}

private function postReview($tag) {
// nothing to do if Slack is not configured
$config = Engine::param('slack');
Expand Down
Loading

0 comments on commit aaa91a9

Please sign in to comment.