Make timezone Station-specific and remove now-unused offset code.

This commit is contained in:
Buster Neece 2019-05-13 16:25:36 -05:00
parent 6e618ef595
commit 48427d46a3
No known key found for this signature in database
GPG Key ID: 6D9E12FF03411F4E
17 changed files with 215 additions and 388 deletions

View File

@ -281,18 +281,6 @@ return [
]
],
'schedule_tz' => [
'select',
[
'label' => __('Time Zone'),
'label_class' => 'mb-2',
'description' => __('The time zone for these scheduled times and the days specified below.'),
'options' => \Azura\Timezone::fetchSelect(),
'default' => $customization->getTimeZone(),
'form_group_class' => 'col-md-6 mt-1',
]
],
'schedule_days' => [
'checkbox',
[
@ -307,7 +295,7 @@ return [
6 => __('Saturday'),
7 => __('Sunday'),
],
'form_group_class' => 'col-md-6 mt-3',
'form_group_class' => 'col-md-12 mt-3',
]
],

View File

@ -89,19 +89,6 @@ return [
'legend_class' => 'col-sm-12',
'elements' => [
'timezone' => [
'select',
[
'label' => __('Time Zone'),
'description' => __('All times displayed on the site will be based on this time zone.') . '<br>' . sprintf(__('Current server time is <b>%s</b>.'),
date('g:ia')),
'options' => \Azura\Timezone::fetchSelect(),
'default' => \App\Customization::DEFAULT_TIMEZONE,
'label_class' => 'mb-2',
'form_group_class' => 'col-sm-12 mt-3',
]
],
'locale' => [
'radio',
[

View File

@ -31,18 +31,6 @@ return [
],
],
Entity\Settings::TIMEZONE => [
'select',
[
'label' => __('System Default Time Zone'),
'description' => __('For users who have not customized their time zone, all times displayed on the site will be based on this time zone.'),
'options' => \Azura\Timezone::fetchSelect(),
'default' => 'UTC',
'label_class' => 'mb-2',
'form_group_class' => 'col-sm-12 mt-3',
],
],
Entity\Settings::PREFER_BROWSER_URL => [
'toggle',
[

View File

@ -61,6 +61,18 @@ return [
]
],
'timezone' => [
'select',
[
'label' => __('Time Zone'),
'description' => __('Scheduled playlists and other timed items will be controlled by this time zone.'),
'options' => \Azura\Timezone::fetchSelect(),
'default' => \App\Customization::DEFAULT_TIMEZONE,
'label_class' => 'mb-2',
'form_group_class' => 'col-sm-12 mt-3',
]
],
'enable_public_page' => [
'toggle',
[

View File

@ -52,6 +52,9 @@ class PlaylistsController extends AbstractStationCrudController
}
}
$tz = new \DateTimeZone($station->getTimezone());
$now = Chronos::now($tz);
$playlists = [];
$songs_query = $this->em->createQuery(/** @lang DQL */'
@ -63,8 +66,16 @@ class PlaylistsController extends AbstractStationCrudController
foreach ($all_playlists as $playlist) {
$playlist_row = $this->record_repo->toArray($playlist);
if ($playlist->getIsEnabled() && $playlist->getType() === 'default') {
$playlist_row['probability'] = round(($playlist->getWeight() / $total_weights) * 100, 1) . '%';
if ($playlist->getIsEnabled()) {
if (Entity\StationPlaylist::TYPE_DEFAULT === $playlist->getType()) {
$playlist_row['probability'] = round(($playlist->getWeight() / $total_weights) * 100, 1) . '%';
} elseif (Entity\StationPlaylist::TYPE_SCHEDULED === $playlist->getType()) {
$schedule_start = Entity\StationPlaylist::getDateTime($playlist->getScheduleStartTime(), $now);
$schedule_end = Entity\StationPlaylist::getDateTime($playlist->getScheduleEndTime(), $now);
$playlist_row['schedule_start'] = $schedule_start->toIso8601String();
$playlist_row['schedule_end'] = $schedule_end->toIso8601String();
}
}
$song_totals = $songs_query->setParameter('playlist', $playlist)
@ -93,22 +104,20 @@ class PlaylistsController extends AbstractStationCrudController
*/
public function scheduleAction(Request $request, Response $response, $station_id): ResponseInterface
{
$utc = new \DateTimeZone('UTC');
$user_tz = new \DateTimeZone(date_default_timezone_get());
$station = $request->getStation();
$tz = new \DateTimeZone($station->getTimezone());
$start_date_str = substr($request->getParam('start'), 0, 10);
$start_date = Chronos::createFromFormat('Y-m-d', $start_date_str, $utc)
$start_date = Chronos::createFromFormat('Y-m-d', $start_date_str, $tz)
->subDay();
$end_date_str = substr($request->getParam('end'), 0, 10);
$end_date = Chronos::createFromFormat('Y-m-d', $end_date_str, $utc);
$station = $request->getStation();
$end_date = Chronos::createFromFormat('Y-m-d', $end_date_str, $tz);
/** @var Entity\StationPlaylist[] $all_playlists */
$playlists = $station->getPlaylists()->filter(function($record) {
/** @var Entity\StationPlaylist $record */
return ($record->getType() === 'scheduled');
return ($record->getType() === Entity\StationPlaylist::TYPE_SCHEDULED);
});
$events = [];
@ -132,9 +141,6 @@ class PlaylistsController extends AbstractStationCrudController
$playlist_end = $playlist_end->addDay();
}
$playlist_start = $playlist_start->setTimezone($user_tz);
$playlist_end = $playlist_end->setTimezone($user_tz);
$events[] = [
'id' => $playlist->getId(),
'title' => $playlist->getName(),

View File

@ -38,10 +38,8 @@ class Customization
public function init(Request $request): Request
{
if (!$this->app_settings->isCli() || $this->app_settings->isTesting()) {
$timezone = $this->getTimeZone();
$locale = $this->getLocale();
} else {
$timezone = self::DEFAULT_TIMEZONE;
$locale = self::DEFAULT_LOCALE;
}
@ -57,11 +55,8 @@ class Customization
// Register translation superglobal functions
$translator->register();
self::setGlobalValues($timezone, $locale);
return $request
->withAttribute('locale', $locale)
->withAttribute('timezone', $timezone);
self::setGlobalValues($locale);
return $request->withAttribute('locale', $locale);
}
/**
@ -74,94 +69,6 @@ class Customization
$this->user = $user;
}
/**
* Get the user's custom time zone or the system default.
*
* @return string
*/
public function getTimeZone(): string
{
if ($this->user !== null && !empty($this->user->getTimezone())) {
return $this->user->getTimezone();
}
return $this->getDefaultTimeZone();
}
/**
* Return either the configured global default timezone or the system's regular default.
*
* @return string
*/
public function getDefaultTimeZone(): string
{
$global_tz = $this->settings_repo->getSetting(Entity\Settings::TIMEZONE);
if (!empty($global_tz)) {
return $global_tz;
}
return date_default_timezone_get();
}
/**
* Format the given UNIX timestamp into a locale-friendly time.
*
* @param int $timestamp
* @param bool $use_utc
* @param bool $show_timezone_abbr
* @return string Formatted time for presentation.
*/
public function formatTime($timestamp = null, $use_utc = false, $show_timezone_abbr = false): string
{
$timestamp = $timestamp ?? time();
$time_formats = $this->app_settings['time_formats'];
$locale = $this->getLocale();
$time_format = $time_formats[$locale] ?? $time_formats['default'];
if ($show_timezone_abbr) {
$time_format .= ($use_utc) ? ' \U\T\C' : ' T';
}
return ($use_utc) ? gmdate($time_format, $timestamp) : date($time_format, $timestamp);
}
/**
* Format a date/time using PHP's IntlDateFormatter constants.
*
* @param int $timestamp
* @param bool $use_utc
* @param int|null $date_display One of:
* IntlDateFormatter::NONE - Do not include
* IntlDateFormatter::FULL - (Tuesday, April 12, 1952 AD or 3:30:42pm PST)
* IntlDateFormatter::LONG (default) - (January 12, 1952 or 3:30:32pm)
* IntlDateFormatter::MEDIUM - (Jan 12, 1952)
* IntlDateFormatter::SHORT - (12/13/52 or 3:30pm)
* @param int|null $time_display One of the above.
* @return string
*/
public function formatDateTime(
$timestamp,
$use_utc = false,
$date_display = \IntlDateFormatter::LONG,
$time_display = \IntlDateFormatter::LONG): string
{
$timezone = ($use_utc) ? 'UTC' : date_default_timezone_get();
$locale = str_replace('.UTF-8', '', $this->getLocale());
$fmt = new \IntlDateFormatter(
$locale,
$date_display,
$time_display,
$timezone,
\IntlDateFormatter::GREGORIAN
);
return $fmt->format($timestamp);
}
/**
* Return the user-customized, browser-specified or system default locale.
*
@ -340,20 +247,14 @@ class Customization
}
/**
* @param null $timezone
* @param null $locale
*/
public static function setGlobalValues($timezone = null, $locale = null): void
public static function setGlobalValues($locale = null): void
{
if (empty($timezone)) {
$timezone = self::DEFAULT_TIMEZONE;
}
if (empty($locale)) {
$locale = self::DEFAULT_LOCALE;
}
date_default_timezone_set($timezone);
putenv("LANG=" . $locale);
setlocale(\LC_ALL, $locale);
}

View File

@ -1,34 +0,0 @@
<?php declare(strict_types=1);
namespace App\Entity\Migration;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;
/**
* Auto-generated Migration: Please modify to your needs!
*/
final class Version20190513124232 extends AbstractMigration
{
public function up(Schema $schema) : void
{
// this up() migration is auto-generated, please modify it to your needs
$this->abortIf($this->connection->getDatabasePlatform()->getName() !== 'mysql', 'Migration can only be executed safely on \'mysql\'.');
// Move "play once per day" playlists to be standard scheduled ones with the same start/end time.
$this->addSql('UPDATE station_playlists SET type="scheduled", schedule_start_time=play_once_time, schedule_end_time=play_once_time, schedule_days=play_once_days WHERE type = "once_per_day"');
$this->addSql('ALTER TABLE station_playlists ADD schedule_tz VARCHAR(100) DEFAULT NULL, DROP play_once_time, DROP play_once_days');
// Set all legacy playlists to be scheduled against UTC for back-compatibility.
$this->addSql('UPDATE station_playlists SET schedule_tz="UTC" WHERE type = "scheduled"');
}
public function down(Schema $schema) : void
{
// this down() migration is auto-generated, please modify it to your needs
$this->abortIf($this->connection->getDatabasePlatform()->getName() !== 'mysql', 'Migration can only be executed safely on \'mysql\'.');
$this->addSql('ALTER TABLE station_playlists ADD play_once_time SMALLINT NOT NULL, ADD play_once_days VARCHAR(50) DEFAULT NULL COLLATE utf8mb4_general_ci, DROP schedule_tz');
}
}

View File

@ -0,0 +1,104 @@
<?php declare(strict_types=1);
namespace App\Entity\Migration;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;
/**
* Auto-generated Migration: Please modify to your needs!
*/
final class Version20190513163051 extends AbstractMigration
{
public function up(Schema $schema) : void
{
// this up() migration is auto-generated, please modify it to your needs
$this->abortIf($this->connection->getDatabasePlatform()->getName() !== 'mysql', 'Migration can only be executed safely on \'mysql\'.');
// Move "play once per day" playlists to be standard scheduled ones with the same start/end time.
$this->addSql('UPDATE station_playlists SET type="scheduled", schedule_start_time=play_once_time, schedule_end_time=play_once_time, schedule_days=play_once_days WHERE type = "once_per_day"');
$this->addSql('ALTER TABLE station ADD timezone VARCHAR(100) DEFAULT NULL');
$this->addSql('ALTER TABLE station_playlists DROP play_once_time, DROP play_once_days');
$this->addSql('ALTER TABLE users DROP timezone');
}
public function postUp(Schema $schema)
{
// Use the system setting for "global timezone" to set the station timezones.
$global_tz = $this->connection->fetchColumn('SELECT setting_value FROM settings WHERE setting_key="timezone"');
if (!empty($global_tz)) {
$global_tz = json_decode($global_tz, true);
} else {
$global_tz = 'UTC';
}
// Set all stations' timezones to this value.
$this->connection->update('station', [
'timezone' => $global_tz,
], [1 => 1]);
// Calculate the offset of any currently scheduled playlists.
if ('UTC' !== $global_tz) {
$system_tz = new \DateTimeZone('UTC');
$system_dt = new \DateTime('now', $system_tz);
$system_offset = $system_tz->getOffset($system_dt);
$app_tz = new \DateTimeZone($global_tz);
$app_dt = new \DateTime('now', $app_tz);
$app_offset = $app_tz->getOffset($app_dt);
$offset = $system_offset - $app_offset;
$offset_hours = (int)floor($offset / 3600);
if (0 !== $offset_hours) {
$playlists = $this->connection->fetchAll('SELECT sp.* FROM station_playlists AS sp WHERE sp.type = "scheduled"');
foreach($playlists as $playlist) {
$this->connection->update('station_playlists', [
'schedule_start_time' => $this->_applyOffset($playlist['schedule_start_time'], $offset_hours),
'schedule_end_time' => $this->_applyOffset($playlist['schedule_end_time'], $offset_hours),
], [
'id' => $playlist['id'],
]);
}
}
}
}
/**
* @param mixed $time_code
* @param int $offset_hours
* @return int
*/
protected function _applyOffset($time_code, $offset_hours): int
{
$hours = (int)floor($time_code / 100);
$mins = $time_code % 100;
$hours += $offset_hours;
/** @noinspection SummerTimeUnsafeTimeManipulationInspection */
$hours %= 24;
if ($hours < 0) {
/** @noinspection SummerTimeUnsafeTimeManipulationInspection */
$hours += 24;
}
return ($hours*100)+$mins;
}
public function down(Schema $schema) : void
{
// this down() migration is auto-generated, please modify it to your needs
$this->abortIf($this->connection->getDatabasePlatform()->getName() !== 'mysql', 'Migration can only be executed safely on \'mysql\'.');
$this->addSql('ALTER TABLE station DROP timezone');
$this->addSql('ALTER TABLE station_playlists ADD play_once_time SMALLINT NOT NULL, ADD play_once_days VARCHAR(50) DEFAULT NULL COLLATE utf8mb4_general_ci');
$this->addSql('ALTER TABLE users ADD timezone VARCHAR(100) DEFAULT NULL COLLATE utf8mb4_general_ci');
}
}

View File

@ -1,6 +1,7 @@
<?php
namespace App\Entity;
use App\Customization;
use App\Radio\Quota;
use Azura\Normalizer\Annotation\DeepNormalize;
use Brick\Math\BigInteger;
@ -289,6 +290,14 @@ class Station
*/
protected $storage_used_bytes;
/**
* @ORM\Column(name="timezone", type="string", length=100, nullable=true)
*
* @OA\Property(example="UTC")
* @var string|null The time zone that station operations should take place in.
*/
protected $timezone = 'UTC';
/**
* @ORM\OneToMany(targetEntity="SongHistory", mappedBy="station")
* @ORM\OrderBy({"timestamp_start" = "DESC"})
@ -1114,6 +1123,26 @@ class Station
return Quota::getPercentage($this->getStorageUsedBytes(), $this->getRawStorageAvailable());
}
/**
* @return string
*/
public function getTimezone(): string
{
if (!empty($this->timezone)) {
return $this->timezone;
}
return Customization::DEFAULT_TIMEZONE;
}
/**
* @param string|null $timezone
*/
public function setTimezone(?string $timezone): void
{
$this->timezone = $timezone;
}
/**
* @return Collection
*/

View File

@ -184,14 +184,6 @@ class StationPlaylist
*/
protected $play_per_hour_minute = 0;
/**
* @ORM\Column(name="schedule_tz", type="string", length=100, nullable=true)
*
* @OA\Property(example="UTC")
* @var string|null
*/
protected $schedule_tz = 'UTC';
/**
* @ORM\Column(name="schedule_start_time", type="smallint")
*
@ -515,14 +507,6 @@ class StationPlaylist
return (int)$this->schedule_start_time;
}
/**
* @return string
*/
public function getScheduleStartTimeText(): string
{
return self::formatTimeCodeForInput($this->schedule_start_time);
}
/**
* @param int $schedule_start_time
*/
@ -539,14 +523,6 @@ class StationPlaylist
return (int)$this->schedule_end_time;
}
/**
* @return string
*/
public function getScheduleEndTimeText(): string
{
return self::formatTimeCodeForInput($this->schedule_end_time);
}
/**
* @param int $schedule_end_time
*/
@ -564,10 +540,13 @@ class StationPlaylist
return 0;
}
$start_time = self::getTimestamp($this->schedule_start_time);
$end_time = self::getTimestamp($this->schedule_end_time);
$start_time = self::getDateTime($this->schedule_start_time)
->getTimestamp();
$end_time = self::getDateTime($this->schedule_end_time)
->getTimestamp();
if ($start_time > $end_time) {
/** @noinspection SummerTimeUnsafeTimeManipulationInspection */
return 86400 - ($start_time - $end_time);
}
@ -590,46 +569,6 @@ class StationPlaylist
$this->schedule_days = implode(',', (array)$schedule_days);
}
/**
* @return int
*/
public function getPlayOnceTime(): int
{
return $this->play_once_time;
}
/**
* @return string
*/
public function getPlayOnceTimeText(): string
{
return self::formatTimeCodeForInput($this->play_once_time);
}
/**
* @param int $play_once_time
*/
public function setPlayOnceTime(int $play_once_time): void
{
$this->play_once_time = $play_once_time;
}
/**
* @return array
*/
public function getPlayOnceDays(): array
{
return explode(',', $this->play_once_days);
}
/**
* @param array $play_once_days
*/
public function setPlayOnceDays($play_once_days): void
{
$this->play_once_days = implode(',', (array)$play_once_days);
}
/**
* @return int
*/
@ -791,31 +730,6 @@ class StationPlaylist
}
}
/**
* Given a time code i.e. "2300", return a UNIX timestamp that can be used to format the time for display.
*
* @param string|int $time_code
* @return int
*/
public static function getTimestamp($time_code): int
{
return self::getDateTime($time_code)
->getTimestamp();
}
/**
* Given a time code i.e. "2300", return a time suitable for HTML5 inputs, i.e. "23:00".
*
* @param string|int $time_code
* @return string
*/
public static function formatTimeCodeForInput($time_code): string
{
$now = Chronos::now(new \DateTimeZone(date_default_timezone_get()));
return self::getDateTime($time_code, $now)
->format('H:i');
}
/**
* Return a \DateTime object (or null) for a given time code, by default in the UTC time zone.
*
@ -832,19 +746,4 @@ class StationPlaylist
$time_code = str_pad($time_code, 4, '0', STR_PAD_LEFT);
return $now->setTime(substr($time_code, 0, 2), substr($time_code, 2));
}
/**
* Return the current UTC time in "time code" style.
*
* @param Chronos|null $now
* @return int
*/
public static function getCurrentTimeCode(Chronos $now = null): int
{
if ($now === null) {
$now = Chronos::now(new \DateTimeZone('UTC'));
}
return (int)$now->format('Hi');
}
}

View File

@ -59,14 +59,6 @@ class User
*/
protected $name;
/**
* @ORM\Column(name="timezone", type="string", length=100, nullable=true)
*
* @OA\Property(example="America/Chicago")
* @var string|null
*/
protected $timezone;
/**
* @ORM\Column(name="locale", type="string", length=25, nullable=true)
*
@ -236,22 +228,6 @@ class User
$this->name = $this->_truncateString($name, 100);
}
/**
* @return null|string
*/
public function getTimezone(): ?string
{
return $this->timezone;
}
/**
* @param null|string $timezone
*/
public function setTimezone($timezone): void
{
$this->timezone = $timezone;
}
/**
* @return null|string
*/

View File

@ -12,6 +12,16 @@ class PlaylistTime extends Time
$this->attributes['pattern'] = '[0-9]{2}:[0-9]{2}';
$this->attributes['placeholder'] = '13:45';
// Handle the "time code" format used by the database entity,
// which is just the regular 24-hour time minus the ":".
$this->filters[] = function($new_value) {
if (!empty($new_value) && false === strpos($new_value, ':')) {
$time_code = str_pad($new_value, 4, '0', STR_PAD_LEFT);
return substr($time_code, 0, 2).':'.substr($time_code, 2);
}
return $new_value;
};
}
public function getValue()

View File

@ -379,15 +379,11 @@ class Liquidsoap extends AbstractBackend implements EventSubscriberInterface
*/
protected function _getScheduledPlaylistPlayTime(Entity\StationPlaylist $playlist): string
{
$playlist_tz = 'UTC'; // TODO
$playlist_start_time = $playlist->getScheduleStartTime();
$playlist_end_time = $playlist->getScheduleEndTime();
$start_time = $this->_getOffsetTimeCode($playlist_start_time, $playlist_tz);
$end_time = $this->_getOffsetTimeCode($playlist_end_time, $playlist_tz);
$start_time = $playlist->getScheduleStartTime();
$end_time = $playlist->getScheduleEndTime();
// Handle multi-day playlists.
if ($playlist_start_time > $playlist_end_time) {
if ($start_time > $end_time) {
$play_times = [
$this->_formatTimeCode($start_time).'-23h59m',
'00h00m-'.$this->_formatTimeCode($end_time),
@ -417,7 +413,7 @@ class Liquidsoap extends AbstractBackend implements EventSubscriberInterface
}
// Handle once-per-day playlists.
$play_time = ($playlist_start_time === $playlist_end_time)
$play_time = ($start_time === $end_time)
? $this->_formatTimeCode($start_time)
: $this->_formatTimeCode($start_time) . '-' . $this->_formatTimeCode($end_time);
@ -436,6 +432,20 @@ class Liquidsoap extends AbstractBackend implements EventSubscriberInterface
return $play_time;
}
/**
* Configure the time offset
*
* @param int $time_code
* @return string
*/
protected function _formatTimeCode($time_code): string
{
$hours = floor($time_code / 100);
$mins = $time_code % 100;
return $hours . 'h' . $mins . 'm';
}
/**
* Write a playlist's contents to file so Liquidsoap can process it, and optionally notify
* Liquidsoap of the change.
@ -719,57 +729,6 @@ class Liquidsoap extends AbstractBackend implements EventSubscriberInterface
return 'list.hd(get_process_lines("'.$command.'"), default="")';
}
/**
* Configure the time offset
*
* @param int $time_code
* @return string
*/
protected function _formatTimeCode($time_code): string
{
$hours = floor($time_code / 100);
$mins = $time_code % 100;
return $hours . 'h' . $mins . 'm';
}
/**
* Given a time code (i.e. from a playlist), calculate the offset time code
*
* @param int $time_code
* @param string $tz
* @return int
*/
protected function _getOffsetTimeCode($time_code, $tz = 'UTC'): int
{
$hours = floor($time_code / 100);
$mins = $time_code % 100;
$system_time_zone = \App\Utilities::getSystemTimeZone();
if ($system_time_zone !== $tz) {
$system_tz = new \DateTimeZone($system_time_zone);
$system_dt = new \DateTime('now', $system_tz);
$system_offset = $system_tz->getOffset($system_dt);
$app_tz = new \DateTimeZone($tz);
$app_dt = new \DateTime('now', $app_tz);
$app_offset = $app_tz->getOffset($app_dt);
$offset = $system_offset - $app_offset;
$offset_hours = floor($offset / 3600);
$hours += $offset_hours;
}
$hours %= 24;
if ($hours < 0) {
$hours += 24;
}
return (int)(($hours*100)+$mins);
}
/**
* Filter a user-supplied string to be a valid LiquidSoap config entry.
*

View File

@ -127,23 +127,23 @@ class Configuration
[,$program_name] = explode(':', $adapter->getProgramName($station));
$config_lines = [
'user' => 'azuracast',
'priority' => $priority,
'command' => $adapter->getCommand($station),
'directory' => $config_path,
'stdout_logfile' => $adapter->getLogPath($station),
'user' => 'azuracast',
'priority' => $priority,
'command' => $adapter->getCommand($station),
'directory' => $config_path,
'environment' => 'TZ="'.$station->getTimezone().'"',
'stdout_logfile' => $adapter->getLogPath($station),
'stdout_logfile_maxbytes' => '5MB',
'stdout_logfile_backups' => '10',
'redirect_stderr' => 'true',
'redirect_stderr' => 'true',
];
$supervisor_config[] = '[program:' . $program_name . ']';
foreach($config_lines as $config_key => $config_value) {
$supervisor_config[] = $config_key . '=' . $config_value;
}
$supervisor_config[] = '';
return implode("\n", $supervisor_config);
}

View File

@ -29,7 +29,7 @@
<h3 class="card-subtitle"><?=implode(', ', $sync_info['contents']) ?></h3>
</div>
<div class="card-body">
<p class="card-text"><?=$customization->formatDateTime($sync_info['latest']) ?><br><small><?=sprintf(__('%s ago'), $sync_info['diff_text']) ?></small></p>
<p class="card-text"><?=sprintf(__('%s ago'), $sync_info['diff_text']) ?></p>
</div>
<div class="card-actions">
<a class="btn btn-outline-primary" role="button" href="<?=$router->named('admin:index:sync', ['type' => $sync_key]) ?>">

View File

@ -1,4 +1,9 @@
$(function() {
$('time').each(function() {
var tz_display = $(this).data('content');
$(this).text(moment(tz_display).format('LT'));
});
$('a[data-toggle="tab"]').on('shown.bs.tab', function (e) {
var target = $(e.target).attr("href");

View File

@ -96,16 +96,13 @@ $assets->load('fullcalendar')
<?=__('Weight') ?>: <?=(int)$row['weight'] ?> (<?=$row['probability'] ?>)
<?php elseif ($row['type'] === StationPlaylist::TYPE_SCHEDULED): ?>
<?=__('Scheduled') ?><br>
<?=__('Plays between %s and %s', $customization->formatTime(StationPlaylist::getTimestamp($row['schedule_start_time'])), $customization->formatTime(StationPlaylist::getTimestamp($row['schedule_end_time']))) ?>
<?=__('Plays between %s and %s', '<time data-content="'.$row['schedule_start'].'"></time>', '<time data-content="'.$row['schedule_end'].'"></time>') ?>
<?php elseif ($row['type'] === StationPlaylist::TYPE_ONCE_PER_X_SONGS): ?>
<?=__('Once per %d Songs', $row['play_per_songs']) ?>
<?php elseif ($row['type'] === StationPlaylist::TYPE_ONCE_PER_X_MINUTES): ?>
<?=__('Once per %d Minutes', $row['play_per_minutes']) ?>
<?php elseif ($row['type'] === StationPlaylist::TYPE_ONCE_PER_HOUR): ?>
<?=__('Once per Hour (at :%02d)', $row['play_per_hour_minute']) ?>
<?php elseif ($row['type'] === StationPlaylist::TYPE_ONCE_PER_DAY): ?>
<?=__('Once per Day') ?><br>
<?=__('Plays at %s', $customization->formatTime(StationPlaylist::getTimestamp($row['play_once_time']))) ?>
<?php elseif ($row['type'] === StationPlaylist::TYPE_ADVANCED): ?>
<?=__('Custom') ?>
<?php endif; ?>