diff --git a/css/zoostyle.css b/css/zoostyle.css index af3df26f3..cd63f7c2e 100644 --- a/css/zoostyle.css +++ b/css/zoostyle.css @@ -327,6 +327,20 @@ P.zktitle A { } /* general */ +.playlistHdr { + font-family: verdana, arial, helvetica, sans-serif; + font-size: 11pt; + font-weight: bold; + color: white; + background-color: #2530A7; + line-height: 24px; +} +.playlistHdr > div { + font-size: 11pt; + float: right; + font-size: 10px; +} + TH { font-family: verdana, arial, helvetica, sans-serif; font-size: 11pt; @@ -458,18 +472,52 @@ A.calLink { color: #000000; background-color: #cccccc; } + +/* up/down & edit links for playlist edits */ +.songManager { + top: 8px; + height: 20px; + width: 24px; + position: relative; + font-size:12px; +} + +.songManager a { + position: absolute; + line-height: 1.4; + background-repeat: no-repeat; + width: 10px; + height: 4px; +} .songUp, .sortUp { + top: 0px; background-position: center; background-repeat: no-repeat; background-image: URL('../img/arrow_up.gif'); } .songDown, .sortDown { + top: 8px; background-position: center; background-repeat: no-repeat; background-image: URL('../img/arrow_down.gif'); } +.songEdit { + top: -2px; + left: 12px; + font-size:14px; +} + +.songLabel { + font-size:10px; +} .albumReview { background-image: URL('../img/rinfo.gif'); + width:11px; + height:11px; +} +.songDivider hr { + height: 0px; + background-color: gray; } .editorUp { top: 0px; diff --git a/db/zkdbSchema.sql b/db/zkdbSchema.sql index 4ab302e53..cfbacda75 100644 --- a/db/zkdbSchema.sql +++ b/db/zkdbSchema.sql @@ -353,7 +353,6 @@ CREATE TABLE IF NOT EXISTS `tracknames` ( -- -- Table structure for table `tracks` --- CREATE TABLE IF NOT EXISTS `tracks` ( `id` int(11) NOT NULL AUTO_INCREMENT, @@ -363,6 +362,7 @@ CREATE TABLE IF NOT EXISTS `tracks` ( `track` varchar(80) DEFAULT NULL, `album` varchar(80) DEFAULT NULL, `label` varchar(80) DEFAULT NULL, + `created` timestamp DEFAULT NULL, PRIMARY KEY (`id`), KEY `list` (`list`), KEY `tag` (`tag`), diff --git a/engine/impl/Library.php b/engine/impl/Library.php index c4985d7b8..848050ed6 100644 --- a/engine/impl/Library.php +++ b/engine/impl/Library.php @@ -246,7 +246,7 @@ public function markAlbumsReviewed(&$albums, $loggedIn = 0) { $chain = []; $tags = []; $queryset = ""; - for($i = 0; $i < sizeof($albums); $i++) { + for($i = 0; $albums != null && $i < sizeof($albums); $i++) { $tag = array_key_exists("tag", $albums[$i])?$albums[$i]["tag"]:0; if($tag) { if(array_key_exists($tag, $tags)) diff --git a/engine/impl/Playlist.php b/engine/impl/Playlist.php index 747eca0ce..c079dde9f 100644 --- a/engine/impl/Playlist.php +++ b/engine/impl/Playlist.php @@ -24,6 +24,8 @@ namespace ZK\Engine; +use \Datetime; +use \DateTimeZone; use ZK\Engine\ILibrary; @@ -162,7 +164,7 @@ public function updatePlaylist($playlist, $date, $time, $description, $airname) } public function getTrack($id) { - $query = "SELECT tag, artist, track, album, label, id FROM tracks " . + $query = "SELECT created, tag, artist, track, album, label, id FROM tracks " . "WHERE id = ?"; $stmt = $this->prepare($query); $stmt->bindValue(1, (int)$id, \PDO::PARAM_INT); @@ -170,7 +172,7 @@ public function getTrack($id) { } public function getTracks($playlist, $desc = 0) { - $query = "SELECT tag, artist, track, album, label, id FROM tracks " . + $query = "SELECT tag, artist, track, album, label, id, created FROM tracks " . "WHERE list = ? ORDER BY id"; if($desc) $query .= " DESC"; @@ -178,23 +180,59 @@ public function getTracks($playlist, $desc = 0) { $stmt->bindValue(1, (int)$playlist, \PDO::PARAM_INT); return $this->execute($stmt); } - - public function insertTrack($playlist, $tag, $artist, $track, $album, $label) { + + // return true if "now" is within the show start/end time & date. + // NOTE: this routine must be tolerant of improperly formatted dates. + public function isWithinShow($listRow) { + $TIME_FORMAT = "Y-m-d Gi"; // eg, 2019-01-01 1234 + $retVal = false; + + try { + $timeAr = explode("-", $listRow[2]); + if (count($timeAr) == 2) { + $timeStr1 = $listRow[1] . " " . $timeAr[0]; + $start = DateTime::createFromFormat($TIME_FORMAT, $timeStr1); + $endStr = $timeAr[1] == "0000" ? "2359" : $timeAr[1]; + $timeStr2 = $listRow[1] . " " . $endStr; + $end = DateTime::createFromFormat($TIME_FORMAT, $timeStr2); + + if (isset($start) && isset($end)) { + $now = new DateTime("now"); + $retVal = (($now > $start) && ($now < $end)); + } + } + } catch (Throwable $t) { + ; + } + return $retVal; + } + + public function insertTrack($playlistId, $tag, $artist, $track, $album, $label) { + $row = Engine::api(IPlaylist::class)->getPlaylist($playlistId, 1); + + // log time iff 'now' is within playlist start/end time. + $doTimestamp = self::isWithinShow($row); + $timeName = $doTimestamp ? "created, " : ""; + $timeValue = $doTimestamp ? "NOW(), " : ""; + // Insert tag? - $noTag = ($tag == 0) || ($tag == ""); + $haveTag = ($tag != 0) && ($tag != ""); + $tagName = $haveTag ? ", tag" : ""; + $tagValue = $haveTag ? ", ?" : ""; - $query = "INSERT INTO tracks " . - "(list, artist, track, album, label" . ($noTag?")":", tag)") . - " VALUES (?, ?, ?, ?, ?" . - ($noTag?")":", ?)"); + $names = "(" . $timeName . "list, artist, track, album, label " . $tagName . ")"; + $values = " VALUES (" . $timeValue . "?, ?, ?, ?, ?" . $tagValue . ");"; + + $query = "INSERT INTO tracks " . ($names) . ($values); $stmt = $this->prepare($query); - $stmt->bindValue(1, (int)$playlist, \PDO::PARAM_INT); + $stmt->bindValue(1, (int)$playlistId, \PDO::PARAM_INT); $stmt->bindValue(2, $artist); $stmt->bindValue(3, $track); $stmt->bindValue(4, $album); $stmt->bindValue(5, $label); - if(!$noTag) + if($haveTag) $stmt->bindValue(6, $tag); + return $stmt->execute(); } diff --git a/ui/Playlists.php b/ui/Playlists.php index 5fb20eab8..6de7d1ea1 100644 --- a/ui/Playlists.php +++ b/ui/Playlists.php @@ -24,6 +24,7 @@ namespace ZK\UI; + use ZK\Engine\Engine; use ZK\Engine\IDJ; use ZK\Engine\ILibrary; @@ -122,6 +123,24 @@ private function composeTime($fromTime, $toTime) { return $fromTime . "-" . $toTime; } + public static function showStartTime($timeRange) { + $retVal = "??"; + $timeAr = explode("-", $timeRange); + if (count($timeAr) == 2) + $retVal = self::hourToAMPM($timeAr[0]); + + return $retVal; + } + + public static function showEndTime($timeRange) { + $retVal = "??"; + $timeAr = explode("-", $timeRange); + if (count($timeAr) == 2) + $retVal = self::hourToAMPM($timeAr[1]); + + return $retVal; + } + public static function hourToAMPM($hour, $full=0) { $h = (int)floor($hour/100); $m = (int)$hour % 100; @@ -147,6 +166,22 @@ public static function timeToAMPM($time) { } else return strtolower(htmlentities($time)); } + + public static function timestampToDate($time) { + if ($time == null || $time == '') { + return ""; + } else { + return date('D M d, Y ', strtotime($time)); + } + } + + public static function timestampToAMPM($time) { + if ($time == null || $time == '') { + return ""; + } else { + return date('h:ia', strtotime($time)); + } + } public static function timeToZulu($time) { if(strlen($time) == 9 && $time[4] == '-') { @@ -237,6 +272,7 @@ private function emitEditList($editlist) { list($year, $month, $day) = explode("-", $date); if(strlen($fromtime) && strlen($totime)) $time = $this->composeTime($fromtime, $totime); + if(checkdate($month, $day, $year) && ($time != "") && ($description != "")) { // Success - Run the query @@ -456,6 +492,17 @@ public function emitEditListSelNormal() { UI::setFocus("playlist"); } + private function makeEditDiv($row, $playlist) { + $sessionId = $this->session->getSessionID(); + $href = "?session=" . $sessionId . "&playlist=" . $playlist . "&id=" . + $row["id"] . "&action=" . $this->action . "&"; + $editLink = ""; + $upLink = ""; + $downLink = ""; + $retVal = "
" . $upLink . $downLink . $editLink . "
"; + return $retVal; + } + public function emitEditListSelDeleted() { ?>
@@ -484,34 +531,17 @@ public function emitEditListSelDeleted() { UI::setFocus("playlist"); } - private function emitList($playlist, $id) { - ?> -
- - getTracks($playlist, 1); - while($records && ($row = $records->fetch())) { - if(!$header) { - echo " \n"; - $header = 1; - } - $class = ($id==$row["id"])?"sel":"nav"; - echo " \n"; - echo " \n"; - if(substr($row["artist"], 0, strlen(IPlaylist::SPECIAL_TRACK)) == IPlaylist::SPECIAL_TRACK) - echo " \n"; - else - echo " \n"; - } - echo "
 TagArtistTrackAlbum/Label
session->getSessionID()."&playlist=$playlist&id=".$row["id"]."&action=$this->action&seq=upTrack\">\n"; - echo " \"\"
\n"; - echo " \"\"
\n"; - echo " session->getSessionID()."&playlist=$playlist&id=".$row["id"]."&action=$this->action&seq=downTrack\">\n"; - echo " \"\"
session->getSessionID()."&playlist=$playlist&id=".$row["id"]."&action=$this->action&seq=editTrack\">>>
".$row["tag"]."" . - $this->smartURL($row["artist"]) . "" . - $this->smartURL($row["track"]) . "" . - $this->smartURL($row["album"], !$row["tag"]) . "
" . - $this->smartURL($row["label"]) . "
\n"; + // make header for edit & view playlist + private function makePlaylistHeader($isEditMode) { + $editCol = $isEditMode ? "" : ""; + $header = "" . $editCol . "Time" . + "ArtistTrackAlbum/Label"; + return $header; + } + + private function editPlaylist($playlist, $id) { + print("
"); + self::emitPlaylistBody($playlist, true); } private function emitTagForm($playlist, $message) { @@ -705,9 +735,10 @@ private function emitPlaylistTitle($playlist) { // Print the header $script = "?target=export"; $row = Engine::api(IPlaylist::class)->getPlaylist($playlist); + $showDateTime = self::makeShowDateAndTime($row); echo "\n \n \n \n"; - echo " \n \n
"; - echo "Playlist for $row[0]$row[1]Printable playlist
\n"; + echo "$row[0]\n $showDateTime\n"; + echo "
Print\n \n \n"; } private function insertSetSeparator($playlist) { @@ -817,7 +848,7 @@ public function emitEditor() { ?> - emitList($playlist, $id); ?> + editPlaylist($playlist, $id); ?> /" . self::smartURL($row["label"]) . ""; + if($row["tag"]) { + $albumTitle = "".$albumName .""; + + if ($includeLabel) { + $albumTitle = $albumTitle . $labelSpan; + } + } else { + $albumTitle = $this->smartURL($albumName); + if ($includeLabel) + $albumTitle = $albumTitle . $labelSpan; + } + return $albumTitle; + } + + + // converts "last, first" to "first last" + private function swapNames($fullName) { + $retVal = $fullName; + $names1 = explode(", ", $fullName); + if (count($names1) >= 2) { + $names2 = explode(" ", $names1[1]); + $extras = array_slice($names2, 1); + $retVal = $names2[0] . " " . $names1[0] . " " . join(" ", $extras); + } + return $retVal; + } + + private function emitPlaylistBody($playlist, $editMode) { + $REVIEW_DIV = "
"; + $header = self::makePlaylistHeader($editMode); + $editCell = ""; + echo "".$header; + + $records = Engine::api(IPlaylist::class)->getTracks($playlist); + self::viewListGetAlbums($records, $albums); + Engine::api(ILibrary::class)->markAlbumsReviewed($albums); + + if($albums != null && sizeof($albums) > 0) { + foreach($albums as $index => $row) { + if ($editMode) + $editCell = ""; + + if(substr($row["artist"], 0, strlen(IPlaylist::SPECIAL_TRACK)) == IPlaylist::SPECIAL_TRACK) { + echo "".$editCell.""; + continue; + } + + $reviewCell = $row["REVIEWED"] ? $REVIEW_DIV : ""; + $artistName = self::swapNames($row["artist"]); + $timeplayed = self::timestampToAMPM($row["created"]); + $albumLink = self::makeAlbumLink($row, true); + echo "" . $editCell . + "" . + "" . + "" . + "" . + "" . + "\n"; + } + } + echo "
" . self::makeEditDiv($row, $playlist) . "

" . $timeplayed . "" . $this->smartURL($artistName) . "" . $this->smartURL($row["track"]) . "" . $reviewCell . "" . $albumLink . "
\n"; + } + + private function makeShowDateAndTime($row) { + $showStart = self::showStartTime($row[2]); + $showEnd = self::showEndTime($row[2]); + $showDate = self::timestampToDate($row[1]); + $showDateTime = $showDate . " " . $showStart . "-" . $showEnd; + return $showDateTime; + } + private function viewList($playlist) { $row = Engine::api(IPlaylist::class)->getPlaylist($playlist, 1); - if($row) { - list($y,$m,$d) = explode("-", $row[1]); - - $script = "?target=export"; - - echo "\n \n
Printable playlist
\n"; - echo "\n"; - echo "\n"; - echo "\n"; - echo "\n"; - echo "
Playlist for $row[0]" . - date("l, j F Y", mktime(0,0,0,$m,$d,$y)) . "  " . - self::timeToAMPM($row[2]) . "DJ: "; - echo "session->getSessionID()."&viewuser=$row[3]\" CLASS=\"nav2\">$row[4]"; - echo "
\n"; - - // Print the tracks - echo "\n"; - echo " \n"; - $records = Engine::api(IPlaylist::class)->getTracks($playlist); - $this->viewListGetAlbums($records, $albums); - Engine::api(ILibrary::class)->markAlbumsReviewed($albums); - if(sizeof($albums) > 0) - foreach($albums as $index => $row) { - if(substr($row["artist"], 0, strlen(IPlaylist::SPECIAL_TRACK)) == IPlaylist::SPECIAL_TRACK) { - echo " \n"; - continue; - } - echo " \n"; - } - echo "
ArtistTrackAlbum/Label

" . $this->smartURL($row["artist"]) . "" . - $this->smartURL($row["track"]) . ""; - if($row["REVIEWED"]) - echo "session->getSessionID(). - "\">\"[i]\""; - else - echo ""; - if($row["tag"]) echo "session->getSessionID(). - "\" CLASS=\"nav\">"; - - echo $this->smartURL($row["album"], !$row["tag"]); - if($row["tag"]) echo ""; - echo "
" . - $this->smartURL($row["LABELNAME"]) . "
\n"; - } else - echo "Sorry, the playlist you have requested does not exist."; + if( !$row) { + echo "Sorry, playlist does not exist " . $playlist . ""; + return; + } + + $showName = $row[0]; + $djId = $row[3]; + $djName = $row[4]; + $showDateTime = self::makeShowDateAndTime($row); + + // make print view header + echo "" . + "\n
session->getSessionID() . "&playlist='" . $playlist . + "&format=html)'>Print View
"; + + $dateDiv = "
".$showDateTime." 
"; + $djLink = "$djName"; + echo "
 " . $showName . " with " . $djLink.$dateDiv . "
"; + + self::emitPlaylistBody($playlist, false); } private function emitViewDJSortFn($a, $b) {