4
0
mirror of https://github.com/AzuraCast/AzuraCast.git synced 2024-06-14 05:06:37 +00:00

Remove string-to-Uri-to-string in many places; update to take advantage of Slim Router perf update.

This commit is contained in:
Buster Neece 2022-11-06 14:42:50 -06:00
parent 2d168c00bb
commit 9d915aba3c
No known key found for this signature in database
GPG Key ID: F1D2E64A0005E80E
102 changed files with 428 additions and 433 deletions

20
composer.lock generated
View File

@ -6332,16 +6332,16 @@
}, },
{ {
"name": "slim/slim", "name": "slim/slim",
"version": "4.10.0", "version": "4.11.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/slimphp/Slim.git", "url": "https://github.com/slimphp/Slim.git",
"reference": "0dfc7d2fdf2553b361d864d51af3fe8a6ad168b0" "reference": "b0f4ca393ea037be9ac7292ba7d0a34d18bac0c7"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/slimphp/Slim/zipball/0dfc7d2fdf2553b361d864d51af3fe8a6ad168b0", "url": "https://api.github.com/repos/slimphp/Slim/zipball/b0f4ca393ea037be9ac7292ba7d0a34d18bac0c7",
"reference": "0dfc7d2fdf2553b361d864d51af3fe8a6ad168b0", "reference": "b0f4ca393ea037be9ac7292ba7d0a34d18bac0c7",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -6356,21 +6356,21 @@
"psr/log": "^1.1 || ^2.0 || ^3.0" "psr/log": "^1.1 || ^2.0 || ^3.0"
}, },
"require-dev": { "require-dev": {
"adriansuter/php-autoload-override": "^1.2", "adriansuter/php-autoload-override": "^1.3",
"ext-simplexml": "*", "ext-simplexml": "*",
"guzzlehttp/psr7": "^2.1", "guzzlehttp/psr7": "^2.4",
"httpsoft/http-message": "^1.0", "httpsoft/http-message": "^1.0",
"httpsoft/http-server-request": "^1.0", "httpsoft/http-server-request": "^1.0",
"laminas/laminas-diactoros": "^2.8", "laminas/laminas-diactoros": "^2.17",
"nyholm/psr7": "^1.5", "nyholm/psr7": "^1.5",
"nyholm/psr7-server": "^1.0", "nyholm/psr7-server": "^1.0",
"phpspec/prophecy": "^1.15", "phpspec/prophecy": "^1.15",
"phpspec/prophecy-phpunit": "^2.0", "phpspec/prophecy-phpunit": "^2.0",
"phpstan/phpstan": "^1.4", "phpstan/phpstan": "^1.8",
"phpunit/phpunit": "^9.5", "phpunit/phpunit": "^9.5",
"slim/http": "^1.2", "slim/http": "^1.2",
"slim/psr7": "^1.5", "slim/psr7": "^1.5",
"squizlabs/php_codesniffer": "^3.6" "squizlabs/php_codesniffer": "^3.7"
}, },
"suggest": { "suggest": {
"ext-simplexml": "Needed to support XML format in BodyParsingMiddleware", "ext-simplexml": "Needed to support XML format in BodyParsingMiddleware",
@ -6443,7 +6443,7 @@
"type": "tidelift" "type": "tidelift"
} }
], ],
"time": "2022-03-14T14:18:23+00:00" "time": "2022-11-06T16:33:39+00:00"
}, },
{ {
"name": "spatie/dropbox-api", "name": "spatie/dropbox-api",

View File

@ -5,7 +5,7 @@
use App\Enums\GlobalPermissions; use App\Enums\GlobalPermissions;
return function (App\Event\BuildAdminMenu $e) { return static function (App\Event\BuildAdminMenu $e) {
$request = $e->getRequest(); $request = $e->getRequest();
$router = $request->getRouter(); $router = $request->getRouter();
@ -13,36 +13,36 @@ return function (App\Event\BuildAdminMenu $e) {
[ [
'maintenance' => [ 'maintenance' => [
'label' => __('System Maintenance'), 'label' => __('System Maintenance'),
'icon' => 'router', 'icon' => 'router',
'items' => [ 'items' => [
'settings' => [ 'settings' => [
'label' => __('System Settings'), 'label' => __('System Settings'),
'url' => (string)$router->named('admin:settings:index'), 'url' => $router->named('admin:settings:index'),
'permission' => GlobalPermissions::Settings, 'permission' => GlobalPermissions::Settings,
], ],
'branding' => [ 'branding' => [
'label' => __('Custom Branding'), 'label' => __('Custom Branding'),
'url' => (string)$router->named('admin:branding:index'), 'url' => $router->named('admin:branding:index'),
'permission' => GlobalPermissions::Settings, 'permission' => GlobalPermissions::Settings,
], ],
'logs' => [ 'logs' => [
'label' => __('System Logs'), 'label' => __('System Logs'),
'url' => (string)$router->named('admin:logs:index'), 'url' => $router->named('admin:logs:index'),
'permission' => GlobalPermissions::Logs, 'permission' => GlobalPermissions::Logs,
], ],
'storage_locations' => [ 'storage_locations' => [
'label' => __('Storage Locations'), 'label' => __('Storage Locations'),
'url' => (string)$router->named('admin:storage_locations:index'), 'url' => $router->named('admin:storage_locations:index'),
'permission' => GlobalPermissions::StorageLocations, 'permission' => GlobalPermissions::StorageLocations,
], ],
'backups' => [ 'backups' => [
'label' => __('Backups'), 'label' => __('Backups'),
'url' => (string)$router->named('admin:backups:index'), 'url' => $router->named('admin:backups:index'),
'permission' => GlobalPermissions::Backups, 'permission' => GlobalPermissions::Backups,
], ],
'debug' => [ 'debug' => [
'label' => __('System Debugger'), 'label' => __('System Debugger'),
'url' => (string)$router->named('admin:debug:index'), 'url' => $router->named('admin:debug:index'),
'permission' => GlobalPermissions::All, 'permission' => GlobalPermissions::All,
], ],
], ],
@ -52,23 +52,23 @@ return function (App\Event\BuildAdminMenu $e) {
'icon' => 'group', 'icon' => 'group',
'items' => [ 'items' => [
'manage_users' => [ 'manage_users' => [
'label' => __('User Accounts'), 'label' => __('User Accounts'),
'url' => (string)$router->named('admin:users:index'), 'url' => $router->named('admin:users:index'),
'permission' => GlobalPermissions::All, 'permission' => GlobalPermissions::All,
], ],
'permissions' => [ 'permissions' => [
'label' => __('Roles & Permissions'), 'label' => __('Roles & Permissions'),
'url' => (string)$router->named('admin:permissions:index'), 'url' => $router->named('admin:permissions:index'),
'permission' => GlobalPermissions::All, 'permission' => GlobalPermissions::All,
], ],
'auditlog' => [ 'auditlog' => [
'label' => __('Audit Log'), 'label' => __('Audit Log'),
'url' => (string)$router->named('admin:auditlog:index'), 'url' => $router->named('admin:auditlog:index'),
'permission' => GlobalPermissions::Logs, 'permission' => GlobalPermissions::Logs,
], ],
'api_keys' => [ 'api_keys' => [
'label' => __('API Keys'), 'label' => __('API Keys'),
'url' => (string)$router->named('admin:api:index'), 'url' => $router->named('admin:api:index'),
'permission' => GlobalPermissions::ApiKeys, 'permission' => GlobalPermissions::ApiKeys,
], ],
], ],
@ -78,33 +78,33 @@ return function (App\Event\BuildAdminMenu $e) {
'icon' => 'volume_up', 'icon' => 'volume_up',
'items' => [ 'items' => [
'manage_stations' => [ 'manage_stations' => [
'label' => __('Stations'), 'label' => __('Stations'),
'url' => (string)$router->named('admin:stations:index'), 'url' => $router->named('admin:stations:index'),
'permission' => GlobalPermissions::Stations, 'permission' => GlobalPermissions::Stations,
], ],
'custom_fields' => [ 'custom_fields' => [
'label' => __('Custom Fields'), 'label' => __('Custom Fields'),
'url' => (string)$router->named('admin:custom_fields:index'), 'url' => $router->named('admin:custom_fields:index'),
'permission' => GlobalPermissions::CustomFields, 'permission' => GlobalPermissions::CustomFields,
], ],
'relays' => [ 'relays' => [
'label' => __('Connected AzuraRelays'), 'label' => __('Connected AzuraRelays'),
'url' => (string)$router->named('admin:relays:index'), 'url' => $router->named('admin:relays:index'),
'permission' => GlobalPermissions::Stations, 'permission' => GlobalPermissions::Stations,
], ],
'shoutcast' => [ 'shoutcast' => [
'label' => __('Install Shoutcast'), 'label' => __('Install Shoutcast'),
'url' => (string)$router->named('admin:install_shoutcast:index'), 'url' => $router->named('admin:install_shoutcast:index'),
'permission' => GlobalPermissions::All, 'permission' => GlobalPermissions::All,
], ],
'stereo_tool' => [ 'stereo_tool' => [
'label' => __('Install Stereo Tool'), 'label' => __('Install Stereo Tool'),
'url' => (string)$router->named('admin:install_stereo_tool:index'), 'url' => $router->named('admin:install_stereo_tool:index'),
'permission' => GlobalPermissions::All, 'permission' => GlobalPermissions::All,
], ],
'geolite' => [ 'geolite' => [
'label' => __('Install GeoLite IP Database'), 'label' => __('Install GeoLite IP Database'),
'url' => (string)$router->named('admin:install_geolite:index'), 'url' => $router->named('admin:install_geolite:index'),
'permission' => GlobalPermissions::All, 'permission' => GlobalPermissions::All,
], ],
], ],

View File

@ -7,7 +7,7 @@ use App\Enums\StationFeatures;
use App\Enums\StationPermissions; use App\Enums\StationPermissions;
use App\Radio\Enums\AudioProcessingMethods; use App\Radio\Enums\AudioProcessingMethods;
return function (App\Event\BuildStationMenu $e) { return static function (App\Event\BuildStationMenu $e) {
$request = $e->getRequest(); $request = $e->getRequest();
$station = $e->getStation(); $station = $e->getStation();
@ -32,7 +32,7 @@ return function (App\Event\BuildStationMenu $e) {
'label' => __('Start Station'), 'label' => __('Start Station'),
'title' => __('Ready to start broadcasting? Click to start your station.'), 'title' => __('Ready to start broadcasting? Click to start your station.'),
'icon' => 'refresh', 'icon' => 'refresh',
'url' => (string)$router->fromHere('api:stations:reload'), 'url' => $router->fromHere('api:stations:reload'),
'class' => 'api-call text-success', 'class' => 'api-call text-success',
'confirm' => $reloadMessage, 'confirm' => $reloadMessage,
'visible' => $hasLocalServices && !$station->getHasStarted(), 'visible' => $hasLocalServices && !$station->getHasStarted(),
@ -42,7 +42,7 @@ return function (App\Event\BuildStationMenu $e) {
'label' => __('Reload to Apply Changes'), 'label' => __('Reload to Apply Changes'),
'title' => __('Click to restart your station and apply configuration changes.'), 'title' => __('Click to restart your station and apply configuration changes.'),
'icon' => 'refresh', 'icon' => 'refresh',
'url' => (string)$router->fromHere('api:stations:reload'), 'url' => $router->fromHere('api:stations:reload'),
'class' => 'api-call text-warning btn-restart-station ' 'class' => 'api-call text-warning btn-restart-station '
. (!$station->getNeedsRestart() ? 'd-none' : ''), . (!$station->getNeedsRestart() ? 'd-none' : ''),
'confirm' => $reloadMessage, 'confirm' => $reloadMessage,
@ -52,12 +52,12 @@ return function (App\Event\BuildStationMenu $e) {
'profile' => [ 'profile' => [
'label' => __('Profile'), 'label' => __('Profile'),
'icon' => 'image', 'icon' => 'image',
'url' => (string)$router->fromHere('stations:profile:index'), 'url' => $router->fromHere('stations:profile:index'),
], ],
'public' => [ 'public' => [
'label' => __('Public Page'), 'label' => __('Public Page'),
'icon' => 'public', 'icon' => 'public',
'url' => (string)$router->named('public:index', ['station_id' => $station->getShortName()]), 'url' => $router->named('public:index', ['station_id' => $station->getShortName()]),
'external' => true, 'external' => true,
'visible' => $station->getEnablePublicPage(), 'visible' => $station->getEnablePublicPage(),
], ],
@ -69,46 +69,46 @@ return function (App\Event\BuildStationMenu $e) {
'files' => [ 'files' => [
'label' => __('Music Files'), 'label' => __('Music Files'),
'icon' => 'library_music', 'icon' => 'library_music',
'url' => (string)$router->fromHere('stations:files:index'), 'url' => $router->fromHere('stations:files:index'),
'permission' => StationPermissions::Media, 'permission' => StationPermissions::Media,
], ],
'reports_duplicates' => [ 'reports_duplicates' => [
'label' => __('Duplicate Songs'), 'label' => __('Duplicate Songs'),
'class' => 'text-muted', 'class' => 'text-muted',
'url' => (string)$router->fromHere('stations:files:index') . '#special:duplicates', 'url' => $router->fromHere('stations:files:index') . '#special:duplicates',
'permission' => StationPermissions::Media, 'permission' => StationPermissions::Media,
], ],
'reports_unprocessable' => [ 'reports_unprocessable' => [
'label' => __('Unprocessable Files'), 'label' => __('Unprocessable Files'),
'class' => 'text-muted', 'class' => 'text-muted',
'url' => (string)$router->fromHere('stations:files:index') . '#special:unprocessable', 'url' => $router->fromHere('stations:files:index') . '#special:unprocessable',
'permission' => StationPermissions::Media, 'permission' => StationPermissions::Media,
], ],
'reports_unassigned' => [ 'reports_unassigned' => [
'label' => __('Unassigned Files'), 'label' => __('Unassigned Files'),
'class' => 'text-muted', 'class' => 'text-muted',
'url' => (string)$router->fromHere('stations:files:index') . '#special:unassigned', 'url' => $router->fromHere('stations:files:index') . '#special:unassigned',
'permission' => StationPermissions::Media, 'permission' => StationPermissions::Media,
], ],
'ondemand' => [ 'ondemand' => [
'label' => __('On-Demand Media'), 'label' => __('On-Demand Media'),
'class' => 'text-muted', 'class' => 'text-muted',
'icon' => 'cloud_download', 'icon' => 'cloud_download',
'url' => (string)$router->named('public:ondemand', ['station_id' => $station->getShortName()]), 'url' => $router->named('public:ondemand', ['station_id' => $station->getShortName()]),
'external' => true, 'external' => true,
'visible' => $station->getEnableOnDemand(), 'visible' => $station->getEnableOnDemand(),
], ],
'sftp_users' => [ 'sftp_users' => [
'label' => __('SFTP Users'), 'label' => __('SFTP Users'),
'class' => 'text-muted', 'class' => 'text-muted',
'url' => (string)$router->fromHere('stations:sftp_users:index'), 'url' => $router->fromHere('stations:sftp_users:index'),
'visible' => StationFeatures::Sftp->supportedForStation($station), 'visible' => StationFeatures::Sftp->supportedForStation($station),
'permission' => StationPermissions::Media, 'permission' => StationPermissions::Media,
], ],
'bulk_media' => [ 'bulk_media' => [
'label' => __('Bulk Media Import/Export'), 'label' => __('Bulk Media Import/Export'),
'class' => 'text-muted', 'class' => 'text-muted',
'url' => (string)$router->fromHere('stations:bulk-media'), 'url' => $router->fromHere('stations:bulk-media'),
'permission' => StationPermissions::Media, 'permission' => StationPermissions::Media,
], ],
], ],
@ -117,7 +117,7 @@ return function (App\Event\BuildStationMenu $e) {
'playlists' => [ 'playlists' => [
'label' => __('Playlists'), 'label' => __('Playlists'),
'icon' => 'queue_music', 'icon' => 'queue_music',
'url' => (string)$router->fromHere('stations:playlists:index'), 'url' => $router->fromHere('stations:playlists:index'),
'visible' => StationFeatures::Media->supportedForStation($station), 'visible' => StationFeatures::Media->supportedForStation($station),
'permission' => StationPermissions::Media, 'permission' => StationPermissions::Media,
], ],
@ -125,7 +125,7 @@ return function (App\Event\BuildStationMenu $e) {
'podcasts' => [ 'podcasts' => [
'label' => __('Podcasts'), 'label' => __('Podcasts'),
'icon' => 'cast', 'icon' => 'cast',
'url' => (string)$router->fromHere('stations:podcasts:index'), 'url' => $router->fromHere('stations:podcasts:index'),
'visible' => StationFeatures::Podcasts->supportedForStation($station), 'visible' => StationFeatures::Podcasts->supportedForStation($station),
'permission' => StationPermissions::Podcasts, 'permission' => StationPermissions::Podcasts,
], ],
@ -138,20 +138,21 @@ return function (App\Event\BuildStationMenu $e) {
'streamers' => [ 'streamers' => [
'label' => __('Streamer/DJ Accounts'), 'label' => __('Streamer/DJ Accounts'),
'icon' => 'mic', 'icon' => 'mic',
'url' => (string)$router->fromHere('stations:streamers:index'), 'url' => $router->fromHere('stations:streamers:index'),
'permission' => StationPermissions::Streamers, 'permission' => StationPermissions::Streamers,
], ],
'web_dj' => [ 'web_dj' => [
'label' => __('Web DJ'), 'label' => __('Web DJ'),
'icon' => 'surround_sound', 'icon' => 'surround_sound',
'url' => (string)$router->named( 'url' => (string)(
$router->namedAsUri(
'public:dj', 'public:dj',
['station_id' => $station->getShortName()], ['station_id' => $station->getShortName()],
[], [],
true true
) )->withScheme('https')
->withScheme('https'), ),
'visible' => $station->getEnablePublicPage(), 'visible' => $station->getEnablePublicPage(),
'external' => true, 'external' => true,
], ],
@ -161,7 +162,7 @@ return function (App\Event\BuildStationMenu $e) {
'webhooks' => [ 'webhooks' => [
'label' => __('Web Hooks'), 'label' => __('Web Hooks'),
'icon' => 'code', 'icon' => 'code',
'url' => (string)$router->fromHere('stations:webhooks:index'), 'url' => $router->fromHere('stations:webhooks:index'),
'visible' => StationFeatures::Webhooks->supportedForStation($station), 'visible' => StationFeatures::Webhooks->supportedForStation($station),
'permission' => StationPermissions::WebHooks, 'permission' => StationPermissions::WebHooks,
], ],
@ -173,24 +174,24 @@ return function (App\Event\BuildStationMenu $e) {
'items' => [ 'items' => [
'reports_overview' => [ 'reports_overview' => [
'label' => __('Station Statistics'), 'label' => __('Station Statistics'),
'url' => (string)$router->fromHere('stations:reports:overview'), 'url' => $router->fromHere('stations:reports:overview'),
], ],
'reports_listeners' => [ 'reports_listeners' => [
'label' => __('Listeners'), 'label' => __('Listeners'),
'url' => (string)$router->fromHere('stations:reports:listeners'), 'url' => $router->fromHere('stations:reports:listeners'),
], ],
'reports_requests' => [ 'reports_requests' => [
'label' => __('Song Requests'), 'label' => __('Song Requests'),
'url' => (string)$router->fromHere('stations:reports:requests'), 'url' => $router->fromHere('stations:reports:requests'),
'visible' => $station->getEnableRequests(), 'visible' => $station->getEnableRequests(),
], ],
'reports_timeline' => [ 'reports_timeline' => [
'label' => __('Song Playback Timeline'), 'label' => __('Song Playback Timeline'),
'url' => (string)$router->fromHere('stations:reports:timeline'), 'url' => $router->fromHere('stations:reports:timeline'),
], ],
'reports_soundexchange' => [ 'reports_soundexchange' => [
'label' => __('SoundExchange Royalties'), 'label' => __('SoundExchange Royalties'),
'url' => (string)$router->fromHere('stations:reports:soundexchange'), 'url' => $router->fromHere('stations:reports:soundexchange'),
], ],
], ],
], ],
@ -202,41 +203,41 @@ return function (App\Event\BuildStationMenu $e) {
'mounts' => [ 'mounts' => [
'label' => __('Mount Points'), 'label' => __('Mount Points'),
'icon' => 'wifi_tethering', 'icon' => 'wifi_tethering',
'url' => (string)$router->fromHere('stations:mounts:index'), 'url' => $router->fromHere('stations:mounts:index'),
'visible' => StationFeatures::MountPoints->supportedForStation($station), 'visible' => StationFeatures::MountPoints->supportedForStation($station),
'permission' => StationPermissions::MountPoints, 'permission' => StationPermissions::MountPoints,
], ],
'hls_streams' => [ 'hls_streams' => [
'label' => __('HLS Streams'), 'label' => __('HLS Streams'),
'url' => (string)$router->fromHere('stations:hls_streams:index'), 'url' => $router->fromHere('stations:hls_streams:index'),
'visible' => StationFeatures::HlsStreams->supportedForStation($station), 'visible' => StationFeatures::HlsStreams->supportedForStation($station),
'permission' => StationPermissions::MountPoints, 'permission' => StationPermissions::MountPoints,
], ],
'remotes' => [ 'remotes' => [
'label' => __('Remote Relays'), 'label' => __('Remote Relays'),
'icon' => 'router', 'icon' => 'router',
'url' => (string)$router->fromHere('stations:remotes:index'), 'url' => $router->fromHere('stations:remotes:index'),
'visible' => StationFeatures::RemoteRelays->supportedForStation($station), 'visible' => StationFeatures::RemoteRelays->supportedForStation($station),
'permission' => StationPermissions::RemoteRelays, 'permission' => StationPermissions::RemoteRelays,
], ],
'fallback' => [ 'fallback' => [
'label' => __('Custom Fallback File'), 'label' => __('Custom Fallback File'),
'class' => 'text-muted', 'class' => 'text-muted',
'url' => (string)$router->fromHere('stations:fallback'), 'url' => $router->fromHere('stations:fallback'),
'visible' => StationFeatures::Media->supportedForStation($station), 'visible' => StationFeatures::Media->supportedForStation($station),
'permission' => StationPermissions::Broadcasting, 'permission' => StationPermissions::Broadcasting,
], ],
'ls_config' => [ 'ls_config' => [
'label' => __('Edit Liquidsoap Configuration'), 'label' => __('Edit Liquidsoap Configuration'),
'class' => 'text-muted', 'class' => 'text-muted',
'url' => (string)$router->fromHere('stations:util:ls_config'), 'url' => $router->fromHere('stations:util:ls_config'),
'visible' => StationFeatures::CustomLiquidsoapConfig->supportedForStation($station), 'visible' => StationFeatures::CustomLiquidsoapConfig->supportedForStation($station),
'permission' => StationPermissions::Broadcasting, 'permission' => StationPermissions::Broadcasting,
], ],
'stations:stereo_tool_config' => [ 'stations:stereo_tool_config' => [
'label' => __('Upload Stereo Tool Configuration'), 'label' => __('Upload Stereo Tool Configuration'),
'class' => 'text-muted', 'class' => 'text-muted',
'url' => (string)$router->fromHere('stations:stereo_tool_config'), 'url' => $router->fromHere('stations:stereo_tool_config'),
'visible' => $settings->getEnableAdvancedFeatures() 'visible' => $settings->getEnableAdvancedFeatures()
&& StationFeatures::Media->supportedForStation($station) && StationFeatures::Media->supportedForStation($station)
&& AudioProcessingMethods::StereoTool === $backendConfig->getAudioProcessingMethodEnum(), && AudioProcessingMethods::StereoTool === $backendConfig->getAudioProcessingMethodEnum(),
@ -245,14 +246,14 @@ return function (App\Event\BuildStationMenu $e) {
'queue' => [ 'queue' => [
'label' => __('Upcoming Song Queue'), 'label' => __('Upcoming Song Queue'),
'class' => 'text-muted', 'class' => 'text-muted',
'url' => (string)$router->fromHere('stations:queue:index'), 'url' => $router->fromHere('stations:queue:index'),
'permission' => StationPermissions::Broadcasting, 'permission' => StationPermissions::Broadcasting,
'visible' => $station->supportsAutoDjQueue(), 'visible' => $station->supportsAutoDjQueue(),
], ],
'reload' => [ 'reload' => [
'label' => __('Reload Configuration'), 'label' => __('Reload Configuration'),
'class' => 'text-muted api-call', 'class' => 'text-muted api-call',
'url' => (string)$router->fromHere('api:stations:reload'), 'url' => $router->fromHere('api:stations:reload'),
'confirm' => $willNotDisconnectMessage, 'confirm' => $willNotDisconnectMessage,
'permission' => StationPermissions::Broadcasting, 'permission' => StationPermissions::Broadcasting,
'visible' => $hasLocalServices && $reloadSupported, 'visible' => $hasLocalServices && $reloadSupported,
@ -260,7 +261,7 @@ return function (App\Event\BuildStationMenu $e) {
'restart' => [ 'restart' => [
'label' => __('Restart Broadcasting'), 'label' => __('Restart Broadcasting'),
'class' => 'text-muted api-call', 'class' => 'text-muted api-call',
'url' => (string)$router->fromHere('api:stations:restart'), 'url' => $router->fromHere('api:stations:restart'),
'confirm' => $willDisconnectMessage, 'confirm' => $willDisconnectMessage,
'permission' => StationPermissions::Broadcasting, 'permission' => StationPermissions::Broadcasting,
'visible' => $hasLocalServices, 'visible' => $hasLocalServices,
@ -271,7 +272,7 @@ return function (App\Event\BuildStationMenu $e) {
'logs' => [ 'logs' => [
'label' => __('Logs'), 'label' => __('Logs'),
'icon' => 'web_stories', 'icon' => 'web_stories',
'url' => (string)$router->fromHere('stations:logs'), 'url' => $router->fromHere('stations:logs'),
'permission' => StationPermissions::Logs, 'permission' => StationPermissions::Logs,
], ],
] ]

View File

@ -78,7 +78,7 @@ return static function (RouteCollectorProxy $group) {
'/relays', '/relays',
function (ServerRequest $request, Response $response) { function (ServerRequest $request, Response $response) {
return $response->withRedirect( return $response->withRedirect(
(string)$request->getRouter()->fromHere('api:internal:relays') $request->getRouter()->fromHere('api:internal:relays')
); );
} }
); );

View File

@ -16,7 +16,7 @@ return static function (RouteCollectorProxy $app) {
'', '',
function (ServerRequest $request, Response $response, ...$params) { function (ServerRequest $request, Response $response, ...$params) {
return $response->withRedirect( return $response->withRedirect(
(string)$request->getRouter()->fromHere('stations:profile:index') $request->getRouter()->fromHere('stations:profile:index')
); );
} }
)->setName('stations:index:index'); )->setName('stations:index:index');

View File

@ -22,7 +22,7 @@ final class ApiKeysAction
id: 'admin-api-keys', id: 'admin-api-keys',
title: __('API Keys'), title: __('API Keys'),
props: [ props: [
'apiUrl' => (string)$router->named('api:admin:api-keys'), 'apiUrl' => $router->named('api:admin:api-keys'),
], ],
); );
} }

View File

@ -22,7 +22,7 @@ final class AuditLogAction
id: 'admin-audit-log', id: 'admin-audit-log',
title: __('Audit Log'), title: __('Audit Log'),
props: [ props: [
'baseApiUrl' => (string)$router->fromHere('api:admin:auditlog'), 'baseApiUrl' => $router->fromHere('api:admin:auditlog'),
] ]
); );
} }

View File

@ -30,9 +30,9 @@ final class BackupsAction
id: 'admin-backups', id: 'admin-backups',
title: __('Backups'), title: __('Backups'),
props: [ props: [
'listUrl' => (string)$router->named('api:admin:backups'), 'listUrl' => $router->named('api:admin:backups'),
'runBackupUrl' => (string)$router->named('api:admin:backups:run'), 'runBackupUrl' => $router->named('api:admin:backups:run'),
'settingsUrl' => (string)$router->named('api:admin:settings', [ 'settingsUrl' => $router->named('api:admin:settings', [
'group' => Entity\Settings::GROUP_BACKUP, 'group' => Entity\Settings::GROUP_BACKUP,
]), ]),
'isDocker' => $this->environment->isDocker(), 'isDocker' => $this->environment->isDocker(),

View File

@ -24,16 +24,16 @@ final class BrandingAction
id: 'admin-branding', id: 'admin-branding',
title: __('Custom Branding'), title: __('Custom Branding'),
props: [ props: [
'settingsApiUrl' => (string)$router->named('api:admin:settings', [ 'settingsApiUrl' => $router->named('api:admin:settings', [
'group' => Settings::GROUP_BRANDING, 'group' => Settings::GROUP_BRANDING,
]), ]),
'browserIconApiUrl' => (string)$router->named('api:admin:custom_assets', [ 'browserIconApiUrl' => $router->named('api:admin:custom_assets', [
'type' => AssetTypes::BrowserIcon->value, 'type' => AssetTypes::BrowserIcon->value,
]), ]),
'backgroundApiUrl' => (string)$router->named('api:admin:custom_assets', [ 'backgroundApiUrl' => $router->named('api:admin:custom_assets', [
'type' => AssetTypes::Background->value, 'type' => AssetTypes::Background->value,
]), ]),
'albumArtApiUrl' => (string)$router->named('api:admin:custom_assets', [ 'albumArtApiUrl' => $router->named('api:admin:custom_assets', [
'type' => AssetTypes::AlbumArt->value, 'type' => AssetTypes::AlbumArt->value,
]), ]),
], ],

View File

@ -23,7 +23,7 @@ final class CustomFieldsAction
id: 'admin-custom-fields', id: 'admin-custom-fields',
title: __('Custom Fields'), title: __('Custom Fields'),
props: [ props: [
'listUrl' => (string)$router->fromHere('api:admin:custom_fields'), 'listUrl' => $router->fromHere('api:admin:custom_fields'),
'autoAssignTypes' => MetadataTags::getNames(), 'autoAssignTypes' => MetadataTags::getNames(),
] ]
); );

View File

@ -28,6 +28,6 @@ final class ClearCacheAction
// Flash an update to ensure the session is recreated. // Flash an update to ensure the session is recreated.
$request->getFlash()->addMessage($resultOutput, Flash::SUCCESS); $request->getFlash()->addMessage($resultOutput, Flash::SUCCESS);
return $response->withRedirect((string)$request->getRouter()->fromHere('admin:debug:index')); return $response->withRedirect($request->getRouter()->fromHere('admin:debug:index'));
} }
} }

View File

@ -32,6 +32,6 @@ final class ClearQueueAction
// Flash an update to ensure the session is recreated. // Flash an update to ensure the session is recreated.
$request->getFlash()->addMessage($resultOutput, Flash::SUCCESS); $request->getFlash()->addMessage($resultOutput, Flash::SUCCESS);
return $response->withRedirect((string)$request->getRouter()->fromHere('admin:debug:index')); return $response->withRedirect($request->getRouter()->fromHere('admin:debug:index'));
} }
} }

View File

@ -22,7 +22,7 @@ final class GeoLiteAction
id: 'admin-geolite', id: 'admin-geolite',
title: __('Install GeoLite IP Database'), title: __('Install GeoLite IP Database'),
props: [ props: [
'apiUrl' => (string)$router->named('api:admin:geolite'), 'apiUrl' => $router->named('api:admin:geolite'),
], ],
); );
} }

View File

@ -30,8 +30,8 @@ final class IndexAction
title: __('Administration'), title: __('Administration'),
props: [ props: [
'adminPanels' => $viewData['admin_panels'] ?? [], 'adminPanels' => $viewData['admin_panels'] ?? [],
'statsUrl' => (string)$router->named('api:admin:server:stats'), 'statsUrl' => $router->named('api:admin:server:stats'),
'servicesUrl' => (string)$router->named('api:admin:services'), 'servicesUrl' => $router->named('api:admin:services'),
] ]
); );
} }

