diff --git a/api/Playlists.php b/api/Playlists.php index 6e80cf3f..55c7942f 100644 --- a/api/Playlists.php +++ b/api/Playlists.php @@ -680,15 +680,13 @@ public function deleteResource(RequestInterface $request): ResponseInterface { } private function injectMetadata($api, $rqMeta, $rsMeta, $listId, $size, $entry) { - ob_start(); $action = $rqMeta->getOptional("action", ""); - PlaylistBuilder::newInstance([ + $fragment = PlaylistBuilder::newInstance([ "action" => $action, "editMode" => true, "authUser" => true ])->observe($entry); - $rsMeta->set("html", ob_get_contents()); - ob_end_clean(); + $rsMeta->set("html", $fragment); // seq is one of: // -1 client playlist is out of sync with the service diff --git a/ui/PlaylistBuilder.php b/ui/PlaylistBuilder.php index 6f69fe5a..2c5aa06f 100644 --- a/ui/PlaylistBuilder.php +++ b/ui/PlaylistBuilder.php @@ -32,13 +32,7 @@ class PlaylistBuilder extends PlaylistObserver { private const PARAMS = [ "action", "editMode", "authUser" ]; protected $params; - protected $break; - - protected static function timestampToLocale($timestamp) { - // colon is included in 24hr format for symmetry with fxtime - $timeSpec = UI::isUsLocale() ? 'h:i a' : 'H:i'; - return $timestamp ? date($timeSpec, $timestamp) : ''; - } + protected $template; public static function newInstance(array $params) { // validate all parameters are present @@ -54,88 +48,35 @@ public static function newInstance(array $params) { return new PlaylistBuilder($params); } - protected function makeAlbumLink($entry, $includeLabel) { - $albumName = $entry->getAlbum(); - $labelName = $entry->getLabel(); - - if(empty($albumName) && empty($labelName)) - return ""; - - $albumTitle = $entry->getTag() ? - "" . htmlentities($albumName) . "" : - UI::smartURL($albumName); - - if($includeLabel) - $albumTitle .= " / " . UI::smartURL($labelName) . ""; - - return $albumTitle; - } - - protected function makeEditDiv($entry) { - $href = "?id=" . $entry->getId() . "&subaction=" . $this->params["action"] . "&seq=editTrack"; - $editLink = ""; - $dnd = "
"; - return "
" . $dnd . $editLink . "
"; + protected function renderBlock($block, $entry) { + return $this->template->renderBlock($block, [ + "params" => $this->params, + "entry" => $entry + ]); } protected function __construct(array $params) { + $templateFact = new TemplateFactoryUI(); + $this->template = $templateFact->load('list/block-event.html'); $this->params = $params; + $this->params['break'] = false; + $this->params['usLocale'] = UI::isUsLocale(); $this->on('comment', function($entry) { - $editCell = $this->params["editMode"] ? "" . - $this->makeEditDiv($entry) . "" : ""; - $created = $entry->getCreatedTimestamp(); - $timeplayed = self::timestampToLocale($created); - echo "" . $editCell . - "$timeplayed" . - "".UI::markdown($entry->getComment()). - "\n"; - $this->break = false; + $fragment = $this->renderBlock('comment', $entry); + $this->params['break'] = false; + return $fragment; })->on('logEvent', function($entry) { - $created = $entry->getCreatedTimestamp(); - $timeplayed = self::timestampToLocale($created); - if($this->params["authUser"]) { - // display log entries only for authenticated users - $editCell = $this->params["editMode"] ? "" . - $this->makeEditDiv($entry) . "" : ""; - echo "" . $editCell . - "$timeplayed" . - "".$entry->getLogEventType()."" . - "".$entry->getLogEventCode()."" . - "\n"; - $this->break = false; - } else if(!$this->break) { - echo "" . - "$timeplayed
\n"; - $this->break = true; - } + $fragment = $this->renderBlock('logEvent', $entry); + $this->params['break'] = !$this->params['authUser']; + return $fragment; })->on('setSeparator', function($entry) { - if($this->params["editMode"] || !$this->break) { - $editCell = $this->params["editMode"] ? "" . - $this->makeEditDiv($entry) . "" : ""; - $created = $entry->getCreatedTimestamp(); - $timeplayed = self::timestampToLocale($created); - echo "" . $editCell . - "$timeplayed
\n"; - $this->break = true; - } + $fragment = $this->renderBlock('setSeparator', $entry); + $this->params['break'] = true; + return $fragment; })->on('spin', function($entry) { - $editCell = $this->params["editMode"] ? "" . - $this->makeEditDiv($entry) . "" : ""; - $created = $entry->getCreatedTimestamp(); - $timeplayed = self::timestampToLocale($created); - $reviewCell = $entry->getReviewed() ? "
" : ""; - $artistName = $entry->getTag() ? PlaylistEntry::swapNames($entry->getArtist()) : $entry->getArtist(); - - $albumLink = $this->makeAlbumLink($entry, true); - echo "" . $editCell . - "$timeplayed" . - "" . UI::smartURL($artistName) . "" . - "" . UI::smartURL($entry->getTrack()) . "" . - "$reviewCell" . - "$albumLink" . - "\n"; - $this->break = false; + $fragment = $this->renderBlock('spin', $entry); + $this->params['break'] = false; + return $fragment; }); } } diff --git a/ui/Playlists.php b/ui/Playlists.php index f1dc42e1..2dd24605 100644 --- a/ui/Playlists.php +++ b/ui/Playlists.php @@ -263,48 +263,28 @@ private function getDJAirNames() { return $airNames; } - // make header for edit & view playlist - private function makePlaylistHeader($isEditMode) { - $editCol = $isEditMode ? "" : ""; - $header = "" . $editCol . "Time" . - "ArtistTrackAlbum/Label"; - return $header; - } - private function emitPlaylistBody($playlist, $editMode) { - $header = $this->makePlaylistHeader($editMode); - $editCell = ""; - echo "\n"; - echo "" . $header . ""; - $api = Engine::api(IPlaylist::class); - $entries = $api->getTracks($playlist, $editMode)->asArray(); - Engine::api(ILibrary::class)->markAlbumsReviewed($entries); + $tracks = $api->getTracks($playlist, $editMode)->asArray(); + Engine::api(ILibrary::class)->markAlbumsReviewed($tracks); $observer = PlaylistBuilder::newInstance([ "action" => $this->subaction, "editMode" => $editMode, "authUser" => $this->session->isAuth("u") ]); - echo "\n"; - if($entries != null && sizeof($entries) > 0) { - foreach($entries as $entry) - $observer->observe(new PlaylistEntry($entry)); - } - echo "
\n"; - if($editMode) { - UI::emitJS('js/playlists.track.js'); - } else { - $show = $api->getPlaylist($playlist); - if($api->isNowWithinShow($show)) - UI::emitJS('js/playlists.live.js'); - } + $entries = array_map(function($track) { + return new PlaylistEntry($track); + }, $tracks); + + $this->addVar("observer", $observer); + $this->addVar("entries", $entries); + $this->addVar("editMode", $editMode); } private function emitPlaylistBanner($playlistId, $playlist, $editMode) { $showName = $playlist['description']; - $djId = $playlist['id']; $djName = $playlist['airname'] ?? "None"; $showDateTime = self::makeShowDateAndTime($playlist); @@ -312,280 +292,108 @@ private function emitPlaylistBanner($playlistId, $playlist, $editMode) { $this->extra = "Share Playlist: "; - if(!$editMode && $this->session->isAuth("v")) - $showDateTime .= " 
"; - - $djName = htmlentities($djName, ENT_QUOTES, 'UTF-8'); - $djLink = $djId ? "$djName" : $djName; - - echo "
{$showDateTime} 
\n"; -?> - - emitPlaylistBody($playlistId, true); + $this->addVar("showDateTime", $showDateTime); } private function emitTagForm($playlistId, $message) { $playlist = Engine::api(IPlaylist::class)->getPlaylist($playlistId, 1); $this->emitPlaylistBanner($playlistId, $playlist, true); $this->emitTrackAdder($playlistId, $playlist); + $this->setTemplate('list/entry-add.html'); } private function emitTrackAdder($playlistId, $playlist, $editTrack = false) { - $isLiveShow = !$editTrack && Engine::api(IPlaylist::class)->isNowWithinShow($playlist); - $nmeAr = Engine::param('nme'); - $nmeOpts = ''; - $nmePrefix = self::NME_PREFIX; - if ($nmeAr) { - foreach ($nmeAr as $nme) - $nmeOpts = $nmeOpts . ""; - } - - ?> -
- - - - - - -
- - - - - -
-
-
-
- - data-focus /> - Artist name or tag number - - -
-
- - autocomplete='off'/> - - -
-
- - /> -
-
- - /> -
-
-
-
- - -
- (0/ characters)
- formatting help -
- - -
- -
-
-
- - data-focus/> -
-
-
- getTimestampWindow($playlistId); - $time = null; - $api->getTracksWithObserver($playlistId, - (new PlaylistObserver())->on('comment logEvent setSeparator spin', function($entry) use(&$time, $editTrack) { - $created = $entry->getCreatedTime(); - if($created) $time = $created; - return $editTrack === $entry->getId(); - }) - ); - if(!$time) { - $startTime = $api->getTimestampWindow($playlistId, false)['start']; - $time = $startTime->format('H:i:s'); - } + $api = Engine::api(IPlaylist::class); - // this is probably unnecessary, as desktop browsers *should* - // degrade 'tel' to 'text', *but* as this is a hack to - // deal with the lack of keyDown support in mobile input - // type=text, we'll include 'tel' only for mobile devices... - $ttype = preg_match('/tablet|mobile|android/i', - $_SERVER['HTTP_USER_AGENT'] ?? '') ? "tel" : "text"; - - // colon is included in 24hr format for symmetry with fxtime, - // which it is referencing - $timeSpec = UI::isUsLocale() ? 'g:i a' : 'H:i'; - $startAMPM = $window['start']->format($timeSpec); - $endAMPM = $window['end']->format($timeSpec); - $timeMsg = "($startAMPM - $endAMPM)"; - - echo "
- - format('H:i')."' max='".$window['end']->format('H:i')."' data-live='".($isLiveShow?1:0)."' data-last-val='$time' /> - $timeMsg -
\n"; - ?> -
- -
- - - - - - - - -
-
-
-
-
-
-
-

You have reached the end time of your show.

-

Extend by: -

-
- - -
-
-
- addVar('NME_PREFIX', self::NME_PREFIX); + $this->addVar('BASE_URL', Engine::getBaseURL()); + + /* TZO is server equivalent of javascript Date.getTimezoneOffset() */ + $this->addVar('TZO', round(date('Z')/-60, 2)); + $this->addVar('playlistId', $playlistId); + $this->addvar('playlist', $playlist); + $this->addVar('editTrack', $editTrack); + $this->addVar('isLive', $api->isNowWithinShow($playlist)); + + $this->addVar('MAX_FIELD_LENGTH', PlaylistEntry::MAX_FIELD_LENGTH); + $this->addVar('MAX_COMMENT_LENGTH', PlaylistEntry::MAX_COMMENT_LENGTH); + $this->addVar('nmes', Engine::param('nme')); + + $window = $api->getTimestampWindow($playlistId); + $time = null; + $api->getTracksWithObserver($playlistId, + (new PlaylistObserver())->on('comment logEvent setSeparator spin', function($entry) use(&$time, $editTrack) { + $created = $entry->getCreatedTime(); + if($created) $time = $created; + return $editTrack === $entry->getId(); + }) + ); + if(!$time) { + $startTime = $api->getTimestampWindow($playlistId, false)['start']; + $time = $startTime->format('H:i:s'); + } + $this->addVar('time', $time); + + // this is probably unnecessary, as desktop browsers *should* + // degrade 'tel' to 'text', *but* as this is a hack to + // deal with the lack of keyDown support in mobile input + // type=text, we'll include 'tel' only for mobile devices... + $ttype = preg_match('/tablet|mobile|android/i', + $_SERVER['HTTP_USER_AGENT'] ?? '') ? "tel" : "text"; + $this->addVar("ttype", $ttype); + + // colon is included in 24hr format for symmetry with fxtime, + // which it is referencing + $timeSpec = UI::isUsLocale() ? 'g:i a' : 'H:i'; + $startAMPM = $window['start']->format($timeSpec); + $endAMPM = $window['end']->format($timeSpec); + $timeMsg = "($startAMPM - $endAMPM)"; + $this->addVar("window", $window); + $this->addVar("timeMsg", $timeMsg); } private function emitEditForm($playlistId, $id, $album) { - ?> -
 Editing highlighted item
- - getType()) { - case PlaylistEntry::TYPE_SET_SEPARATOR: - $type = "set-separator"; - break; - case PlaylistEntry::TYPE_COMMENT: - $type = "comment-entry"; - echo "\n"; - break; - case PlaylistEntry::TYPE_LOG_EVENT: - $type = self::NME_PREFIX . $entry->getLogEventType(); - echo "\n"; - break; - default: - $type = "manual-entry"; - foreach (['tag', 'artist', 'album', 'label', 'title'] as $field) - echo "\n"; - break; - } - echo "\n"; - echo "\n"; - $playlist = Engine::api(IPlaylist::class)->getPlaylist($playlistId, 1); $showName = $playlist['description']; $djName = $playlist['airname'] ?? "None"; $this->title = "$showName with $djName " . self::timestampToDate($playlist['showdate']); $this->emitTrackAdder($playlistId, $playlist, $id); + $this->addVar("entry", $entry); + $this->setTemplate('list/entry-edit.html'); } public function emitEditor() { - $playlist = $_REQUEST["playlist"] ?? null; + $playlistId = $_REQUEST["playlist"] ?? null; $seq = $_REQUEST["seq"] ?? null; $id = $_REQUEST["id"] ?? null; - ?> - - - -
- getTrack($id); if($albuminfo) { // if editing a track, always get the playlist from // the track, even if one is supplied in the request - $playlist = $albuminfo['list']; + $playlistId = $albuminfo['list']; } } $message = ""; - if(is_null($playlist) || !$this->isOwner($playlist)) { + if(is_null($playlistId) || !$this->isOwner($playlistId)) { $seq = "error"; $message = "access error"; } switch ($seq) { case "editTrack": - $this->emitEditForm($playlist, $id, $albuminfo); + $this->emitEditForm($playlistId, $id, $albuminfo); break; default: - $this->emitTagForm($playlist, $message); + $this->emitTagForm($playlistId, $message); break; } - ?> -
- editPlaylist($playlist, $id); ?> -
- emitPlaylistBody($playlistId, true); } private function insertTrack($playlistId, $tag, $artist, $track, $album, $label, $spinTime) { @@ -873,6 +681,10 @@ private function viewList($playlistId) { $this->emitPlaylistBanner($playlistId, $row, false); $this->emitPlaylistBody($playlistId, false); + + $this->addVar("playlist", $row); + $this->addVar("playlistId", $playlistId); + $this->setTemplate('list/view.html'); } public function emitViewDJ() { diff --git a/ui/TemplateFactoryUI.php b/ui/TemplateFactoryUI.php index 4f6859ff..1b98426b 100644 --- a/ui/TemplateFactoryUI.php +++ b/ui/TemplateFactoryUI.php @@ -33,6 +33,9 @@ public function __construct() { $filter = new \Twig\TwigFilter('smartURL', [ '\ZK\UI\UICommon', 'smartURL' ], [ 'is_safe' => [ 'html' ] ]); $this->twig->addFilter($filter); + + $filter = new \Twig\TwigFilter('markdown', [ '\ZK\UI\UICommon', 'markdown' ], [ 'is_safe' => [ 'html' ] ]); + $this->twig->addFilter($filter); } public function setContext($menu = null, $menuItem = null, $html = null) { diff --git a/ui/templates/default/list/block-banner.html b/ui/templates/default/list/block-banner.html new file mode 100644 index 00000000..cfb246c9 --- /dev/null +++ b/ui/templates/default/list/block-banner.html @@ -0,0 +1,30 @@ +{% set showName = playlist.description %} +{% set djId = playlist.id %} +{% set djName = playlist.airname ?? "None" %} +{% set djLink = djId ? "" ~ djName | e ~ "" : djName | e %} + +
{{ showDateTime }} +{%- if editMode and app.session.isAuth('v') -%} + 
" +{%- endif -%} + 
+ + diff --git a/ui/templates/default/list/block-body.html b/ui/templates/default/list/block-body.html new file mode 100644 index 00000000..e7df4dae --- /dev/null +++ b/ui/templates/default/list/block-body.html @@ -0,0 +1,22 @@ + + + + {{ editMode ? "" }} + + + + + + + + + {%~ for entry in entries %} + {{~ observer.observe(entry) | raw }} + {%~ endfor %} + +
TimeArtistTrackAlbum/Label
+{% if editMode %} + +{% elseif isLive %} + +{% endif %} diff --git a/ui/templates/default/list/block-entry.html b/ui/templates/default/list/block-entry.html new file mode 100644 index 00000000..e31371bf --- /dev/null +++ b/ui/templates/default/list/block-entry.html @@ -0,0 +1,115 @@ +{% set isLiveShow = not editTrack and isLive %} +
+ + {#~ TZO is server equivalent of javascript Date.getTimezoneOffset() #} + + + + + +
+ {%~ if not editTrack %} + + {% endif %} + + +
+
+
+
+ + + Artist name or tag number + + +
+
+ + + + +
+
+ + +
+
+ + +
+
+
+
+ + +
+ (0/{{ MAX_COMMENT_LENGTH }} characters)
+ formatting help +
+ + +
+ {%~ include 'block-markdown-help.html' %} +
+
+
+ + +
+
+
+
+ + + {{ timeMsg }} +
+
+ +
+ {% if editTrack %} + + + + {% else %} + + + {% endif %} +
+
+
+
+
+
+
+

You have reached the end time of your show.

+

Extend by: +

+
+ + +
+
+
diff --git a/ui/templates/default/list/block-event.html b/ui/templates/default/list/block-event.html new file mode 100644 index 00000000..25e32ed8 --- /dev/null +++ b/ui/templates/default/list/block-event.html @@ -0,0 +1,72 @@ +{% macro makeTime(params, entry) %} + {%~ set created = entry.getCreatedTimestamp() %} + {%~ set format = params.usLocale ? "h:i a" : "H:i" %} + {{ created ? created | date(format) }} +{%- endmacro %} + +{% macro makeEditDiv(params, entry) %} + {%~ if params.editMode %} + {%~ set href = "?id=" ~ entry.getId() | e('url') ~ "&subaction=" ~ params.action | e('url') ~ "&seq=editTrack" %} + {%~ set editLink = "" %} + {%~ set dnd = "
" %} + +
{{ dnd | raw }}{{ editLink | raw }}
+ + {%~ endif %} + {{~ _self.makeTime(params, entry) }} +{%- endmacro %} + +{% macro makeAlbumLink(entry, includeLabel) %} + {%~ set albumName = entry.getAlbum() %} + {%~ set labelName = entry.getLabel() %} + {%~ if albumName | length or labelName | length %} + {%~ set albumTitle = entry.getTag() ? "" ~ albumName | e ~ "" : albumName | e %} + {%~ if includeLabel %} + {%~ set albumTitle = albumTitle ~ " / " ~ labelName | e ~ "" %} + {%~ endif %} + {{~ albumTitle | raw -}} + {%~ endif %} +{% endmacro %} + +{% block comment %} + + {{~ _self.makeEditDiv(params, entry) }} + {{ entry.getComment() | markdown }} + +{% endblock %} + +{% block logEvent %} + {%~ if params.authUser %} + + {{~ _self.makeEditDiv(params, entry) }} + {{ entry.getLogEventType() }} + {{ entry.getLogEventCode() }} + + {%~ elseif not params.break %} + + {{~ _self.makeTime(params, entry) }} +
+ + {%~ endif %} +{% endblock %} + +{% block setSeparator %} + {%~ if params.editMode or not params.break %} + + {{~ _self.makeEditDiv(params, entry) }} +
+ + {%~ endif %} +{% endblock %} + +{% block spin %} + {%~ set reviewCell = entry.getReviewed() ? "
" %} + {%~ set artistName = entry.getTag() ? entry.swapNames(entry.getArtist()) : entry.getArtist() %} + + {{~ _self.makeEditDiv(params, entry) }} + {{ artistName | smartURL }} + {{ entry.getTrack() | smartURL }} + {{ reviewCell | raw }} + {{ _self.makeAlbumLink(entry, true) }} + +{% endblock %} diff --git a/ui/templates/default/list/entry-add.html b/ui/templates/default/list/entry-add.html new file mode 100644 index 00000000..700b2433 --- /dev/null +++ b/ui/templates/default/list/entry-add.html @@ -0,0 +1,3 @@ +{% include 'list/block-banner.html' %} +{% include 'list/block-entry.html' %} +{% include 'list/block-body.html' %} diff --git a/ui/templates/default/list/entry-edit.html b/ui/templates/default/list/entry-edit.html new file mode 100644 index 00000000..cf7b9450 --- /dev/null +++ b/ui/templates/default/list/entry-edit.html @@ -0,0 +1,23 @@ +
 Editing highlighted item
+ +{% if entry.getType() == constant('TYPE_SET_SEPARATOR', entry) %} +{% set type='set-separator' %} +{% elseif entry.getType() == constant('TYPE_COMMENT', entry) %} +{% set type='comment-entry' %} + +{% elseif entry.getType() == constant('TYPE_LOG_EVENT', entry) %} +{% set type=NME_PREFIX ~ entry.getLogEventType() %} + +{% else %} +{% set type='manual-entry' %} +{% set album = entry.asArray() %} +{% for field in ['tag', 'artist', 'album', 'label', 'title'] %} + +{% endfor %} +{% endif %} + + + + +{% include 'list/block-entry.html' %} +{% include 'list/block-body.html' %} diff --git a/ui/templates/default/list/view.html b/ui/templates/default/list/view.html new file mode 100644 index 00000000..13324fe2 --- /dev/null +++ b/ui/templates/default/list/view.html @@ -0,0 +1,2 @@ +{% include 'list/block-banner.html' %} +{% include 'list/block-body.html' %}