Add schedule embed widget for embedding the stations playlist schedule view into websites (#4323)
This commit is contained in:
parent
fc44a4ea91
commit
328caf3987
|
@ -496,6 +496,12 @@ return [
|
||||||
// Auto-managed by Assets
|
// Auto-managed by Assets
|
||||||
],
|
],
|
||||||
|
|
||||||
|
'Vue_PublicSchedule' => [
|
||||||
|
'order' => 10,
|
||||||
|
'require' => ['vue-component-common', 'bootstrap-vue', 'moment_base', 'moment_timezone'],
|
||||||
|
// Auto-managed by Assets
|
||||||
|
],
|
||||||
|
|
||||||
'Vue_PublicWebDJ' => [
|
'Vue_PublicWebDJ' => [
|
||||||
'order' => 10,
|
'order' => 10,
|
||||||
'require' => ['vue-component-common'],
|
'require' => ['vue-component-common'],
|
||||||
|
|
|
@ -33,6 +33,9 @@ return function (App $app) {
|
||||||
$group->get('/ondemand[/{embed:embed}]', Controller\Frontend\PublicPages\OnDemandAction::class)
|
$group->get('/ondemand[/{embed:embed}]', Controller\Frontend\PublicPages\OnDemandAction::class)
|
||||||
->setName('public:ondemand');
|
->setName('public:ondemand');
|
||||||
|
|
||||||
|
$group->get('/schedule[/{embed:embed}]', Controller\Frontend\PublicPages\ScheduleAction::class)
|
||||||
|
->setName('public:schedule');
|
||||||
|
|
||||||
$group->get('/podcasts', Controller\Frontend\PublicPages\PodcastsController::class)
|
$group->get('/podcasts', Controller\Frontend\PublicPages\PodcastsController::class)
|
||||||
->setName('public:podcasts');
|
->setName('public:podcasts');
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,61 @@
|
||||||
|
<template>
|
||||||
|
<div class="card" style="height: 100%;">
|
||||||
|
<div class="card-header bg-primary-dark">
|
||||||
|
<div class="d-flex align-items-center">
|
||||||
|
<div class="flex-shrink">
|
||||||
|
<h2 class="card-title py-2">
|
||||||
|
<template v-if="stationName">
|
||||||
|
{{ stationName }}
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
<translate key="lang_title">Schedule</translate>
|
||||||
|
</template>
|
||||||
|
</h2>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="station-schedule-calendar">
|
||||||
|
<schedule ref="schedule" :schedule-url="scheduleUrl" :station-time-zone="stationTimeZone"
|
||||||
|
:locale="locale"></schedule>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
.schedule.embed {
|
||||||
|
.container {
|
||||||
|
max-width: 100%;
|
||||||
|
padding: 0 !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#station-schedule-calendar {
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import Schedule from '../Common/ScheduleView';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: { Schedule },
|
||||||
|
props: {
|
||||||
|
scheduleUrl: String,
|
||||||
|
stationName: String,
|
||||||
|
locale: String,
|
||||||
|
stationTimeZone: String
|
||||||
|
},
|
||||||
|
mounted () {
|
||||||
|
moment.relativeTimeThreshold('ss', 1);
|
||||||
|
moment.relativeTimeRounding(function (value) {
|
||||||
|
return Math.round(value * 10) / 10;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
formatTime (time) {
|
||||||
|
return moment(time).tz(this.stationTimeZone).format('LT');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
|
@ -72,7 +72,8 @@ export const profileEmbedModalProps = {
|
||||||
publicPageEmbedUri: String,
|
publicPageEmbedUri: String,
|
||||||
publicOnDemandEmbedUri: String,
|
publicOnDemandEmbedUri: String,
|
||||||
publicRequestEmbedUri: String,
|
publicRequestEmbedUri: String,
|
||||||
publicHistoryEmbedUri: String
|
publicHistoryEmbedUri: String,
|
||||||
|
publicScheduleEmbedUri: String
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -88,6 +89,10 @@ export default {
|
||||||
{
|
{
|
||||||
value: 'history',
|
value: 'history',
|
||||||
text: this.$gettext('History')
|
text: this.$gettext('History')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'schedule',
|
||||||
|
text: this.$gettext('Schedule')
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@ -142,6 +147,9 @@ export default {
|
||||||
case 'requests':
|
case 'requests':
|
||||||
return this.publicRequestEmbedUri;
|
return this.publicRequestEmbedUri;
|
||||||
|
|
||||||
|
case 'schedule':
|
||||||
|
return this.publicScheduleEmbedUri;
|
||||||
|
|
||||||
case 'player':
|
case 'player':
|
||||||
default:
|
default:
|
||||||
return this.publicPageEmbedUri;
|
return this.publicPageEmbedUri;
|
||||||
|
@ -170,6 +178,9 @@ export default {
|
||||||
case 'history':
|
case 'history':
|
||||||
return '300px';
|
return '300px';
|
||||||
|
|
||||||
|
case 'schedule':
|
||||||
|
return '800px'
|
||||||
|
|
||||||
case 'player':
|
case 'player':
|
||||||
default:
|
default:
|
||||||
return '150px';
|
return '150px';
|
||||||
|
|
|
@ -37,6 +37,12 @@
|
||||||
<a :href="publicPodcastsUri">{{ publicPodcastsUri }}</a>
|
<a :href="publicPodcastsUri">{{ publicPodcastsUri }}</a>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td key="lang_profile_schedule" v-translate>Schedule</td>
|
||||||
|
<td>
|
||||||
|
<a :href="publicScheduleUri">{{ publicScheduleUri }}</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
<div class="card-actions" v-if="userCanManageProfile">
|
<div class="card-actions" v-if="userCanManageProfile">
|
||||||
|
@ -85,6 +91,7 @@ export const profilePublicProps = {
|
||||||
publicWebDjUri: String,
|
publicWebDjUri: String,
|
||||||
publicOnDemandUri: String,
|
publicOnDemandUri: String,
|
||||||
publicPodcastsUri: String,
|
publicPodcastsUri: String,
|
||||||
|
publicScheduleUri: String,
|
||||||
togglePublicPageUri: String
|
togglePublicPageUri: String
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -13,6 +13,7 @@ module.exports = {
|
||||||
PublicOnDemand: './vue/Public/OnDemand.vue',
|
PublicOnDemand: './vue/Public/OnDemand.vue',
|
||||||
PublicPlayer: './vue/Public/Player.vue',
|
PublicPlayer: './vue/Public/Player.vue',
|
||||||
PublicRequests: './vue/Public/Requests.vue',
|
PublicRequests: './vue/Public/Requests.vue',
|
||||||
|
PublicSchedule: './vue/Public/Schedule.vue',
|
||||||
PublicWebDJ: './vue/Public/WebDJ.vue',
|
PublicWebDJ: './vue/Public/WebDJ.vue',
|
||||||
StationsMedia: './vue/Stations/Media.vue',
|
StationsMedia: './vue/Stations/Media.vue',
|
||||||
StationsMounts: './vue/Stations/Mounts.vue',
|
StationsMounts: './vue/Stations/Mounts.vue',
|
||||||
|
|
|
@ -0,0 +1,36 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Controller\Frontend\PublicPages;
|
||||||
|
|
||||||
|
use App\Exception\StationNotFoundException;
|
||||||
|
use App\Http\Response;
|
||||||
|
use App\Http\ServerRequest;
|
||||||
|
use Psr\Http\Message\ResponseInterface;
|
||||||
|
|
||||||
|
class ScheduleAction
|
||||||
|
{
|
||||||
|
public function __invoke(
|
||||||
|
ServerRequest $request,
|
||||||
|
Response $response,
|
||||||
|
bool $embed = false
|
||||||
|
): ResponseInterface {
|
||||||
|
// Override system-wide iframe refusal
|
||||||
|
$response = $response->withHeader('X-Frame-Options', '*');
|
||||||
|
|
||||||
|
$station = $request->getStation();
|
||||||
|
|
||||||
|
if (!$station->getEnablePublicPage()) {
|
||||||
|
throw new StationNotFoundException();
|
||||||
|
}
|
||||||
|
|
||||||
|
return $request->getView()->renderToResponse(
|
||||||
|
$response,
|
||||||
|
'frontend/public/schedule',
|
||||||
|
[
|
||||||
|
'embed' => $embed,
|
||||||
|
'station' => $station,
|
||||||
|
'station_tz' => $station->getTimezone(),
|
||||||
|
]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,33 @@
|
||||||
|
<?php
|
||||||
|
/** @var App\Entity\Station $station */
|
||||||
|
|
||||||
|
$pageClass = 'schedule station-' . $station->getShortName();
|
||||||
|
if ($embed) {
|
||||||
|
$pageClass .= ' embed';
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->layout(
|
||||||
|
'minimal',
|
||||||
|
[
|
||||||
|
'page_class' => $pageClass,
|
||||||
|
'title' => __('Schedule') . ' - ' . $this->e($station->getName()),
|
||||||
|
'hide_footer' => true,
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
$props = [
|
||||||
|
'scheduleUrl' => (string)$router->named('api:stations:schedule', ['station_id' => $station->getId()]),
|
||||||
|
'stationName' => $station->getName(),
|
||||||
|
'locale' => substr($customization->getLocale()->getLocale(), 0, 2),
|
||||||
|
'stationTimeZone' => $station_tz,
|
||||||
|
];
|
||||||
|
|
||||||
|
/** @var \App\Assets $assets */
|
||||||
|
$assets->addVueRender('Vue_PublicSchedule', '#station-schedule', $props);
|
||||||
|
?>
|
||||||
|
|
||||||
|
<section id="content" role="main" class="d-flex align-items-stretch" style="height: 100vh;">
|
||||||
|
<div class="container pt-5 pb-5 h-100" style="flex: 1;">
|
||||||
|
<div id="station-schedule"></div>
|
||||||
|
</div>
|
||||||
|
</section>
|
|
@ -72,6 +72,12 @@ $props = [
|
||||||
[],
|
[],
|
||||||
true
|
true
|
||||||
),
|
),
|
||||||
|
'publicScheduleUri' => (string)$router->named(
|
||||||
|
'public:schedule',
|
||||||
|
['station_id' => $station->getShortName()],
|
||||||
|
[],
|
||||||
|
true
|
||||||
|
),
|
||||||
'publicOnDemandEmbedUri' => (string)$router->named(
|
'publicOnDemandEmbedUri' => (string)$router->named(
|
||||||
'public:ondemand',
|
'public:ondemand',
|
||||||
['station_id' => $station->getShortName(), 'embed' => 'embed'],
|
['station_id' => $station->getShortName(), 'embed' => 'embed'],
|
||||||
|
@ -90,6 +96,12 @@ $props = [
|
||||||
[],
|
[],
|
||||||
true
|
true
|
||||||
),
|
),
|
||||||
|
'publicScheduleEmbedUri' => (string)$router->named(
|
||||||
|
'public:schedule',
|
||||||
|
['station_id' => $station->getShortName(), 'embed' => 'embed'],
|
||||||
|
[],
|
||||||
|
true
|
||||||
|
),
|
||||||
|
|
||||||
'togglePublicPageUri' => $router->fromHere(
|
'togglePublicPageUri' => $router->fromHere(
|
||||||
'stations:profile:toggle',
|
'stations:profile:toggle',
|
||||||
|
|
Loading…
Reference in New Issue