View File

@ -30,7 +30,7 @@ final class LogsAction
$stationLogs[] = [ $stationLogs[] = [
'id' => $station->getIdRequired(), 'id' => $station->getIdRequired(),
'name' => $station->getName(), 'name' => $station->getName(),
'url' => (string)$router->named('api:stations:logs', [ 'url' => $router->named('api:stations:logs', [
'station_id' => $station->getIdRequired(), 'station_id' => $station->getIdRequired(),
]), ]),
]; ];
@ -43,7 +43,7 @@ final class LogsAction
id: 'admin-logs', id: 'admin-logs',
title: __('System Logs'), title: __('System Logs'),
props: [ props: [
'systemLogsUrl' => (string)$router->fromHere('api:admin:logs'), 'systemLogsUrl' => $router->fromHere('api:admin:logs'),
'stationLogs' => $stationLogs, 'stationLogs' => $stationLogs,
], ],
); );

View File

@ -30,7 +30,7 @@ final class PermissionsAction
id: 'admin-permissions', id: 'admin-permissions',
title: __('Roles & Permissions'), title: __('Roles & Permissions'),
props: [ props: [
'listUrl' => (string)$router->fromHere('api:admin:roles'), 'listUrl' => $router->fromHere('api:admin:roles'),
'stations' => $this->stationRepo->fetchSelect(), 'stations' => $this->stationRepo->fetchSelect(),
'globalPermissions' => $actions['global'], 'globalPermissions' => $actions['global'],
'stationPermissions' => $actions['station'], 'stationPermissions' => $actions['station'],

View File

@ -29,11 +29,11 @@ final class SettingsAction
id: 'admin-settings', id: 'admin-settings',
title: __('System Settings'), title: __('System Settings'),
props: [ props: [
'apiUrl' => (string)$router->named('api:admin:settings', [ 'apiUrl' => $router->named('api:admin:settings', [
'group' => Settings::GROUP_GENERAL, 'group' => Settings::GROUP_GENERAL,
]), ]),
'testMessageUrl' => (string)$router->named('api:admin:send-test-message'), 'testMessageUrl' => $router->named('api:admin:send-test-message'),
'acmeUrl' => (string)$router->named('api:admin:acme'), 'acmeUrl' => $router->named('api:admin:acme'),
'releaseChannel' => $this->version->getReleaseChannelEnum()->value, 'releaseChannel' => $this->version->getReleaseChannelEnum()->value,
], ],
); );

