Settings overhaul, part 2

Make Settings a controlled entity.
This commit is contained in:
Buster "Silver Eagle" Neece 2020-12-04 02:41:55 -06:00
parent 5727f12b6f
commit aed854a17c
No known key found for this signature in database
GPG Key ID: 6D9E12FF03411F4E
86 changed files with 1443 additions and 902 deletions

View File

@ -1,7 +1,5 @@
<?php
use App\Entity;
return [
'groups' => [
@ -10,7 +8,7 @@ return [
'elements' => [
Entity\Settings::BACKUP_ENABLED => [
'backupEnabled' => [
'toggle',
[
'label' => __('Run Automatic Nightly Backups'),
@ -22,7 +20,7 @@ return [
],
],
Entity\Settings::BACKUP_TIME => [
'backupTimeCode' => [
'PlaylistTime',
[
'label' => __('Scheduled Backup Time'),
@ -31,7 +29,7 @@ return [
],
],
Entity\Settings::BACKUP_EXCLUDE_MEDIA => [
'backupExcludeMedia' => [
'toggle',
[
'label' => __('Exclude Media from Backups'),
@ -43,7 +41,7 @@ return [
],
],
Entity\Settings::BACKUP_KEEP_COPIES => [
'backupKeepCopies' => [
'number',
[
'label' => __('Number of Backup Copies to Keep'),
@ -55,7 +53,7 @@ return [
],
],
Entity\Settings::BACKUP_STORAGE_LOCATION => [
'backupStorageLocation' => [
'select',
[
'label' => __('Storage Location'),

View File

@ -1,7 +1,5 @@
<?php
use App\Entity;
return [
'method' => 'post',
@ -10,7 +8,7 @@ return [
'use_grid' => true,
'elements' => [
Entity\Settings::PUBLIC_THEME => [
'publicTheme' => [
'radio',
[
'label' => __('Base Theme for Public Pages'),
@ -24,7 +22,7 @@ return [
],
],
Entity\Settings::HIDE_ALBUM_ART => [
'hideAlbumArt' => [
'toggle',
[
'label' => __('Hide Album Art on Public Pages'),
@ -36,7 +34,7 @@ return [
],
],
Entity\Settings::HOMEPAGE_REDIRECT_URL => [
'homepageRedirectUrl' => [
'text',
[
'label' => __('Homepage Redirect URL'),
@ -46,7 +44,7 @@ return [
],
],
Entity\Settings::DEFAULT_ALBUM_ART_URL => [
'defaultAlbumArtUrl' => [
'text',
[
'label' => __('Default Album Art URL'),
@ -56,7 +54,7 @@ return [
],
],
Entity\Settings::HIDE_PRODUCT_NAME => [
'hideProductName' => [
'toggle',
[
'label' => __('Hide AzuraCast Branding on Public Pages'),
@ -68,7 +66,7 @@ return [
],
],
Entity\Settings::CUSTOM_CSS_PUBLIC => [
'publicCustomCss' => [
'textarea',
[
'label' => __('Custom CSS for Public Pages'),
@ -82,7 +80,7 @@ return [
],
],
Entity\Settings::CUSTOM_JS_PUBLIC => [
'publicCustomJs' => [
'textarea',
[
'label' => __('Custom JS for Public Pages'),
@ -96,7 +94,7 @@ return [
],
],
Entity\Settings::CUSTOM_CSS_INTERNAL => [
'internalCustomCss' => [
'textarea',
[
'label' => __('Custom CSS for Internal Pages'),

View File

@ -20,7 +20,7 @@ return [
],
],
App\Entity\Settings::GEOLITE_LICENSE_KEY => [
'geoliteLicenseKey' => [
'text',
[
'label' => __('MaxMind License Key'),

View File

@ -28,7 +28,7 @@ return [
'tab' => 'system',
'elements' => [
Entity\Settings::BASE_URL => [
'baseUrl' => [
'url',
[
'label' => __('Site Base URL'),
@ -38,7 +38,7 @@ return [
],
],
Entity\Settings::INSTANCE_NAME => [
'instanceName' => [
'text',
[
'label' => __('AzuraCast Instance Name'),
@ -47,7 +47,7 @@ return [
],
],
Entity\Settings::PREFER_BROWSER_URL => [
'preferBrowserUrl' => [
'toggle',
[
'label' => __('Prefer Browser URL (If Available)'),
@ -59,7 +59,7 @@ return [
],
],
Entity\Settings::USE_RADIO_PROXY => [
'useRadioProxy' => [
'toggle',
[
'label' => __('Use Web Proxy for Radio'),
@ -71,7 +71,7 @@ return [
],
],
Entity\Settings::HISTORY_KEEP_DAYS => [
'historyKeepDays' => [
'radio',
[
'label' => __('Days of Playback History to Keep'),
@ -84,12 +84,12 @@ return [
730 => __('Last 2 Years'),
0 => __('Indefinitely'),
],
'default' => \App\Entity\SongHistory::DEFAULT_DAYS_TO_KEEP,
'default' => App\Entity\SongHistory::DEFAULT_DAYS_TO_KEEP,
'form_group_class' => 'col-sm-6',
],
],
Entity\Settings::NOWPLAYING_USE_WEBSOCKETS => [
'enableWebsockets' => [
'toggle',
[
'label' => __('Use WebSockets for Now Playing Updates'),
@ -110,7 +110,7 @@ return [
'elements' => [
Entity\Settings::ALWAYS_USE_SSL => [
'alwaysUseSsl' => [
'toggle',
[
'label' => __('Always Use HTTPS'),
@ -122,7 +122,7 @@ return [
],
],
Entity\Settings::API_ACCESS_CONTROL => [
'apiAccessControl' => [
'text',
[
'label' => __('API "Access-Control-Allow-Origin" header'),
@ -141,7 +141,7 @@ return [
'tab' => 'privacy',
'elements' => [
Entity\Settings::LISTENER_ANALYTICS => [
'analytics' => [
'radio',
[
'label' => __('Listener Analytics Collection'),
@ -174,7 +174,7 @@ return [
],
],
Entity\Settings::CENTRAL_UPDATES => [
'checkForUpdates' => [
'toggle',
[
'label' => __('Show Update Announcements'),

View File

@ -11,12 +11,12 @@ return [
// URL Router helper
App\Http\Router::class => function (
Environment $settings,
Environment $environment,
Slim\App $app,
App\Entity\Repository\SettingsRepository $settingsRepo
App\Entity\Settings $settings
) {
$route_parser = $app->getRouteCollector()->getRouteParser();
return new App\Http\Router($settings, $route_parser, $settingsRepo);
return new App\Http\Router($environment, $route_parser, $settings);
},
App\Http\RouterInterface::class => DI\Get(App\Http\Router::class),
@ -139,6 +139,11 @@ return [
},
Doctrine\ORM\EntityManagerInterface::class => DI\Get(App\Doctrine\DecoratedEntityManager::class),
// Database settings
App\Entity\Settings::class => function (App\Entity\Repository\SettingsTableRepository $settingsTableRepo) {
return $settingsTableRepo->readSettings(true);
},
// Redis cache
Redis::class => function (Environment $environment) {
$redis_host = $environment->isDocker() ? 'redis' : 'localhost';

View File

@ -8,9 +8,9 @@ class Config
{
protected string $baseFolder;
public function __construct(Environment $settings)
public function __construct(Environment $environment)
{
$this->baseFolder = $settings->getConfigDirectory();
$this->baseFolder = $environment->getConfigDirectory();
}
/**

View File

@ -3,7 +3,6 @@
namespace App\Console\Command\Internal;
use App\Console\Command\CommandAbstract;
use App\Entity;
use App\Service\AzuraCastCentral;
use Symfony\Component\Console\Style\SymfonyStyle;
@ -11,7 +10,6 @@ class GetIpCommand extends CommandAbstract
{
public function __invoke(
SymfonyStyle $io,
Entity\Repository\SettingsRepository $settings_repo,
AzuraCastCentral $acCentral
): int {
$io->write($acCentral->getIp());

View File

@ -11,7 +11,7 @@ class ListCommand extends CommandAbstract
{
public function __invoke(
SymfonyStyle $io,
Entity\Repository\SettingsRepository $settings_repo
Entity\Repository\SettingsTableRepository $settingsTableRepo
): int {
$io->title(__('AzuraCast Settings'));
@ -21,7 +21,7 @@ class ListCommand extends CommandAbstract
];
$rows = [];
$all_settings = $settings_repo->fetchAll();
$all_settings = $settingsTableRepo->readSettingsArray(false);
foreach ($all_settings as $setting_key => $setting_value) {
$value = print_r($setting_value, true);
$value = Utilities::truncateText($value, 600);

View File

@ -10,14 +10,14 @@ class SetCommand extends CommandAbstract
{
public function __invoke(
SymfonyStyle $io,
Entity\Repository\SettingsRepository $settings_repo,
Entity\Repository\SettingsTableRepository $settingsTableRepo,
string $settingKey,
string $settingValue
): int {
$io->title('AzuraCast Settings');
if (strtolower($settingValue) === 'null') {
$settings_repo->deleteSetting($settingKey);
$settingsTableRepo->writeSettings([$settingKey => null]);
$io->success(sprintf('Setting "%s" removed.', $settingKey));
return 0;
@ -27,7 +27,7 @@ class SetCommand extends CommandAbstract
$settingValue = json_decode($settingValue, true, 512, JSON_THROW_ON_ERROR);
}
$settings_repo->setSetting($settingKey, $settingValue);
$settingsTableRepo->writeSettings([$settingKey => $settingValue]);
$io->success(sprintf('Setting "%s" updated.', $settingKey));

View File

@ -17,7 +17,8 @@ class SetupCommand extends CommandAbstract
OutputInterface $output,
Environment $environment,
ContainerInterface $di,
Entity\Repository\SettingsRepository $settingsRepo,
Entity\Settings $settings,
Entity\Repository\SettingsTableRepository $settingsTableRepo,
Entity\Repository\StationRepository $stationRepo,
AzuraCastCentral $acCentral,
bool $update = false,
@ -73,7 +74,7 @@ class SetupCommand extends CommandAbstract
$this->runCommand($output, 'queue:clear');
$settingsRepo->deleteSetting(Entity\Settings::NOWPLAYING);
$settings->setNowplaying(null);
$stationRepo->clearNowPlaying();
$io->newLine();
@ -83,10 +84,14 @@ class SetupCommand extends CommandAbstract
$this->runCommand($output, 'azuracast:radio:restart');
// Clear settings that should be reset upon update.
$settingsRepo->setSetting(Entity\Settings::UPDATE_LAST_RUN, time());
$settingsRepo->deleteSetting(Entity\Settings::UPDATE_RESULTS);
$settingsRepo->deleteSetting(Entity\Settings::UNIQUE_IDENTIFIER);
$settingsRepo->deleteSetting(Entity\Settings::EXTERNAL_IP);
$settings->updateUpdateLastRun();
$settings->setUpdateResults(null);
$settings->setExternalIp(null);
if (!$update) {
$settings->setAppUniqueIdentifier(null);
}
$settingsTableRepo->writeSettings($settings);
$io->newLine();

View File

@ -4,7 +4,6 @@ namespace App\Controller\Admin;
use App\Config;
use App\Controller\AbstractLogViewerController;
use App\Entity\Repository\SettingsRepository;
use App\Entity\Repository\StorageLocationRepository;
use App\Entity\Settings;
use App\Entity\StorageLocation;
@ -22,7 +21,7 @@ use Symfony\Component\Messenger\MessageBus;
class BackupsController extends AbstractLogViewerController
{
protected SettingsRepository $settingsRepo;
protected Settings $settings;
protected StorageLocationRepository $storageLocationRepo;
@ -33,14 +32,14 @@ class BackupsController extends AbstractLogViewerController
protected string $csrfNamespace = 'admin_backups';
public function __construct(
SettingsRepository $settings_repo,
StorageLocationRepository $storageLocationRepo,
Settings $settings,
RunBackupTask $backup_task,
MessageBus $messageBus
) {
$this->settingsRepo = $settings_repo;
$this->storageLocationRepo = $storageLocationRepo;
$this->settings = $settings;
$this->backupTask = $backup_task;
$this->messageBus = $messageBus;
}
@ -60,10 +59,10 @@ class BackupsController extends AbstractLogViewerController
return $request->getView()->renderToResponse($response, 'admin/backups/index', [
'backups' => $backups,
'is_enabled' => (bool)$this->settingsRepo->getSetting(Settings::BACKUP_ENABLED, false),
'last_run' => $this->settingsRepo->getSetting(Settings::BACKUP_LAST_RUN, 0),
'last_result' => $this->settingsRepo->getSetting(Settings::BACKUP_LAST_RESULT, 0),
'last_output' => $this->settingsRepo->getSetting(Settings::BACKUP_LAST_OUTPUT, ''),
'is_enabled' => $this->settings->isBackupEnabled(),
'last_run' => $this->settings->getBackupLastRun(),
'last_result' => $this->settings->getBackupLastResult(),
'last_output' => $this->settings->getBackupLastOutput(),
'csrf' => $request->getCsrf()->generate($this->csrfNamespace),
]);
}

View File

@ -2,7 +2,7 @@
namespace App\Controller\Admin;
use App\Entity\Repository\SettingsRepository;
use App\Entity\Repository\SettingsTableRepository;
use App\Entity\Settings;
use App\Form\GeoLiteSettingsForm;
use App\Http\Response;
@ -52,12 +52,14 @@ class InstallGeoLiteController
public function uninstallAction(
ServerRequest $request,
Response $response,
SettingsRepository $settingsRepo,
Settings $settings,
SettingsTableRepository $settingsTableRepo,
$csrf
): ResponseInterface {
$request->getCsrf()->verify($csrf, $this->csrf_namespace);
$settingsRepo->setSetting(Settings::GEOLITE_LICENSE_KEY, '');
$settings->setGeoliteLicenseKey(null);
$settingsTableRepo->writeSettings($settings);
@unlink(GeoLite::getDatabasePath());

View File

@ -9,7 +9,6 @@ use App\Http\ServerRequest;
use Doctrine\ORM\EntityManagerInterface;
use OpenApi\Annotations as OA;
use Psr\Http\Message\ResponseInterface;
use Symfony\Component\Serializer\Normalizer\AbstractNormalizer;
use Symfony\Component\Serializer\Serializer;
use Symfony\Component\Validator\Validator\ValidatorInterface;
@ -21,13 +20,14 @@ class SettingsController
protected ValidatorInterface $validator;
protected Entity\Repository\SettingsRepository $settings_repo;
protected Entity\Repository\SettingsTableRepository $settingsTableRepo;
protected Entity\Api\Admin\Settings $api_settings;
protected Entity\Settings $settings;
public function __construct(
EntityManagerInterface $em,
Entity\Repository\SettingsRepository $settings_repo,
Entity\Repository\SettingsTableRepository $settingsTableRepo,
Entity\Settings $settings,
Serializer $serializer,
ValidatorInterface $validator
) {
@ -35,12 +35,8 @@ class SettingsController
$this->serializer = $serializer;
$this->validator = $validator;
$this->settings_repo = $settings_repo;
$all_settings = $settings_repo->fetchAll();
/** @var Entity\Api\Admin\Settings $api_settings */
$api_settings = $this->serializer->denormalize($all_settings, Entity\Api\Admin\Settings::class);
$this->api_settings = $api_settings;
$this->settingsTableRepo = $settingsTableRepo;
$this->settings = $settings;
}
/**
@ -48,7 +44,7 @@ class SettingsController
* tags={"Administration: Settings"},
* description="List the current values of all editable system settings.",
* @OA\Response(response=200, description="Success",
* @OA\JsonContent(ref="#/components/schemas/Api_Admin_Settings")
* @OA\JsonContent(ref="#/components/schemas/Settings")
* ),
* @OA\Response(response=403, description="Access denied"),
* security={{"api_key": {}}},
@ -59,7 +55,7 @@ class SettingsController
*/
public function listAction(ServerRequest $request, Response $response): ResponseInterface
{
return $response->withJson($this->api_settings);
return $response->withJson($this->serializer->normalize($this->settings, null));
}
/**
@ -67,7 +63,7 @@ class SettingsController
* tags={"Administration: Settings"},
* description="Update settings to modify any settings provided.",
* @OA\RequestBody(
* @OA\JsonContent(ref="#/components/schemas/Api_Admin_Settings")
* @OA\JsonContent(ref="#/components/schemas/Settings")
* ),
* @OA\Response(response=200, description="Success",
* @OA\JsonContent(ref="#/components/schemas/Api_Status")
@ -83,23 +79,7 @@ class SettingsController
*/
public function updateAction(ServerRequest $request, Response $response): ResponseInterface
{
$api_settings_obj = $this->serializer->denormalize(
$request->getParsedBody(),
Entity\Api\Admin\Settings::class,
null,
[
AbstractNormalizer::OBJECT_TO_POPULATE => $this->api_settings,
]
);
$errors = $this->validator->validate($api_settings_obj);
if (count($errors) > 0) {
throw new ValidationException((string)$errors);
}
$api_settings = $this->serializer->normalize($api_settings_obj);
$this->settings_repo->setSettings($api_settings);
$this->settingsTableRepo->writeSettings($request->getParsedBody());
return $response->withJson(new Entity\Api\Status());
}

View File

@ -17,7 +17,7 @@ class NowplayingController implements EventSubscriberInterface
{
protected EntityManagerInterface $em;
protected Entity\Repository\SettingsRepository $settingsRepo;
protected Entity\Settings $settings;
protected CacheInterface $cache;
@ -25,12 +25,12 @@ class NowplayingController implements EventSubscriberInterface
public function __construct(
EntityManagerInterface $em,
Entity\Repository\SettingsRepository $settingsRepo,
Entity\Settings $settings,
CacheInterface $cache,
EventDispatcher $dispatcher
) {
$this->em = $em;
$this->settingsRepo = $settingsRepo;
$this->settings = $settings;
$this->cache = $cache;
$this->dispatcher = $dispatcher;
}
@ -140,12 +140,12 @@ class NowplayingController implements EventSubscriberInterface
public function loadFromCache(LoadNowPlaying $event): void
{
$event->setNowPlaying((array)$this->cache->get(Entity\Settings::NOWPLAYING), 'redis');
$event->setNowPlaying((array)$this->cache->get('nowplaying'), 'redis');
}
public function loadFromSettings(LoadNowPlaying $event): void
{
$event->setNowPlaying((array)$this->settingsRepo->getSetting(Entity\Settings::NOWPLAYING), 'settings');
$event->setNowPlaying((array)$this->settings->getNowplaying(), 'settings');
}
public function loadFromStations(LoadNowPlaying $event): void

View File

@ -2,7 +2,6 @@
namespace App\Controller\Frontend\Account;
use App\Entity\Repository\SettingsRepository;
use App\Entity\Settings;
use App\Entity\User;
use App\Exception\RateLimitExceededException;
@ -21,13 +20,13 @@ class LoginAction
Response $response,
EntityManagerInterface $em,
RateLimit $rateLimit,
SettingsRepository $settingsRepo
Settings $settings
): ResponseInterface {
$auth = $request->getAuth();
$acl = $request->getAcl();
// Check installation completion progress.
if ($settingsRepo->getSetting(Settings::SETUP_COMPLETE, 0) === 0) {
if (!$settings->isSetupComplete()) {
$num_users = (int)$em->createQuery(
<<<'DQL'
SELECT COUNT(u.id) FROM App\Entity\User u
@ -84,7 +83,7 @@ class LoginAction
}
// Redirect to complete setup if it's not completed yet.
if ($settingsRepo->getSetting(Settings::SETUP_COMPLETE, 0) === 0) {
if (!$settings->isSetupComplete()) {
$flash->addMessage(
sprintf(
'<b>%s</b><br>%s',

View File

@ -19,7 +19,7 @@ class DashboardController
{
protected EntityManagerInterface $em;
protected Entity\Repository\SettingsRepository $settingsRepo;
protected Entity\Settings $settings;
protected Acl $acl;
@ -33,15 +33,15 @@ class DashboardController
public function __construct(
EntityManagerInterface $em,
Entity\Repository\SettingsRepository $settingsRepo,
Acl $acl,
Entity\Settings $settings,
CacheInterface $cache,
Adapters $adapter_manager,
EventDispatcher $dispatcher
) {
$this->em = $em;
$this->settingsRepo = $settingsRepo;
$this->acl = $acl;
$this->settings = $settings;
$this->cache = $cache;
$this->adapter_manager = $adapter_manager;
$this->dispatcher = $dispatcher;
@ -116,10 +116,7 @@ class DashboardController
}
// Detect current analytics level.
$analytics_level = $this->settingsRepo->getSetting(
Entity\Settings::LISTENER_ANALYTICS,
Entity\Analytics::LEVEL_ALL
);
$analytics_level = $this->settings->getAnalytics();
if ($analytics_level === Entity\Analytics::LEVEL_NONE) {
$metrics = null;

View File

@ -9,17 +9,13 @@ use Psr\Http\Message\ResponseInterface;
class IndexController
{
protected Entity\Repository\SettingsRepository $settings_repo;
public function __construct(Entity\Repository\SettingsRepository $settings_repo)
{
$this->settings_repo = $settings_repo;
}
public function indexAction(ServerRequest $request, Response $response): ResponseInterface
{
public function indexAction(
ServerRequest $request,
Response $response,
Entity\Settings $settings
): ResponseInterface {
// Redirect to complete setup, if it hasn't been completed yet.
if ($this->settings_repo->getSetting(Entity\Settings::SETUP_COMPLETE, 0) === 0) {
if (!$settings->isSetupComplete()) {
return $response->withRedirect($request->getRouter()->named('setup:index'));
}
@ -28,7 +24,7 @@ class IndexController
if (!($user instanceof Entity\User)) {
// Redirect to a custom homepage URL if specified in settings.
$homepage_redirect = trim($this->settings_repo->getSetting(Entity\Settings::HOMEPAGE_REDIRECT_URL));
$homepage_redirect = trim($settings->getHomepageRedirectUrl());
if (!empty($homepage_redirect)) {
return $response->withRedirect($homepage_redirect, 302);

View File

@ -17,17 +17,21 @@ class SetupController
{
protected EntityManagerInterface $em;
protected Entity\Repository\SettingsRepository $settingsRepo;
protected Entity\Settings $settings;
protected Environment $settings;
protected Entity\Repository\SettingsTableRepository $settingsTableRepo;
protected Environment $environment;
public function __construct(
EntityManagerInterface $em,
Entity\Repository\SettingsRepository $settingsRepository,
Environment $settings
Entity\Repository\SettingsTableRepository $settingsRepository,
Environment $environment,
Entity\Settings $settings
) {
$this->em = $em;
$this->settingsRepo = $settingsRepository;
$this->settingsTableRepo = $settingsRepository;
$this->environment = $environment;
$this->settings = $settings;
}
@ -50,7 +54,7 @@ class SetupController
*/
protected function getSetupStep(ServerRequest $request): string
{
if (0 !== (int)$this->settingsRepo->getSetting(Entity\Settings::SETUP_COMPLETE, 0)) {
if ($this->settings->isSetupComplete()) {
return 'complete';
}
@ -110,7 +114,7 @@ class SetupController
{
// Verify current step.
$current_step = $this->getSetupStep($request);
if ($current_step !== 'register' && $this->settings->isProduction()) {
if ($current_step !== 'register' && $this->environment->isProduction()) {
return $response->withRedirect($request->getRouter()->named('setup:' . $current_step));
}
@ -170,7 +174,7 @@ class SetupController
): ResponseInterface {
// Verify current step.
$current_step = $this->getSetupStep($request);
if ($current_step !== 'station' && $this->settings->isProduction()) {
if ($current_step !== 'station' && $this->environment->isProduction()) {
return $response->withRedirect($request->getRouter()->named('setup:' . $current_step));
}
@ -199,12 +203,13 @@ class SetupController
): ResponseInterface {
// Verify current step.
$current_step = $this->getSetupStep($request);
if ($current_step !== 'settings' && $this->settings->isProduction()) {
if ($current_step !== 'settings' && $this->environment->isProduction()) {
return $response->withRedirect($request->getRouter()->named('setup:' . $current_step));
}
if ($settingsForm->process($request)) {
$this->settingsRepo->setSetting(Entity\Settings::SETUP_COMPLETE, time());
$this->settings->updateSetupComplete();
$this->settingsTableRepo->writeSettings($this->settings);
// Notify the user and redirect to homepage.
$request->getFlash()->addMessage(

View File

@ -13,15 +13,12 @@ class ListenersController
public function __invoke(
ServerRequest $request,
Response $response,
Entity\Repository\SettingsRepository $settingsRepo,
Entity\Settings $settings,
IpGeolocation $ipGeo
): ResponseInterface {
$view = $request->getView();
$analytics_level = $settingsRepo->getSetting(
Entity\Settings::LISTENER_ANALYTICS,
Entity\Analytics::LEVEL_ALL
);
$analytics_level = $settings->getAnalytics();
if ($analytics_level !== Entity\Analytics::LEVEL_ALL) {
return $view->renderToResponse($response, 'stations/reports/restricted');

View File

@ -17,17 +17,17 @@ class OverviewController
{
protected EntityManagerInterface $em;
protected Entity\Repository\SettingsRepository $settingsRepo;
protected Entity\Settings $settings;
protected Entity\Repository\AnalyticsRepository $analyticsRepo;
public function __construct(
EntityManagerInterface $em,
Entity\Repository\SettingsRepository $settingsRepo,
Entity\Settings $settings,
Entity\Repository\AnalyticsRepository $analyticsRepo
) {
$this->em = $em;
$this->settingsRepo = $settingsRepo;
$this->settings = $settings;
$this->analyticsRepo = $analyticsRepo;
}
@ -37,10 +37,7 @@ class OverviewController
$station_tz = $station->getTimezoneObject();
// Get current analytics level.
$analytics_level = $this->settingsRepo->getSetting(
Entity\Settings::LISTENER_ANALYTICS,
Entity\Analytics::LEVEL_ALL
);
$analytics_level = $this->settings->getAnalytics();
if ($analytics_level === Entity\Analytics::LEVEL_NONE) {
// The entirety of the dashboard can't be shown, so redirect user to the profile page.

View File

@ -17,16 +17,16 @@ class StreamersController
protected AzuraCastCentral $ac_central;
protected Entity\Repository\SettingsRepository $settingsRepo;
protected Entity\Settings $settings;
public function __construct(
EntityManagerInterface $em,
AzuraCastCentral $ac_central,
Entity\Repository\SettingsRepository $settingsRepo
Entity\Settings $settings
) {
$this->em = $em;
$this->ac_central = $ac_central;
$this->settingsRepo = $settingsRepo;
$this->settings = $settings;
}
public function __invoke(ServerRequest $request, Response $response): ResponseInterface
@ -61,7 +61,7 @@ class StreamersController
$be_settings = $station->getBackendConfig();
return $view->renderToResponse($response, 'stations/streamers/index', [
'server_url' => $this->settingsRepo->getSetting(Entity\Settings::BASE_URL, ''),
'server_url' => $this->settings->getBaseUrl(),
'stream_port' => $backend->getStreamPort($station),
'ip' => $this->ac_central->getIp(),
'dj_mount_point' => $be_settings['dj_mount_point'] ?? '/',

View File

@ -14,9 +14,14 @@ class Customization
public const DEFAULT_LOCALE = 'en_US.UTF-8';
public const DEFAULT_THEME = 'light';
public const THEME_LIGHT = 'light';
public const THEME_DARK = 'dark';
protected ?Entity\User $user = null;
protected Entity\Repository\SettingsRepository $settingsRepo;
protected Entity\Settings $settings;
protected Environment $environment;
protected string $locale = self::DEFAULT_LOCALE;
@ -27,11 +32,14 @@ class Customization
protected string $instanceName = '';
public function __construct(
Entity\Repository\SettingsRepository $settingsRepo,
Entity\Settings $settings,
Environment $environment,
ServerRequestInterface $request
) {
$this->settingsRepo = $settingsRepo;
$this->instanceName = (string)$this->settingsRepo->getSetting(Entity\Settings::INSTANCE_NAME, '');
$this->settings = $settings;
$this->environment = $environment;
$this->instanceName = $settings->getInstanceName() ?? '';
// Register current user
$this->user = $request->getAttribute(ServerRequest::ATTR_USER);
@ -44,7 +52,7 @@ class Customization
if (!empty($queryParams['theme'])) {
$this->publicTheme = $this->theme = $queryParams['theme'];
} else {
$this->publicTheme = $this->settingsRepo->getSetting(Entity\Settings::PUBLIC_THEME, $this->publicTheme);
$this->publicTheme = $settings->getPublicTheme() ?? $this->publicTheme;
if (null !== $this->user && !empty($this->user->getTheme())) {
$this->theme = (string)$this->user->getTheme();
@ -74,9 +82,7 @@ class Customization
*/
protected function initLocale(?ServerRequestInterface $request = null): string
{
$settings = Environment::getInstance();
$supported_locales = $settings['locale']['supported'];
$supported_locales = $this->environment['locale']['supported'];
$try_locales = [];
// Prefer user-based profile locale.
@ -160,7 +166,7 @@ class Customization
*/
public function getCustomPublicCss(): string
{
return (string)$this->settingsRepo->getSetting(Entity\Settings::CUSTOM_CSS_PUBLIC, '');
return $this->settings->getPublicCustomCss() ?? '';
}
/**
@ -168,7 +174,7 @@ class Customization
*/
public function getCustomPublicJs(): string
{
return (string)$this->settingsRepo->getSetting(Entity\Settings::CUSTOM_JS_PUBLIC, '');
return $this->settings->getPublicCustomJs() ?? '';
}
/**
@ -176,7 +182,7 @@ class Customization
*/
public function getCustomInternalCss(): string
{
return (string)$this->settingsRepo->getSetting(Entity\Settings::CUSTOM_CSS_INTERNAL, '');
return $this->settings->getInternalCustomCss() ?? '';
}
/**
@ -184,7 +190,7 @@ class Customization
*/
public function hideAlbumArt(): bool
{
return (bool)$this->settingsRepo->getSetting(Entity\Settings::HIDE_ALBUM_ART, false);
return $this->settings->getHideAlbumArt();
}
/**
@ -194,18 +200,16 @@ class Customization
*/
public function getPageTitle($title = null): string
{
$settings = Environment::getInstance();
if (!$this->hideProductName()) {
if ($title) {
$title .= ' - ' . $settings[Environment::APP_NAME];
$title .= ' - ' . $this->environment[Environment::APP_NAME];
} else {
$title = $settings[Environment::APP_NAME];
$title = $this->environment[Environment::APP_NAME];
}
}
if (!$settings->isProduction()) {
$title = '(' . ucfirst($settings[Environment::APP_ENV]) . ') ' . $title;
if (!$this->environment->isProduction()) {
$title = '(' . ucfirst($this->environment[Environment::APP_ENV]) . ') ' . $title;
}
return $title;
@ -216,7 +220,7 @@ class Customization
*/
public function hideProductName(): bool
{
return (bool)$this->settingsRepo->getSetting(Entity\Settings::HIDE_PRODUCT_NAME, false);
return $this->settings->getHideProductName();
}
public function useWebSocketsForNowPlaying(): bool
@ -225,7 +229,7 @@ class Customization
return false;
}
return (bool)$this->settingsRepo->getSetting(Entity\Settings::NOWPLAYING_USE_WEBSOCKETS, false);
return $this->settings->getEnableWebsockets();
}
/**

View File

@ -20,19 +20,19 @@ class Repository
protected Serializer $serializer;
protected Environment $settings;
protected Environment $environment;
protected LoggerInterface $logger;
public function __construct(
EntityManagerInterface $em,
Serializer $serializer,
Environment $settings,
Environment $environment,
LoggerInterface $logger
) {
$this->em = $em;
$this->serializer = $serializer;
$this->settings = $settings;
$this->environment = $environment;
$this->logger = $logger;
if (!isset($this->entityClass)) {

View File

@ -1,213 +0,0 @@
<?php
namespace App\Entity\Api\Admin;
use App\Entity;
use OpenApi\Annotations as OA;
use Symfony\Component\Validator\Constraints as Assert;
/**
* @OA\Schema(type="object", schema="Api_Admin_Settings")
*/
class Settings
{
/**
* Site Base URL
*
* @Assert\NotBlank
* @OA\Property()
* @var string
*/
public string $base_url;
/**
* AzuraCast Instance Name
*
* @OA\Property()
* @var string|null
*/
public ?string $instance_name;
/**
* System Default Time Zone
*
* @OA\Property()
* @var string|null
*/
public ?string $timezone = 'UTC';
/**
* Prefer Browser URL (If Available)
*
* @OA\Property()
* @Assert\Choice({0,1})
* @var int
*/
public int $prefer_browser_url = 0;
/**
* Use Web Proxy for Radio
*
* @OA\Property()
* @Assert\Choice({0,1})
* @var int
*/
public int $use_radio_proxy = 0;
/**
* Days of Playback History to Keep
*
* @OA\Property()
* @Assert\Choice({0,14,30,60,365,730})
* @var int
*/
public int $history_keep_days = Entity\SongHistory::DEFAULT_DAYS_TO_KEEP;
/**
* Always Use HTTPS
*
* @OA\Property()
* @Assert\Choice({0,1})
* @var int
*/
public int $always_use_ssl = 0;
/**
* API "Access-Control-Allow-Origin" header
*
* @OA\Property()
* @var string
*/
public string $api_access_control;
/**
* Listener Analytics Collection
*
* @OA\Property()
* @Assert\Choice({Entity\Analytics::LEVEL_NONE, Entity\Analytics::LEVEL_NO_IP, Entity\Analytics::LEVEL_ALL})
* @var string
*/
public string $analytics = Entity\Analytics::LEVEL_ALL;
/**
* Check for Updates and Announcements
*
* @OA\Property()
* @Assert\Choice({
* Entity\Settings::UPDATES_NONE,
* Entity\Settings::UPDATES_RELEASE_ONLY,
* Entity\Settings::UPDATES_ALL
* })
* @var int
*/
public int $central_updates_channel = Entity\Settings::UPDATES_RELEASE_ONLY;
/**
* Base Theme for Public Pages
*
* @OA\Property()
* @Assert\Choice({"light", "dark"})
* @var string
*/
public string $public_theme = 'light';
/**
* Hide Album Art on Public Pages
*
* @OA\Property()
* @Assert\Choice({0,1})
* @var int
*/
public int $hide_album_art = 0;
/**
* Homepage Redirect URL
*
* @OA\Property()
* @var string|null
*/
public ?string $homepage_redirect_url;
/**
* Default Album Art URL
*
* @OA\Property()
* @var string|null
*/
public ?string $default_album_art_url;
/**
* Hide AzuraCast Branding on Public Pages
*
* @OA\Property()
* @Assert\Choice({0,1})
* @var int
*/
public int $hide_product_name = 0;
/**
* Custom CSS for Public Pages
*
* @OA\Property()
* @var string|null
*/
public ?string $custom_css_public;
/**
* Custom JS for Public Pages
*
* @OA\Property()
* @var string|null
*/
public ?string $custom_js_public;
/**
* Custom CSS for Internal Pages
*
* @OA\Property()
* @var string|null
*/
public ?string $custom_css_internal;
/**
* @param int $prefer_browser_url
*/
public function setPreferBrowserUrl(int $prefer_browser_url): void
{
$this->prefer_browser_url = $prefer_browser_url;
}
/**
* @param int $use_radio_proxy
*/
public function setUseRadioProxy(int $use_radio_proxy): void
{
$this->use_radio_proxy = $use_radio_proxy;
}
/**
* @param int $history_keep_days
*/
public function setHistoryKeepDays(int $history_keep_days): void
{
$this->history_keep_days = $history_keep_days;
}
/**
* @param int $always_use_ssl
*/
public function setAlwaysUseSsl(int $always_use_ssl): void
{
$this->always_use_ssl = $always_use_ssl;
}
/**
* @param int $central_updates_channel
*/
public function setCentralUpdatesChannel(int $central_updates_channel): void
{
$this->central_updates_channel = $central_updates_channel;
}
/**
* @param int $hide_album_art
*/
public function setHideAlbumArt(int $hide_album_art): void
{
$this->hide_album_art = $hide_album_art;
}
/**
* @param int $hide_product_name
*/
public function setHideProductName(int $hide_product_name): void
{
$this->hide_product_name = $hide_product_name;
}
}

View File

@ -10,32 +10,36 @@ class Settings extends AbstractFixture
{
public function load(ObjectManager $em): void
{
$settings = [
Entity\Settings::BASE_URL => getenv('INIT_BASE_URL') ?? 'docker.local',
Entity\Settings::INSTANCE_NAME => getenv('INIT_INSTANCE_NAME') ?? 'local test',
Entity\Settings::GEOLITE_LICENSE_KEY => getenv('INIT_GEOLITE_LICENSE_KEY') ?? '',
Entity\Settings::PREFER_BROWSER_URL => 1,
Entity\Settings::SETUP_COMPLETE => time(),
Entity\Settings::USE_RADIO_PROXY => 1,
Entity\Settings::CENTRAL_UPDATES => Entity\Settings::UPDATES_NONE,
Entity\Settings::EXTERNAL_IP => '127.0.0.1',
];
$settings = new Entity\Settings();
$settings->setBaseUrl(getenv('INIT_BASE_URL') ?? 'docker.local');
$settings->setInstanceName(getenv('INIT_INSTANCE_NAME') ?? 'local test');
$settings->setGeoliteLicenseKey(getenv('INIT_GEOLITE_LICENSE_KEY') ?? '');
$settings->setPreferBrowserUrl(true);
$settings->updateSetupComplete();
$settings->setUseRadioProxy(true);
$settings->setCheckForUpdates(true);
$settings->setExternalIp('127.0.0.1');
$isDemoMode = (!empty(getenv('INIT_DEMO_API_KEY') ?? ''));
if ($isDemoMode) {
$settings[Entity\Settings::LISTENER_ANALYTICS] = Entity\Analytics::LEVEL_NO_IP;
$settings[Entity\Settings::CUSTOM_JS_PUBLIC] = <<<EOF
$(function() {
if ($('body').hasClass('login-content')) {
$('input[name="username"]').val('demo@azuracast.com');
$('input[name="password"]').val('demo');
}
});
EOF;
$settings->setAnalytics(Entity\Analytics::LEVEL_NO_IP);
$settings->setPublicCustomJs(
<<<'JS'
$(function() {
if ($('body').hasClass('login-content')) {
$('input[name="username"]').val('demo@azuracast.com');
$('input[name="password"]').val('demo');
}
});
JS
);
}
$settings = json_decode(json_encode($settings), true);
foreach ($settings as $setting_key => $setting_value) {
$record = new Entity\Settings($setting_key);
$record = new Entity\SettingsTable($setting_key);
$record->setSettingValue($setting_value);
$em->persist($record);
}

View File

@ -0,0 +1,101 @@
<?php
declare(strict_types=1);
namespace App\Entity\Migration;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;
/**
* Auto-generated Migration: Please modify to your needs!
*/
final class Version20201204043539 extends AbstractMigration
{
public function getDescription(): string
{
return 'Settings migration';
}
public function preUp(Schema $schema): void
{
$settingsRaw = $this->connection->fetchAllAssociative('SELECT * FROM settings WHERE setting_value IS NOT NULL');
$settings = [];
foreach ($settingsRaw as $row) {
$settings[$row['setting_key']] = json_decode($row['setting_value'], true, 512, JSON_THROW_ON_ERROR);
}
$newSettings = array_filter([
'baseUrl' => $settings['base_url'] ?? null,
'instanceName' => $settings['instance_name'] ?? null,
'preferBrowserUrl' => $this->toBool($settings['prefer_browser_url'] ?? null),
'useRadioProxy' => $this->toBool($settings['use_radio_proxy'] ?? null),
'historyKeepDays' => $this->toInt($settings['history_keep_days'] ?? null),
'alwaysUseSsl' => $this->toBool($settings['always_use_ssl'] ?? null),
'apiAccessControl' => $settings['api_access_control'] ?? null,
'enableWebsockets' => $this->toBool($settings['nowplaying_use_websockets'] ?? null),
'analytics' => $settings['analytics'] ?? null,
'checkForUpdates' => $this->toBool($settings['central_updates_channel'] ?? null),
'appUniqueIdentifier' => $settings['central_app_uuid'] ?? null,
'updateResults' => $settings['central_update_results'] ?? null,
'updateLastRun' => $this->toInt($settings['central_update_last_run'] ?? null),
'hideAlbumArt' => $this->toBool($settings['hide_album_art'] ?? null),
'homepageRedirectUrl' => $settings['homepage_redirect_url'] ?? null,
'defaultAlbumArtUrl' => $settings['default_album_art_url'] ?? null,
'hideProductName' => $this->toBool($settings['hide_product_name'] ?? null),
'publicTheme' => $settings['public_theme'] ?? null,
'publicCustomCss' => $settings['custom_css_public'] ?? null,
'publicCustomJs' => $settings['custom_js_public'] ?? null,
'internalCustomCss' => $settings['custom_css_internal'] ?? null,
'backupEnabled' => $this->toBool($settings['backup_enabled'] ?? null),
'backupTimeCode' => $settings['backup_time'] ?? null,
'backupExcludeMedia' => $this->toBool($settings['backup_exclude_media'] ?? null),
'backupKeepCopies' => $settings['backup_keep_copies'] ?? null,
'backupStorageLocation' => $this->toInt($settings['backup_storage_location'] ?? null),
'backupLastRun' => $this->toInt($settings['backup_last_run'] ?? null),
'backupLastResult' => $settings['backup_last_result'] ?? null,
'backupLastOutput' => $settings['backup_last_output'] ?? null,
'setupCompleteTime' => $this->toInt($settings['setup_complete'] ?? null),
'nowplaying' => $settings['nowplaying'] ?? null,
'syncNowplayingLastRun' => $this->toInt($settings['nowplaying_last_run'] ?? null),
'syncShortLastRun' => $this->toInt($settings['sync_fast_last_run'] ?? null),
'syncMediumLastRun' => $this->toInt($settings['sync_last_run'] ?? null),
'syncLongLastRun' => $this->toInt($settings['sync_slow_last_run'] ?? null),
'externalIp' => $settings['external_ip'] ?? null,
'geoliteLicenseKey' => $settings['geolite_license_key'] ?? null,
'geoliteLastRun' => $this->toInt($settings['geolite_last_run'] ?? null),
], function ($value) {
return null !== $value;
});
$this->connection->delete('settings', [1 => 1]);
foreach ($newSettings as $settingKey => $settingValue) {
$this->connection->insert('settings', [
'setting_key' => $settingKey,
'setting_value' => json_encode($settingValue, JSON_THROW_ON_ERROR),
]);
}
}
protected function toInt($value): ?int
{
return (null === $value) ? null : (int)$value;
}
protected function toBool($value): ?bool
{
return (null === $value) ? null : (bool)$value;
}
public function up(Schema $schema): void
{
// Noop
}
public function down(Schema $schema): void
{
// Noop
}
}

View File

@ -1,179 +0,0 @@
<?php
namespace App\Entity\Repository;
use App\Doctrine\Repository;
use App\Entity;
use Ramsey\Uuid\Uuid;
class SettingsRepository extends Repository
{
protected static array $cachedSettings;
/**
* @param array $settings
*/
public function setSettings(array $settings): void
{
$all_records_raw = $this->repository->findAll();
$all_records = [];
foreach ($all_records_raw as $record) {
/** @var Entity\Settings $record */
$all_records[$record->getSettingKey()] = $record;
}
$changes = [];
foreach ($settings as $setting_key => $setting_value) {
if (isset($all_records[$setting_key])) {
$record = $all_records[$setting_key];
$prev = $record->getSettingValue();
} else {
$record = new Entity\Settings($setting_key);
$prev = null;
}
$record->setSettingValue($setting_value);
$this->em->persist($record);
// Update cached value
self::$cachedSettings[$setting_key] = $setting_value;
// Include change in audit log.
if ($prev !== $setting_value) {
$changes[$setting_key] = [$prev, $setting_value];
}
}
if (!empty($changes)) {
$auditLog = new Entity\AuditLog(
Entity\AuditLog::OPER_UPDATE,
Entity\Settings::class,
'Settings',
null,
null,
$changes
);
$this->em->persist($auditLog);
}
$this->em->flush();
}
/**
* @param string $key
*/
public function deleteSetting($key): void
{
$record = $this->repository->findOneBy(['setting_key' => $key]);
if ($record instanceof Entity\Settings) {
$this->em->remove($record);
$this->em->flush();
}
unset(self::$cachedSettings[$key]);
}
/**
* @return mixed[]
*/
public function fetchAll(): array
{
$all_records_raw = $this->repository->findAll();
$all_records = [];
foreach ($all_records_raw as $record) {
/** @var Entity\Settings $record */
$all_records[$record->getSettingKey()] = $record->getSettingValue();
}
return $all_records;
}
/**
* Force a clearing of the cache.
*/
public function clearCache(): void
{
// Regenerate cache and flush static value.
$this->fetchArray(false);
}
/**
* @param bool $cached
* @param null $order_by
* @param string $order_dir
*
* @return mixed[]
*/
public function fetchArray($cached = true, $order_by = null, $order_dir = 'ASC'): array
{
if (!isset(self::$cachedSettings) || !$cached) {
$settings_raw = $this->em->createQuery(
<<<'DQL'
SELECT s FROM App\Entity\Settings s ORDER BY s.setting_key ASC
DQL
)->getArrayResult();
self::$cachedSettings = [];
foreach ($settings_raw as $setting) {
self::$cachedSettings[$setting['setting_key']] = $setting['setting_value'];
}
}
return self::$cachedSettings;
}
/**
* @return string A persistent unique identifier for this installation.
*/
public function getUniqueIdentifier(): string
{
$app_uuid = $this->getSetting(Entity\Settings::UNIQUE_IDENTIFIER);
if (!empty($app_uuid)) {
return $app_uuid;
}
$app_uuid = Uuid::uuid4()->toString();
$this->setSetting(Entity\Settings::UNIQUE_IDENTIFIER, $app_uuid);
return $app_uuid;
}
/**
* @param string $key
* @param mixed|null $default_value
* @param bool $cached
*
* @return mixed|null
*/
public function getSetting($key, $default_value = null, $cached = true)
{
$settings = $this->fetchArray($cached);
return $settings[$key] ?? $default_value;
}
/**
* @param string $key
* @param mixed $value
*/
public function setSetting($key, $value): void
{
$record = $this->repository->findOneBy(['setting_key' => $key]);
if (!$record instanceof Entity\Settings) {
$record = new Entity\Settings($key);
}
$record->setSettingValue($value);
$this->em->persist($record);
$this->em->flush();
// Update cached value
self::$cachedSettings[$key] = $value;
}
}

View File

@ -0,0 +1,159 @@
<?php
namespace App\Entity\Repository;
use App\Doctrine\Repository;
use App\Entity;
use App\Environment;
use App\Exception\ValidationException;
use Doctrine\ORM\EntityManagerInterface;
use Psr\Log\LoggerInterface;
use Psr\SimpleCache\CacheInterface;
use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;
use Symfony\Component\Serializer\Serializer;
use Symfony\Component\Validator\Validator\ValidatorInterface;
class SettingsTableRepository extends Repository
{
protected const CACHE_KEY = 'settings';
protected const CACHE_TTL = 600;
protected CacheInterface $cache;
protected ValidatorInterface $validator;
public function __construct(
EntityManagerInterface $em,
Serializer $serializer,
Environment $environment,
LoggerInterface $logger,
CacheInterface $cache,
ValidatorInterface $validator
) {
parent::__construct($em, $serializer, $environment, $logger);
$this->cache = $cache;
$this->validator = $validator;
}
public function readSettings(bool $cached = true): Entity\Settings
{
if (Entity\Settings::hasInstance()) {
return Entity\Settings::getInstance();
} else {
$settings = $this->arrayToObject($this->readSettingsArray($cached));
Entity\Settings::setInstance($settings);
return $settings;
}
}
/**
* @param bool $cached
*
* @return mixed[]
*/
public function readSettingsArray(bool $cached = true): array
{
if ($cached && $this->cache->has(self::CACHE_KEY)) {
return $this->cache->get(self::CACHE_KEY);
}
$allRecords = [];
foreach ($this->repository->findAll() as $record) {
/** @var Entity\SettingsTable $record */
$allRecords[$record->getSettingKey()] = $record->getSettingValue();
}
$this->cache->set(self::CACHE_KEY, $allRecords, self::CACHE_TTL);
return $allRecords;
}
/**
* @param Entity\Settings|array $settingsObj
*/
public function writeSettings($settingsObj): void
{
if (is_array($settingsObj)) {
$settingsObj = $this->arrayToObject($settingsObj, $this->readSettings(false));
}
$errors = $this->validator->validate($settingsObj);
if (count($errors) > 0) {
$e = new ValidationException((string)$errors);
$e->setDetailedErrors($errors);
throw $e;
}
$settings = $this->objectToArray($settingsObj);
$this->cache->set(self::CACHE_KEY, $settings, self::CACHE_TTL);
$currentRecords = $this->repository->findAll();
$allRecords = [];
foreach ($currentRecords as $record) {
/** @var Entity\SettingsTable $record */
$allRecords[$record->getSettingKey()] = $record;
}
$changes = [];
foreach ($settings as $settingKey => $settingValue) {
if (isset($allRecords[$settingKey])) {
$record = $allRecords[$settingKey];
$prev = $record->getSettingValue();
} else {
$record = new Entity\SettingsTable($settingKey);
$prev = null;
}
$record->setSettingValue($settingValue);
$this->em->persist($record);
// Include change in audit log.
if ($prev !== $settingValue) {
$changes[$settingKey] = [$prev, $settingValue];
}
}
if (!empty($changes)) {
$auditLog = new Entity\AuditLog(
Entity\AuditLog::OPER_UPDATE,
Entity\SettingsTable::class,
'Settings',
null,
null,
$changes
);
$this->em->persist($auditLog);
}
$this->em->flush();
}
/**
* @param Entity\Settings $settings
*
* @return mixed[]
*/
protected function objectToArray(Entity\Settings $settings): array
{
return $this->serializer->normalize($settings, null);
}
protected function arrayToObject(array $settings, ?Entity\Settings $existingSettings = null): Entity\Settings
{
$settings = array_filter($settings, function ($value) {
return null !== $value;
});
$context = [];
if (null !== $existingSettings) {
$context[ObjectNormalizer::OBJECT_TO_POPULATE] = $existingSettings;
}
return $this->serializer->denormalize($settings, Entity\Settings::class, null, $context);
}
}

View File

@ -21,7 +21,7 @@ class SongHistoryRepository extends Repository
public function __construct(
EntityManagerInterface $em,
Serializer $serializer,
Environment $settings,
Environment $environment,
LoggerInterface $logger,
ListenerRepository $listenerRepository,
StationQueueRepository $stationQueueRepository
@ -29,7 +29,7 @@ class SongHistoryRepository extends Repository
$this->listenerRepository = $listenerRepository;
$this->stationQueueRepository = $stationQueueRepository;
parent::__construct($em, $serializer, $settings, $logger);
parent::__construct($em, $serializer, $environment, $logger);
}
/**

View File

@ -40,7 +40,7 @@ class StationMediaRepository extends Repository
public function __construct(
EntityManagerInterface $em,
Serializer $serializer,
Environment $settings,
Environment $environment,
LoggerInterface $logger,
MetadataManagerInterface $metadataManager,
CustomFieldRepository $customFieldRepo,
@ -48,7 +48,7 @@ class StationMediaRepository extends Repository
StorageLocationRepository $storageLocationRepo,
FilesystemManager $filesystem
) {
parent::__construct($em, $serializer, $settings, $logger);
parent::__construct($em, $serializer, $environment, $logger);
$this->customFieldRepo = $customFieldRepo;
$this->spmRepo = $spmRepo;

View File

@ -19,11 +19,11 @@ class StationPlaylistMediaRepository extends Repository
public function __construct(
EntityManagerInterface $em,
Serializer $serializer,
Environment $settings,
Environment $environment,
LoggerInterface $logger,
StationQueueRepository $queueRepo
) {
parent::__construct($em, $serializer, $settings, $logger);
parent::__construct($em, $serializer, $environment, $logger);
$this->queueRepo = $queueRepo;
}

View File

@ -32,32 +32,32 @@ class StationRepository extends Repository
protected CacheInterface $cache;
protected SettingsRepository $settingsRepo;
protected StorageLocationRepository $storageLocationRepo;
protected Entity\Settings $settings;
public function __construct(
EntityManagerInterface $em,
Serializer $serializer,
Environment $settings,
SettingsRepository $settingsRepo,
Environment $environment,
StorageLocationRepository $storageLocationRepo,
LoggerInterface $logger,
CheckMediaTask $mediaSync,
Adapters $adapters,
Configuration $configuration,
ValidatorInterface $validator,
CacheInterface $cache
CacheInterface $cache,
Entity\Settings $settings
) {
$this->mediaSync = $mediaSync;
$this->adapters = $adapters;
$this->configuration = $configuration;
$this->validator = $validator;
$this->cache = $cache;
$this->settingsRepo = $settingsRepo;
$this->storageLocationRepo = $storageLocationRepo;
$this->settings = $settings;
parent::__construct($em, $serializer, $settings, $logger);
parent::__construct($em, $serializer, $environment, $logger);
}
/**
@ -341,7 +341,7 @@ class StationRepository extends Repository
}
}
$custom_url = trim($this->settingsRepo->getSetting(Entity\Settings::DEFAULT_ALBUM_ART_URL));
$custom_url = trim($this->settings->getDefaultAlbumArtUrl());
if (!empty($custom_url)) {
return new Uri($custom_url);

View File

@ -21,11 +21,11 @@ class StationRequestRepository extends Repository
public function __construct(
EntityManagerInterface $em,
Serializer $serializer,
Environment $settings,
Environment $environment,
LoggerInterface $logger,
StationMediaRepository $mediaRepo
) {
parent::__construct($em, $serializer, $settings, $logger);
parent::__construct($em, $serializer, $environment, $logger);
$this->mediaRepo = $mediaRepo;
}

View File

@ -19,11 +19,11 @@ class StationScheduleRepository extends Repository
public function __construct(
EntityManagerInterface $em,
Serializer $serializer,
Environment $settings,
Environment $environment,
LoggerInterface $logger,
Scheduler $scheduler
) {
parent::__construct($em, $serializer, $settings, $logger);
parent::__construct($em, $serializer, $environment, $logger);
$this->scheduler = $scheduler;
}

View File

@ -23,13 +23,13 @@ class StationStreamerRepository extends Repository
public function __construct(
EntityManagerInterface $em,
Serializer $serializer,
Environment $settings,
Environment $environment,
LoggerInterface $logger,
Scheduler $scheduler,
StationStreamerBroadcastRepository $broadcastRepo,
FilesystemManager $filesystem
) {
parent::__construct($em, $serializer, $settings, $logger);
parent::__construct($em, $serializer, $environment, $logger);
$this->scheduler = $scheduler;
$this->broadcastRepo = $broadcastRepo;

View File

@ -2,116 +2,706 @@
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
use App\Customization;
use App\Entity;
use App\Event\GetSyncTasks;
use App\Traits\AvailableStaticallyTrait;
use OpenApi\Annotations as OA;
use Symfony\Component\Validator\Constraints as Assert;
/**
* @ORM\Table(name="settings")
* @ORM\Entity()
* @OA\Schema(type="object", schema="Settings")
*/
class Settings
{
// Predefined settings constants.
public const BASE_URL = 'base_url';
public const INSTANCE_NAME = 'instance_name';
public const PREFER_BROWSER_URL = 'prefer_browser_url';
public const USE_RADIO_PROXY = 'use_radio_proxy';
public const HISTORY_KEEP_DAYS = 'history_keep_days';
public const ALWAYS_USE_SSL = 'always_use_ssl';
public const API_ACCESS_CONTROL = 'api_access_control';
public const NOWPLAYING_USE_WEBSOCKETS = 'nowplaying_use_websockets';
public const LISTENER_ANALYTICS = 'analytics';
public const CENTRAL_UPDATES = 'central_updates_channel';
// Custom branding constants.
public const PUBLIC_THEME = 'public_theme';
public const HIDE_ALBUM_ART = 'hide_album_art';
public const HOMEPAGE_REDIRECT_URL = 'homepage_redirect_url';
public const DEFAULT_ALBUM_ART_URL = 'default_album_art_url';
public const HIDE_PRODUCT_NAME = 'hide_product_name';
public const CUSTOM_CSS_PUBLIC = 'custom_css_public';
public const CUSTOM_JS_PUBLIC = 'custom_js_public';
public const CUSTOM_CSS_INTERNAL = 'custom_css_internal';
// Backup settings
public const BACKUP_ENABLED = 'backup_enabled';
public const BACKUP_TIME = 'backup_time';
public const BACKUP_EXCLUDE_MEDIA = 'backup_exclude_media';
public const BACKUP_KEEP_COPIES = 'backup_keep_copies';
public const BACKUP_STORAGE_LOCATION = 'backup_storage_location';
// Internal settings
public const SETUP_COMPLETE = 'setup_complete';
public const NOWPLAYING_LAST_STARTED = 'nowplaying_last_started';
public const NOWPLAYING_LAST_RUN = 'nowplaying_last_run';
public const NOWPLAYING = 'nowplaying';
public const SHORT_SYNC_LAST_RUN = 'sync_fast_last_run';
public const MEDIUM_SYNC_LAST_RUN = 'sync_last_run';
public const LONG_SYNC_LAST_RUN = 'sync_slow_last_run';
public const UPDATES_NONE = 0;
public const UPDATES_ALL = 1;
public const UPDATES_RELEASE_ONLY = 2;
public const UNIQUE_IDENTIFIER = 'central_app_uuid';
public const UPDATE_RESULTS = 'central_update_results';
public const UPDATE_LAST_RUN = 'central_update_last_run';
public const EXTERNAL_IP = 'external_ip';
public const BACKUP_LAST_RUN = 'backup_last_run';
public const BACKUP_LAST_RESULT = 'backup_last_result';
public const BACKUP_LAST_OUTPUT = 'backup_last_output';
public const GEOLITE_LICENSE_KEY = 'geolite_license_key';
public const GEOLITE_LAST_RUN = 'geolite_last_run';
use AvailableStaticallyTrait;
/**
* @ORM\Column(name="setting_key", type="string", length=64)
* @ORM\Id
* @ORM\GeneratedValue(strategy="NONE")
* @OA\Property(example="https://your.azuracast.site")
* @var string Site Base URL
*/
protected string $baseUrl = '';
public function getBaseUrl(): string
{
return $this->baseUrl;
}
public function setBaseUrl(string $baseUrl): void
{
$this->baseUrl = $baseUrl;
}
/**
* @OA\Property(example="My AzuraCast Instance")
* @var string|null AzuraCast Instance Name
*/
protected ?string $instanceName = null;
public function getInstanceName(): ?string
{
return $this->instanceName;
}
public function setInstanceName(?string $instanceName): void
{
$this->instanceName = $instanceName;
}
/**
* @OA\Property(example="false")
* @var bool Prefer Browser URL (If Available)
*/
protected bool $preferBrowserUrl = false;
public function getPreferBrowserUrl(): bool
{
return (bool)($this->preferBrowserUrl);
}
public function setPreferBrowserUrl(bool $preferBrowserUrl): void
{
$this->preferBrowserUrl = $preferBrowserUrl;
}
/**
* @OA\Property(example="false")
* @var bool Use Web Proxy for Radio
*/
protected bool $useRadioProxy = false;
public function getUseRadioProxy(): bool
{
return (bool)($this->useRadioProxy);
}
public function setUseRadioProxy(bool $useRadioProxy): void
{
$this->useRadioProxy = $useRadioProxy;
}
/**
* @OA\Property()
* @Assert\Choice({0,14,30,60,365,730})
* @var int Days of Playback History to Keep
*/
protected int $historyKeepDays = Entity\SongHistory::DEFAULT_DAYS_TO_KEEP;
public function getHistoryKeepDays(): int
{
return $this->historyKeepDays;
}
public function setHistoryKeepDays(int $historyKeepDays): void
{
$this->historyKeepDays = $historyKeepDays;
}
/**
* @OA\Property(example="false")
* @var bool Always Use HTTPS
*/
protected bool $alwaysUseSsl = false;
public function getAlwaysUseSsl(): bool
{
return (bool)$this->alwaysUseSsl;
}
public function setAlwaysUseSsl(bool $alwaysUseSsl): void
{
$this->alwaysUseSsl = $alwaysUseSsl;
}
/**
* @OA\Property(example="*")
* @var string API "Access-Control-Allow-Origin" header
*/
protected string $apiAccessControl = '';
public function getApiAccessControl(): string
{
return $this->apiAccessControl;
}
public function setApiAccessControl(string $apiAccessControl): void
{
$this->apiAccessControl = $apiAccessControl;
}
/**
* @OA\Property(example="false")
* @var bool Whether to use Websockets for Now Playing data updates.
*/
protected bool $enableWebsockets = false;
public function getEnableWebsockets(): bool
{
return (bool)$this->enableWebsockets;
}
public function setEnableWebsockets(bool $enableWebsockets): void
{
$this->enableWebsockets = $enableWebsockets;
}
/**
* Listener Analytics Collection
*
* @OA\Property()
* @Assert\Choice({Entity\Analytics::LEVEL_NONE, Entity\Analytics::LEVEL_NO_IP, Entity\Analytics::LEVEL_ALL})
* @var string
*/
protected $setting_key;
protected string $analytics = Entity\Analytics::LEVEL_ALL;
/**
* @ORM\Column(name="setting_value", type="json", nullable=true)
* @var mixed
*/
protected $setting_value;
public function __construct(string $setting_key)
public function getAnalytics(): string
{
$this->setting_key = $setting_key;
return $this->analytics;
}
public function getSettingKey(): string
public function setAnalytics(string $analytics): void
{
return $this->setting_key;
$this->analytics = $analytics;
}
/**
* @return mixed
* @OA\Property(example="true")
* @var bool Check for Updates and Announcements
*/
public function getSettingValue()
protected bool $checkForUpdates = true;
public function getCheckForUpdates(): bool
{
return $this->setting_value;
return $this->checkForUpdates;
}
public function setSettingValue($setting_value): void
public function setCheckForUpdates(bool $checkForUpdates): void
{
$this->setting_value = $setting_value;
$this->checkForUpdates = $checkForUpdates;
}
/**
* @return mixed
* @OA\Property(example="")
* @var string|null The unique identifier for this installation (for update checks).
*/
public function getValue()
protected ?string $appUniqueIdentifier = null;
public function getAppUniqueIdentifier(): ?string
{
return $this->setting_value;
return $this->appUniqueIdentifier;
}
public function setAppUniqueIdentifier(?string $appUniqueIdentifier): void
{
$this->appUniqueIdentifier = $appUniqueIdentifier;
}
/**
* @OA\Property(example="")
* @var mixed[]|null Results of the latest update check.
*/
protected ?array $updateResults;
/**
* @return mixed[]|null
*/
public function getUpdateResults(): ?array
{
return $this->updateResults;
}
public function setUpdateResults(?array $updateResults): void
{
$this->updateResults = $updateResults;
}
/**
* @OA\Property(example=SAMPLE_TIMESTAMP)
* @var int The UNIX timestamp when updates were last checked.
*/
protected int $updateLastRun = 0;
public function getUpdateLastRun(): int
{
return $this->updateLastRun;
}
public function setUpdateLastRun(int $updateLastRun): void
{
$this->updateLastRun = $updateLastRun;
}
public function updateUpdateLastRun(): void
{
$this->setUpdateLastRun(time());
}
/**
* @OA\Property(example=Customization::DEFAULT_THEME)
* @Assert\Choice({Customization::THEME_LIGHT, Customization::THEME_DARK})
* @var string Base Theme for Public Pages
*/
protected string $publicTheme = Customization::DEFAULT_THEME;
public function getPublicTheme(): string
{
return $this->publicTheme;
}
public function setPublicTheme(string $publicTheme): void
{
$this->publicTheme = $publicTheme;
}
/**
* @OA\Property(example="false")
* @var bool Hide Album Art on Public Pages
*/
protected bool $hideAlbumArt = false;
public function getHideAlbumArt(): bool
{
return $this->hideAlbumArt;
}
public function setHideAlbumArt(bool $hideAlbumArt): void
{
$this->hideAlbumArt = $hideAlbumArt;
}
/**
* @OA\Property(example="https://example.com/")
* @var string|null Homepage Redirect URL
*/
protected ?string $homepageRedirectUrl = null;
public function getHomepageRedirectUrl(): ?string
{
return $this->homepageRedirectUrl;
}
public function setHomepageRedirectUrl(?string $homepageRedirectUrl): void
{
$this->homepageRedirectUrl = $homepageRedirectUrl;
}
/**
* @OA\Property(example="https://example.com/image.jpg")
* @var string|null Default Album Art URL
*/
protected ?string $defaultAlbumArtUrl = null;
public function getDefaultAlbumArtUrl(): ?string
{
return $this->defaultAlbumArtUrl;
}
public function setDefaultAlbumArtUrl(?string $defaultAlbumArtUrl): void
{
$this->defaultAlbumArtUrl = $defaultAlbumArtUrl;
}
/**
* @OA\Property(example="false")
* @var bool Hide AzuraCast Branding on Public Pages
*/
protected bool $hideProductName = false;
public function getHideProductName(): bool
{
return (bool)$this->hideProductName;
}
public function setHideProductName(bool $hideProductName): void
{
$this->hideProductName = $hideProductName;
}
/**
* @OA\Property(example="")
* @var string|null Custom CSS for Public Pages
*/
protected ?string $publicCustomCss = null;
public function getPublicCustomCss(): ?string
{
return $this->publicCustomCss;
}
public function setPublicCustomCss(?string $publicCustomCss): void
{
$this->publicCustomCss = $publicCustomCss;
}
/**
* @OA\Property(example="")
* @var string|null Custom JS for Public Pages
*/
protected ?string $publicCustomJs = null;
public function getPublicCustomJs(): ?string
{
return $this->publicCustomJs;
}
public function setPublicCustomJs(?string $publicCustomJs): void
{
$this->publicCustomJs = $publicCustomJs;
}
/**
* @OA\Property(example="")
* @var string|null Custom CSS for Internal Pages
*/
protected ?string $internalCustomCss = null;
public function getInternalCustomCss(): ?string
{
return $this->internalCustomCss;
}
public function setInternalCustomCss(?string $internalCustomCss): void
{
$this->internalCustomCss = $internalCustomCss;
}
/**
* @OA\Property(example="false")
* @var bool Whether backup is enabled.
*/
protected bool $backupEnabled = false;
public function isBackupEnabled(): bool
{
return $this->backupEnabled;
}
public function setBackupEnabled(bool $backupEnabled): void
{
$this->backupEnabled = $backupEnabled;
}
/**
* @OA\Property(example=400)
* @var int|null The timecode (i.e. 400 for 4:00AM) when automated backups should run.
*/
protected ?int $backupTimeCode = null;
public function getBackupTimeCode(): ?int
{
return $this->backupTimeCode;
}
public function setBackupTimeCode(?int $backupTimeCode): void
{
$this->backupTimeCode = $backupTimeCode;
}
/**
* @OA\Property(example="false")
* @var bool Whether to exclude media in automated backups.
*/
protected bool $backupExcludeMedia = false;
public function getBackupExcludeMedia(): bool
{
return $this->backupExcludeMedia;
}
public function setBackupExcludeMedia(bool $backupExcludeMedia): void
{
$this->backupExcludeMedia = $backupExcludeMedia;
}
/**
* @OA\Property(example=2)
* @var int Number of backups to keep, or infinite if zero/null.
*/
protected int $backupKeepCopies = 0;
public function getBackupKeepCopies(): int
{
return $this->backupKeepCopies;
}
public function setBackupKeepCopies(int $backupKeepCopies): void
{
$this->backupKeepCopies = $backupKeepCopies;
}
/**
* @OA\Property(example=1)
* @var int|null The storage location ID for automated backups.
*/
protected ?int $backupStorageLocation = null;
public function getBackupStorageLocation(): ?int
{
return $this->backupStorageLocation;
}
public function setBackupStorageLocation(?int $backupStorageLocation): void
{
$this->backupStorageLocation = $backupStorageLocation;
}
/**
* @OA\Property(example=SAMPLE_TIMESTAMP)
* @var int The UNIX timestamp when automated backup was last run.
*/
protected int $backupLastRun = 0;
public function getBackupLastRun(): int
{
return $this->backupLastRun;
}
public function setBackupLastRun(int $backupLastRun): void
{
$this->backupLastRun = $backupLastRun;
}
public function updateBackupLastRun(): void
{
$this->setBackupLastRun(time());
}
/**
* @OA\Property(example="")
* @var string|null The result of the latest automated backup task.
*/
protected ?string $backupLastResult = null;
public function getBackupLastResult(): ?string
{
return $this->backupLastResult;
}
public function setBackupLastResult(?string $backupLastResult): void
{
$this->backupLastResult = $backupLastResult;
}
/**
* @OA\Property(example="")
* @var string|null The output of the latest automated backup task.
*/
protected ?string $backupLastOutput = null;
public function getBackupLastOutput(): ?string
{
return $this->backupLastOutput;
}
public function setBackupLastOutput(?string $backupLastOutput): void
{
$this->backupLastOutput = $backupLastOutput;
}
/**
* @OA\Property(example=SAMPLE_TIMESTAMP)
* @var int The UNIX timestamp when setup was last completed.
*/
protected int $setupCompleteTime = 0;
public function getSetupCompleteTime(): int
{
return $this->setupCompleteTime;
}
public function isSetupComplete(): bool
{
return (0 !== $this->setupCompleteTime);
}
public function setSetupCompleteTime(int $setupCompleteTime): void
{
$this->setupCompleteTime = $setupCompleteTime;
}
public function updateSetupComplete(): void
{
$this->setSetupCompleteTime(time());
}
/**
* @OA\Property(example="")
* @var mixed[]|null The current cached now playing data.
*/
protected ?array $nowplaying = null;
/**
* @return mixed[]|null
*/
public function getNowplaying(): ?array
{
return $this->nowplaying;
}
/**
* @param Entity\Api\NowPlaying|array|null $nowplaying
*/
public function setNowplaying($nowplaying): void
{
if ($nowplaying instanceof Entity\Api\NowPlaying) {
$nowplaying = json_decode(
json_encode($nowplaying, JSON_THROW_ON_ERROR),
true,
512,
JSON_THROW_ON_ERROR
);
}
$this->nowplaying = $nowplaying;
}
/**
* @OA\Property(example=SAMPLE_TIMESTAMP)
* @var int The UNIX timestamp when the now playing sync task was last run.
*/
protected int $syncNowplayingLastRun = 0;
public function getSyncNowplayingLastRun(): int
{
return $this->syncNowplayingLastRun;
}
public function setSyncNowplayingLastRun(int $syncNowplayingLastRun): void
{
$this->syncNowplayingLastRun = $syncNowplayingLastRun;
}
/**
* @OA\Property(example=SAMPLE_TIMESTAMP)
* @var int The UNIX timestamp when the 60-second "short" sync task was last run.
*/
protected int $syncShortLastRun = 0;
public function getSyncShortLastRun(): int
{
return $this->syncShortLastRun;
}
public function setSyncShortLastRun(int $syncShortLastRun): void
{
$this->syncShortLastRun = $syncShortLastRun;
}
/**
* @OA\Property(example=SAMPLE_TIMESTAMP)
* @var int The UNIX timestamp when the 5-minute "medium" sync task was last run.
*/
protected int $syncMediumLastRun = 0;
public function getSyncMediumLastRun(): int
{
return $this->syncMediumLastRun;
}
public function setSyncMediumLastRun(int $syncMediumLastRun): void
{
$this->syncMediumLastRun = $syncMediumLastRun;
}
/**
* @OA\Property(example=SAMPLE_TIMESTAMP)
* @var int The UNIX timestamp when the 1-hour "long" sync task was last run.
*/
protected int $syncLongLastRun = 0;
public function getSyncLongLastRun(): int
{
return $this->syncLongLastRun;
}
public function setSyncLongLastRun(int $syncLongLastRun): void
{
$this->syncLongLastRun = $syncLongLastRun;
}
public function getSyncLastRunTime(string $type): int
{
$timesByType = [
GetSyncTasks::SYNC_NOWPLAYING => $this->syncNowplayingLastRun,
GetSyncTasks::SYNC_SHORT => $this->syncShortLastRun,
GetSyncTasks::SYNC_MEDIUM => $this->syncMediumLastRun,
GetSyncTasks::SYNC_LONG => $this->syncLongLastRun,
];
return $timesByType[$type] ?? 0;
}
public function updateSyncLastRunTime(string $type): void
{
switch ($type) {
case GetSyncTasks::SYNC_NOWPLAYING:
$this->syncNowplayingLastRun = time();
break;
case GetSyncTasks::SYNC_SHORT:
$this->syncShortLastRun = time();
break;
case GetSyncTasks::SYNC_MEDIUM:
$this->syncMediumLastRun = time();
break;
case GetSyncTasks::SYNC_LONG:
$this->syncLongLastRun = time();
break;
}
}
/**
* @OA\Property(example="192.168.1.1")
* @var string|null This installation's external IP.
*/
protected ?string $externalIp = null;
public function getExternalIp(): ?string
{
return $this->externalIp;
}
public function setExternalIp(?string $externalIp): void
{
$this->externalIp = $externalIp;
}
/**
* @OA\Property(example="")
* @var string|null The license key for the Maxmind Geolite download.
*/
protected ?string $geoliteLicenseKey = null;
public function getGeoliteLicenseKey(): ?string
{
return $this->geoliteLicenseKey;
}
public function setGeoliteLicenseKey(?string $geoliteLicenseKey): void
{
$this->geoliteLicenseKey = $geoliteLicenseKey;
}
/**
* @OA\Property(example=SAMPLE_TIMESTAMP)
* @var int The UNIX timestamp when the Maxmind Geolite was last downloaded.
*/
protected int $geoliteLastRun = 0;
public function getGeoliteLastRun(): int
{
return $this->geoliteLastRun;
}
public function setGeoliteLastRun(int $geoliteLastRun): void
{
$this->geoliteLastRun = $geoliteLastRun;
}
public function updateGeoliteLastRun(): void
{
$this->setGeoliteLastRun(time());
}
}

View File

@ -0,0 +1,49 @@
<?php
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* @ORM\Table(name="settings")
* @ORM\Entity()
*/
class SettingsTable
{
/**
* @ORM\Column(name="setting_key", type="string", length=64)
* @ORM\Id
* @ORM\GeneratedValue(strategy="NONE")
* @var string
*/
protected $setting_key;
/**
* @ORM\Column(name="setting_value", type="json", nullable=true)
* @var mixed
*/
protected $setting_value;
public function __construct(string $setting_key)
{
$this->setting_key = $setting_key;
}
public function getSettingKey(): string
{
return $this->setting_key;
}
/**
* @return mixed
*/
public function getSettingValue()
{
return $this->setting_value;
}
public function setSettingValue($setting_value): void
{
$this->setting_value = $setting_value;
}
}

View File

@ -11,21 +11,21 @@ abstract class AbstractSettingsForm extends Form
{
protected EntityManagerInterface $em;
protected Entity\Repository\SettingsRepository $settingsRepo;
protected Entity\Repository\SettingsTableRepository $settingsTableRepo;
protected Environment $settings;
protected Environment $environment;
public function __construct(
EntityManagerInterface $em,
Entity\Repository\SettingsRepository $settingsRepo,
Environment $settings,
Entity\Repository\SettingsTableRepository $settingsTableRepo,
Environment $environment,
array $formConfig
) {
parent::__construct($formConfig);
$this->em = $em;
$this->settings = $settings;
$this->settingsRepo = $settingsRepo;
$this->environment = $environment;
$this->settingsTableRepo = $settingsTableRepo;
}
public function getEntityManager(): EntityManagerInterface
@ -33,25 +33,25 @@ abstract class AbstractSettingsForm extends Form
return $this->em;
}
public function getEntityRepository(): Entity\Repository\SettingsRepository
public function getEntityRepository(): Entity\Repository\SettingsTableRepository
{
return $this->settingsRepo;
return $this->settingsTableRepo;
}
public function getSettings(): Environment
public function getEnvironment(): Environment
{
return $this->settings;
return $this->environment;
}
public function process(ServerRequest $request): bool
{
// Populate the form with existing values (if they exist).
$defaults = $this->settingsRepo->fetchArray(false);
$defaults = $this->settingsTableRepo->readSettingsArray(false);
// Use current URI from request if the base URL isn't set.
if (!isset($defaults[Entity\Settings::BASE_URL])) {
if (!isset($defaults['baseUrl'])) {
$currentUri = $request->getUri()->withPath('');
$defaults[Entity\Settings::BASE_URL] = (string)$currentUri;
$defaults['baseUrl'] = (string)$currentUri;
}
$this->populate($defaults);
@ -59,7 +59,7 @@ abstract class AbstractSettingsForm extends Form
// Handle submission.
if ('POST' === $request->getMethod() && $this->isValid($request->getParsedBody())) {
$data = $this->getValues();
$this->settingsRepo->setSettings($data);
$this->settingsTableRepo->writeSettings($data);
return true;
}

View File

@ -11,20 +11,20 @@ class BackupSettingsForm extends AbstractSettingsForm
{
public function __construct(
EntityManagerInterface $em,
Entity\Repository\SettingsRepository $settingsRepo,
Entity\Repository\SettingsTableRepository $settingsTableRepo,
Entity\Repository\StorageLocationRepository $storageLocationRepo,
Environment $settings,
Environment $environment,
Config $config
) {
$formConfig = $config->get('forms/backup', [
'settings' => $settings,
'settings' => $environment,
'storageLocations' => $storageLocationRepo->fetchSelectByType(Entity\StorageLocation::TYPE_BACKUP, true),
]);
parent::__construct(
$em,
$settingsRepo,
$settings,
$settingsTableRepo,
$environment,
$formConfig
);
}

View File

@ -11,18 +11,18 @@ class BrandingSettingsForm extends AbstractSettingsForm
{
public function __construct(
EntityManagerInterface $em,
Entity\Repository\SettingsRepository $settingsRepo,
Environment $settings,
Entity\Repository\SettingsTableRepository $settingsRepo,
Environment $environment,
Config $config
) {
$formConfig = $config->get('forms/branding', [
'settings' => $settings,
'settings' => $environment,
]);
parent::__construct(
$em,
$settingsRepo,
$settings,
$environment,
$formConfig
);
}

View File

@ -14,8 +14,8 @@ class GeoLiteSettingsForm extends AbstractSettingsForm
public function __construct(
EntityManagerInterface $em,
Entity\Repository\SettingsRepository $settingsRepo,
Environment $settings,
Entity\Repository\SettingsTableRepository $settingsRepo,
Environment $environment,
Config $config,
UpdateGeoLiteTask $syncTask
) {
@ -24,7 +24,7 @@ class GeoLiteSettingsForm extends AbstractSettingsForm
parent::__construct(
$em,
$settingsRepo,
$settings,
$environment,
$formConfig
);

View File

@ -13,20 +13,20 @@ class SettingsForm extends AbstractSettingsForm
{
public function __construct(
EntityManagerInterface $em,
Entity\Repository\SettingsRepository $settingsRepo,
Environment $settings,
Entity\Repository\SettingsTableRepository $settingsRepo,
Environment $environment,
Version $version,
Config $config
) {
$formConfig = $config->get('forms/settings', [
'settings' => $settings,
'settings' => $environment,
'version' => $version,
]);
parent::__construct(
$em,
$settingsRepo,
$settings,
$environment,
$formConfig
);
}
@ -34,7 +34,7 @@ class SettingsForm extends AbstractSettingsForm
public function process(ServerRequest $request): bool
{
if ('https' !== $request->getUri()->getScheme()) {
$alwaysUseSsl = $this->getField(Entity\Settings::ALWAYS_USE_SSL);
$alwaysUseSsl = $this->getField('alwaysUseSsl');
$alwaysUseSsl->setAttribute('disabled', 'disabled');
$alwaysUseSsl->setOption(
'description',

View File

@ -35,7 +35,7 @@ class StationCloneForm extends StationForm
Configuration $configuration,
CheckMediaTask $media_sync,
Config $config,
Environment $settings,
Environment $environment,
FilesystemManager $filesystem
) {
parent::__construct(
@ -46,7 +46,7 @@ class StationCloneForm extends StationForm
$storageLocationRepo,
$acl,
$config,
$settings
$environment
);
$form_config = $config->get('forms/station_clone');

View File

@ -21,7 +21,7 @@ class StationForm extends EntityForm
protected Acl $acl;
protected Environment $settings;
protected Environment $environment;
public function __construct(
EntityManagerInterface $em,
@ -31,13 +31,13 @@ class StationForm extends EntityForm
Entity\Repository\StorageLocationRepository $storageLocationRepo,
Acl $acl,
Config $config,
Environment $settings
Environment $environment
) {
$this->acl = $acl;
$this->entityClass = Entity\Station::class;
$this->station_repo = $station_repo;
$this->storageLocationRepo = $storageLocationRepo;
$this->settings = $settings;
$this->environment = $environment;
$form_config = $config->get('forms/station');
parent::__construct($em, $serializer, $validator, $form_config);
@ -46,7 +46,7 @@ class StationForm extends EntityForm
public function configure(array $options): void
{
// Hide "advanced" fields if advanced features are hidden on this installation.
if (!$this->settings->enableAdvancedFeatures()) {
if (!$this->environment->enableAdvancedFeatures()) {
foreach ($options['groups'] as $groupId => $group) {
foreach ($group['elements'] as $elementKey => $element) {
$elementOptions = (array)$element[1];

View File

@ -22,7 +22,7 @@ class StationWebhookForm extends EntityForm
EntityManagerInterface $em,
Serializer $serializer,
ValidatorInterface $validator,
Environment $settings,
Environment $environment,
Config $config,
Router $router
) {
@ -32,7 +32,7 @@ class StationWebhookForm extends EntityForm
$config_injections = [
'router' => $router,
'triggers' => $webhook_config['triggers'],
'app_settings' => $settings,
'app_settings' => $environment,
];
foreach ($webhook_config['webhooks'] as $webhook_key => $webhook_info) {

View File

@ -18,10 +18,10 @@ class UserProfileForm extends EntityForm
Serializer $serializer,
ValidatorInterface $validator,
Config $config,
Environment $settings
Environment $environment
) {
$form_config = $config->get('forms/profile', [
'settings' => $settings,
'settings' => $environment,
]);
parent::__construct($em, $serializer, $validator, $form_config);

View File

@ -33,18 +33,18 @@ class ErrorHandler extends \Slim\Handlers\ErrorHandler
protected ViewFactory $viewFactory;
protected Environment $settings;
protected Environment $environment;
public function __construct(
App $app,
Logger $logger,
Router $router,
ViewFactory $viewFactory,
Environment $settings
Environment $environment
) {
parent::__construct($app->getCallableResolver(), $app->getResponseFactory(), $logger);
$this->settings = $settings;
$this->environment = $environment;
$this->viewFactory = $viewFactory;
$this->router = $router;
}
@ -66,7 +66,7 @@ class ErrorHandler extends \Slim\Handlers\ErrorHandler
$this->loggerLevel = LogLevel::WARNING;
}
$this->showDetailed = (!$this->settings->isProduction() && !in_array(
$this->showDetailed = (!$this->environment->isProduction() && !in_array(
$this->loggerLevel,
[LogLevel::DEBUG, LogLevel::INFO, LogLevel::NOTICE],
true
@ -80,7 +80,7 @@ class ErrorHandler extends \Slim\Handlers\ErrorHandler
{
$xhr = $req->getHeaderLine('X-Requested-With') === 'XMLHttpRequest';
if ($xhr || $this->settings->isCli() || $this->settings->isTesting()) {
if ($xhr || $this->environment->isCli() || $this->environment->isTesting()) {
return true;
}

View File

@ -17,18 +17,18 @@ class Router implements RouterInterface
{
protected RouteParserInterface $routeParser;
protected Environment $settings;
protected Environment $environment;
protected ?ServerRequestInterface $currentRequest = null;
protected Entity\Repository\SettingsRepository $settingsRepo;
protected Entity\Settings $settings;
public function __construct(
Environment $settings,
Environment $environment,
RouteParserInterface $routeParser,
Entity\Repository\SettingsRepository $settingsRepo
Entity\Settings $settings
) {
$this->settingsRepo = $settingsRepo;
$this->environment = $environment;
$this->settings = $settings;
$this->routeParser = $routeParser;
}
@ -154,7 +154,7 @@ class Router implements RouterInterface
public function getBaseUrl(bool $useRequest = true): UriInterface
{
$settingsBaseUrl = $this->settingsRepo->getSetting(Entity\Settings::BASE_URL, '');
$settingsBaseUrl = $this->settings->getBaseUrl();
if (!empty($settingsBaseUrl)) {
if (strpos($settingsBaseUrl, 'http') !== 0) {
$settingsBaseUrl = 'http://' . $settingsBaseUrl;
@ -165,7 +165,7 @@ class Router implements RouterInterface
$baseUrl = new Uri('');
}
$useHttps = (bool)$this->settingsRepo->getSetting(Entity\Settings::ALWAYS_USE_SSL, 0);
$useHttps = $this->settings->getAlwaysUseSsl();
if ($useRequest && $this->currentRequest instanceof ServerRequestInterface) {
$currentUri = $this->currentRequest->getUri();
@ -174,7 +174,7 @@ class Router implements RouterInterface
$useHttps = true;
}
$preferBrowserUrl = (bool)$this->settingsRepo->getSetting(Entity\Settings::PREFER_BROWSER_URL, 0);
$preferBrowserUrl = $this->settings->getPreferBrowserUrl();
if ($preferBrowserUrl || $baseUrl->getHost() === '') {
$ignoredHosts = ['web', 'nginx', 'localhost'];
if (!in_array($currentUri->getHost(), $ignoredHosts, true)) {

View File

@ -19,18 +19,18 @@ class EnforceSecurity implements MiddlewareInterface
{
protected ResponseFactoryInterface $responseFactory;
protected Entity\Repository\SettingsRepository $settings_repo;
protected Assets $assets;
protected Entity\Settings $settings;
public function __construct(
App $app,
Entity\Repository\SettingsRepository $settings_repo,
Assets $assets
Assets $assets,
Entity\Settings $settings
) {
$this->responseFactory = $app->getResponseFactory();
$this->settings_repo = $settings_repo;
$this->assets = $assets;
$this->settings = $settings;
}
/**
@ -39,7 +39,7 @@ class EnforceSecurity implements MiddlewareInterface
*/
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
{
$always_use_ssl = (bool)$this->settings_repo->getSetting('always_use_ssl', 0);
$always_use_ssl = $this->settings->getAlwaysUseSsl();
$internal_api_url = mb_stripos($request->getUri()->getPath(), '/api/internal') === 0;
// Assemble Content Security Policy (CSP)

View File

@ -19,18 +19,18 @@ class GetCurrentUser implements MiddlewareInterface
{
protected Entity\Repository\UserRepository $userRepo;
protected Entity\Repository\SettingsRepository $settingsRepo;
protected Entity\Settings $settings;
protected Environment $environment;
public function __construct(
Entity\Repository\UserRepository $userRepo,
Entity\Repository\SettingsRepository $settingsRepo,
Environment $environment
Environment $environment,
Entity\Settings $settings
) {
$this->userRepo = $userRepo;
$this->settingsRepo = $settingsRepo;
$this->environment = $environment;
$this->settings = $settings;
}
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
@ -49,7 +49,7 @@ class GetCurrentUser implements MiddlewareInterface
->withAttribute('is_logged_in', (null !== $user));
// Initialize Customization (timezones, locales, etc) based on the current logged in user.
$customization = new Customization($this->settingsRepo, $request);
$customization = new Customization($this->settings, $this->environment, $request);
$request = $request
->withAttribute('locale', $customization->getLocale())

View File

@ -20,14 +20,14 @@ class InjectSession implements MiddlewareInterface
{
protected SessionPersistenceInterface $sessionPersistence;
protected Environment $settings;
protected Environment $environment;
public function __construct(
SessionPersistenceInterface $sessionPersistence,
Environment $settings
Environment $environment
) {
$this->sessionPersistence = $sessionPersistence;
$this->settings = $settings;
$this->environment = $environment;
}
/**
@ -38,7 +38,7 @@ class InjectSession implements MiddlewareInterface
{
$session = new LazySession($this->sessionPersistence, $request);
$csrf = new Csrf($session, $this->settings);
$csrf = new Csrf($session, $this->environment);
Csrf::setInstance($csrf);
$flash = new Flash($session);

View File

@ -16,14 +16,14 @@ class Api
{
protected Entity\Repository\ApiKeyRepository $api_repo;
protected Entity\Repository\SettingsRepository $settings_repo;
protected Entity\Settings $settings;
public function __construct(
Entity\Repository\ApiKeyRepository $apiKeyRepository,
Entity\Repository\SettingsRepository $settingsRepository
Entity\Settings $settings
) {
$this->api_repo = $apiKeyRepository;
$this->settings_repo = $settingsRepository;
$this->settings = $settings;
}
public function __invoke(ServerRequest $request, RequestHandlerInterface $handler): ResponseInterface
@ -40,12 +40,12 @@ class Api
}
// Set default cache control for API pages.
$prefer_browser_url = (bool)$this->settings_repo->getSetting(Entity\Settings::PREFER_BROWSER_URL, 0);
$prefer_browser_url = $this->settings->getPreferBrowserUrl();
$response = $handler->handle($request);
// Check for a user-set CORS header override.
$acao_header = trim($this->settings_repo->getSetting(Entity\Settings::API_ACCESS_CONTROL));
$acao_header = trim($this->settings->getApiAccessControl());
if (!empty($acao_header)) {
if ('*' === $acao_header) {
$response = $response->withHeader('Access-Control-Allow-Origin', '*');
@ -55,7 +55,7 @@ class Api
if (!empty($origin)) {
$rawOrigins = array_map('trim', explode(',', $acao_header));
$rawOrigins[] = $this->settings_repo->getSetting(Entity\Settings::BASE_URL);
$rawOrigins[] = $this->settings->getBaseUrl();
$origins = [];
foreach ($rawOrigins as $rawOrigin) {

View File

@ -15,12 +15,12 @@ class ReopenEntityManagerMiddleware implements MiddlewareInterface
{
protected DecoratedEntityManager $em;
protected Environment $settings;
protected Environment $environment;
public function __construct(DecoratedEntityManager $em, Environment $settings)
public function __construct(DecoratedEntityManager $em, Environment $environment)
{
$this->em = $em;
$this->settings = $settings;
$this->environment = $environment;
}
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
@ -30,7 +30,7 @@ class ReopenEntityManagerMiddleware implements MiddlewareInterface
try {
return $handler->handle($request);
} finally {
if (!$this->settings->isTesting()) {
if (!$this->environment->isTesting()) {
$this->em->getConnection()->close();
}

View File

@ -11,14 +11,14 @@ use Carbon\CarbonImmutable;
class RecentBackupCheck
{
protected Entity\Repository\SettingsRepository $settingsRepo;
protected Entity\Settings $settings;
protected Environment $appSettings;
protected Environment $environment;
public function __construct(Entity\Repository\SettingsRepository $settingsRepo, Environment $appSettings)
public function __construct(Entity\Settings $settings, Environment $environment)
{
$this->settingsRepo = $settingsRepo;
$this->appSettings = $appSettings;
$this->settings = $settings;
$this->environment = $environment;
}
public function __invoke(GetNotifications $event): void
@ -30,19 +30,19 @@ class RecentBackupCheck
return;
}
if (!$this->appSettings->isProduction()) {
if (!$this->environment->isProduction()) {
return;
}
$threshold = CarbonImmutable::now()->subWeeks(2)->getTimestamp();
// Don't show backup warning for freshly created installations.
$setupComplete = (int)$this->settingsRepo->getSetting(Entity\Settings::SETUP_COMPLETE, 0);
$setupComplete = $this->settings->getSetupCompleteTime();
if ($setupComplete >= $threshold) {
return;
}
$backupLastRun = $this->settingsRepo->getSetting(Entity\Settings::BACKUP_LAST_RUN, 0);
$backupLastRun = $this->settings->getBackupLastRun();
if ($backupLastRun < $threshold) {
$router = $request->getRouter();

View File

@ -12,14 +12,14 @@ class SyncTaskCheck
{
protected Runner $syncRunner;
protected Entity\Repository\SettingsRepository $settingsRepo;
protected Entity\Settings $settings;
public function __construct(
Runner $syncRunner,
Entity\Repository\SettingsRepository $settingsRepo
Entity\Settings $settings
) {
$this->syncRunner = $syncRunner;
$this->settingsRepo = $settingsRepo;
$this->settings = $settings;
}
public function __invoke(GetNotifications $event): void
@ -31,7 +31,7 @@ class SyncTaskCheck
return;
}
$setupComplete = (int)$this->settingsRepo->getSetting(Entity\Settings::SETUP_COMPLETE, 0);
$setupComplete = $this->settings->isSetupComplete();
$syncTasks = $this->syncRunner->getSyncTimes();
foreach ($syncTasks as $taskKey => $task) {

View File

@ -10,13 +10,13 @@ use App\Version;
class UpdateCheck
{
protected Entity\Repository\SettingsRepository $settingsRepo;
protected Entity\Settings $settings;
protected Version $version;
public function __construct(Entity\Repository\SettingsRepository $settingsRepo, Version $version)
public function __construct(Entity\Settings $settings, Version $version)
{
$this->settingsRepo = $settingsRepo;
$this->settings = $settings;
$this->version = $version;
}
@ -29,12 +29,12 @@ class UpdateCheck
return;
}
$checkForUpdates = (bool)$this->settingsRepo->getSetting(Entity\Settings::CENTRAL_UPDATES, 1);
$checkForUpdates = $this->settings->getCheckForUpdates();
if (!$checkForUpdates) {
return;
}
$updateData = $this->settingsRepo->getSetting(Entity\Settings::UPDATE_RESULTS);
$updateData = $this->settings->getUpdateResults();
if (empty($updateData)) {
return;
}

View File

@ -27,7 +27,7 @@ abstract class AbstractFrontend extends AbstractAdapter
protected Router $router;
protected Entity\Repository\SettingsRepository $settingsRepo;
protected Entity\Settings $settings;
protected Entity\Repository\StationMountRepository $stationMountRepo;
@ -38,8 +38,8 @@ abstract class AbstractFrontend extends AbstractAdapter
AdapterFactory $adapterFactory,
Client $client,
Router $router,
Entity\Repository\SettingsRepository $settingsRepo,
Entity\Repository\StationMountRepository $stationMountRepo
Entity\Repository\StationMountRepository $stationMountRepo,
Entity\Settings $settings
) {
parent::__construct($em, $supervisor, $dispatcher);
@ -47,8 +47,8 @@ abstract class AbstractFrontend extends AbstractAdapter
$this->http_client = $client;
$this->router = $router;
$this->settingsRepo = $settingsRepo;
$this->stationMountRepo = $stationMountRepo;
$this->settings = $settings;
}
/**
@ -149,7 +149,7 @@ abstract class AbstractFrontend extends AbstractAdapter
$base_url = $this->router->getBaseUrl();
}
$use_radio_proxy = $this->settingsRepo->getSetting('use_radio_proxy', 0);
$use_radio_proxy = $this->settings->getUseRadioProxy();
if (
$use_radio_proxy

View File

@ -106,7 +106,7 @@ class Icecast extends AbstractFrontend
$config_dir = $station->getRadioConfigDir();
$environment = Environment::getInstance();
$settingsBaseUrl = $this->settingsRepo->getSetting(Entity\Settings::BASE_URL, 'http://localhost');
$settingsBaseUrl = $this->settings->getBaseUrl() ?: 'http://localhost';
if (strpos($settingsBaseUrl, 'http') !== 0) {
$settingsBaseUrl = 'http://' . $settingsBaseUrl;
}

View File

@ -15,7 +15,7 @@ abstract class AbstractRemote
{
protected EntityManagerInterface $em;
protected Entity\Repository\SettingsRepository $settingsRepo;
protected Entity\Settings $settings;
protected Client $http_client;
@ -25,13 +25,13 @@ abstract class AbstractRemote
public function __construct(
EntityManagerInterface $em,
Entity\Repository\SettingsRepository $settingsRepo,
Entity\Settings $settings,
Client $http_client,
Logger $logger,
AdapterFactory $adapterFactory
) {
$this->em = $em;
$this->settingsRepo = $settingsRepo;
$this->settings = $settings;
$this->http_client = $http_client;
$this->logger = $logger;
$this->adapterFactory = $adapterFactory;

View File

@ -67,7 +67,7 @@ class AzuraRelay extends AbstractRemote
$fe_config = $station->getFrontendConfig();
$radio_port = $fe_config->getPort();
$use_radio_proxy = $this->settingsRepo->getSetting('use_radio_proxy', 0);
$use_radio_proxy = $this->settings->getUseRadioProxy();
if (
$use_radio_proxy

View File

@ -9,12 +9,12 @@ class RateLimit
{
protected Redis $redis;
protected Environment $settings;
protected Environment $environment;
public function __construct(Redis $redis, Environment $settings)
public function __construct(Redis $redis, Environment $environment)
{
$this->redis = $redis;
$this->settings = $settings;
$this->environment = $environment;
}
/**
@ -31,7 +31,7 @@ class RateLimit
int $timeout = 5,
int $interval = 2
): bool {
if ($this->settings->isTesting() || $this->settings->isCli()) {
if ($this->environment->isTesting() || $this->environment->isCli()) {
return true;
}

View File

@ -8,6 +8,7 @@ use App\Version;
use Exception;
use GuzzleHttp\Client;
use Psr\Log\LoggerInterface;
use Ramsey\Uuid\Uuid;
class AzuraCastCentral
{
@ -17,24 +18,28 @@ class AzuraCastCentral
protected Client $httpClient;
protected Entity\Repository\SettingsRepository $settingsRepo;
protected Entity\Settings $settings;
protected Entity\Repository\SettingsTableRepository $settingsTableRepo;
protected Version $version;
protected LoggerInterface $logger;
public function __construct(
Entity\Repository\SettingsRepository $settingsRepo,
Environment $environment,
Version $version,
Client $httpClient,
LoggerInterface $logger
LoggerInterface $logger,
Entity\Settings $settings,
Entity\Repository\SettingsTableRepository $settingsTableRepo
) {
$this->settingsRepo = $settingsRepo;
$this->environment = $environment;
$this->version = $version;
$this->httpClient = $httpClient;
$this->logger = $logger;
$this->settings = $settings;
$this->settingsTableRepo = $settingsTableRepo;
}
/**
@ -44,10 +49,8 @@ class AzuraCastCentral
*/
public function checkForUpdates(): ?array
{
$app_uuid = $this->settingsRepo->getUniqueIdentifier();
$request_body = [
'id' => $app_uuid,
'id' => $this->getUniqueIdentifier(),
'is_docker' => $this->environment->isDocker(),
'environment' => $this->environment[Environment::APP_ENV],
'release_channel' => $this->version->getReleaseChannel(),
@ -78,6 +81,20 @@ class AzuraCastCentral
return null;
}
public function getUniqueIdentifier(): string
{
$appUuid = $this->settings->getAppUniqueIdentifier();
if (empty($appUuid)) {
$appUuid = Uuid::uuid4()->toString();
$this->settings->setAppUniqueIdentifier($appUuid);
$this->settingsTableRepo->writeSettings($this->settings);
}
return $appUuid;
}
/**
* Ping the AzuraCast Central server to retrieve this installation's likely public-facing IP.
*
@ -86,7 +103,7 @@ class AzuraCastCentral
public function getIp(bool $cached = true): ?string
{
$ip = ($cached)
? $this->settingsRepo->getSetting(Entity\Settings::EXTERNAL_IP)
? $this->settings->getExternalIp()
: null;
if (empty($ip)) {
@ -106,7 +123,8 @@ class AzuraCastCentral
}
if (!empty($ip) && $cached) {
$this->settingsRepo->setSetting(Entity\Settings::EXTERNAL_IP, $ip);
$this->settings->setExternalIp($ip);
$this->settingsTableRepo->writeSettings($this->settings);
}
}

View File

@ -16,12 +16,12 @@ class Csrf
protected SessionInterface $session;
protected Environment $settings;
protected Environment $environment;
public function __construct(SessionInterface $session, Environment $settings)
public function __construct(SessionInterface $session, Environment $environment)
{
$this->session = $session;
$this->settings = $settings;
$this->environment = $environment;
}
/**
@ -56,7 +56,7 @@ class Csrf
*/
public function verify(string $key, string $namespace = self::DEFAULT_NAMESPACE): void
{
if ($this->settings->isTesting()) {
if ($this->environment->isTesting()) {
return;
}

View File

@ -3,7 +3,7 @@
namespace App\Sync;
use App\Entity;
use App\Entity\Repository\SettingsRepository;
use App\Entity\Repository\SettingsTableRepository;
use App\Environment;
use App\Event\GetSyncTasks;
use App\EventDispatcher;
@ -17,19 +17,23 @@ class Runner
{
protected Logger $logger;
protected SettingsRepository $settingsRepo;
protected Entity\Settings $settings;
protected SettingsTableRepository $settingsTableRepo;
protected LockFactory $lockFactory;
protected EventDispatcher $eventDispatcher;
public function __construct(
SettingsRepository $settingsRepo,
SettingsTableRepository $settingsRepo,
Entity\Settings $settings,
Logger $logger,
LockFactory $lockFactory,
EventDispatcher $eventDispatcher
) {
$this->settingsRepo = $settingsRepo;
$this->settingsTableRepo = $settingsRepo;
$this->settings = $settings;
$this->logger = $logger;
$this->lockFactory = $lockFactory;
$this->eventDispatcher = $eventDispatcher;
@ -38,7 +42,7 @@ class Runner
public function runSyncTask(string $type, bool $force = false): void
{
// Immediately halt if setup is not complete.
if ($this->settingsRepo->getSetting(Entity\Settings::SETUP_COMPLETE, 0) == 0) {
if (!$this->settings->isSetupComplete()) {
die('Setup not complete; halting synchronized task.');
}
@ -98,7 +102,8 @@ class Runner
));
}
$this->settingsRepo->setSetting($syncInfo['lastRunSetting'], time());
$this->settings->updateSyncLastRunTime($type);
$this->settingsTableRepo->writeSettings($this->settings);
} finally {
$lock->release();
}
@ -109,8 +114,6 @@ class Runner
*/
public function getSyncTimes(): array
{
$this->settingsRepo->clearCache();
$shortTaskTimeout = $_ENV['SYNC_SHORT_EXECUTION_TIME'] ?? 600;
$longTaskTimeout = $_ENV['SYNC_LONG_EXECUTION_TIME'] ?? 1800;
@ -120,7 +123,6 @@ class Runner
'contents' => [
__('Now Playing Data'),
],
'lastRunSetting' => Entity\Settings::NOWPLAYING_LAST_RUN,
'timeout' => $shortTaskTimeout,
'interval' => 15,
],
@ -129,7 +131,6 @@ class Runner
'contents' => [
__('Song Requests Queue'),
],
'lastRunSetting' => Entity\Settings::SHORT_SYNC_LAST_RUN,
'timeout' => $shortTaskTimeout,
'interval' => 60,
],
@ -138,7 +139,6 @@ class Runner
'contents' => [
__('Check Media Folders'),
],
'lastRunSetting' => Entity\Settings::MEDIUM_SYNC_LAST_RUN,
'timeout' => $shortTaskTimeout,
'interval' => 300,
],
@ -148,14 +148,13 @@ class Runner
__('Analytics/Statistics'),
__('Cleanup'),
],
'lastRunSetting' => Entity\Settings::LONG_SYNC_LAST_RUN,
'timeout' => $longTaskTimeout,
'interval' => 3600,
],
];
foreach ($syncs as &$sync_info) {
$sync_info['latest'] = $this->settingsRepo->getSetting($sync_info['lastRunSetting'], 0);
foreach ($syncs as $task => &$sync_info) {
$sync_info['latest'] = $this->settings->getSyncLastRunTime($task);
$sync_info['diff'] = time() - $sync_info['latest'];
}

View File

@ -10,18 +10,18 @@ abstract class AbstractTask
{
protected EntityManagerInterface $em;
protected Entity\Repository\SettingsRepository $settingsRepo;
protected LoggerInterface $logger;
protected Entity\Settings $settings;
public function __construct(
EntityManagerInterface $em,
Entity\Repository\SettingsRepository $settingsRepo,
LoggerInterface $logger
LoggerInterface $logger,
Entity\Settings $settings
) {
$this->em = $em;
$this->settingsRepo = $settingsRepo;
$this->logger = $logger;
$this->settings = $settings;
}
abstract public function run(bool $force = false): void;

View File

@ -16,12 +16,12 @@ class BuildQueueTask extends AbstractTask
public function __construct(
EntityManagerInterface $em,
Entity\Repository\SettingsRepository $settingsRepo,
LoggerInterface $logger,
Entity\Settings $settings,
AutoDJ $autoDJ,
LockFactory $lockFactory
) {
parent::__construct($em, $settingsRepo, $logger);
parent::__construct($em, $logger, $settings);
$this->autoDJ = $autoDJ;
$this->lockFactory = $lockFactory;

View File

@ -18,13 +18,13 @@ class CheckFolderPlaylistsTask extends AbstractTask
public function __construct(
EntityManagerInterface $em,
Entity\Repository\SettingsRepository $settingsRepo,
LoggerInterface $logger,
Entity\Settings $settings,
Entity\Repository\StationPlaylistMediaRepository $spmRepo,
Entity\Repository\StationPlaylistFolderRepository $folderRepo,
FilesystemManager $filesystem
) {
parent::__construct($em, $settingsRepo, $logger);
parent::__construct($em, $logger, $settings);
$this->spmRepo = $spmRepo;
$this->folderRepo = $folderRepo;

View File

@ -31,15 +31,15 @@ class CheckMediaTask extends AbstractTask
public function __construct(
EntityManagerInterface $em,
Entity\Repository\SettingsRepository $settingsRepo,
LoggerInterface $logger,
Entity\Settings $settings,
Entity\Repository\StationMediaRepository $mediaRepo,
Entity\Repository\StorageLocationRepository $storageLocationRepo,
FilesystemManager $filesystem,
MessageBus $messageBus,
QueueManager $queueManager
) {
parent::__construct($em, $settingsRepo, $logger);
parent::__construct($em, $logger, $settings);
$this->storageLocationRepo = $storageLocationRepo;
$this->mediaRepo = $mediaRepo;

View File

@ -20,13 +20,13 @@ class CheckRequests extends AbstractTask
public function __construct(
EntityManagerInterface $em,
Entity\Repository\SettingsRepository $settingsRepo,
LoggerInterface $logger,
Entity\Settings $settings,
Entity\Repository\StationRequestRepository $requestRepo,
Adapters $adapters,
EventDispatcher $dispatcher
) {
parent::__construct($em, $settingsRepo, $logger);
parent::__construct($em, $logger, $settings);
$this->requestRepo = $requestRepo;
$this->dispatcher = $dispatcher;

View File

@ -15,21 +15,25 @@ class CheckUpdatesTask extends AbstractTask
protected AzuraCastCentral $azuracastCentral;
protected Entity\Repository\SettingsTableRepository $settingsTableRepo;
public function __construct(
EntityManagerInterface $em,
Entity\Repository\SettingsRepository $settingsRepo,
LoggerInterface $logger,
Entity\Settings $settings,
Entity\Repository\SettingsTableRepository $settingsTableRepo,
AzuraCastCentral $azuracastCentral
) {
parent::__construct($em, $settingsRepo, $logger);
parent::__construct($em, $logger, $settings);
$this->settingsTableRepo = $settingsTableRepo;
$this->azuracastCentral = $azuracastCentral;
}
public function run(bool $force = false): void
{
if (!$force) {
$update_last_run = (int)$this->settingsRepo->getSetting(Entity\Settings::UPDATE_LAST_RUN, 0);
$update_last_run = $this->settings->getUpdateLastRun();
if ($update_last_run > (time() - self::UPDATE_THRESHOLD)) {
$this->logger->debug('Not checking for updates; checked too recently.');
@ -46,7 +50,9 @@ class CheckUpdatesTask extends AbstractTask
$updates = $this->azuracastCentral->checkForUpdates();
if (!empty($updates)) {
$this->settingsRepo->setSetting(Entity\Settings::UPDATE_RESULTS, $updates);
$this->settings->setUpdateResults($updates);
$this->settingsTableRepo->writeSettings($this->settings);
$this->logger->info('Successfully checked for updates.', ['results' => $updates]);
} else {
$this->logger->error('Error parsing update data response from AzuraCast central.');
@ -56,6 +62,7 @@ class CheckUpdatesTask extends AbstractTask
return;
}
$this->settingsRepo->setSetting(Entity\Settings::UPDATE_LAST_RUN, time());
$this->settings->updateUpdateLastRun();
$this->settingsTableRepo->writeSettings($this->settings);
}
}

View File

@ -14,12 +14,12 @@ class CleanupHistoryTask extends AbstractTask
public function __construct(
EntityManagerInterface $em,
Entity\Repository\SettingsRepository $settingsRepo,
LoggerInterface $logger,
Entity\Settings $settings,
Entity\Repository\SongHistoryRepository $historyRepo,
Entity\Repository\ListenerRepository $listenerRepo
) {
parent::__construct($em, $settingsRepo, $logger);
parent::__construct($em, $logger, $settings);
$this->historyRepo = $historyRepo;
$this->listenerRepo = $listenerRepo;
@ -27,10 +27,7 @@ class CleanupHistoryTask extends AbstractTask
public function run(bool $force = false): void
{
$daysToKeep = (int)$this->settingsRepo->getSetting(
Entity\Settings::HISTORY_KEEP_DAYS,
Entity\SongHistory::DEFAULT_DAYS_TO_KEEP
);
$daysToKeep = $this->settings->getHistoryKeepDays();
if ($daysToKeep !== 0) {
$this->historyRepo->cleanup($daysToKeep);

View File

@ -15,11 +15,11 @@ class CleanupStorageTask extends AbstractTask
public function __construct(
EntityManagerInterface $em,
Entity\Repository\SettingsRepository $settingsRepo,
LoggerInterface $logger,
Entity\Settings $settings,
FilesystemManager $filesystem
) {
parent::__construct($em, $settingsRepo, $logger);
parent::__construct($em, $logger, $settings);
$this->filesystem = $filesystem;
}

View File

@ -41,6 +41,8 @@ class NowPlayingTask extends AbstractTask implements EventSubscriberInterface
protected Entity\Repository\ListenerRepository $listenerRepo;
protected Entity\Repository\SettingsTableRepository $settingsTableRepo;
protected LockFactory $lockFactory;
protected Entity\ApiGenerator\NowPlayingApiGenerator $nowPlayingApiGenerator;
@ -51,8 +53,8 @@ class NowPlayingTask extends AbstractTask implements EventSubscriberInterface
public function __construct(
EntityManagerInterface $em,
Entity\Repository\SettingsRepository $settingsRepository,
LoggerInterface $logger,
Entity\Settings $settings,
Adapters $adapters,
AutoDJ $autodj,
CacheInterface $cache,
@ -62,9 +64,10 @@ class NowPlayingTask extends AbstractTask implements EventSubscriberInterface
RouterInterface $router,
Entity\Repository\ListenerRepository $listenerRepository,
Entity\Repository\StationQueueRepository $queueRepo,
Entity\Repository\SettingsTableRepository $settingsTableRepo,
Entity\ApiGenerator\NowPlayingApiGenerator $nowPlayingApiGenerator
) {
parent::__construct($em, $settingsRepository, $logger);
parent::__construct($em, $logger, $settings);
$this->adapters = $adapters;
$this->autodj = $autodj;
@ -76,10 +79,11 @@ class NowPlayingTask extends AbstractTask implements EventSubscriberInterface
$this->listenerRepo = $listenerRepository;
$this->queueRepo = $queueRepo;
$this->settingsTableRepo = $settingsTableRepo;
$this->nowPlayingApiGenerator = $nowPlayingApiGenerator;
$this->analyticsLevel = (string)$settingsRepository->getSetting('analytics', Entity\Analytics::LEVEL_ALL);
$this->analyticsLevel = $settings->getAnalytics();
}
/**
@ -103,8 +107,10 @@ class NowPlayingTask extends AbstractTask implements EventSubscriberInterface
{
$nowplaying = $this->loadNowPlaying($force);
$this->cache->set(Entity\Settings::NOWPLAYING, $nowplaying, 120);
$this->settingsRepo->setSetting(Entity\Settings::NOWPLAYING, $nowplaying);
$this->cache->set('nowplaying', $nowplaying, 120);
$this->settings->setNowplaying($nowplaying);
$this->settingsTableRepo->writeSettings($this->settings);
}
/**

View File

@ -12,11 +12,11 @@ class ReactivateStreamerTask extends AbstractTask
public function __construct(
EntityManagerInterface $em,
Entity\Repository\SettingsRepository $settingsRepo,
LoggerInterface $logger,
Entity\Settings $settings,
Entity\Repository\StationStreamerRepository $streamerRepo
) {
parent::__construct($em, $settingsRepo, $logger);
parent::__construct($em, $logger, $settings);
$this->streamerRepo = $streamerRepo;
}

View File

@ -13,7 +13,7 @@ use Symfony\Component\Finder\Finder;
class RotateLogsTask extends AbstractTask
{
protected Environment $appSettings;
protected Environment $environment;
protected Adapters $adapters;
@ -23,16 +23,16 @@ class RotateLogsTask extends AbstractTask
public function __construct(
EntityManagerInterface $em,
Entity\Repository\SettingsRepository $settingsRepo,
LoggerInterface $logger,
Environment $appSettings,
Entity\Settings $settings,
Environment $environment,
Adapters $adapters,
Supervisor $supervisor,
Entity\Repository\StorageLocationRepository $storageLocationRepo
) {
parent::__construct($em, $settingsRepo, $logger);
parent::__construct($em, $logger, $settings);
$this->appSettings = $appSettings;
$this->environment = $environment;
$this->adapters = $adapters;
$this->supervisor = $supervisor;
$this->storageLocationRepo = $storageLocationRepo;
@ -57,19 +57,16 @@ class RotateLogsTask extends AbstractTask
}
// Rotate the main AzuraCast log.
$rotate = new Rotate\Rotate($this->appSettings->getTempDirectory() . '/app.log');
$rotate = new Rotate\Rotate($this->environment->getTempDirectory() . '/app.log');
$rotate->keep(5);
$rotate->size('5MB');
$rotate->run();
// Rotate the automated backups.
$backups_to_keep = (int)$this->settingsRepo->getSetting(Entity\Settings::BACKUP_KEEP_COPIES, 0);
$backups_to_keep = $this->settings->getBackupKeepCopies();
if ($backups_to_keep > 0) {
$backupStorageId = (int)$this->settingsRepo->getSetting(
Entity\Settings::BACKUP_STORAGE_LOCATION,
null
);
$backupStorageId = (int)$this->settings->getBackupStorageLocation();
if ($backupStorageId > 0) {
$storageLocation = $this->storageLocationRepo->findByType(

View File

@ -17,13 +17,13 @@ class RunAnalyticsTask extends AbstractTask
public function __construct(
EntityManagerInterface $em,
Entity\Repository\SettingsRepository $settingsRepo,
LoggerInterface $logger,
Entity\Settings $settings,
Entity\Repository\AnalyticsRepository $analyticsRepo,
Entity\Repository\ListenerRepository $listenerRepo,
Entity\Repository\SongHistoryRepository $historyRepo
) {
parent::__construct($em, $settingsRepo, $logger);
parent::__construct($em, $logger, $settings);
$this->analyticsRepo = $analyticsRepo;
$this->listenerRepo = $listenerRepo;
@ -32,7 +32,7 @@ class RunAnalyticsTask extends AbstractTask
public function run(bool $force = false): void
{
$analytics_level = $this->settingsRepo->getSetting('analytics', Entity\Analytics::LEVEL_ALL);
$analytics_level = $this->settings->getAnalytics();
switch ($analytics_level) {
case Entity\Analytics::LEVEL_NONE:

View File

@ -20,12 +20,12 @@ class RunAutomatedAssignmentTask extends AbstractTask
public function __construct(
EntityManagerInterface $em,
Entity\Repository\SettingsRepository $settingsRepo,
LoggerInterface $logger,
Entity\Settings $settings,
Entity\Repository\StationMediaRepository $mediaRepo,
Adapters $adapters
) {
parent::__construct($em, $settingsRepo, $logger);
parent::__construct($em, $logger, $settings);
$this->mediaRepo = $mediaRepo;
$this->adapters = $adapters;

View File

@ -19,17 +19,21 @@ class RunBackupTask extends AbstractTask
protected Application $console;
protected Entity\Repository\SettingsTableRepository $settingsTableRepo;
public function __construct(
EntityManagerInterface $em,
Entity\Repository\SettingsRepository $settingsRepo,
LoggerInterface $logger,
Entity\Settings $settings,
MessageBus $messageBus,
Application $console
Application $console,
Entity\Repository\SettingsTableRepository $settingsTableRepo
) {
parent::__construct($em, $settingsRepo, $logger);
parent::__construct($em, $logger, $settings);
$this->messageBus = $messageBus;
$this->console = $console;
$this->settingsTableRepo = $settingsTableRepo;
}
/**
@ -40,7 +44,8 @@ class RunBackupTask extends AbstractTask
public function __invoke(Message\AbstractMessage $message): void
{
if ($message instanceof Message\BackupMessage) {
$this->settingsRepo->setSetting(Entity\Settings::BACKUP_LAST_RUN, time());
$this->settings->updateBackupLastRun();
$this->settingsTableRepo->writeSettings($this->settings);
[$result_code, $result_output] = $this->runBackup(
$message->path,
@ -49,8 +54,9 @@ class RunBackupTask extends AbstractTask
$message->storageLocationId
);
$this->settingsRepo->setSetting(Entity\Settings::BACKUP_LAST_RESULT, $result_code);
$this->settingsRepo->setSetting(Entity\Settings::BACKUP_LAST_OUTPUT, $result_output);
$this->settings->setBackupLastResult($result_code);
$this->settings->setBackupLastOutput($result_output);
$this->settingsTableRepo->writeSettings($this->settings);
}
}
@ -88,7 +94,7 @@ class RunBackupTask extends AbstractTask
public function run(bool $force = false): void
{
$backup_enabled = (bool)$this->settingsRepo->getSetting(Entity\Settings::BACKUP_ENABLED, false);
$backup_enabled = $this->settings->isBackupEnabled();
if (!$backup_enabled) {
$this->logger->debug('Automated backups disabled; skipping...');
return;
@ -97,11 +103,11 @@ class RunBackupTask extends AbstractTask
$now_utc = CarbonImmutable::now('UTC');
$threshold = $now_utc->subDay()->getTimestamp();
$last_run = $this->settingsRepo->getSetting(Entity\Settings::BACKUP_LAST_RUN, 0);
$last_run = $this->settings->getBackupLastRun();
if ($last_run <= $threshold) {
// Check if the backup time matches (if it's set).
$backupTimecode = $this->settingsRepo->getSetting(Entity\Settings::BACKUP_TIME, null);
$backupTimecode = $this->settings->getBackupTimeCode();
if (null !== $backupTimecode && '' !== $backupTimecode) {
$isWithinTimecode = false;
@ -128,10 +134,7 @@ class RunBackupTask extends AbstractTask
}
// Trigger a new backup.
$storageLocationId = (int)$this->settingsRepo->getSetting(
Entity\Settings::BACKUP_STORAGE_LOCATION,
0
);
$storageLocationId = (int)($this->settings->getBackupStorageLocation() ?? 0);
if ($storageLocationId <= 0) {
$storageLocationId = null;
}
@ -139,7 +142,7 @@ class RunBackupTask extends AbstractTask
$message = new Message\BackupMessage();
$message->storageLocationId = $storageLocationId;
$message->path = 'automatic_backup.zip';
$message->excludeMedia = (bool)$this->settingsRepo->getSetting(Entity\Settings::BACKUP_EXCLUDE_MEDIA, 0);
$message->excludeMedia = $this->settings->getBackupExcludeMedia();
$this->messageBus->dispatch($message);
}

View File

@ -21,23 +21,27 @@ class UpdateGeoLiteTask extends AbstractTask
protected IpGeolocation $geoLite;
protected Entity\Repository\SettingsTableRepository $settingsTableRepo;
public function __construct(
EntityManagerInterface $em,
Entity\Repository\SettingsRepository $settingsRepo,
LoggerInterface $logger,
Entity\Settings $settings,
Client $httpClient,
IpGeolocation $geoLite
IpGeolocation $geoLite,
Entity\Repository\SettingsTableRepository $settingsTableRepo
) {
parent::__construct($em, $settingsRepo, $logger);
parent::__construct($em, $logger, $settings);
$this->httpClient = $httpClient;
$this->geoLite = $geoLite;
$this->settingsTableRepo = $settingsTableRepo;
}
public function run(bool $force = false): void
{
if (!$force) {
$lastRun = (int)$this->settingsRepo->getSetting(Entity\Settings::GEOLITE_LAST_RUN, 0);
$lastRun = $this->settings->getGeoliteLastRun();
if ($lastRun > (time() - self::UPDATE_THRESHOLD)) {
$this->logger->debug('Not checking for updates; checked too recently.');
@ -55,12 +59,13 @@ class UpdateGeoLiteTask extends AbstractTask
]);
}
$this->settingsRepo->setSetting(Entity\Settings::GEOLITE_LAST_RUN, time());
$this->settings->updateGeoliteLastRun();
$this->settingsTableRepo->writeSettings($this->settings);
}
public function updateDatabase(): void
{
$licenseKey = trim($this->settingsRepo->getSetting(Entity\Settings::GEOLITE_LICENSE_KEY));
$licenseKey = trim($this->settings->getGeoliteLicenseKey());
if (empty($licenseKey)) {
$this->logger->info('Not checking for GeoLite updates; no license key provided.');

View File

@ -15,6 +15,13 @@ trait AvailableStaticallyTrait
return self::$instance;
}
/**
*/
public static function hasInstance(): bool
{
return isset(self::$instance);
}
/**
* @param static $instance
*/

View File

@ -23,18 +23,22 @@ class LocalWebhookHandler
protected CacheInterface $cache;
protected Entity\Repository\SettingsRepository $settingsRepo;
protected Entity\Settings $settings;
protected Entity\Repository\SettingsTableRepository $settingsTableRepo;
public function __construct(
Logger $logger,
Client $httpClient,
CacheInterface $cache,
Entity\Repository\SettingsRepository $settingsRepo
Entity\Settings $settings,
Entity\Repository\SettingsTableRepository $settingsTableRepo
) {
$this->logger = $logger;
$this->httpClient = $httpClient;
$this->cache = $cache;
$this->settingsRepo = $settingsRepo;
$this->settings = $settings;
$this->settingsTableRepo = $settingsTableRepo;
}
public function dispatch(SendWebhooks $event): void
@ -46,7 +50,7 @@ class LocalWebhookHandler
// Replace the relevant station information in the cache and database.
$this->logger->debug('Updating NowPlaying cache...');
$np_full = $this->cache->get('api_nowplaying_data');
$np_full = $this->cache->get('nowplaying');
if ($np_full) {
$np_new = [];
@ -59,8 +63,10 @@ class LocalWebhookHandler
}
}
$this->cache->set('api_nowplaying_data', $np_new, 120);
$this->settingsRepo->setSetting('nowplaying', $np_new);
$this->cache->set('nowplaying', $np_new, 120);
$this->settings->setNowplaying($np_new);
$this->settingsTableRepo->writeSettings($this->settings);
}
}

View File

@ -14,7 +14,7 @@ $this->layout('minimal', [
<?php if ($customization->hideProductName()): ?>
<?=__('Welcome!')?>
<?php else: ?>
<?=sprintf(__('Welcome to %s!'), $settings['name'])?>
<?=sprintf(__('Welcome to %s!'), $environment['name'])?>
<?php endif; ?>
</h2>
<h3 class="text-center">

View File

@ -153,7 +153,7 @@ $user = $request->getUser();
<footer id="footer" <?php if (empty($sidebar)): ?>class="footer-alt"<?php endif; ?> role="contentinfo">
<?=__('Powered by %s',
'<a href="https://www.azuracast.com/" target="_blank">' . $settings['name'] . '</a> &bull; ' . $version->getVersionText() . ' &bull; ' . ($environment->isDocker() ? 'Docker' : 'Ansible') . ' &bull; PHP ' . \PHP_MAJOR_VERSION . '.' . \PHP_MINOR_VERSION)?>
'<a href="https://www.azuracast.com/" target="_blank">' . $environment['name'] . '</a> &bull; ' . $version->getVersionText() . ' &bull; ' . ($environment->isDocker() ? 'Docker' : 'Ansible') . ' &bull; PHP ' . \PHP_MAJOR_VERSION . '.' . \PHP_MINOR_VERSION)?>
<br>
<?=__('Like our software? <a href="%s" target="_blank">Donate to support AzuraCast!</a>',
'https://www.azuracast.com/donate.html')?>

View File

@ -46,7 +46,7 @@ $page_class .= ' theme-' . $customization->getPublicTheme();
<?php if (!$customization->hideProductName() && !$hide_footer): ?>
<footer id="footer" class="footer-alt" role="contentinfo">
<?=sprintf(__('Powered by %s'),
'<a href="https://azuracast.com/" target="_blank">' . $settings['name'] . '</a>' . ' ')?><br>
'<a href="https://azuracast.com/" target="_blank">' . $environment['name'] . '</a>' . ' ')?><br>
<?=sprintf(__('Mascot designed by %s'),
'<a href="https://tysontan.deviantart.com/" target="_blank">Tyson Tan</a>')?>
</footer>

View File

@ -5,7 +5,7 @@ $props = [
'locale' => substr($customization->getLocale(), 0, 2),
'filesUrl' => $router->fromHere('stations:files:index'),
'stationTimeZone' => $station_tz,
'enableAdvancedFeatures' => $settings->enableAdvancedFeatures(),
'enableAdvancedFeatures' => $environment->enableAdvancedFeatures(),
]
?>
var station_playlists;
@ -19,4 +19,4 @@ $(function () {
});
}
});
});
});

View File

@ -10,7 +10,7 @@ abstract class CestAbstract
protected App\Environment $environment;
protected Entity\Repository\SettingsRepository $settingsRepo;
protected Entity\Repository\SettingsTableRepository $settingsTableRepo;
protected Entity\Repository\StationRepository $stationRepo;
@ -25,7 +25,7 @@ abstract class CestAbstract
$this->di = $tests_module->container;
$this->em = $tests_module->em;
$this->settingsRepo = $this->di->get(Entity\Repository\SettingsRepository::class);
$this->settingsTableRepo = $this->di->get(Entity\Repository\SettingsTableRepository::class);
$this->stationRepo = $this->di->get(Entity\Repository\StationRepository::class);
$this->environment = $this->di->get(App\Environment::class);
}
@ -48,7 +48,10 @@ abstract class CestAbstract
{
$I->wantTo('Start with an incomplete setup.');
$this->settingsRepo->setSetting('setup_complete', 0);
$settings = $this->settingsTableRepo->readSettings(false);
$settings->setSetupCompleteTime(0);
$this->settingsTableRepo->writeSettings($settings);
$this->_cleanTables();
}
@ -89,8 +92,10 @@ abstract class CestAbstract
$this->test_station = $this->stationRepo->create($test_station);
// Set settings.
$this->settingsRepo->setSetting('setup_complete', time());
$this->settingsRepo->setSetting('base_url', 'localhost');
$settings = $this->settingsTableRepo->readSettings(false);
$settings->updateSetupComplete();
$settings->setBaseUrl('localhost');
$this->settingsTableRepo->writeSettings($settings);
}
protected function getTestStation(): Entity\Station