Skip to content

Commit

Permalink
Add hashtag hyperlinks and trending cloud (#492)
Browse files Browse the repository at this point in the history
  • Loading branch information
RocketMan committed Dec 11, 2024
1 parent 3246664 commit d4a8a8e
Show file tree
Hide file tree
Showing 17 changed files with 296 additions and 17 deletions.
1 change: 1 addition & 0 deletions api/Albums.php
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ class Albums implements RequestHandlerInterface {
"track" => [ ILibrary::TRACK_NAME, null ],
"label.id" => [ ILibrary::ALBUM_PUBKEY, null ],
"reviews.airname.id" => [ ILibrary::ALBUM_AIRNAME, null ],
"reviews.hashtag" => [ ILibrary::ALBUM_HASHTAG, null ],
"match(artist)" => [ -1, "artists" ],
"match(artist,album)" => [ -1, "albums" ],
"match(album,artist)" => [ -1, "albums" ],
Expand Down
4 changes: 2 additions & 2 deletions controllers/Validate.php
Original file line number Diff line number Diff line change
Expand Up @@ -554,7 +554,7 @@ public function validateLibrary() {
'attributes' => [
'airname' => $airname,
'date' => '2022-02-09',
'review' => 'This is a review'
'review' => 'This is a #review #test'
],
'relationships' => [
'album' => [
Expand All @@ -575,7 +575,7 @@ public function validateLibrary() {
}

if($this->doTest("validate review", $success9)) {
$success10 = $this->searchAlbum($albumname2, "review", "review", "This is a review");
$success10 = $this->searchAlbum($albumname2, "review", "review", "This is a #review #test");
$this->showSuccess($success10);
}

Expand Down
53 changes: 53 additions & 0 deletions css/trending.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/* fonts */

div.jqcloud {
font-size: 120%;
}

@media (max-width: 800px) {
div.jqcloud {
font-size: 100%;
}
}

div.jqcloud a {
font-size: inherit;
text-decoration: none;
}

div.jqcloud span.w10 { font-size: 300%; }
div.jqcloud span.w9 { font-size: 280%; }
div.jqcloud span.w8 { font-size: 260%; }
div.jqcloud span.w7 { font-size: 240%; }
div.jqcloud span.w6 { font-size: 220%; }
div.jqcloud span.w5 { font-size: 200%; }
div.jqcloud span.w4 { font-size: 180%; }
div.jqcloud span.w3 { font-size: 140%; }
div.jqcloud span.w2 { font-size: 120%; }
div.jqcloud span.w1 { font-size: 100%; }

/* colors */

div.jqcloud { color: var(--theme-link-colour); }
div.jqcloud a { color: inherit; }

div.jqcloud a:hover { filter: saturate(2); }
div.jqcloud span.w10 { filter: saturate(1.3); }
div.jqcloud span.w9 { filter: saturate(1.25); }
div.jqcloud span.w8 { filter: saturate(1.2); }
div.jqcloud span.w7 { filter: saturate(1.1); }
div.jqcloud span.w6 { filter: saturate(1.0); }
div.jqcloud span.w5 { filter: saturate(0.9); }
div.jqcloud span.w4 { filter: saturate(0.85); }
div.jqcloud span.w3 { filter: saturate(0.80); }
div.jqcloud span.w2 { filter: saturate(0.75); }
div.jqcloud span.w1 { filter: saturate(0.70); }

/* layout */

div.jqcloud {
overflow: hidden;
position: relative;
}

div.jqcloud span { padding: 4px; }
15 changes: 15 additions & 0 deletions css/zoostyle.css
Original file line number Diff line number Diff line change
Expand Up @@ -784,6 +784,11 @@ th.sec {
font-weight: bold;
color: #cc0000;
}
.quiet-notice {
font-size: 20px;
color: #ccc;
}

.input { }
.sel:link { color: #ff0000; }
.sel:visited { color: #ff0000; }
Expand Down Expand Up @@ -1576,6 +1581,16 @@ a:hover.copy {
padding-top: 15px;
}

.album-hashtag-area a {
color: inherit;
}
.album-hashtag-area a:hover {
text-decoration: none;
}
.album-hashtag-area a:hover .album-hashtag {
filter: brightness(115%);
}

.album-hashtag {
border-radius: 6px;
border: 1px solid #666;
Expand Down
10 changes: 10 additions & 0 deletions db/convert_v2_11_7_to_v3_0_0.sql
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,16 @@ ALTER TABLE `reviews` ADD COLUMN `exportid` varchar(80) DEFAULT NULL;
ALTER TABLE `tracknames` ADD COLUMN `duration` time DEFAULT NULL;
ALTER TABLE `colltracknames` ADD COLUMN `duration` time DEFAULT NULL;

CREATE TABLE IF NOT EXISTS `reviews_hashtags` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`tag` int(11) NOT NULL,
`user` varchar(8) NOT NULL,
`hashtag` varchar(190) NOT NULL,
PRIMARY KEY (`id`),
KEY `tu` (`tag`,`user`),
KEY `hashtag` (`hashtag`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 ;

SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT;
SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS;
SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION;
16 changes: 16 additions & 0 deletions db/zkdbSchema.sql
Original file line number Diff line number Diff line change
Expand Up @@ -335,6 +335,22 @@ CREATE TABLE IF NOT EXISTS `reviews` (

-- --------------------------------------------------------

--
-- Table structure for table `reviews_hashtags`
--

CREATE TABLE IF NOT EXISTS `reviews_hashtags` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`tag` int(11) NOT NULL,
`user` varchar(8) NOT NULL,
`hashtag` varchar(190) NOT NULL,
PRIMARY KEY (`id`),
KEY `tu` (`tag`,`user`),
KEY `hashtag` (`hashtag`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 ;

-- --------------------------------------------------------

--
-- Table structure for table `sessions`
--
Expand Down
1 change: 1 addition & 0 deletions engine/ILibrary.php
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ interface ILibrary {
const TRACK_KEY = 9;
const TRACK_NAME = 10;
const ALBUM_LOCATION = 11;
const ALBUM_HASHTAG = 12;

const OP_PREV_LINE = 0;
const OP_NEXT_LINE = 1;
Expand Down
3 changes: 2 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-2023 Jim Mason <[email protected]>
* @copyright Copyright (C) 1997-2024 Jim Mason <[email protected]>
* @link https://zookeeper.ibinx.com/
* @license GPL-3.0
*
Expand Down Expand Up @@ -47,6 +47,7 @@ interface IReview {
function getRecentReviews($user = "", $weeks = 0, $limit = 0, $loggedIn = 0);
function getActiveReviewers($viewAll=0, $loggedIn=0);
function getReviews($tag, $byName=1, $user = "", $loggedIn = 0, $byId = 0);
function getTrending(int $limit = 50);
function insertReview($tag, $private, $airname, $review, $user);
function updateReview($tag, $private, $airname, $review, $user);
function deleteReview($tag, $user);
Expand Down
19 changes: 16 additions & 3 deletions engine/impl/LibraryImpl.php
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,7 @@ private static function orderBy($sortBy) {
$query = "ORDER BY name$desc, album$desc, artist$desc ";
break;
case "created":
case "added":
$query = "ORDER BY a.created$desc, artist$desc ";
break;
case "date":
Expand Down Expand Up @@ -306,11 +307,21 @@ public function searchPos($tableIndex, &$pos, $count, $search, $sortBy = 0) {
$query .= self::orderBy($sortBy);
$query .= "LIMIT ?, ?";
break;
case ILibrary::ALBUM_HASHTAG:
$query = "SELECT artist, album, category, medium, size, ".
"a.created, a.updated, a.pubkey, location, bin, a.tag, iscoll, p.name ".
"FROM reviews_hashtags r LEFT JOIN albumvol a ON a.tag = r.tag ".
"LEFT JOIN publist p ON p.pubkey = a.pubkey ".
"WHERE hashtag = ? GROUP BY r.tag ";
$query .= self::orderBy($sortBy);
$query .= "LIMIT ?, ?";
$bindType = 3;
break;
default:
error_log("searchPos: unknown key '$tableIndex'");
return;
}

// Collation for utf8mb4 coalesces related characters, such as
// 'a', 'a-umlaut', 'a-acute', and so on, for searching. However,
// it does not coalese various punctuation, such as apostrophe
Expand Down Expand Up @@ -386,11 +397,13 @@ public function searchPos($tableIndex, &$pos, $count, $search, $sortBy = 0) {
else if($lim = strpos($query, " LIMIT"))
$query = substr($query, 0, $lim);

// For UNION queries, we must count number of aggregate rows.
// For UNION queries and for queries which contain GROUP BY,
// we must count the number of aggregate rows.
//
// This will work also for simple queries, but in that
// case, we just do a count, as it's a tad more efficient.
if(strpos($query, " UNION SELECT ")) {
if(strpos($query, " UNION SELECT ") ||
strpos($query, " GROUP BY ")) {
$query = "SELECT COUNT(*) FROM (" . $query . ") x";
} else {
$from = strpos($query, "FROM");
Expand Down
61 changes: 57 additions & 4 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-2023 Jim Mason <[email protected]>
* @copyright Copyright (C) 1997-2024 Jim Mason <[email protected]>
* @link https://zookeeper.ibinx.com/
* @license GPL-3.0
*
Expand Down Expand Up @@ -156,8 +156,44 @@ public function getReviews($tag, $byName=1, $user = "", $loggedIn = 0, $byId = 0
$stmt->bindValue(2, $user);
return $stmt->executeAndFetchAll(\PDO::FETCH_BOTH);
}

public function getTrending(int $limit = 50) {
$query = "SELECT hashtag, count(*) freq FROM reviews_hashtags " .
"GROUP BY hashtag ORDER BY id DESC LIMIT ?";
$stmt = $this->prepare($query);
$stmt->bindValue(1, $limit, \PDO::PARAM_INT);
return $stmt->executeAndFetchAll();
}

protected function syncHashtags(int $tag, string $user, ?string $review = null) {
$this->adviseLock($tag);

$query = "DELETE FROM reviews_hashtags WHERE tag = ? AND user = ?";
$stmt = $this->prepare($query);
$stmt->bindValue(1, $tag);
$stmt->bindValue(2, $user);
$stmt->execute();

if($review && preg_match_all('/#(\pL\w*)/u', $review, $matches)) {
$query = "INSERT INTO reviews_hashtags (tag, user, hashtag) VALUES (?, ?, ?)";
$stmt = $this->prepare($query);
$stmt->bindValue(1, $tag);
$stmt->bindValue(2, $user);
$normalized = array_unique(array_map('strtolower', $matches[1]));
$hashtags = array_intersect_key($matches[1], $normalized);
foreach($hashtags as $hashtag) {
$stmt->bindValue(3, $hashtag);
$stmt->execute();
}
}

$this->adviseUnlock($tag);
}

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

$query = "INSERT INTO reviews " .
"(tag, user, created, private, review, airname) VALUES (" .
"?, ?, " .
Expand All @@ -171,7 +207,13 @@ public function insertReview($tag, $private, $airname, $review, $user) {
$stmt->bindValue(4, $review);
if($airname)
$stmt->bindValue(5, $airname);
return $stmt->execute()?$stmt->rowCount():0;
$count = $stmt->execute() ? $stmt->rowCount() : 0;

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

return $count;
}

public function updateReview($tag, $private, $airname, $review, $user) {
Expand All @@ -188,7 +230,12 @@ public function updateReview($tag, $private, $airname, $review, $user) {
$stmt->bindValue($p++, $review);
$stmt->bindValue($p++, $tag);
$stmt->bindValue($p++, $user);
return $stmt->execute()?$stmt->rowCount():0;
$count = $stmt->execute() ? $stmt->rowCount() : 0;

if($count)
$this->syncHashtags($tag, $user, $private ? null : $review);

return $count;
}

public function deleteReview($tag, $user) {
Expand All @@ -197,7 +244,13 @@ public function deleteReview($tag, $user) {
$stmt = $this->prepare($query);
$stmt->bindValue(1, $tag);
$stmt->bindValue(2, $user);
return $stmt->execute()?$stmt->rowCount():0;
$count = $stmt->execute() ? $stmt->rowCount() : 0;

// delete any associated hashtags
if($count)
$this->syncHashtags($tag, $user);

return $count;
}

public function setExportId($tag, $user, $exportId) {
Expand Down
14 changes: 11 additions & 3 deletions js/search.library.js
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ function emitAlbumsEx(table, data) {
tr.append(header("Album", true));
tr.append(header("Collection", false));
tr.append(header("Media", false).attr('colSpan', 2));
tr.append(header("Added", false));
tr.append(header("Added", $("#type").val() == 'hashtags'));
tr.append(header("Label", true));
table.append($("<THEAD>").append(tr));

Expand Down Expand Up @@ -215,7 +215,8 @@ var requestMap = {
albumsByPubkey: "album?filter[label.id]=",
tracks: "album?filter[track]=",
labels: "label?filter[name]=",
reviews: "album?filter[reviews.airname.id]="
reviews: "album?filter[reviews.airname.id]=",
hashtags: "album?filter[reviews.hashtag]="
};

var lists = {
Expand Down Expand Up @@ -370,13 +371,17 @@ var lists = {
},
};

// hashtags and albums share the same marshaller
lists.hashtags = lists.albums;

function search(size, offset) {
var suffix, type = $("#type").val();
if(!type || !requestMap[type])
return;
switch(type) {
case "albumsByPubkey":
case "reviews":
case "hashtags":
suffix = "";
break;
default:
Expand Down Expand Up @@ -428,6 +433,9 @@ function search(size, offset) {
case "albumsByPubkey":
ttype = "albums";
break;
case "hashtags":
ttype = "albums tagged #" + $("#fkey").val();
break;
default:
ttype = type;
break;
Expand Down Expand Up @@ -457,7 +465,7 @@ function search(size, offset) {
if($("#sortBy").val() == "")
$("#sortBy").val("Artist");
lists[type](results, response);
} else {
} else if (type != 'hashtags' ) {
if($("#m").is(":checked"))
results.append('Hint: Uncheck "Exact match" box to broaden search.');
else
Expand Down
Loading

0 comments on commit d4a8a8e

Please sign in to comment.