View File

@ -27,7 +27,7 @@ final class ShoutcastAction
id: 'admin-shoutcast', id: 'admin-shoutcast',
title: __('Install Shoutcast 2 DNAS'), title: __('Install Shoutcast 2 DNAS'),
props: [ props: [
'apiUrl' => (string)$router->named('api:admin:shoutcast'), 'apiUrl' => $router->named('api:admin:shoutcast'),
], ],
); );
} }

View File

@ -32,7 +32,7 @@ final class StationsAction
props: array_merge( props: array_merge(
$this->stationFormComponent->getProps($request), $this->stationFormComponent->getProps($request),
[ [
'listUrl' => (string)$router->fromHere('api:admin:stations'), 'listUrl' => $router->fromHere('api:admin:stations'),
'frontendTypes' => $this->adapters->listFrontendAdapters(), 'frontendTypes' => $this->adapters->listFrontendAdapters(),
'backendTypes' => $this->adapters->listBackendAdapters(), 'backendTypes' => $this->adapters->listBackendAdapters(),
] ]

View File

@ -22,7 +22,7 @@ final class StereoToolAction
id: 'admin-stereo-tool', id: 'admin-stereo-tool',
title: __('Install Stereo Tool'), title: __('Install Stereo Tool'),
props: [ props: [
'apiUrl' => (string)$router->named('api:admin:stereo_tool'), 'apiUrl' => $router->named('api:admin:stereo_tool'),
], ],
); );
} }

View File

@ -22,7 +22,7 @@ final class StorageLocationsAction
id: 'admin-storage-locations', id: 'admin-storage-locations',
title: __('Storage Locations'), title: __('Storage Locations'),
props: [ props: [
'listUrl' => (string)$router->fromHere('api:admin:storage_locations'), 'listUrl' => $router->fromHere('api:admin:storage_locations'),
], ],
); );
} }

View File

@ -28,7 +28,7 @@ final class UsersAction
id: 'admin-users', id: 'admin-users',
title: __('Users'), title: __('Users'),
props: [ props: [
'listUrl' => (string)$router->fromHere('api:admin:users'), 'listUrl' => $router->fromHere('api:admin:users'),
'roles' => $this->roleRepo->fetchSelect(), 'roles' => $this->roleRepo->fetchSelect(),
] ]
); );

View File

@ -69,7 +69,7 @@ abstract class AbstractApiCrudController
if ($record instanceof IdentifiableEntityInterface) { if ($record instanceof IdentifiableEntityInterface) {
$return['links'] = [ $return['links'] = [
'self' => (string)$router->fromHere( 'self' => $router->fromHere(
routeName: $this->resourceRouteName, routeName: $this->resourceRouteName,
routeParams: ['id' => $record->getIdRequired()], routeParams: ['id' => $record->getIdRequired()],
absolute: !$isInternal absolute: !$isInternal

View File

@ -34,7 +34,7 @@ final class GenerateCertificateAction
[ [
'success' => true, 'success' => true,
'links' => [ 'links' => [
'log' => (string)$router->fromHere('api:admin:acme-log', [ 'log' => $router->fromHere('api:admin:acme-log', [
'path' => basename($tempFile), 'path' => basename($tempFile),
]), ]),
], ],

View File

@ -61,11 +61,11 @@ final class GetAction
$paginator = Paginator::fromArray($backups, $request); $paginator = Paginator::fromArray($backups, $request);
$paginator->setPostprocessor(function ($row) use ($router) { $paginator->setPostprocessor(function ($row) use ($router) {
$row['links'] = [ $row['links'] = [
'download' => (string)$router->fromHere( 'download' => $router->fromHere(
'api:admin:backups:download', 'api:admin:backups:download',
['path' => $row['pathEncoded']] ['path' => $row['pathEncoded']]
), ),
'delete' => (string)$router->fromHere( 'delete' => $router->fromHere(
'api:admin:backups:delete', 'api:admin:backups:delete',
['path' => $row['pathEncoded']] ['path' => $row['pathEncoded']]
), ),

View File

@ -46,7 +46,7 @@ final class RunAction
'excludeMedia' => $message->excludeMedia, 'excludeMedia' => $message->excludeMedia,
'outputPath' => $message->outputPath, 'outputPath' => $message->outputPath,
'links' => [ 'links' => [
'log' => (string)$router->named( 'log' => $router->named(
'api:admin:backups:log', 'api:admin:backups:log',
['path' => basename($tempFile)] ['path' => basename($tempFile)]
), ),

View File

@ -35,7 +35,7 @@ final class LogsAction
function (string $key, array $row) use ($router) { function (string $key, array $row) use ($router) {
$row['key'] = $key; $row['key'] = $key;
$row['links'] = [ $row['links'] = [
'self' => (string)$router->named( 'self' => $router->named(
'api:admin:log', 'api:admin:log',
[ [
'log' => $key, 'log' => $key,

View File

@ -33,7 +33,7 @@ final class ServiceControlController
$row['links'] = []; $row['links'] = [];
if ($canRestart) { if ($canRestart) {
$row['links']['restart'] = (string)$router->fromHere( $row['links']['restart'] = $router->fromHere(
'api:admin:services:restart', 'api:admin:services:restart',
['service' => $service->name] ['service' => $service->name]
); );

View File

@ -192,17 +192,17 @@ class StationsController extends AbstractAdminApiCrudController
$router = $request->getRouter(); $router = $request->getRouter();
$return['links'] = [ $return['links'] = [
'self' => (string)$router->fromHere( 'self' => $router->fromHere(
routeName: $this->resourceRouteName, routeName: $this->resourceRouteName,
routeParams: ['id' => $record->getIdRequired()], routeParams: ['id' => $record->getIdRequired()],
absolute: !$isInternal absolute: !$isInternal
), ),
'manage' => (string)$router->named( 'manage' => $router->named(
routeName: 'stations:index:index', routeName: 'stations:index:index',
routeParams: ['station_id' => $record->getIdRequired()], routeParams: ['station_id' => $record->getIdRequired()],
absolute: !$isInternal absolute: !$isInternal
), ),
'clone' => (string)$router->fromHere( 'clone' => $router->fromHere(
routeName: 'api:admin:station:clone', routeName: 'api:admin:station:clone',
routeParams: ['id' => $record->getIdRequired()], routeParams: ['id' => $record->getIdRequired()],
absolute: !$isInternal absolute: !$isInternal

View File

@ -177,12 +177,12 @@ class UsersController extends AbstractAdminApiCrudController
$return['is_me'] = $currentUser->getIdRequired() === $record->getIdRequired(); $return['is_me'] = $currentUser->getIdRequired() === $record->getIdRequired();
$return['links'] = [ $return['links'] = [
'self' => (string)$router->fromHere( 'self' => $router->fromHere(
routeName: $this->resourceRouteName, routeName: $this->resourceRouteName,
routeParams: ['id' => $record->getIdRequired()], routeParams: ['id' => $record->getIdRequired()],
absolute: !$isInternal absolute: !$isInternal
), ),
'masquerade' => (string)$router->fromHere( 'masquerade' => $router->fromHere(
routeName: 'account:masquerade', routeName: 'account:masquerade',
routeParams: [ routeParams: [
'id' => $record->getIdRequired(), 'id' => $record->getIdRequired(),

View File

@ -49,12 +49,12 @@ final class StationsAction
$row->fromParentObject($np); $row->fromParentObject($np);
$row->links = [ $row->links = [
'public' => (string)$router->named('public:index', ['station_id' => $station->getShortName()]), 'public' => $router->named('public:index', ['station_id' => $station->getShortName()]),
'manage' => (string)$router->named('stations:index:index', ['station_id' => $station->getId()]), 'manage' => $router->named('stations:index:index', ['station_id' => $station->getId()]),
]; ];
if ($listenersEnabled && $acl->isAllowed(StationPermissions::Reports, $station->getId())) { if ($listenersEnabled && $acl->isAllowed(StationPermissions::Reports, $station->getId())) {
$row->links['listeners'] = (string)$router->named( $row->links['listeners'] = $router->named(
'stations:reports:listeners', 'stations:reports:listeners',
['station_id' => $station->getId()] ['station_id' => $station->getId()]
); );

View File

@ -23,7 +23,7 @@ final class OpenApiAction
$apiBaseUrl = str_replace( $apiBaseUrl = str_replace(
'/openapi.yml', '/openapi.yml',
'', '',
(string)$request->getRouter()->fromHere(absolute: true) $request->getRouter()->fromHere(absolute: true)
); );
$yaml = $this->apiDocsCommand->generate(true, $apiBaseUrl)?->toYaml(); $yaml = $this->apiDocsCommand->generate(true, $apiBaseUrl)?->toYaml();

View File

@ -406,7 +406,7 @@ final class ListAction
$artMediaId .= '-' . $row->media->art_updated_at; $artMediaId .= '-' . $row->media->art_updated_at;
} }
$row->media->art = (string)$router->named( $row->media->art = $router->named(
'api:stations:media:art', 'api:stations:media:art',
[ [
'station_id' => $stationId, 'station_id' => $stationId,
@ -415,21 +415,21 @@ final class ListAction
); );
$row->media->links = [ $row->media->links = [
'play' => (string)$router->named( 'play' => $router->named(
'api:stations:files:play', 'api:stations:files:play',
['station_id' => $stationId, 'id' => $row->media->media_id], ['station_id' => $stationId, 'id' => $row->media->media_id],
[], [],
true true
), ),
'edit' => (string)$router->named( 'edit' => $router->named(
'api:stations:file', 'api:stations:file',
['station_id' => $stationId, 'id' => $row->media->media_id], ['station_id' => $stationId, 'id' => $row->media->media_id],
), ),
'art' => (string)$router->named( 'art' => $router->named(
'api:stations:media:art-internal', 'api:stations:media:art-internal',
['station_id' => $stationId, 'media_id' => $row->media->media_id] ['station_id' => $stationId, 'media_id' => $row->media->media_id]
), ),
'waveform' => (string)$router->named( 'waveform' => $router->named(
'api:stations:media:waveform', 'api:stations:media:waveform',
[ [
'station_id' => $stationId, 'station_id' => $stationId,
@ -440,12 +440,12 @@ final class ListAction
} }
$row->links = [ $row->links = [
'download' => (string)$router->named( 'download' => $router->named(
'api:stations:files:download', 'api:stations:files:download',
['station_id' => $stationId], ['station_id' => $stationId],
['file' => $row->path] ['file' => $row->path]
), ),
'rename' => (string)$router->named( 'rename' => $router->named(
'api:stations:files:rename', 'api:stations:files:rename',
['station_id' => $stationId], ['station_id' => $stationId],
['file' => $row->path] ['file' => $row->path]

View File

@ -35,7 +35,7 @@ final class LogsAction
function (string $key, array $row) use ($router, $station_id) { function (string $key, array $row) use ($router, $station_id) {
$row['key'] = $key; $row['key'] = $key;
$row['links'] = [ $row['links'] = [
'self' => (string)$router->named( 'self' => $router->named(
'api:stations:log', 'api:stations:log',
[ [
'station_id' => $station_id, 'station_id' => $station_id,

View File

@ -200,7 +200,7 @@ final class MountsController extends AbstractStationApiCrudController
$frontend = $this->adapters->getFrontendAdapter($station); $frontend = $this->adapters->getFrontendAdapter($station);
$return['links']['intro'] = (string)$router->fromHere( $return['links']['intro'] = $router->fromHere(
routeName: 'api:stations:mounts:intro', routeName: 'api:stations:mounts:intro',
routeParams: ['id' => $record->getId()], routeParams: ['id' => $record->getId()],
absolute: true absolute: true

View File

@ -135,7 +135,7 @@ final class ListAction
station: $station station: $station
); );
$row->playlist = $playlist['name']; $row->playlist = $playlist['name'];
$row->download_url = (string)$router->named( $row->download_url = $router->named(
'api:stations:ondemand:download', 'api:stations:ondemand:download',
[ [
'station_id' => $station->getId(), 'station_id' => $station->getId(),

View File

@ -54,7 +54,7 @@ final class GetOrderAction
array_map( array_map(
static function (array $row) use ($router, $station): array { static function (array $row) use ($router, $station): array {
$row['media']['links'] = [ $row['media']['links'] = [
'play' => (string)$router->named( 'play' => $router->named(
'api:stations:files:play', 'api:stations:files:play',
['station_id' => $station->getIdRequired(), 'id' => $row['media']['unique_id']], ['station_id' => $station->getIdRequired(), 'id' => $row['media']['unique_id']],
[], [],

View File

@ -220,7 +220,7 @@ final class PlaylistsController extends AbstractScheduledEntityController
'title' => $playlist->getName(), 'title' => $playlist->getName(),
'start' => $start->toIso8601String(), 'start' => $start->toIso8601String(),
'end' => $end->toIso8601String(), 'end' => $end->toIso8601String(),
'edit_url' => (string)$request->getRouter()->named( 'edit_url' => $request->getRouter()->named(
'api:stations:playlist', 'api:stations:playlist',
['station_id' => $station->getId(), 'id' => $playlist->getId()] ['station_id' => $station->getId(), 'id' => $playlist->getId()]
), ),
@ -257,39 +257,39 @@ final class PlaylistsController extends AbstractScheduledEntityController
$router = $request->getRouter(); $router = $request->getRouter();
$return['links'] = [ $return['links'] = [
'toggle' => (string)$router->fromHere( 'toggle' => $router->fromHere(
'api:stations:playlist:toggle', 'api:stations:playlist:toggle',
['id' => $record->getId()], ['id' => $record->getId()],
[], [],
!$isInternal !$isInternal
), ),
'order' => (string)$router->fromHere( 'order' => $router->fromHere(
'api:stations:playlist:order', 'api:stations:playlist:order',
['id' => $record->getId()], ['id' => $record->getId()],
[], [],
!$isInternal !$isInternal
), ),
'reshuffle' => (string)$router->fromHere( 'reshuffle' => $router->fromHere(
routeName: 'api:stations:playlist:reshuffle', routeName: 'api:stations:playlist:reshuffle',
routeParams: ['id' => $record->getId()], routeParams: ['id' => $record->getId()],
absolute: !$isInternal absolute: !$isInternal
), ),
'queue' => (string)$router->fromHere( 'queue' => $router->fromHere(
routeName: 'api:stations:playlist:queue', routeName: 'api:stations:playlist:queue',
routeParams: ['id' => $record->getId()], routeParams: ['id' => $record->getId()],
absolute: !$isInternal absolute: !$isInternal
), ),
'import' => (string)$router->fromHere( 'import' => $router->fromHere(
routeName: 'api:stations:playlist:import', routeName: 'api:stations:playlist:import',
routeParams: ['id' => $record->getId()], routeParams: ['id' => $record->getId()],
absolute: !$isInternal absolute: !$isInternal
), ),
'clone' => (string)$router->fromHere( 'clone' => $router->fromHere(
routeName: 'api:stations:playlist:clone', routeName: 'api:stations:playlist:clone',
routeParams: ['id' => $record->getId()], routeParams: ['id' => $record->getId()],
absolute: !$isInternal absolute: !$isInternal
), ),
'self' => (string)$router->fromHere( 'self' => $router->fromHere(
$this->resourceRouteName, $this->resourceRouteName,
['id' => $record->getId()], ['id' => $record->getId()],
[], [],
@ -298,7 +298,7 @@ final class PlaylistsController extends AbstractScheduledEntityController
]; ];
foreach (['pls', 'm3u'] as $format) { foreach (['pls', 'm3u'] as $format) {
$return['links']['export'][$format] = (string)$router->fromHere( $return['links']['export'][$format] = $router->fromHere(
routeName: 'api:stations:playlist:export', routeName: 'api:stations:playlist:export',
routeParams: ['id' => $record->getId(), 'format' => $format], routeParams: ['id' => $record->getId(), 'format' => $format],
absolute: !$isInternal absolute: !$isInternal

View File

@ -376,24 +376,24 @@ final class PodcastEpisodesController extends AbstractApiCrudController
$return->art_updated_at = $record->getArtUpdatedAt(); $return->art_updated_at = $record->getArtUpdatedAt();
$return->has_custom_art = (0 !== $return->art_updated_at); $return->has_custom_art = (0 !== $return->art_updated_at);
$return->art = (string)$router->fromHere( $return->art = $router->fromHere(
routeName: 'api:stations:podcast:episode:art', routeName: 'api:stations:podcast:episode:art',
routeParams: ['episode_id' => $record->getId() . '|' . $record->getArtUpdatedAt()], routeParams: ['episode_id' => $record->getId() . '|' . $record->getArtUpdatedAt()],
absolute: true absolute: true
); );
$return->links = [ $return->links = [
'self' => (string)$router->fromHere( 'self' => $router->fromHere(
routeName: $this->resourceRouteName, routeName: $this->resourceRouteName,
routeParams: ['episode_id' => $record->getId()], routeParams: ['episode_id' => $record->getId()],
absolute: !$isInternal absolute: !$isInternal
), ),
'public' => (string)$router->fromHere( 'public' => $router->fromHere(
routeName: 'public:podcast:episode', routeName: 'public:podcast:episode',
routeParams: ['episode_id' => $record->getId()], routeParams: ['episode_id' => $record->getId()],
absolute: !$isInternal absolute: !$isInternal
), ),
'download' => (string)$router->fromHere( 'download' => $router->fromHere(
routeName: 'api:stations:podcast:episode:download', routeName: 'api:stations:podcast:episode:download',
routeParams: ['episode_id' => $record->getId()], routeParams: ['episode_id' => $record->getId()],
absolute: !$isInternal absolute: !$isInternal
@ -404,12 +404,12 @@ final class PodcastEpisodesController extends AbstractApiCrudController
$station = $request->getStation(); $station = $request->getStation();
if ($acl->isAllowed(StationPermissions::Podcasts, $station)) { if ($acl->isAllowed(StationPermissions::Podcasts, $station)) {
$return->links['art'] = (string)$router->fromHere( $return->links['art'] = $router->fromHere(
routeName: 'api:stations:podcast:episode:art-internal', routeName: 'api:stations:podcast:episode:art-internal',
routeParams: ['episode_id' => $record->getId()], routeParams: ['episode_id' => $record->getId()],
absolute: !$isInternal absolute: !$isInternal
); );
$return->links['media'] = (string)$router->fromHere( $return->links['media'] = $router->fromHere(
routeName: 'api:stations:podcast:episode:media-internal', routeName: 'api:stations:podcast:episode:media-internal',
routeParams: ['episode_id' => $record->getId()], routeParams: ['episode_id' => $record->getId()],
absolute: !$isInternal absolute: !$isInternal

View File

@ -312,29 +312,29 @@ final class PodcastsController extends AbstractApiCrudController
$return->episodes = $episodes; $return->episodes = $episodes;
$return->has_custom_art = (0 !== $record->getArtUpdatedAt()); $return->has_custom_art = (0 !== $record->getArtUpdatedAt());
$return->art = (string)$router->fromHere( $return->art = $router->fromHere(
routeName: 'api:stations:podcast:art', routeName: 'api:stations:podcast:art',
routeParams: ['podcast_id' => $record->getId() . '|' . $record->getArtUpdatedAt()], routeParams: ['podcast_id' => $record->getId() . '|' . $record->getArtUpdatedAt()],
absolute: true absolute: true
); );
$return->links = [ $return->links = [
'self' => (string)$router->fromHere( 'self' => $router->fromHere(
routeName: $this->resourceRouteName, routeName: $this->resourceRouteName,
routeParams: ['podcast_id' => $record->getId()], routeParams: ['podcast_id' => $record->getId()],
absolute: !$isInternal absolute: !$isInternal
), ),
'episodes' => (string)$router->fromHere( 'episodes' => $router->fromHere(
routeName: 'api:stations:podcast:episodes', routeName: 'api:stations:podcast:episodes',
routeParams: ['podcast_id' => $record->getId()], routeParams: ['podcast_id' => $record->getId()],
absolute: !$isInternal absolute: !$isInternal
), ),
'public_episodes' => (string)$router->fromHere( 'public_episodes' => $router->fromHere(
routeName: 'public:podcast:episodes', routeName: 'public:podcast:episodes',
routeParams: ['podcast_id' => $record->getId()], routeParams: ['podcast_id' => $record->getId()],
absolute: !$isInternal absolute: !$isInternal
), ),
'public_feed' => (string)$router->fromHere( 'public_feed' => $router->fromHere(
routeName: 'public:podcast:feed', routeName: 'public:podcast:feed',
routeParams: ['podcast_id' => $record->getId()], routeParams: ['podcast_id' => $record->getId()],
absolute: !$isInternal absolute: !$isInternal
@ -344,18 +344,18 @@ final class PodcastsController extends AbstractApiCrudController
$acl = $request->getAcl(); $acl = $request->getAcl();
if ($acl->isAllowed(StationPermissions::Podcasts, $station)) { if ($acl->isAllowed(StationPermissions::Podcasts, $station)) {
$return->links['art'] = (string)$router->fromHere( $return->links['art'] = $router->fromHere(
routeName: 'api:stations:podcast:art-internal', routeName: 'api:stations:podcast:art-internal',
routeParams: ['podcast_id' => $record->getId()], routeParams: ['podcast_id' => $record->getId()],
absolute: !$isInternal absolute: !$isInternal
); );
$return->links['episode_new_art'] = (string)$router->fromHere( $return->links['episode_new_art'] = $router->fromHere(
routeName: 'api:stations:podcast:episodes:new-art', routeName: 'api:stations:podcast:episodes:new-art',
routeParams: ['podcast_id' => $record->getId()], routeParams: ['podcast_id' => $record->getId()],
absolute: !$isInternal absolute: !$isInternal
); );
$return->links['episode_new_media'] = (string)$router->fromHere( $return->links['episode_new_media'] = $router->fromHere(
routeName: 'api:stations:podcast:episodes:new-media', routeName: 'api:stations:podcast:episodes:new-media',
routeParams: ['podcast_id' => $record->getId()], routeParams: ['podcast_id' => $record->getId()],
absolute: !$isInternal absolute: !$isInternal

View File

@ -150,7 +150,12 @@ final class QueueController extends AbstractStationApiCrudController
$apiResponse->log = $this->queue->getQueueRowLog($record); $apiResponse->log = $this->queue->getQueueRowLog($record);
$apiResponse->links = [ $apiResponse->links = [
'self' => (string)$router->fromHere($this->resourceRouteName, ['id' => $record->getId()], [], !$isInternal), 'self' => $router->fromHere(
$this->resourceRouteName,
['id' => $record->getId()],
[],
!$isInternal
),
]; ];
return $apiResponse; return $apiResponse;

View File

@ -197,7 +197,7 @@ final class RemotesController extends AbstractStationApiCrudController
$return->is_editable = $record->isEditable(); $return->is_editable = $record->isEditable();
$return->links = [ $return->links = [
'self' => (string)$router->fromHere( 'self' => $router->fromHere(
routeName: $this->resourceRouteName, routeName: $this->resourceRouteName,
routeParams: ['id' => $record->getIdRequired()], routeParams: ['id' => $record->getIdRequired()],
absolute: !$isInternal absolute: !$isInternal

View File

@ -51,7 +51,7 @@ final class RequestsController
$row['links'] = []; $row['links'] = [];
if (0 === $row['played_at']) { if (0 === $row['played_at']) {
$row['links']['delete'] = (string)$router->fromHere( $row['links']['delete'] = $router->fromHere(
'api:stations:reports:requests:delete', 'api:stations:reports:requests:delete',
['request_id' => $row['id']] ['request_id' => $row['id']]
); );

View File

@ -124,7 +124,7 @@ final class RequestsController
$row = new Entity\Api\StationRequest(); $row = new Entity\Api\StationRequest();
$row->song = ($this->songApiGenerator)($media_row, $station, $baseUrl); $row->song = ($this->songApiGenerator)($media_row, $station, $baseUrl);
$row->request_id = $media_row->getUniqueId(); $row->request_id = $media_row->getUniqueId();
$row->request_url = (string)$router->named( $row->request_url = $router->named(
'api:requests:submit', 'api:requests:submit',
[ [
'station_id' => $station->getId(), 'station_id' => $station->getId(),

View File

@ -91,13 +91,13 @@ final class BroadcastsController extends AbstractApiCrudController
'path' => $recordingPath, 'path' => $recordingPath,
'size' => $fsRecordings->fileSize($recordingPath), 'size' => $fsRecordings->fileSize($recordingPath),
'links' => [ 'links' => [
'download' => (string)$router->fromHere( 'download' => $router->fromHere(
'api:stations:streamer:broadcast:download', 'api:stations:streamer:broadcast:download',
$routeParams, $routeParams,
[], [],
true true
), ),
'delete' => (string)$router->fromHere( 'delete' => $router->fromHere(
'api:stations:streamer:broadcast:delete', 'api:stations:streamer:broadcast:delete',
$routeParams, $routeParams,
[], [],

View File

@ -260,7 +260,7 @@ final class StreamersController extends AbstractScheduledEntityController
'title' => $streamer->getDisplayName(), 'title' => $streamer->getDisplayName(),
'start' => $start->toIso8601String(), 'start' => $start->toIso8601String(),
'end' => $end->toIso8601String(), 'end' => $end->toIso8601String(),
'edit_url' => (string)$request->getRouter()->named( 'edit_url' => $request->getRouter()->named(
'api:stations:streamer', 'api:stations:streamer',
['station_id' => $station->getId(), 'id' => $streamer->getId()] ['station_id' => $station->getId(), 'id' => $streamer->getId()]
), ),
@ -283,18 +283,18 @@ final class StreamersController extends AbstractScheduledEntityController
$isInternal = ('true' === $request->getParam('internal', 'false')); $isInternal = ('true' === $request->getParam('internal', 'false'));
$return['has_custom_art'] = (0 !== $record->getArtUpdatedAt()); $return['has_custom_art'] = (0 !== $record->getArtUpdatedAt());
$return['art'] = (string)$router->fromHere( $return['art'] = $router->fromHere(
routeName: 'api:stations:streamer:art', routeName: 'api:stations:streamer:art',
routeParams: ['id' => $record->getIdRequired() . '|' . $record->getArtUpdatedAt()], routeParams: ['id' => $record->getIdRequired() . '|' . $record->getArtUpdatedAt()],
absolute: !$isInternal absolute: !$isInternal
); );
$return['links']['broadcasts'] = (string)$router->fromHere( $return['links']['broadcasts'] = $router->fromHere(
routeName: 'api:stations:streamer:broadcasts', routeName: 'api:stations:streamer:broadcasts',
routeParams: ['id' => $record->getId()], routeParams: ['id' => $record->getId()],
absolute: !$isInternal absolute: !$isInternal
); );
$return['links']['art'] = (string)$router->fromHere( $return['links']['art'] = $router->fromHere(
routeName: 'api:stations:streamer:art-internal', routeName: 'api:stations:streamer:art-internal',
routeParams: ['id' => $record->getId()], routeParams: ['id' => $record->getId()],
absolute: !$isInternal absolute: !$isInternal

View File

@ -42,7 +42,7 @@ final class TestAction
[ [
'success' => true, 'success' => true,
'links' => [ 'links' => [
'log' => (string)$router->fromHere('api:stations:webhook:test-log', [ 'log' => $router->fromHere('api:stations:webhook:test-log', [
'path' => basename($tempFile), 'path' => basename($tempFile),
]), ]),
], ],

View File

@ -192,17 +192,17 @@ final class WebhooksController extends AbstractStationApiCrudController
$router = $request->getRouter(); $router = $request->getRouter();
$return['links'] = [ $return['links'] = [
'self' => (string)$router->fromHere( 'self' => $router->fromHere(
routeName: $this->resourceRouteName, routeName: $this->resourceRouteName,
routeParams: ['id' => $record->getIdRequired()], routeParams: ['id' => $record->getIdRequired()],
absolute: !$isInternal absolute: !$isInternal
), ),
'toggle' => (string)$router->fromHere( 'toggle' => $router->fromHere(
routeName: 'api:stations:webhook:toggle', routeName: 'api:stations:webhook:toggle',
routeParams: ['id' => $record->getIdRequired()], routeParams: ['id' => $record->getIdRequired()],
absolute: !$isInternal absolute: !$isInternal
), ),
'test' => (string)$router->fromHere( 'test' => $router->fromHere(
routeName: 'api:stations:webhook:test', routeName: 'api:stations:webhook:test',
routeParams: ['id' => $record->getIdRequired()], routeParams: ['id' => $record->getIdRequired()],
absolute: !$isInternal absolute: !$isInternal

View File

@ -17,6 +17,6 @@ final class EndMasqueradeAction
$auth = $request->getAuth(); $auth = $request->getAuth();
$auth->endMasquerade(); $auth->endMasquerade();
return $response->withRedirect((string)$request->getRouter()->named('admin:users:index')); return $response->withRedirect($request->getRouter()->named('admin:users:index'));
} }
} }

View File

@ -87,7 +87,7 @@ final class ForgotPasswordAction
Flash::SUCCESS Flash::SUCCESS
); );
return $response->withRedirect((string)$request->getRouter()->named('account:login')); return $response->withRedirect($request->getRouter()->named('account:login'));
} }
return $view->renderToResponse($response, 'frontend/account/forgot'); return $view->renderToResponse($response, 'frontend/account/forgot');

View File

@ -41,12 +41,12 @@ final class LoginAction
)->getSingleScalarResult(); )->getSingleScalarResult();
if (0 === $num_users) { if (0 === $num_users) {
return $response->withRedirect((string)$request->getRouter()->named('setup:index')); return $response->withRedirect($request->getRouter()->named('setup:index'));
} }
} }
if ($auth->isLoggedIn()) { if ($auth->isLoggedIn()) {
return $response->withRedirect((string)$request->getRouter()->named('dashboard')); return $response->withRedirect($request->getRouter()->named('dashboard'));
} }
$flash = $request->getFlash(); $flash = $request->getFlash();
@ -88,7 +88,7 @@ final class LoginAction
// Redirect for 2FA. // Redirect for 2FA.
if (!$auth->isLoginComplete()) { if (!$auth->isLoginComplete()) {
return $response->withRedirect((string)$request->getRouter()->named('account:login:2fa')); return $response->withRedirect($request->getRouter()->named('account:login:2fa'));
} }
// Redirect to complete setup if it's not completed yet. // Redirect to complete setup if it's not completed yet.
@ -101,7 +101,7 @@ final class LoginAction
), ),
Flash::SUCCESS Flash::SUCCESS
); );
return $response->withRedirect((string)$request->getRouter()->named('setup:index')); return $response->withRedirect($request->getRouter()->named('setup:index'));
} }
$flash->addMessage( $flash->addMessage(
@ -111,7 +111,7 @@ final class LoginAction
$referrer = $session->get('login_referrer'); $referrer = $session->get('login_referrer');
return $response->withRedirect( return $response->withRedirect(
(!empty($referrer)) ? $referrer : (string)$request->getRouter()->named('dashboard') (!empty($referrer)) ? $referrer : $request->getRouter()->named('dashboard')
); );
} }

View File

@ -17,6 +17,6 @@ final class LogoutAction
$auth = $request->getAuth(); $auth = $request->getAuth();
$auth->logout(); $auth->logout();
return $response->withRedirect((string)$request->getRouter()->named('account:login')); return $response->withRedirect($request->getRouter()->named('account:login'));
} }
} }

View File

@ -42,6 +42,6 @@ final class MasqueradeAction
Flash::SUCCESS Flash::SUCCESS
); );
return $response->withRedirect((string)$request->getRouter()->named('dashboard')); return $response->withRedirect($request->getRouter()->named('dashboard'));
} }
} }

View File

@ -38,7 +38,7 @@ final class RecoverAction
Flash::ERROR Flash::ERROR
); );
return $response->withRedirect((string)$request->getRouter()->named('account:login')); return $response->withRedirect($request->getRouter()->named('account:login'));
} }
$csrf = $request->getCsrf(); $csrf = $request->getCsrf();
@ -73,7 +73,7 @@ final class RecoverAction
Flash::SUCCESS Flash::SUCCESS
); );
return $response->withRedirect((string)$request->getRouter()->named('dashboard')); return $response->withRedirect($request->getRouter()->named('dashboard'));
} catch (Throwable $e) { } catch (Throwable $e) {
$error = $e->getMessage(); $error = $e->getMessage();
} }

View File

@ -36,7 +36,7 @@ final class TwoFactorAction
return $response->withRedirect($referrer); return $response->withRedirect($referrer);
} }
return $response->withRedirect((string)$request->getRouter()->named('dashboard')); return $response->withRedirect($request->getRouter()->named('dashboard'));
} }
$flash->addMessage( $flash->addMessage(

View File

@ -35,15 +35,15 @@ final class DashboardAction
id: 'dashboard', id: 'dashboard',
title: __('Dashboard'), title: __('Dashboard'),
props: [ props: [
'userUrl' => (string)$router->named('api:frontend:account:me'), 'userUrl' => $router->named('api:frontend:account:me'),
'profileUrl' => (string)$router->named('profile:index'), 'profileUrl' => $router->named('profile:index'),
'adminUrl' => (string)$router->named('admin:index:index'), 'adminUrl' => $router->named('admin:index:index'),
'showAdmin' => $acl->isAllowed(GlobalPermissions::View), 'showAdmin' => $acl->isAllowed(GlobalPermissions::View),
'notificationsUrl' => (string)$router->named('api:frontend:dashboard:notifications'), 'notificationsUrl' => $router->named('api:frontend:dashboard:notifications'),
'showCharts' => $showCharts, 'showCharts' => $showCharts,
'chartsUrl' => (string)$router->named('api:frontend:dashboard:charts'), 'chartsUrl' => $router->named('api:frontend:dashboard:charts'),
'manageStationsUrl' => (string)$router->named('admin:stations:index'), 'manageStationsUrl' => $router->named('admin:stations:index'),
'stationsUrl' => (string)$router->named('api:frontend:dashboard:stations'), 'stationsUrl' => $router->named('api:frontend:dashboard:stations'),
'showAlbumArt' => !$settings->getHideAlbumArt(), 'showAlbumArt' => !$settings->getHideAlbumArt(),
] ]
); );

View File

@ -23,7 +23,7 @@ final class IndexAction
// Redirect to complete setup, if it hasn't been completed yet. // Redirect to complete setup, if it hasn't been completed yet.
$settings = $this->settingsRepo->readSettings(); $settings = $this->settingsRepo->readSettings();
if (!$settings->isSetupComplete()) { if (!$settings->isSetupComplete()) {
return $response->withRedirect((string)$request->getRouter()->named('setup:index')); return $response->withRedirect($request->getRouter()->named('setup:index'));
} }
// Redirect to login screen if the user isn't logged in. // Redirect to login screen if the user isn't logged in.
@ -36,10 +36,10 @@ final class IndexAction
return $response->withRedirect((string)$homepageRedirect, 302); return $response->withRedirect((string)$homepageRedirect, 302);
} }
return $response->withRedirect((string)$request->getRouter()->named('account:login')); return $response->withRedirect($request->getRouter()->named('account:login'));
} }
// Redirect to dashboard if no other custom redirection rules exist. // Redirect to dashboard if no other custom redirection rules exist.
return $response->withRedirect((string)$request->getRouter()->named('dashboard')); return $response->withRedirect($request->getRouter()->named('dashboard'));
} }
} }

View File

@ -28,10 +28,10 @@ final class IndexAction
id: 'account', id: 'account',
title: __('My Account'), title: __('My Account'),
props: [ props: [
'userUrl' => (string)$router->named('api:frontend:account:me'), 'userUrl' => $router->named('api:frontend:account:me'),
'changePasswordUrl' => (string)$router->named('api:frontend:account:password'), 'changePasswordUrl' => $router->named('api:frontend:account:password'),
'twoFactorUrl' => (string)$router->named('api:frontend:account:two-factor'), 'twoFactorUrl' => $router->named('api:frontend:account:two-factor'),
'apiKeysApiUrl' => (string)$router->named('api:frontend:api-keys'), 'apiKeysApiUrl' => $router->named('api:frontend:api-keys'),
'supportedLocales' => $supportedLocales, 'supportedLocales' => $supportedLocales,
] ]
); );

View File

@ -35,7 +35,7 @@ final class ThemeAction
$referrer = $request->getHeaderLine('Referer'); $referrer = $request->getHeaderLine('Referer');
return $response->withRedirect( return $response->withRedirect(
$referrer ?: (string)$request->getRouter()->named('dashboard') $referrer ?: $request->getRouter()->named('dashboard')
); );
} }
} }

View File

@ -52,7 +52,7 @@ final class HistoryAction
'useNchan' => $useNChan, 'useNchan' => $useNChan,
'nowPlayingUri' => $useNChan 'nowPlayingUri' => $useNChan
? '/api/live/nowplaying/' . urlencode($station->getShortName()) ? '/api/live/nowplaying/' . urlencode($station->getShortName())
: (string)$router->named('api:nowplaying:index', ['station_id' => $station->getId()]), : $router->named('api:nowplaying:index', ['station_id' => $station->getId()]),
], ],
); );
} }

View File

@ -69,7 +69,7 @@ final class OnDemandAction
'hide_footer' => true, 'hide_footer' => true,
], ],
props: [ props: [
'listUrl' => (string)$router->fromHere('api:stations:ondemand:list'), 'listUrl' => $router->fromHere('api:stations:ondemand:list'),
'showDownloadButton' => $station->getEnableOnDemandDownload(), 'showDownloadButton' => $station->getEnableOnDemandDownload(),
'customFields' => $customFields, 'customFields' => $customFields,
'stationName' => $station->getName(), 'stationName' => $station->getName(),

View File

@ -63,7 +63,7 @@ final class PlayerAction
$props['nowPlayingUri'] = '/api/live/nowplaying/' . urlencode($station->getShortName()); $props['nowPlayingUri'] = '/api/live/nowplaying/' . urlencode($station->getShortName());
} else { } else {
$props['useNchan'] = false; $props['useNchan'] = false;
$props['nowPlayingUri'] = (string)$router->named( $props['nowPlayingUri'] = $router->named(
'api:nowplaying:index', 'api:nowplaying:index',
['station_id' => $station->getId()] ['station_id' => $station->getId()]
); );
@ -92,11 +92,11 @@ final class PlayerAction
// Render full page player. // Render full page player.
$props['stationName'] = $station->getName(); $props['stationName'] = $station->getName();
$props['enableRequests'] = $station->getEnableRequests(); $props['enableRequests'] = $station->getEnableRequests();
$props['downloadPlaylistUri'] = (string)$router->named( $props['downloadPlaylistUri'] = $router->named(
'public:playlist', 'public:playlist',
['station_id' => $station->getShortName(), 'format' => 'pls'] ['station_id' => $station->getShortName(), 'format' => 'pls']
); );
$props['requestListUri'] = (string)$router->named( $props['requestListUri'] = $router->named(
'api:requests:list', 'api:requests:list',
['station_id' => $station->getId()] ['station_id' => $station->getId()]
); );

View File

@ -44,7 +44,7 @@ final class PodcastEpisodeAction
$episode = $this->episodeRepository->fetchEpisodeForStation($station, $episode_id); $episode = $this->episodeRepository->fetchEpisodeForStation($station, $episode_id);
$podcastEpisodesLink = (string)$router->named( $podcastEpisodesLink = $router->named(
'public:podcast:episodes', 'public:podcast:episodes',
[ [
'station_id' => $station->getId(), 'station_id' => $station->getId(),
@ -57,7 +57,7 @@ final class PodcastEpisodeAction
return $response->withRedirect($podcastEpisodesLink); return $response->withRedirect($podcastEpisodesLink);
} }
$feedLink = (string)$router->named( $feedLink = $router->named(
'public:podcast:feed', 'public:podcast:feed',
[ [
'station_id' => $station->getId(), 'station_id' => $station->getId(),

View File

@ -57,7 +57,7 @@ final class PodcastEpisodesAction
} }
); );
$podcastsLink = (string)$router->fromHere( $podcastsLink = $router->fromHere(
'public:podcasts', 'public:podcasts',
[ [
'station_id' => $station->getId(), 'station_id' => $station->getId(),
@ -69,7 +69,7 @@ final class PodcastEpisodesAction
return $response->withRedirect($podcastsLink); return $response->withRedirect($podcastsLink);
} }
$feedLink = (string)$router->named( $feedLink = $router->named(
'public:podcast:feed', 'public:podcast:feed',
[ [
'station_id' => $station->getId(), 'station_id' => $station->getId(),

View File

@ -136,7 +136,7 @@ final class PodcastFeedAction
$channelLink = $podcast->getLink(); $channelLink = $podcast->getLink();
if (empty($channelLink)) { if (empty($channelLink)) {
$channelLink = (string)$serverRequest->getRouter()->fromHere( $channelLink = $serverRequest->getRouter()->fromHere(
routeName: 'public:podcast:episodes', routeName: 'public:podcast:episodes',
absolute: true absolute: true
); );
@ -237,7 +237,7 @@ final class PodcastFeedAction
); );
if ($podcastsFilesystem->fileExists(Podcast::getArtPath($podcast->getIdRequired()))) { if ($podcastsFilesystem->fileExists(Podcast::getArtPath($podcast->getIdRequired()))) {
$podcastArtworkSrc = (string)$this->router->fromHere( $podcastArtworkSrc = $this->router->fromHere(
routeName: 'api:stations:podcast:art', routeName: 'api:stations:podcast:art',
routeParams: ['podcast_id' => $podcast->getIdRequired() . '|' . $podcast->getArtUpdatedAt()], routeParams: ['podcast_id' => $podcast->getIdRequired() . '|' . $podcast->getArtUpdatedAt()],
absolute: true absolute: true
@ -275,7 +275,7 @@ final class PodcastFeedAction
$episodeLink = $episode->getLink(); $episodeLink = $episode->getLink();
if (empty($episodeLink)) { if (empty($episodeLink)) {
$episodeLink = (string)$this->router->fromHere( $episodeLink = $this->router->fromHere(
routeName: 'public:podcast:episode', routeName: 'public:podcast:episode',
routeParams: ['episode_id' => $episode->getId()], routeParams: ['episode_id' => $episode->getId()],
absolute: true absolute: true
@ -318,7 +318,7 @@ final class PodcastFeedAction
): RssEnclosure { ): RssEnclosure {
$rssEnclosure = new RssEnclosure(); $rssEnclosure = new RssEnclosure();
$podcastMediaPlayUrl = (string)$this->router->fromHere( $podcastMediaPlayUrl = $this->router->fromHere(
routeName: 'api:stations:podcast:episode:download', routeName: 'api:stations:podcast:episode:download',
routeParams: ['episode_id' => $episode->getId()], routeParams: ['episode_id' => $episode->getId()],
absolute: true absolute: true
@ -345,7 +345,7 @@ final class PodcastFeedAction
); );
if ($podcastsFilesystem->fileExists(PodcastEpisode::getArtPath($episode->getIdRequired()))) { if ($podcastsFilesystem->fileExists(PodcastEpisode::getArtPath($episode->getIdRequired()))) {
$episodeArtworkSrc = (string)$this->router->fromHere( $episodeArtworkSrc = $this->router->fromHere(
routeName: 'api:stations:podcast:episode:art', routeName: 'api:stations:podcast:episode:art',
routeParams: ['episode_id' => $episode->getId() . '|' . $episode->getArtUpdatedAt()], routeParams: ['episode_id' => $episode->getId() . '|' . $episode->getArtUpdatedAt()],
absolute: true absolute: true

View File

@ -45,7 +45,7 @@ final class RequestsAction
props: [ props: [
'customFields' => $this->customFieldRepo->fetchArray(), 'customFields' => $this->customFieldRepo->fetchArray(),
'showAlbumArt' => !$customization->hideAlbumArt(), 'showAlbumArt' => !$customization->hideAlbumArt(),
'requestListUri' => (string)$router->named('api:requests:list', [ 'requestListUri' => $router->named('api:requests:list', [
'station_id' => $station->getId(), 'station_id' => $station->getId(),
]), ]),
], ],

View File

@ -42,7 +42,7 @@ final class ScheduleAction
'hide_footer' => true, 'hide_footer' => true,
], ],
props: [ props: [
'scheduleUrl' => (string)$router->named('api:stations:schedule', [ 'scheduleUrl' => $router->named('api:stations:schedule', [
'station_id' => $station->getId(), 'station_id' => $station->getId(),
]), ]),
'stationName' => $station->getName(), 'stationName' => $station->getName(),

View File

@ -43,7 +43,7 @@ final class SetupController
Response $response Response $response
): ResponseInterface { ): ResponseInterface {
$current_step = $this->getSetupStep($request); $current_step = $this->getSetupStep($request);
return $response->withRedirect((string)$request->getRouter()->named('setup:' . $current_step)); return $response->withRedirect($request->getRouter()->named('setup:' . $current_step));
} }
/** /**
@ -57,7 +57,7 @@ final class SetupController
// Verify current step. // Verify current step.
$current_step = $this->getSetupStep($request); $current_step = $this->getSetupStep($request);
if ($current_step !== 'register' && $this->environment->isProduction()) { if ($current_step !== 'register' && $this->environment->isProduction()) {
return $response->withRedirect((string)$request->getRouter()->named('setup:' . $current_step)); return $response->withRedirect($request->getRouter()->named('setup:' . $current_step));
} }
$csrf = $request->getCsrf(); $csrf = $request->getCsrf();
@ -97,7 +97,7 @@ final class SetupController
$acl = $request->getAcl(); $acl = $request->getAcl();
$acl->reload(); $acl->reload();
return $response->withRedirect((string)$request->getRouter()->named('setup:index')); return $response->withRedirect($request->getRouter()->named('setup:index'));
} catch (Throwable $e) { } catch (Throwable $e) {
$error = $e->getMessage(); $error = $e->getMessage();
} }
@ -127,9 +127,11 @@ final class SetupController
// Verify current step. // Verify current step.
$current_step = $this->getSetupStep($request); $current_step = $this->getSetupStep($request);
if ($current_step !== 'station' && $this->environment->isProduction()) { if ($current_step !== 'station' && $this->environment->isProduction()) {
return $response->withRedirect((string)$request->getRouter()->named('setup:' . $current_step)); return $response->withRedirect($request->getRouter()->named('setup:' . $current_step));
} }
$router = $request->getRouter();
return $request->getView()->renderVuePage( return $request->getView()->renderVuePage(
response: $response, response: $response,
component: 'Vue_SetupStation', component: 'Vue_SetupStation',
@ -138,8 +140,8 @@ final class SetupController
props: array_merge( props: array_merge(
$this->stationFormComponent->getProps($request), $this->stationFormComponent->getProps($request),
[ [
'createUrl' => (string)$request->getRouter()->named('api:admin:stations'), 'createUrl' => $router->named('api:admin:stations'),
'continueUrl' => (string)$request->getRouter()->named('setup:settings'), 'continueUrl' => $router->named('setup:settings'),
] ]
) )
); );
@ -158,7 +160,7 @@ final class SetupController
// Verify current step. // Verify current step.
$current_step = $this->getSetupStep($request); $current_step = $this->getSetupStep($request);
if ($current_step !== 'settings' && $this->environment->isProduction()) { if ($current_step !== 'settings' && $this->environment->isProduction()) {
return $response->withRedirect((string)$router->named('setup:' . $current_step)); return $response->withRedirect($router->named('setup:' . $current_step));
} }
return $request->getView()->renderVuePage( return $request->getView()->renderVuePage(
@ -167,11 +169,11 @@ final class SetupController
id: 'setup-settings', id: 'setup-settings',
title: __('System Settings'), title: __('System Settings'),
props: [ props: [
'apiUrl' => (string)$router->named('api:admin:settings', [ 'apiUrl' => $router->named('api:admin:settings', [
'group' => Entity\Settings::GROUP_GENERAL, 'group' => Entity\Settings::GROUP_GENERAL,
]), ]),
'releaseChannel' => $this->version->getReleaseChannelEnum()->value, 'releaseChannel' => $this->version->getReleaseChannelEnum()->value,
'continueUrl' => (string)$router->named('dashboard'), 'continueUrl' => $router->named('dashboard'),
], ],
); );
} }
@ -188,7 +190,7 @@ final class SetupController
): ResponseInterface { ): ResponseInterface {
$request->getFlash()->addMessage('<b>' . __('Setup has already been completed!') . '</b>', Flash::ERROR); $request->getFlash()->addMessage('<b>' . __('Setup has already been completed!') . '</b>', Flash::ERROR);
return $response->withRedirect((string)$request->getRouter()->named('dashboard')); return $response->withRedirect($request->getRouter()->named('dashboard'));
} }
/** /**

View File

@ -23,7 +23,7 @@ final class BulkMediaAction
id: 'station-bulk-media', id: 'station-bulk-media',
title: __('Bulk Media Import/Export'), title: __('Bulk Media Import/Export'),
props: [ props: [
'apiUrl' => (string)$router->fromHere('api:stations:files:bulk'), 'apiUrl' => $router->fromHere('api:stations:files:bulk'),
], ],
); );
} }

View File

@ -60,8 +60,8 @@ final class EditLiquidsoapConfigAction
id: 'station-liquidsoap-config', id: 'station-liquidsoap-config',
title: __('Edit Liquidsoap Configuration'), title: __('Edit Liquidsoap Configuration'),
props: [ props: [
'settingsUrl' => (string)$router->fromHere('api:stations:liquidsoap-config'), 'settingsUrl' => $router->fromHere('api:stations:liquidsoap-config'),
'restartStatusUrl' => (string)$router->fromHere('api:stations:restart-status'), 'restartStatusUrl' => $router->fromHere('api:stations:restart-status'),
'config' => $areas, 'config' => $areas,
'sections' => $configSections, 'sections' => $configSections,
], ],

View File

@ -25,7 +25,7 @@ final class FallbackAction
id: 'station-fallback', id: 'station-fallback',
title: __('Custom Fallback File'), title: __('Custom Fallback File'),
props: [ props: [
'apiUrl' => (string)$router->fromHere('api:stations:fallback'), 'apiUrl' => $router->fromHere('api:stations:fallback'),
'recordHasFallback' => !empty($station->getFallbackPath()), 'recordHasFallback' => !empty($station->getFallbackPath()),
], ],
); );

View File

@ -48,13 +48,13 @@ final class FilesAction
id: 'media-manager', id: 'media-manager',
title: __('Music Files'), title: __('Music Files'),
props: [ props: [
'listUrl' => (string)$router->fromHere('api:stations:files:list'), 'listUrl' => $router->fromHere('api:stations:files:list'),
'batchUrl' => (string)$router->fromHere('api:stations:files:batch'), 'batchUrl' => $router->fromHere('api:stations:files:batch'),
'uploadUrl' => (string)$router->fromHere('api:stations:files:upload'), 'uploadUrl' => $router->fromHere('api:stations:files:upload'),
'listDirectoriesUrl' => (string)$router->fromHere('api:stations:files:directories'), 'listDirectoriesUrl' => $router->fromHere('api:stations:files:directories'),
'mkdirUrl' => (string)$router->fromHere('api:stations:files:mkdir'), 'mkdirUrl' => $router->fromHere('api:stations:files:mkdir'),
'renameUrl' => (string)$router->fromHere('api:stations:files:rename'), 'renameUrl' => $router->fromHere('api:stations:files:rename'),
'quotaUrl' => (string)$router->fromHere('api:stations:quota', [ 'quotaUrl' => $router->fromHere('api:stations:quota', [
'type' => Entity\Enums\StorageLocationTypes::StationMedia->value, 'type' => Entity\Enums\StorageLocationTypes::StationMedia->value,
]), ]),
'initialPlaylists' => $playlists, 'initialPlaylists' => $playlists,
@ -62,7 +62,7 @@ final class FilesAction
'validMimeTypes' => MimeType::getProcessableTypes(), 'validMimeTypes' => MimeType::getProcessableTypes(),
'stationTimeZone' => $station->getTimezone(), 'stationTimeZone' => $station->getTimezone(),
'showSftp' => StationFeatures::Sftp->supportedForStation($station), 'showSftp' => StationFeatures::Sftp->supportedForStation($station),
'sftpUrl' => (string)$router->fromHere('stations:sftp_users:index'), 'sftpUrl' => $router->fromHere('stations:sftp_users:index'),
'supportsImmediateQueue' => $backendEnum->isEnabled(), 'supportsImmediateQueue' => $backendEnum->isEnabled(),
], ],
); );

View File

@ -22,8 +22,8 @@ final class HlsStreamsAction
id: 'station-hls-streams', id: 'station-hls-streams',
title: __('HLS Streams'), title: __('HLS Streams'),
props: [ props: [
'listUrl' => (string)$router->fromHere('api:stations:hls_streams'), 'listUrl' => $router->fromHere('api:stations:hls_streams'),
'restartStatusUrl' => (string)$router->fromHere('api:stations:restart-status'), 'restartStatusUrl' => $router->fromHere('api:stations:restart-status'),
], ],
); );
} }

View File

@ -23,7 +23,7 @@ final class LogsAction
id: 'stations-help', id: 'stations-help',
title: __('Help'), title: __('Help'),
props: [ props: [
'logsUrl' => (string)$router->fromHere('api:stations:logs'), 'logsUrl' => $router->fromHere('api:stations:logs'),
], ],
); );
} }

View File

@ -32,9 +32,9 @@ final class MountsAction
id: 'station-mounts', id: 'station-mounts',
title: __('Mount Points'), title: __('Mount Points'),
props: [ props: [
'listUrl' => (string)$router->fromHere('api:stations:mounts'), 'listUrl' => $router->fromHere('api:stations:mounts'),
'newIntroUrl' => (string)$router->fromHere('api:stations:mounts:new-intro'), 'newIntroUrl' => $router->fromHere('api:stations:mounts:new-intro'),
'restartStatusUrl' => (string)$router->fromHere('api:stations:restart-status'), 'restartStatusUrl' => $router->fromHere('api:stations:restart-status'),
'stationFrontendType' => $station->getFrontendType(), 'stationFrontendType' => $station->getFrontendType(),
'showAdvanced' => $settings->getEnableAdvancedFeatures(), 'showAdvanced' => $settings->getEnableAdvancedFeatures(),
], ],

View File

@ -32,10 +32,10 @@ final class PlaylistsAction
id: 'station-playlist', id: 'station-playlist',
title: __('Playlists'), title: __('Playlists'),
props: [ props: [
'listUrl' => (string)$router->fromHere('api:stations:playlists'), 'listUrl' => $router->fromHere('api:stations:playlists'),
'scheduleUrl' => (string)$router->fromHere('api:stations:playlists:schedule'), 'scheduleUrl' => $router->fromHere('api:stations:playlists:schedule'),
'filesUrl' => (string)$router->fromHere('stations:files:index'), 'filesUrl' => $router->fromHere('stations:files:index'),
'restartStatusUrl' => (string)$router->fromHere('api:stations:restart-status'), 'restartStatusUrl' => $router->fromHere('api:stations:restart-status'),
'stationTimeZone' => $station->getTimezone(), 'stationTimeZone' => $station->getTimezone(),
'useManualAutoDj' => $station->useManualAutoDJ(), 'useManualAutoDj' => $station->useManualAutoDJ(),
'enableAdvancedFeatures' => $settings->getEnableAdvancedFeatures(), 'enableAdvancedFeatures' => $settings->getEnableAdvancedFeatures(),

View File

@ -32,10 +32,10 @@ final class PodcastsAction
id: 'station-podcasts', id: 'station-podcasts',
title: __('Podcasts'), title: __('Podcasts'),
props: [ props: [
'listUrl' => (string)$router->fromHere('api:stations:podcasts'), 'listUrl' => $router->fromHere('api:stations:podcasts'),
'newArtUrl' => (string)$router->fromHere('api:stations:podcasts:new-art'), 'newArtUrl' => $router->fromHere('api:stations:podcasts:new-art'),
'stationUrl' => (string)$router->fromHere('stations:index:index'), 'stationUrl' => $router->fromHere('stations:index:index'),
'quotaUrl' => (string)$router->fromHere('api:stations:quota', [ 'quotaUrl' => $router->fromHere('api:stations:quota', [
'type' => Entity\Enums\StorageLocationTypes::StationPodcasts->value, 'type' => Entity\Enums\StorageLocationTypes::StationPodcasts->value,
]), ]),
'locale' => substr($locale->value, 0, 2), 'locale' => substr($locale->value, 0, 2),

View File

@ -83,7 +83,7 @@ final class ProfileController
'enableStreamers' => $station->getEnableStreamers(), 'enableStreamers' => $station->getEnableStreamers(),
'enablePublicPage' => $station->getEnablePublicPage(), 'enablePublicPage' => $station->getEnablePublicPage(),
'enableOnDemand' => $station->getEnableOnDemand(), 'enableOnDemand' => $station->getEnableOnDemand(),
'profileApiUri' => (string)$router->fromHere('api:stations:profile'), 'profileApiUri' => $router->fromHere('api:stations:profile'),
'hasStarted' => $station->getHasStarted(), 'hasStarted' => $station->getHasStarted(),
// ACL // ACL
@ -96,82 +96,82 @@ final class ProfileController
// Header // Header
'stationName' => $station->getName(), 'stationName' => $station->getName(),
'stationDescription' => $station->getDescription(), 'stationDescription' => $station->getDescription(),
'manageProfileUri' => (string)$router->fromHere('stations:profile:edit'), 'manageProfileUri' => $router->fromHere('stations:profile:edit'),
// Now Playing // Now Playing
'backendSkipSongUri' => (string)$router->fromHere('api:stations:backend', ['do' => 'skip']), 'backendSkipSongUri' => $router->fromHere('api:stations:backend', ['do' => 'skip']),
'backendDisconnectStreamerUri' => (string)$router->fromHere( 'backendDisconnectStreamerUri' => $router->fromHere(
'api:stations:backend', 'api:stations:backend',
['do' => 'disconnect'] ['do' => 'disconnect']
), ),
// Requests // Requests
'requestsViewUri' => (string)$router->fromHere('stations:reports:requests'), 'requestsViewUri' => $router->fromHere('stations:reports:requests'),
'requestsToggleUri' => (string)$router->fromHere( 'requestsToggleUri' => $router->fromHere(
'stations:profile:toggle', 'stations:profile:toggle',
['feature' => 'requests', 'csrf' => $csrf] ['feature' => 'requests', 'csrf' => $csrf]
), ),
// Streamers // Streamers
'streamersViewUri' => (string)$router->fromHere('stations:streamers:index'), 'streamersViewUri' => $router->fromHere('stations:streamers:index'),
'streamersToggleUri' => (string)$router->fromHere( 'streamersToggleUri' => $router->fromHere(
'stations:profile:toggle', 'stations:profile:toggle',
['feature' => 'streamers', 'csrf' => $csrf] ['feature' => 'streamers', 'csrf' => $csrf]
), ),
// Public Pages // Public Pages
'publicPageUri' => (string)$router->named( 'publicPageUri' => $router->named(
routeName: 'public:index', routeName: 'public:index',
routeParams: ['station_id' => $station->getShortName()], routeParams: ['station_id' => $station->getShortName()],
absolute: true absolute: true
), ),
'publicPageEmbedUri' => (string)$router->named( 'publicPageEmbedUri' => $router->named(
routeName: 'public:index', routeName: 'public:index',
routeParams: ['station_id' => $station->getShortName(), 'embed' => 'embed'], routeParams: ['station_id' => $station->getShortName(), 'embed' => 'embed'],
absolute: true absolute: true
), ),
'publicWebDjUri' => (string)$router->named( 'publicWebDjUri' => $router->named(
routeName: 'public:dj', routeName: 'public:dj',
routeParams: ['station_id' => $station->getShortName()], routeParams: ['station_id' => $station->getShortName()],
absolute: true absolute: true
), ),
'publicOnDemandUri' => (string)$router->named( 'publicOnDemandUri' => $router->named(
routeName: 'public:ondemand', routeName: 'public:ondemand',
routeParams: ['station_id' => $station->getShortName()], routeParams: ['station_id' => $station->getShortName()],
absolute: true absolute: true
), ),
'publicPodcastsUri' => (string)$router->named( 'publicPodcastsUri' => $router->named(
routeName: 'public:podcasts', routeName: 'public:podcasts',
routeParams: ['station_id' => $station->getShortName()], routeParams: ['station_id' => $station->getShortName()],
absolute: true absolute: true
), ),
'publicScheduleUri' => (string)$router->named( 'publicScheduleUri' => $router->named(
routeName: 'public:schedule', routeName: 'public:schedule',
routeParams: ['station_id' => $station->getShortName()], routeParams: ['station_id' => $station->getShortName()],
absolute: true absolute: true
), ),
'publicOnDemandEmbedUri' => (string)$router->named( 'publicOnDemandEmbedUri' => $router->named(
routeName: 'public:ondemand', routeName: 'public:ondemand',
routeParams: ['station_id' => $station->getShortName(), 'embed' => 'embed'], routeParams: ['station_id' => $station->getShortName(), 'embed' => 'embed'],
absolute: true absolute: true
), ),
'publicRequestEmbedUri' => (string)$router->named( 'publicRequestEmbedUri' => $router->named(
routeName: 'public:embedrequests', routeName: 'public:embedrequests',
routeParams: ['station_id' => $station->getShortName()], routeParams: ['station_id' => $station->getShortName()],
absolute: true absolute: true
), ),
'publicHistoryEmbedUri' => (string)$router->named( 'publicHistoryEmbedUri' => $router->named(
routeName: 'public:history', routeName: 'public:history',
routeParams: ['station_id' => $station->getShortName()], routeParams: ['station_id' => $station->getShortName()],
absolute: true absolute: true
), ),
'publicScheduleEmbedUri' => (string)$router->named( 'publicScheduleEmbedUri' => $router->named(
routeName: 'public:schedule', routeName: 'public:schedule',
routeParams: ['station_id' => $station->getShortName(), 'embed' => 'embed'], routeParams: ['station_id' => $station->getShortName(), 'embed' => 'embed'],
absolute: true absolute: true
), ),
'togglePublicPageUri' => (string)$router->fromHere( 'togglePublicPageUri' => $router->fromHere(
routeName: 'stations:profile:toggle', routeName: 'stations:profile:toggle',
routeParams: ['feature' => 'public', 'csrf' => $csrf] routeParams: ['feature' => 'public', 'csrf' => $csrf]
), ),
@ -181,18 +181,18 @@ final class ProfileController
'frontendAdminPassword' => $frontendConfig->getAdminPassword(), 'frontendAdminPassword' => $frontendConfig->getAdminPassword(),
'frontendSourcePassword' => $frontendConfig->getSourcePassword(), 'frontendSourcePassword' => $frontendConfig->getSourcePassword(),
'frontendRelayPassword' => $frontendConfig->getRelayPassword(), 'frontendRelayPassword' => $frontendConfig->getRelayPassword(),
'frontendRestartUri' => (string)$router->fromHere('api:stations:frontend', ['do' => 'restart']), 'frontendRestartUri' => $router->fromHere('api:stations:frontend', ['do' => 'restart']),
'frontendStartUri' => (string)$router->fromHere('api:stations:frontend', ['do' => 'start']), 'frontendStartUri' => $router->fromHere('api:stations:frontend', ['do' => 'start']),
'frontendStopUri' => (string)$router->fromHere('api:stations:frontend', ['do' => 'stop']), 'frontendStopUri' => $router->fromHere('api:stations:frontend', ['do' => 'stop']),
// Backend // Backend
'numSongs' => (int)$num_songs, 'numSongs' => (int)$num_songs,
'numPlaylists' => (int)$num_playlists, 'numPlaylists' => (int)$num_playlists,
'manageMediaUri' => (string)$router->fromHere('stations:files:index'), 'manageMediaUri' => $router->fromHere('stations:files:index'),
'managePlaylistsUri' => (string)$router->fromHere('stations:playlists:index'), 'managePlaylistsUri' => $router->fromHere('stations:playlists:index'),
'backendRestartUri' => (string)$router->fromHere('api:stations:backend', ['do' => 'restart']), 'backendRestartUri' => $router->fromHere('api:stations:backend', ['do' => 'restart']),
'backendStartUri' => (string)$router->fromHere('api:stations:backend', ['do' => 'start']), 'backendStartUri' => $router->fromHere('api:stations:backend', ['do' => 'start']),
'backendStopUri' => (string)$router->fromHere('api:stations:backend', ['do' => 'stop']), 'backendStopUri' => $router->fromHere('api:stations:backend', ['do' => 'stop']),
], ],
); );
} }
@ -212,8 +212,8 @@ final class ProfileController
props: array_merge( props: array_merge(
$this->stationFormComponent->getProps($request), $this->stationFormComponent->getProps($request),
[ [
'editUrl' => (string)$router->fromHere('api:stations:profile:edit'), 'editUrl' => $router->fromHere('api:stations:profile:edit'),
'continueUrl' => (string)$router->fromHere('stations:profile:index'), 'continueUrl' => $router->fromHere('stations:profile:index'),
] ]
) )
); );
@ -247,6 +247,6 @@ final class ProfileController
$this->em->persist($station); $this->em->persist($station);
$this->em->flush(); $this->em->flush();
return $response->withRedirect((string)$request->getRouter()->fromHere('stations:profile:index')); return $response->withRedirect($request->getRouter()->fromHere('stations:profile:index'));
} }
} }

View File

@ -24,8 +24,8 @@ final class QueueAction
id: 'station-queue', id: 'station-queue',
title: __('Upcoming Song Queue'), title: __('Upcoming Song Queue'),
props: [ props: [
'listUrl' => (string)$router->fromHere('api:stations:queue'), 'listUrl' => $router->fromHere('api:stations:queue'),
'clearUrl' => (string)$router->fromHere('api:stations:queue:clear'), 'clearUrl' => $router->fromHere('api:stations:queue:clear'),
'stationTimeZone' => $station->getTimezone(), 'stationTimeZone' => $station->getTimezone(),
], ],
); );

View File

@ -23,8 +23,8 @@ final class RemotesAction
id: 'station-remotes', id: 'station-remotes',
title: __('Remote Relays'), title: __('Remote Relays'),
props: [ props: [
'listUrl' => (string)$router->fromHere('api:stations:remotes'), 'listUrl' => $router->fromHere('api:stations:remotes'),
'restartStatusUrl' => (string)$router->fromHere('api:stations:restart-status'), 'restartStatusUrl' => $router->fromHere('api:stations:restart-status'),
], ],
); );
} }

View File

@ -24,7 +24,7 @@ final class ListenersAction
id: 'station-report-listeners', id: 'station-report-listeners',
title: __('Listeners'), title: __('Listeners'),
props: [ props: [
'apiUrl' => (string)$router->fromHere('api:listeners:index'), 'apiUrl' => $router->fromHere('api:listeners:index'),
'stationTz' => $station->getTimezone(), 'stationTz' => $station->getTimezone(),
] ]
); );

View File

@ -40,13 +40,13 @@ final class OverviewAction
props: [ props: [
'stationTz' => $request->getStation()->getTimezone(), 'stationTz' => $request->getStation()->getTimezone(),
'showFullAnalytics' => Entity\Enums\AnalyticsLevel::All === $analyticsLevel, 'showFullAnalytics' => Entity\Enums\AnalyticsLevel::All === $analyticsLevel,
'listenersByTimePeriodUrl' => (string)$router->fromHere('api:stations:reports:overview-charts'), 'listenersByTimePeriodUrl' => $router->fromHere('api:stations:reports:overview-charts'),
'bestAndWorstUrl' => (string)$router->fromHere('api:stations:reports:best-and-worst'), 'bestAndWorstUrl' => $router->fromHere('api:stations:reports:best-and-worst'),
'byStreamUrl' => (string)$router->fromHere('api:stations:reports:by-stream'), 'byStreamUrl' => $router->fromHere('api:stations:reports:by-stream'),
'byBrowserUrl' => (string)$router->fromHere('api:stations:reports:by-browser'), 'byBrowserUrl' => $router->fromHere('api:stations:reports:by-browser'),
'byCountryUrl' => (string)$router->fromHere('api:stations:reports:by-country'), 'byCountryUrl' => $router->fromHere('api:stations:reports:by-country'),
'byClientUrl' => (string)$router->fromHere('api:stations:reports:by-client'), 'byClientUrl' => $router->fromHere('api:stations:reports:by-client'),
'listeningTimeUrl' => (string)$router->fromHere('api:stations:reports:by-listening-time'), 'listeningTimeUrl' => $router->fromHere('api:stations:reports:by-listening-time'),
] ]
); );
} }

View File

@ -24,8 +24,8 @@ final class RequestsAction
id: 'station-report-requests', id: 'station-report-requests',
title: __('Song Requests'), title: __('Song Requests'),
props: [ props: [
'listUrl' => (string)$router->fromHere('api:stations:reports:requests'), 'listUrl' => $router->fromHere('api:stations:reports:requests'),
'clearUrl' => (string)$router->fromHere('api:stations:reports:requests:clear'), 'clearUrl' => $router->fromHere('api:stations:reports:requests:clear'),
'stationTimeZone' => $station->getTimezone(), 'stationTimeZone' => $station->getTimezone(),
] ]
); );

View File

@ -29,7 +29,7 @@ final class SoundExchangeAction
id: 'station-report-soundexchange', id: 'station-report-soundexchange',
title: __('SoundExchange Report'), title: __('SoundExchange Report'),
props: [ props: [
'apiUrl' => (string)$router->fromHere('api:stations:reports:soundexchange'), 'apiUrl' => $router->fromHere('api:stations:reports:soundexchange'),
'startDate' => $defaultStartDate, 'startDate' => $defaultStartDate,
'endDate' => $defaultEndDate, 'endDate' => $defaultEndDate,
] ]

View File

@ -24,7 +24,7 @@ final class TimelineAction
id: 'station-report-timeline', id: 'station-report-timeline',
title: __('Song Playback Timeline'), title: __('Song Playback Timeline'),
props: [ props: [
'baseApiUrl' => (string)$router->fromHere('api:stations:history'), 'baseApiUrl' => $router->fromHere('api:stations:history'),
'stationTimeZone' => $station->getTimezone(), 'stationTimeZone' => $station->getTimezone(),
] ]
); );

View File

@ -37,7 +37,7 @@ final class SftpUsersAction
id: 'station-sftp-users', id: 'station-sftp-users',
title: __('SFTP Users'), title: __('SFTP Users'),
props: [ props: [
'listUrl' => (string)$router->fromHere('api:stations:sftp-users'), 'listUrl' => $router->fromHere('api:stations:sftp-users'),
'connectionInfo' => [ 'connectionInfo' => [
'url' => (string)$baseUrl, 'url' => (string)$baseUrl,
'ip' => $this->acCentral->getIp(), 'ip' => $this->acCentral->getIp(),

View File

@ -36,9 +36,9 @@ final class StreamersAction
id: 'station-streamers', id: 'station-streamers',
title: __('Streamer/DJ Accounts'), title: __('Streamer/DJ Accounts'),
props: [ props: [
'listUrl' => (string)$router->fromHere('api:stations:streamers'), 'listUrl' => $router->fromHere('api:stations:streamers'),
'newArtUrl' => (string)$router->fromHere('api:stations:streamers:new-art'), 'newArtUrl' => $router->fromHere('api:stations:streamers:new-art'),
'scheduleUrl' => (string)$router->fromHere('api:stations:streamers:schedule'), 'scheduleUrl' => $router->fromHere('api:stations:streamers:schedule'),
'stationTimeZone' => $station->getTimezone(), 'stationTimeZone' => $station->getTimezone(),
'connectionInfo' => [ 'connectionInfo' => [
'serverUrl' => $settings->getBaseUrl(), 'serverUrl' => $settings->getBaseUrl(),

View File

@ -24,9 +24,9 @@ final class UploadStereoToolConfigAction
id: 'stations-stereo-tool-config', id: 'stations-stereo-tool-config',
title: __('Upload Stereo Tool Configuration'), title: __('Upload Stereo Tool Configuration'),
props: [ props: [
'restartStatusUrl' => (string)$router->fromHere('api:stations:restart-status'), 'restartStatusUrl' => $router->fromHere('api:stations:restart-status'),
'recordHasStereoToolConfiguration' => !empty($backendConfig->getStereoToolConfigurationPath()), 'recordHasStereoToolConfiguration' => !empty($backendConfig->getStereoToolConfigurationPath()),
'apiUrl' => (string)$router->fromHere('api:stations:stereo_tool_config'), 'apiUrl' => $router->fromHere('api:stations:stereo_tool_config'),
], ],
); );
} }

View File

@ -35,7 +35,7 @@ final class WebhooksAction
id: 'station-webhooks', id: 'station-webhooks',
title: __('Web Hooks'), title: __('Web Hooks'),
props: [ props: [
'listUrl' => (string)$router->fromHere('api:stations:webhooks'), 'listUrl' => $router->fromHere('api:stations:webhooks'),
'webhookTypes' => $webhookConfig['webhooks'], 'webhookTypes' => $webhookConfig['webhooks'],
'webhookTriggers' => $webhookConfig['triggers'], 'webhookTriggers' => $webhookConfig['triggers'],
'enableAdvancedFeatures' => $settings->getEnableAdvancedFeatures(), 'enableAdvancedFeatures' => $settings->getEnableAdvancedFeatures(),

View File

@ -118,7 +118,7 @@ final class NowPlayingApiGenerator
$live->broadcast_start = $broadcastStart; $live->broadcast_start = $broadcastStart;
if (0 !== $currentStreamer->getArtUpdatedAt()) { if (0 !== $currentStreamer->getArtUpdatedAt()) {
$live->art = $this->router->named( $live->art = $this->router->namedAsUri(
routeName: 'api:stations:streamer:art', routeName: 'api:stations:streamer:art',
routeParams: [ routeParams: [
'station_id' => $station->getIdRequired(), 'station_id' => $station->getIdRequired(),

View File

@ -68,7 +68,7 @@ final class SongApiGenerator
$mediaId .= '-' . $mediaUpdatedTimestamp; $mediaId .= '-' . $mediaUpdatedTimestamp;
} }
return $this->router->named( return $this->router->namedAsUri(
routeName: 'api:stations:media:art', routeName: 'api:stations:media:art',
routeParams: [ routeParams: [
'station_id' => $station->getId(), 'station_id' => $station->getId(),
@ -87,7 +87,7 @@ final class SongApiGenerator
if ($isNowPlaying && null !== $station) { if ($isNowPlaying && null !== $station) {
$currentStreamer = $station->getCurrentStreamer(); $currentStreamer = $station->getCurrentStreamer();
if (null !== $currentStreamer && 0 !== $currentStreamer->getArtUpdatedAt()) { if (null !== $currentStreamer && 0 !== $currentStreamer->getArtUpdatedAt()) {
return $this->router->named( return $this->router->namedAsUri(
routeName: 'api:stations:streamer:art', routeName: 'api:stations:streamer:art',
routeParams: [ routeParams: [
'station_id' => $station->getIdRequired(), 'station_id' => $station->getIdRequired(),

View File

@ -36,15 +36,15 @@ final class StationApiGenerator
$response->is_public = $station->getEnablePublicPage(); $response->is_public = $station->getEnablePublicPage();
$response->listen_url = $frontend?->getStreamUrl($station, $baseUri); $response->listen_url = $frontend?->getStreamUrl($station, $baseUri);
$response->public_player_url = (string)$this->router->named( $response->public_player_url = $this->router->named(
'public:index', 'public:index',
['station_id' => $station->getShortName()] ['station_id' => $station->getShortName()]
); );
$response->playlist_pls_url = (string)$this->router->named( $response->playlist_pls_url = $this->router->named(
'public:playlist', 'public:playlist',
['station_id' => $station->getShortName(), 'format' => 'pls'] ['station_id' => $station->getShortName(), 'format' => 'pls']
); );
$response->playlist_m3u_url = (string)$this->router->named( $response->playlist_m3u_url = $this->router->named(
'public:playlist', 'public:playlist',
['station_id' => $station->getShortName(), 'format' => 'm3u'] ['station_id' => $station->getShortName(), 'format' => 'm3u']
); );

View File

@ -189,7 +189,7 @@ final class ErrorHandler extends \Slim\Handlers\ErrorHandler
$response = $sessionPersistence->persistSession($session, $response); $response = $sessionPersistence->persistSession($session, $response);
/** @var Response $response */ /** @var Response $response */
return $response->withRedirect((string)$this->router->named('account:login')); return $response->withRedirect($this->router->named('account:login'));
} }
if ($this->exception instanceof PermissionDeniedException) { if ($this->exception instanceof PermissionDeniedException) {
@ -219,7 +219,7 @@ final class ErrorHandler extends \Slim\Handlers\ErrorHandler
// Bounce back to homepage for permission-denied users. // Bounce back to homepage for permission-denied users.
/** @var Response $response */ /** @var Response $response */
return $response->withRedirect((string)$this->router->named('home')); return $response->withRedirect($this->router->named('home'));
} }
/** @var Response $response */ /** @var Response $response */

View File

@ -6,14 +6,13 @@ namespace App\Http;
use App\Entity; use App\Entity;
use App\Traits\RequestAwareTrait; use App\Traits\RequestAwareTrait;
use FastRoute\RouteParser\Std;
use GuzzleHttp\Psr7\Uri; use GuzzleHttp\Psr7\Uri;
use GuzzleHttp\Psr7\UriResolver; use GuzzleHttp\Psr7\UriResolver;
use InvalidArgumentException; use InvalidArgumentException;
use Psr\Http\Message\ServerRequestInterface; use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Message\UriInterface; use Psr\Http\Message\UriInterface;
use Slim\Interfaces\RouteCollectorInterface;
use Slim\Interfaces\RouteInterface; use Slim\Interfaces\RouteInterface;
use Slim\Interfaces\RouteParserInterface;
use Slim\Routing\RouteContext; use Slim\Routing\RouteContext;
final class Router implements RouterInterface final class Router implements RouterInterface
@ -24,24 +23,10 @@ final class Router implements RouterInterface
private ?RouteInterface $currentRoute = null; private ?RouteInterface $currentRoute = null;
private readonly array $routes;
private readonly Std $routeParser;
public function __construct( public function __construct(
private readonly Entity\Repository\SettingsRepository $settingsRepo, private readonly Entity\Repository\SettingsRepository $settingsRepo,
RouteCollectorInterface $routeCollector private readonly RouteParserInterface $routeParser,
) { ) {
$routes = [];
foreach ($routeCollector->getRoutes() as $route) {
$routeName = $route->getName();
if (null !== $routeName) {
$routes[$routeName] = $route;
}
}
$this->routes = $routes;
$this->routeParser = new Std();
} }
public function setRequest(?ServerRequestInterface $request): void public function setRequest(?ServerRequestInterface $request): void
@ -107,10 +92,7 @@ final class Router implements RouterInterface
return $baseUrl; return $baseUrl;
} }
/** public function fromHereWithQueryAsUri(
* @inheritDoc
*/
public function fromHereWithQuery(
?string $routeName = null, ?string $routeName = null,
array $routeParams = [], array $routeParams = [],
array $queryParams = [], array $queryParams = [],
@ -120,13 +102,23 @@ final class Router implements RouterInterface
$queryParams = array_merge($this->request->getQueryParams(), $queryParams); $queryParams = array_merge($this->request->getQueryParams(), $queryParams);
} }
return $this->fromHereAsUri($routeName, $routeParams, $queryParams, $absolute);
}
public function fromHereWithQuery(
?string $routeName = null,
array $routeParams = [],
array $queryParams = [],
bool $absolute = false
): string {
if ($this->request instanceof ServerRequestInterface) {
$queryParams = array_merge($this->request->getQueryParams(), $queryParams);
}
return $this->fromHere($routeName, $routeParams, $queryParams, $absolute); return $this->fromHere($routeName, $routeParams, $queryParams, $absolute);
} }
/** public function fromHereAsUri(
* @inheritDoc
*/
public function fromHere(
?string $routeName = null, ?string $routeName = null,
array $routeParams = [], array $routeParams = [],
array $queryParams = [], array $queryParams = [],
@ -146,84 +138,56 @@ final class Router implements RouterInterface
); );
} }
return $this->namedAsUri($routeName, $routeParams, $queryParams, $absolute);
}
public function fromHere(
?string $routeName = null,
array $routeParams = [],
array $queryParams = [],
bool $absolute = false
): string {
if (null !== $this->currentRoute) {
if (null === $routeName) {
$routeName = $this->currentRoute->getName();
}
$routeParams = array_merge($this->currentRoute->getArguments(), $routeParams);
}
if (null === $routeName) {
throw new InvalidArgumentException(
'Cannot specify a null route name if no existing route is configured.'
);
}
return $this->named($routeName, $routeParams, $queryParams, $absolute); return $this->named($routeName, $routeParams, $queryParams, $absolute);
} }
/** public function namedAsUri(
* @inheritDoc
*/
public function named(
string $routeName, string $routeName,
array $routeParams = [], array $routeParams = [],
array $queryParams = [], array $queryParams = [],
bool $absolute = false bool $absolute = false
): UriInterface { ): UriInterface {
$relativeUri = $this->getRelativeUri($routeName, $routeParams, $queryParams); $relativeUri = $this->routeParser->relativeUrlFor($routeName, $routeParams, $queryParams);
return ($absolute) return ($absolute)
? self::resolveUri($this->getBaseUrl(), $relativeUri, true) ? self::resolveUri($this->getBaseUrl(), $relativeUri, true)
: $relativeUri; : self::createUri($relativeUri);
} }
private function getRelativeUri(string $routeName, array $data = [], array $queryParams = []): UriInterface public function named(
{ string $routeName,
if (!isset($this->routes[$routeName])) { array $routeParams = [],
throw new \InvalidArgumentException('Named route does not exist for name: ' . $routeName); array $queryParams = [],
} bool $absolute = false
): string {
$relativeUri = $this->routeParser->relativeUrlFor($routeName, $routeParams, $queryParams);
$pattern = $this->routes[$routeName]->getPattern(); return ($absolute)
? (string)self::resolveUri($this->getBaseUrl(), $relativeUri, true)
$segments = []; : $relativeUri;
$segmentName = '';
/*
* $routes is an associative array of expressions representing a route as multiple segments
* There is an expression for each optional parameter plus one without the optional parameters
* The most specific is last, hence why we reverse the array before iterating over it
*/
foreach (array_reverse($this->routeParser->parse($pattern)) as $expression) {
foreach ($expression as $segment) {
/*
* Each $segment is either a string or an array of strings
* containing optional parameters of an expression
*/
if (is_string($segment)) {
$segments[] = $segment;
continue;
}
/** @var string[] $segment */
/*
* If we don't have a data element for this segment in the provided $data
* we cancel testing to move onto the next expression with a less specific item
*/
if (!array_key_exists($segment[0], $data)) {
$segments = [];
$segmentName = $segment[0];
break;
}
$segments[] = $data[$segment[0]];
}
/*
* If we get to this logic block we have found all the parameters
* for the provided $data which means we don't need to continue testing
* less specific expressions
*/
if (!empty($segments)) {
break;
}
}
if (empty($segments)) {
throw new InvalidArgumentException('Missing data for URL segment: ' . $segmentName);
}
$url = new Uri(implode('', $segments));
return ($queryParams)
? $url->withQuery(http_build_query($queryParams))
: $url;
} }
/** /**
@ -240,7 +204,7 @@ final class Router implements RouterInterface
bool $absolute = false bool $absolute = false
): UriInterface { ): UriInterface {
if (!$rel instanceof UriInterface) { if (!$rel instanceof UriInterface) {
$rel = new Uri($rel); $rel = self::createUri($rel);
} }
if (!$absolute) { if (!$absolute) {
@ -258,4 +222,9 @@ final class Router implements RouterInterface
return UriResolver::resolve($base, $rel); return UriResolver::resolve($base, $rel);
} }
public static function createUri(string $uri): UriInterface
{
return new Uri($uri);
}
} }

View File

@ -19,46 +19,61 @@ interface RouterInterface
/** /**
* Simpler format for calling "named" routes with parameters. * Simpler format for calling "named" routes with parameters.
*
* @param string $routeName
* @param array $routeParams
* @param array $queryParams
* @param boolean $absolute Whether to include the full URL.
*/ */
public function named( public function named(
string $routeName, string $routeName,
array $routeParams = [], array $routeParams = [],
array $queryParams = [], array $queryParams = [],
bool $absolute = false bool $absolute = false
): string;
/**
* Same as above, but returning a UriInterface.
*/
public function namedAsUri(
string $routeName,
array $routeParams = [],
array $queryParams = [],
bool $absolute = false
): UriInterface; ): UriInterface;
/** /**
* Return a named route based on the current page and its route arguments. * Return a named route based on the current page and its route arguments.
*
* @param string|null $routeName
* @param array $routeParams
* @param array $queryParams
* @param bool $absolute
*/ */
public function fromHere( public function fromHere(
?string $routeName = null, ?string $routeName = null,
array $routeParams = [], array $routeParams = [],
array $queryParams = [], array $queryParams = [],
bool $absolute = false bool $absolute = false
): string;
/**
* Same as above, but returns a UriInterface.
*/
public function fromHereAsUri(
?string $routeName = null,
array $routeParams = [],
array $queryParams = [],
bool $absolute = false
): UriInterface; ): UriInterface;
/** /**
* Same as $this->fromHere(), but merging the current GET query parameters into the request as well. * Same as $this->fromHere(), but merging the current GET query parameters into the request as well.
*
* @param string|null $routeName
* @param array $routeParams
* @param array $queryParams
* @param bool $absolute
*/ */
public function fromHereWithQuery( public function fromHereWithQuery(
?string $routeName = null, ?string $routeName = null,
array $routeParams = [], array $routeParams = [],
array $queryParams = [], array $queryParams = [],
bool $absolute = false bool $absolute = false
): string;
/**
* Same as above, but returns a UriInterface.
*/
public function fromHereWithQueryAsUri(
?string $routeName = null,
array $routeParams = [],
array $queryParams = [],
bool $absolute = false
): UriInterface; ): UriInterface;
} }

View File

@ -59,7 +59,7 @@ final class BaseUrlCheck
$notification->body = implode(' ', $notificationBodyParts); $notification->body = implode(' ', $notificationBodyParts);
$notification->type = Flash::WARNING; $notification->type = Flash::WARNING;
$notification->actionLabel = __('System Settings'); $notification->actionLabel = __('System Settings');
$notification->actionUrl = (string)$router->named('admin:settings:index'); $notification->actionUrl = $router->named('admin:settings:index');
$event->addNotification($notification); $event->addNotification($notification);
} }

View File

@ -52,7 +52,7 @@ final class RecentBackupCheck
$router = $request->getRouter(); $router = $request->getRouter();
$notification->actionLabel = __('Backups'); $notification->actionLabel = __('Backups');
$notification->actionUrl = (string)$router->named('admin:backups:index'); $notification->actionUrl = $router->named('admin:backups:index');
$event->addNotification($notification); $event->addNotification($notification);
} }

View File

@ -40,7 +40,7 @@ final class ServiceCheck
$router = $request->getRouter(); $router = $request->getRouter();
$notification->actionLabel = __('Administration'); $notification->actionLabel = __('Administration');
$notification->actionUrl = (string)$router->named('admin:index:index'); $notification->actionUrl = $router->named('admin:index:index');
// phpcs:enable // phpcs:enable
$event->addNotification($notification); $event->addNotification($notification);

View File

@ -59,7 +59,7 @@ final class SyncTaskCheck
$router = $request->getRouter(); $router = $request->getRouter();
$notification->actionLabel = __('System Debugger'); $notification->actionLabel = __('System Debugger');
$notification->actionUrl = (string)$router->named('admin:debug:index'); $notification->actionUrl = $router->named('admin:debug:index');
// phpcs:enable // phpcs:enable
$event->addNotification($notification); $event->addNotification($notification);

View File

@ -157,21 +157,21 @@ final class Paginator implements IteratorAggregate, Countable
} }
$pageLinks = []; $pageLinks = [];
$pageLinks['first'] = (string)$this->router->fromHereWithQuery(null, [], ['page' => 1]); $pageLinks['first'] = $this->router->fromHereWithQuery(null, [], ['page' => 1]);
$prevPage = $this->paginator->hasPreviousPage() $prevPage = $this->paginator->hasPreviousPage()
? $this->paginator->getPreviousPage() ? $this->paginator->getPreviousPage()
: 1; : 1;
$pageLinks['previous'] = (string)$this->router->fromHereWithQuery(null, [], ['page' => $prevPage]); $pageLinks['previous'] = $this->router->fromHereWithQuery(null, [], ['page' => $prevPage]);
$nextPage = $this->paginator->hasNextPage() $nextPage = $this->paginator->hasNextPage()
? $this->paginator->getNextPage() ? $this->paginator->getNextPage()
: $this->paginator->getNbPages(); : $this->paginator->getNbPages();
$pageLinks['next'] = (string)$this->router->fromHereWithQuery(null, [], ['page' => $nextPage]); $pageLinks['next'] = $this->router->fromHereWithQuery(null, [], ['page' => $nextPage]);
$pageLinks['last'] = (string)$this->router->fromHereWithQuery(null, [], ['page' => $totalPages]); $pageLinks['last'] = $this->router->fromHereWithQuery(null, [], ['page' => $totalPages]);
return $response->withJson( return $response->withJson(
[ [

Some files were not shown because too many files have changed in this diff Show More