Move port checker to Validation; create StationCloneForm, etc.
This commit is contained in:
parent
c735b07a67
commit
3554d65c75
|
@ -157,12 +157,12 @@
|
|||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/AzuraCast/azuraforms.git",
|
||||
"reference": "2731102fcf407ebc45c5efdd6278d5a1952f43bd"
|
||||
"reference": "3cbaf8d771f217111f91a01f7082a39ed279190d"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/AzuraCast/azuraforms/zipball/2731102fcf407ebc45c5efdd6278d5a1952f43bd",
|
||||
"reference": "2731102fcf407ebc45c5efdd6278d5a1952f43bd",
|
||||
"url": "https://api.github.com/repos/AzuraCast/azuraforms/zipball/3cbaf8d771f217111f91a01f7082a39ed279190d",
|
||||
"reference": "3cbaf8d771f217111f91a01f7082a39ed279190d",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
@ -200,7 +200,7 @@
|
|||
],
|
||||
"description": "A modern, namespaced, configuration-driven forms engine for PHP.",
|
||||
"homepage": "https://github.com/AzuraCast/azuraforms",
|
||||
"time": "2019-03-04T19:59:02+00:00"
|
||||
"time": "2019-04-09T08:54:02+00:00"
|
||||
},
|
||||
{
|
||||
"name": "azuracast/nowplaying",
|
||||
|
|
|
@ -120,7 +120,6 @@ return [
|
|||
'label_class' => 'advanced',
|
||||
'description' => __('No other program can be using this port. Leave blank to automatically assign a port.'),
|
||||
'belongsTo' => 'frontend_config',
|
||||
'class' => 'input-port',
|
||||
]
|
||||
],
|
||||
|
||||
|
@ -292,7 +291,6 @@ return [
|
|||
'label_class' => 'advanced',
|
||||
'description' => __('No other program can be using this port. Leave blank to automatically assign a port.<br><b>Note:</b> The port after this one (n+1) will automatically be used for legacy connections.'),
|
||||
'belongsTo' => 'backend_config',
|
||||
'class' => 'input-port',
|
||||
]
|
||||
],
|
||||
|
||||
|
@ -343,7 +341,6 @@ return [
|
|||
'label_class' => 'advanced',
|
||||
'description' => __('This port is not used by any external process. Only modify this port if the assigned port is in use. Leave blank to automatically assign a port.'),
|
||||
'belongsTo' => 'backend_config',
|
||||
'class' => 'input-port',
|
||||
]
|
||||
],
|
||||
|
||||
|
|
|
@ -106,7 +106,7 @@ return function(App $app)
|
|||
$this->map(['GET', 'POST'], '/add', Controller\Admin\StationsController::class.':editAction')
|
||||
->setName('admin:stations:add');
|
||||
|
||||
$this->map(['GET', 'POST'], '/clone/{id}', Controller\Admin\Stations\CloneController::class)
|
||||
$this->map(['GET', 'POST'], '/clone/{id}', Controller\Admin\StationsController::class.':cloneAction')
|
||||
->setName('admin:stations:clone');
|
||||
|
||||
$this->get('/delete/{id}/{csrf}', Controller\Admin\StationsController::class.':deleteAction')
|
||||
|
|
|
@ -64,7 +64,8 @@ return function (\Azura\Container $di)
|
|||
$di[\App\Sync\Task\Media::class],
|
||||
$di[\App\Radio\Adapters::class],
|
||||
$di[\App\Radio\Configuration::class],
|
||||
$di[\Azura\Cache::class]
|
||||
$di[\Azura\Cache::class],
|
||||
$di[\Symfony\Component\Validator\Validator\ValidatorInterface::class]
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -280,6 +281,12 @@ return function (\Azura\Container $di)
|
|||
);
|
||||
};
|
||||
|
||||
$di[\App\Validator\Constraints\StationPortCheckerValidator::class] = function($di) {
|
||||
return new \App\Validator\Constraints\StationPortCheckerValidator(
|
||||
$di[\App\Radio\Configuration::class]
|
||||
);
|
||||
};
|
||||
|
||||
// Radio management
|
||||
$di->register(new \App\Provider\RadioProvider);
|
||||
|
||||
|
|
|
@ -1,152 +0,0 @@
|
|||
<?php
|
||||
namespace App\Controller\Admin\Stations;
|
||||
|
||||
use Azura\Cache;
|
||||
use App\Radio\Configuration;
|
||||
use Doctrine\ORM\EntityManager;
|
||||
use App\Entity;
|
||||
use App\Http\Request;
|
||||
use App\Http\Response;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
|
||||
class CloneController
|
||||
{
|
||||
/** @var EntityManager */
|
||||
protected $em;
|
||||
|
||||
/** @var Cache */
|
||||
protected $cache;
|
||||
|
||||
/** @var Configuration */
|
||||
protected $configuration;
|
||||
|
||||
/** @var array */
|
||||
protected $form_config;
|
||||
|
||||
/** @var Entity\Repository\StationRepository */
|
||||
protected $record_repo;
|
||||
|
||||
/**
|
||||
* @param EntityManager $em
|
||||
* @param Cache $cache
|
||||
* @param Configuration $configuration
|
||||
* @param array $form_config
|
||||
*
|
||||
* @see \App\Provider\AdminProvider
|
||||
*/
|
||||
public function __construct(EntityManager $em, Cache $cache, Configuration $configuration, array $form_config)
|
||||
{
|
||||
$this->em = $em;
|
||||
$this->cache = $cache;
|
||||
$this->configuration = $configuration;
|
||||
$this->form_config = $form_config;
|
||||
|
||||
$this->record_repo = $em->getRepository(Entity\Station::class);
|
||||
}
|
||||
|
||||
public function __invoke(Request $request, Response $response, $id): ResponseInterface
|
||||
{
|
||||
$record = $this->record_repo->find((int)$id);
|
||||
|
||||
if (!($record instanceof Entity\Station)) {
|
||||
throw new \App\Exception\NotFound(__('%s not found.', __('Station')));
|
||||
}
|
||||
|
||||
$form = new \AzuraForms\Form($this->form_config);
|
||||
|
||||
$form->populate([
|
||||
'name' => $record->getName().' - Copy',
|
||||
'description' => $record->getDescription(),
|
||||
]);
|
||||
|
||||
if ($_POST && $form->isValid($_POST)) {
|
||||
$data = $form->getValues();
|
||||
|
||||
// Assemble new station from old station based on form parameters.
|
||||
$new_record_data = $this->record_repo->toArray($record);
|
||||
$new_record_data['name'] = $data['name'];
|
||||
$new_record_data['description'] = $data['description'];
|
||||
|
||||
$unset_values = [
|
||||
'short_name',
|
||||
'radio_base_dir',
|
||||
'nowplaying',
|
||||
'nowplaying_timestamp',
|
||||
'is_streamer_live',
|
||||
'needs_restart',
|
||||
'has_started',
|
||||
];
|
||||
|
||||
foreach($unset_values as $unset_value) {
|
||||
unset($new_record_data[$unset_value]);
|
||||
}
|
||||
|
||||
if ($data['clone_media'] === 'share') {
|
||||
$new_record_data['radio_media_dir'] = $record->getRadioMediaDir();
|
||||
} else {
|
||||
unset($new_record_data['radio_media_dir'], $new_record_data['storage_used']);
|
||||
}
|
||||
|
||||
// Trigger normal creation process of station.
|
||||
$new_record = $this->record_repo->create($new_record_data);
|
||||
|
||||
// Force port reassignment
|
||||
$this->configuration->assignRadioPorts($new_record, true);
|
||||
|
||||
$this->configuration->writeConfiguration($new_record);
|
||||
|
||||
// Copy associated records if applicable.
|
||||
if ($data['clone_media'] === 'copy') {
|
||||
copy($record->getRadioMediaDir(), $new_record->getRadioMediaDir());
|
||||
}
|
||||
|
||||
if ($data['clone_playlists'] == 1) {
|
||||
foreach ($record->getPlaylists() as $source_record) {
|
||||
$dest_record_data = $this->record_repo->toArray($source_record);
|
||||
unset($dest_record_data['id'], $dest_record_data['station_id']);
|
||||
|
||||
$dest_record = new Entity\StationPlaylist($new_record);
|
||||
$this->record_repo->fromArray($dest_record, $dest_record_data);
|
||||
$this->em->persist($dest_record);
|
||||
}
|
||||
}
|
||||
|
||||
if ($data['clone_streamers'] == 1) {
|
||||
foreach ($record->getStreamers() as $source_record) {
|
||||
$dest_record_data = $this->record_repo->toArray($source_record);
|
||||
unset($dest_record_data['id'], $dest_record_data['station_id']);
|
||||
|
||||
$dest_record = new Entity\StationStreamer($new_record);
|
||||
$this->record_repo->fromArray($dest_record, $dest_record_data);
|
||||
$this->em->persist($dest_record);
|
||||
}
|
||||
}
|
||||
|
||||
if ($data['clone_permissions'] == 1) {
|
||||
foreach ($record->getPermissions() as $source_record) {
|
||||
$dest_record_data = $this->record_repo->toArray($source_record);
|
||||
unset($dest_record_data['id'], $dest_record_data['station_id']);
|
||||
|
||||
$dest_record = new Entity\RolePermission($source_record->getRole(), $new_record);
|
||||
$this->record_repo->fromArray($dest_record, $dest_record_data);
|
||||
$this->em->persist($dest_record);
|
||||
}
|
||||
}
|
||||
|
||||
$this->em->flush();
|
||||
|
||||
// Clear station cache.
|
||||
$this->cache->remove('stations');
|
||||
|
||||
$request->getSession()->flash(__('Changes saved.'), 'green');
|
||||
|
||||
return $response->withRedirect($request->getRouter()->named('admin:stations:index'));
|
||||
}
|
||||
|
||||
return $request->getView()->renderToResponse($response, 'system/form_page', [
|
||||
'form' => $form,
|
||||
'render_mode' => 'edit',
|
||||
'title' => __('Clone Station: %s', $record->getName())
|
||||
]);
|
||||
}
|
||||
}
|
|
@ -15,6 +15,9 @@ class StationsController
|
|||
/** @var Form\StationForm */
|
||||
protected $station_form;
|
||||
|
||||
/** @var Form\StationCloneForm */
|
||||
protected $clone_form;
|
||||
|
||||
/** @var string */
|
||||
protected $csrf_namespace = 'admin_stations';
|
||||
|
||||
|
@ -23,9 +26,13 @@ class StationsController
|
|||
*
|
||||
* @see \App\Provider\AdminProvider
|
||||
*/
|
||||
public function __construct(Form\StationForm $station_form)
|
||||
{
|
||||
public function __construct(
|
||||
Form\StationForm $station_form,
|
||||
Form\StationCloneForm $clone_form
|
||||
) {
|
||||
$this->station_form = $station_form;
|
||||
$this->clone_form = $clone_form;
|
||||
|
||||
$this->record_repo = $station_form->getEntityRepository();
|
||||
}
|
||||
|
||||
|
@ -69,4 +76,23 @@ class StationsController
|
|||
$request->getSession()->flash(__('%s deleted.', __('Station')), 'green');
|
||||
return $response->withRedirect($request->getRouter()->named('admin:stations:index'));
|
||||
}
|
||||
|
||||
public function cloneAction(Request $request, Response $response, $id): ResponseInterface
|
||||
{
|
||||
$record = $this->record_repo->find((int)$id);
|
||||
if (!($record instanceof Entity\Station)) {
|
||||
throw new \App\Exception\NotFound(__('%s not found.', __('Station')));
|
||||
}
|
||||
|
||||
if (false !== $this->clone_form->process($request, $record)) {
|
||||
$request->getSession()->flash(__('Changes saved.'), 'green');
|
||||
return $response->withRedirect($request->getRouter()->named('admin:stations:index'));
|
||||
}
|
||||
|
||||
return $request->getView()->renderToResponse($response, 'system/form_page', [
|
||||
'form' => $this->clone_form,
|
||||
'render_mode' => 'edit',
|
||||
'title' => __('Clone Station: %s', $record->getName())
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -121,16 +121,33 @@ class StationsController extends AbstractGenericCrudController
|
|||
]);
|
||||
}
|
||||
|
||||
/** @inheritDoc */
|
||||
protected function _createRecord($data): object
|
||||
{
|
||||
return $this->station_repo->create($data);
|
||||
}
|
||||
|
||||
/** @inheritDoc */
|
||||
protected function _editRecord($data, $record = null, array $context = []): object
|
||||
{
|
||||
return $this->station_repo->edit($data, $record);
|
||||
$create_mode = (null === $record);
|
||||
|
||||
if (null === $data) {
|
||||
throw new \InvalidArgumentException('Could not parse input data.');
|
||||
}
|
||||
|
||||
$record = $this->_denormalizeToRecord($data, $record, $context);
|
||||
|
||||
$errors = $this->validator->validate($record);
|
||||
if (count($errors) > 0) {
|
||||
$e = new \App\Exception\Validation((string)$errors);
|
||||
$e->setDetailedErrors($errors);
|
||||
throw $e;
|
||||
}
|
||||
|
||||
if ($create_mode) {
|
||||
$this->station_repo->create($record);
|
||||
} else {
|
||||
$this->station_repo->edit($record);
|
||||
}
|
||||
|
||||
$this->em->persist($record);
|
||||
$this->em->flush($record);
|
||||
return $record;
|
||||
}
|
||||
|
||||
/** @inheritDoc */
|
||||
|
|
|
@ -3,6 +3,7 @@ namespace App\Controller\Frontend;
|
|||
|
||||
use App\Acl;
|
||||
use App\Auth;
|
||||
use App\Form\StationForm;
|
||||
use App\Radio\Adapters;
|
||||
use App\Radio\Configuration;
|
||||
use App\Radio\Frontend\SHOUTcast;
|
||||
|
@ -23,8 +24,8 @@ class SetupController
|
|||
/** @var Acl */
|
||||
protected $acl;
|
||||
|
||||
/** @var array */
|
||||
protected $station_form_config;
|
||||
/** @var StationForm */
|
||||
protected $station_form;
|
||||
|
||||
/** @var array */
|
||||
protected $settings_form_config;
|
||||
|
@ -33,7 +34,7 @@ class SetupController
|
|||
* @param EntityManager $em
|
||||
* @param Auth $auth
|
||||
* @param Acl $acl
|
||||
* @param array $station_form_config
|
||||
* @param StationForm $station_form
|
||||
* @param array $settings_form_config
|
||||
*
|
||||
* @see \App\Provider\FrontendProvider
|
||||
|
@ -42,14 +43,14 @@ class SetupController
|
|||
EntityManager $em,
|
||||
Auth $auth,
|
||||
Acl $acl,
|
||||
array $station_form_config,
|
||||
StationForm $station_form,
|
||||
array $settings_form_config
|
||||
)
|
||||
{
|
||||
$this->em = $em;
|
||||
$this->auth = $auth;
|
||||
$this->acl = $acl;
|
||||
$this->station_form_config = $station_form_config;
|
||||
$this->station_form = $station_form;
|
||||
$this->settings_form_config = $settings_form_config;
|
||||
}
|
||||
|
||||
|
@ -149,28 +150,12 @@ class SetupController
|
|||
return $response->withRedirect($request->getRouter()->named('setup:'.$current_step));
|
||||
}
|
||||
|
||||
// Set up station form.
|
||||
$form_config = $this->station_form_config;
|
||||
unset($form_config['groups']['profile']['legend']);
|
||||
|
||||
if (!SHOUTcast::isInstalled()) {
|
||||
$form_config['groups']['select_frontend_type']['elements']['frontend_type'][1]['description'] = __('Want to use SHOUTcast 2? <a href="%s" target="_blank">Install it here</a>, then reload this page.', $request->getRouter()->named('admin:install:shoutcast'));
|
||||
}
|
||||
|
||||
$form = new \AzuraForms\Form($form_config);
|
||||
|
||||
if (!empty($_POST) && $form->isValid($_POST)) {
|
||||
$data = $form->getValues();
|
||||
|
||||
/** @var Entity\Repository\StationRepository $station_repo */
|
||||
$station_repo = $this->em->getRepository(Entity\Station::class);
|
||||
$station_repo->create($data);
|
||||
|
||||
if (false !== $this->station_form->process($request)) {
|
||||
return $response->withRedirect($request->getRouter()->named('setup:settings'));
|
||||
}
|
||||
|
||||
return $request->getView()->renderToResponse($response, 'frontend/setup/station', [
|
||||
'form' => $form,
|
||||
'form' => $this->station_form,
|
||||
]);
|
||||
}
|
||||
|
||||
|
|
|
@ -10,6 +10,7 @@ use Azura\Cache;
|
|||
use Azura\Doctrine\Repository;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Doctrine\ORM\Mapping;
|
||||
use Symfony\Component\Validator\Validator\ValidatorInterface;
|
||||
|
||||
class StationRepository extends Repository
|
||||
{
|
||||
|
@ -22,6 +23,9 @@ class StationRepository extends Repository
|
|||
/** @var Configuration */
|
||||
protected $configuration;
|
||||
|
||||
/** @var ValidatorInterface */
|
||||
protected $validator;
|
||||
|
||||
/** @var Cache */
|
||||
protected $cache;
|
||||
|
||||
|
@ -31,7 +35,8 @@ class StationRepository extends Repository
|
|||
Media $media_sync,
|
||||
Adapters $adapters,
|
||||
Configuration $configuration,
|
||||
Cache $cache
|
||||
Cache $cache,
|
||||
ValidatorInterface $validator
|
||||
) {
|
||||
parent::__construct($em, $class);
|
||||
|
||||
|
@ -39,6 +44,7 @@ class StationRepository extends Repository
|
|||
$this->adapters = $adapters;
|
||||
$this->configuration = $configuration;
|
||||
$this->cache = $cache;
|
||||
$this->validator = $validator;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -89,30 +95,16 @@ class StationRepository extends Repository
|
|||
}
|
||||
|
||||
/**
|
||||
* @param array $data
|
||||
* @param Entity\Station|null $record
|
||||
* @return Entity\Station
|
||||
*/
|
||||
public function editOrCreate($data, Entity\Station $record = null): Entity\Station
|
||||
{
|
||||
return (null === $record)
|
||||
? $this->create($data)
|
||||
: $this->edit($data, $record);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $data
|
||||
* @param Entity\Station $record
|
||||
* @return Entity\Station
|
||||
*/
|
||||
public function edit($data, Entity\Station $record): Entity\Station
|
||||
public function edit(Entity\Station $record): void
|
||||
{
|
||||
$old_frontend = $record->getFrontendType();
|
||||
$old_backend = $record->getBackendType();
|
||||
/** @var Entity\Station $original_record */
|
||||
$original_record = $this->_em->getUnitOfWork()->getOriginalEntityData($record);
|
||||
|
||||
$this->fromArray($record, $data);
|
||||
$this->_em->persist($record);
|
||||
$this->_em->flush($record);
|
||||
// Get the original values to check for changes.
|
||||
$old_frontend = $original_record['frontend_type'];
|
||||
$old_backend = $original_record['backend_type'];
|
||||
|
||||
$frontend_changed = ($old_frontend !== $record->getFrontendType());
|
||||
$backend_changed = ($old_backend !== $record->getBackendType());
|
||||
|
@ -126,22 +118,15 @@ class StationRepository extends Repository
|
|||
$this->configuration->writeConfiguration($record, $adapter_changed);
|
||||
|
||||
$this->cache->remove('stations');
|
||||
|
||||
return $record;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a station based on the specified data.
|
||||
* Handle tasks necessary to a station's creation.
|
||||
*
|
||||
* @param array $data Array of data to populate the station with.
|
||||
* @return Entity\Station
|
||||
* @throws \Exception
|
||||
* @param Entity\Station $station
|
||||
*/
|
||||
public function create($data): Entity\Station
|
||||
public function create(Entity\Station $station): void
|
||||
{
|
||||
$station = new Entity\Station;
|
||||
$this->fromArray($station, $data);
|
||||
|
||||
// Create path for station.
|
||||
$station_base_dir = dirname(APP_INCLUDE_ROOT) . '/stations';
|
||||
|
||||
|
@ -179,8 +164,6 @@ class StationRepository extends Repository
|
|||
$this->_em->flush();
|
||||
|
||||
$this->cache->remove('stations');
|
||||
|
||||
return $station;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -230,21 +213,4 @@ class StationRepository extends Repository
|
|||
|
||||
$this->cache->remove('stations');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed|null $port
|
||||
* @param Entity\Station|null $except_record
|
||||
* @return bool
|
||||
*/
|
||||
public function isPortUsed($port, Entity\Station $except_record = null): bool
|
||||
{
|
||||
if (!empty($port)) {
|
||||
$port = (int)$port;
|
||||
$used_ports = $this->configuration->getUsedPorts($except_record);
|
||||
|
||||
return isset($used_ports[$port]);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@ use Doctrine\Common\Collections\Collection;
|
|||
use Doctrine\ORM\Mapping as ORM;
|
||||
use OpenApi\Annotations as OA;
|
||||
use Symfony\Component\Serializer\Annotation\Groups;
|
||||
use App\Validator\Constraints as AppAssert;
|
||||
|
||||
use App\Radio\Frontend\AbstractFrontend;
|
||||
use App\Radio\Remote\AdapterProxy;
|
||||
|
@ -25,6 +26,7 @@ use Psr\Http\Message\UriInterface;
|
|||
* @ORM\HasLifecycleCallbacks
|
||||
*
|
||||
* @OA\Schema(type="object", schema="Station")
|
||||
* @AppAssert\StationPortChecker()
|
||||
*/
|
||||
class Station
|
||||
{
|
||||
|
@ -41,7 +43,7 @@ class Station
|
|||
* @ORM\GeneratedValue(strategy="IDENTITY")
|
||||
*
|
||||
* @OA\Property(example=1)
|
||||
* @var int
|
||||
* @var int|null
|
||||
*/
|
||||
protected $id;
|
||||
|
||||
|
@ -343,9 +345,9 @@ class Station
|
|||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
* @return int|null
|
||||
*/
|
||||
public function getId(): int
|
||||
public function getId(): ?int
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
|
|
@ -116,7 +116,6 @@ class EntityForm extends \AzuraForms\Form
|
|||
|
||||
$errors = $this->validator->validate($record);
|
||||
if (count($errors) > 0) {
|
||||
$other_errors = [];
|
||||
foreach($errors as $error) {
|
||||
/** @var ConstraintViolation $error */
|
||||
$field_name = $error->getPropertyPath();
|
||||
|
@ -124,16 +123,9 @@ class EntityForm extends \AzuraForms\Form
|
|||
if (isset($this->fields[$field_name])) {
|
||||
$this->fields[$field_name]->addError($error->getMessage());
|
||||
} else {
|
||||
$other_errors[] = $error;
|
||||
$this->addError($error->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
if (count($other_errors) > 0) {
|
||||
$e = new \App\Exception\Validation((string)$errors);
|
||||
$e->setDetailedErrors($errors);
|
||||
throw $e;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -146,6 +138,8 @@ class EntityForm extends \AzuraForms\Form
|
|||
}
|
||||
|
||||
/**
|
||||
* The old ->toArray().
|
||||
*
|
||||
* @param object $record
|
||||
* @param array $context
|
||||
* @return array
|
||||
|
@ -180,6 +174,8 @@ class EntityForm extends \AzuraForms\Form
|
|||
}
|
||||
|
||||
/**
|
||||
* The old ->fromArray().
|
||||
*
|
||||
* @param array $data
|
||||
* @param object|null $record
|
||||
* @param array $context
|
||||
|
|
|
@ -0,0 +1,138 @@
|
|||
<?php
|
||||
namespace App\Form;
|
||||
|
||||
use App\Acl;
|
||||
use App\Entity;
|
||||
use App\Http\Request;
|
||||
use App\Radio\Configuration;
|
||||
use App\Radio\Frontend\SHOUTcast;
|
||||
use Azura\Doctrine\Repository;
|
||||
use Doctrine\ORM\EntityManager;
|
||||
use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;
|
||||
use Symfony\Component\Serializer\Serializer;
|
||||
use Symfony\Component\Validator\ConstraintViolation;
|
||||
use Symfony\Component\Validator\Validator\ValidatorInterface;
|
||||
|
||||
class StationCloneForm extends StationForm
|
||||
{
|
||||
/** @var Configuration */
|
||||
protected $configuration;
|
||||
|
||||
/**
|
||||
* @param EntityManager $em
|
||||
* @param Serializer $serializer
|
||||
* @param ValidatorInterface $validator
|
||||
* @param Acl $acl
|
||||
* @param Configuration $configuration
|
||||
* @param array $form_config
|
||||
*
|
||||
* @see \App\Provider\FormProvider
|
||||
*/
|
||||
public function __construct(
|
||||
EntityManager $em,
|
||||
Serializer $serializer,
|
||||
ValidatorInterface $validator,
|
||||
Acl $acl,
|
||||
Configuration $configuration,
|
||||
array $form_config
|
||||
) {
|
||||
parent::__construct($em, $serializer, $validator, $acl, $form_config);
|
||||
|
||||
$this->configuration = $configuration;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function process(Request $request, $record = null)
|
||||
{
|
||||
if (!$record instanceof Entity\Station) {
|
||||
throw new \InvalidArgumentException('Record must be a station.');
|
||||
}
|
||||
|
||||
$this->populate([
|
||||
'name' => $record->getName().' - Copy',
|
||||
'description' => $record->getDescription(),
|
||||
]);
|
||||
|
||||
if ($request->isPost() && $this->isValid($request->getParsedBody())) {
|
||||
$data = $this->getValues();
|
||||
|
||||
$new_record_data = $this->_normalizeRecord($record);
|
||||
$new_record_data['name'] = $data['name'];
|
||||
$new_record_data['description'] = $data['description'];
|
||||
|
||||
$unset_values = [
|
||||
'short_name',
|
||||
'radio_base_dir',
|
||||
'nowplaying',
|
||||
'nowplaying_timestamp',
|
||||
'is_streamer_live',
|
||||
'needs_restart',
|
||||
'has_started',
|
||||
];
|
||||
|
||||
foreach($unset_values as $unset_value) {
|
||||
unset($new_record_data[$unset_value]);
|
||||
}
|
||||
|
||||
if ('share' === $data['clone_media']) {
|
||||
$new_record_data['radio_media_dir'] = $record->getRadioMediaDir();
|
||||
} else {
|
||||
unset($new_record_data['radio_media_dir'], $new_record_data['storage_used']);
|
||||
}
|
||||
|
||||
$new_record = $this->_denormalizeToRecord($new_record_data);
|
||||
$this->station_repo->create($new_record);
|
||||
|
||||
$this->configuration->assignRadioPorts($new_record, true);
|
||||
$this->configuration->writeConfiguration($new_record);
|
||||
|
||||
// Copy associated records if applicable.
|
||||
if ('copy' === $data['clone_media']) {
|
||||
copy($record->getRadioMediaDir(), $new_record->getRadioMediaDir());
|
||||
}
|
||||
|
||||
if (1 == $data['clone_playlists']) {
|
||||
foreach ($record->getPlaylists() as $source_record) {
|
||||
$dest_record_data = $this->_normalizeRecord($source_record);
|
||||
unset($dest_record_data['id'], $dest_record_data['station_id']);
|
||||
|
||||
$dest_record = $this->serializer->denormalize($data, Entity\StationPlaylist::class, null, [
|
||||
ObjectNormalizer::OBJECT_TO_POPULATE => new Entity\StationPlaylist($new_record),
|
||||
]);
|
||||
$this->em->persist($dest_record);
|
||||
}
|
||||
}
|
||||
|
||||
if (1 == $data['clone_streamers']) {
|
||||
foreach ($record->getStreamers() as $source_record) {
|
||||
$dest_record_data = $this->_normalizeRecord($source_record);
|
||||
unset($dest_record_data['id'], $dest_record_data['station_id']);
|
||||
|
||||
$dest_record = $this->serializer->denormalize($data, Entity\StationStreamer::class, null, [
|
||||
ObjectNormalizer::OBJECT_TO_POPULATE => new Entity\StationStreamer($new_record),
|
||||
]);
|
||||
$this->em->persist($dest_record);
|
||||
}
|
||||
}
|
||||
|
||||
if (1 == $data['clone_permissions']) {
|
||||
foreach ($record->getPermissions() as $source_record) {
|
||||
$dest_record_data = $this->_normalizeRecord($source_record);
|
||||
unset($dest_record_data['id'], $dest_record_data['station_id']);
|
||||
|
||||
$dest_record = $this->serializer->denormalize($data, Entity\RolePermission::class, null, [
|
||||
ObjectNormalizer::OBJECT_TO_POPULATE => new Entity\RolePermission($source_record->getRole(), $new_record),
|
||||
]);
|
||||
$this->em->persist($dest_record);
|
||||
}
|
||||
}
|
||||
|
||||
$this->em->flush();
|
||||
return $new_record;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
|
@ -4,9 +4,11 @@ namespace App\Form;
|
|||
use App\Acl;
|
||||
use App\Entity;
|
||||
use App\Http\Request;
|
||||
use App\Radio\Frontend\SHOUTcast;
|
||||
use Azura\Doctrine\Repository;
|
||||
use Doctrine\ORM\EntityManager;
|
||||
use Symfony\Component\Serializer\Serializer;
|
||||
use Symfony\Component\Validator\ConstraintViolation;
|
||||
use Symfony\Component\Validator\Validator\ValidatorInterface;
|
||||
|
||||
class StationForm extends EntityForm
|
||||
|
@ -73,29 +75,43 @@ class StationForm extends EntityForm
|
|||
unset($this->options['groups']['admin']);
|
||||
}
|
||||
|
||||
// Add the port checker validator (which depends on the current record) to the appropriate fields.
|
||||
$port_checker = function($value) use ($record) {
|
||||
if ($this->station_repo->isPortUsed($value, $record)) {
|
||||
return __('This port is currently in use by another station.');
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
foreach($this->fields as $field) {
|
||||
$attrs = $field->getAttributes();
|
||||
|
||||
if (isset($attrs['class']) && strpos($attrs['class'], 'input-port') !== false) {
|
||||
$field->addValidator($port_checker);
|
||||
}
|
||||
if (!SHOUTcast::isInstalled()) {
|
||||
$this->options['groups']['select_frontend_type']['elements']['frontend_type'][1]['description'] = __('Want to use SHOUTcast 2? <a href="%s" target="_blank">Install it here</a>, then reload this page.', $request->getRouter()->named('admin:install:shoutcast'));
|
||||
}
|
||||
|
||||
if (null !== $record) {
|
||||
$create_mode = (null === $record);
|
||||
if (!$create_mode) {
|
||||
$this->populate($this->_normalizeRecord($record));
|
||||
}
|
||||
|
||||
if ($request->isPost() && $this->isValid($request->getParsedBody())) {
|
||||
$data = $this->getValues();
|
||||
return $this->station_repo->editOrCreate($data, $record);
|
||||
$record = $this->_denormalizeToRecord($data, $record);
|
||||
|
||||
$errors = $this->validator->validate($record);
|
||||
if (count($errors) > 0) {
|
||||
foreach($errors as $error) {
|
||||
/** @var ConstraintViolation $error */
|
||||
$field_name = $error->getPropertyPath();
|
||||
|
||||
if (isset($this->fields[$field_name])) {
|
||||
$this->fields[$field_name]->addError($error->getMessage());
|
||||
} else {
|
||||
$this->addError($error->getMessage());
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($create_mode) {
|
||||
$this->station_repo->create($record);
|
||||
} else {
|
||||
$this->station_repo->edit($record);
|
||||
}
|
||||
|
||||
$this->em->persist($record);
|
||||
$this->em->flush($record);
|
||||
return $record;
|
||||
}
|
||||
|
||||
return false;
|
||||
|
|
|
@ -74,19 +74,8 @@ class AdminProvider implements ServiceProviderInterface
|
|||
|
||||
$di[Admin\StationsController::class] = function($di) {
|
||||
return new Admin\StationsController(
|
||||
$di[\App\Form\StationForm::class]
|
||||
);
|
||||
};
|
||||
|
||||
$di[Admin\Stations\CloneController::class] = function($di) {
|
||||
/** @var \Azura\Config $config */
|
||||
$config = $di[\Azura\Config::class];
|
||||
|
||||
return new Admin\Stations\CloneController(
|
||||
$di[EntityManager::class],
|
||||
$di[\Azura\Cache::class],
|
||||
$di[\App\Radio\Configuration::class],
|
||||
$config->get('forms/station_clone')
|
||||
$di[\App\Form\StationForm::class],
|
||||
$di[\App\Form\StationCloneForm::class]
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
@ -46,6 +46,20 @@ class FormProvider implements ServiceProviderInterface
|
|||
);
|
||||
};
|
||||
|
||||
$di[Form\StationCloneForm::class] = function($di) {
|
||||
/** @var \Azura\Config $config */
|
||||
$config = $di[\Azura\Config::class];
|
||||
|
||||
return new Form\StationCloneForm(
|
||||
$di[EntityManager::class],
|
||||
$di[Serializer::class],
|
||||
$di[ValidatorInterface::class],
|
||||
$di[\App\Acl::class],
|
||||
$di[\App\Radio\Configuration::class],
|
||||
$config->get('forms/station_clone')
|
||||
);
|
||||
};
|
||||
|
||||
$di[Form\UserForm::class] = function($di) {
|
||||
/** @var \Azura\Config $config */
|
||||
$config = $di[\Azura\Config::class];
|
||||
|
|
|
@ -70,7 +70,7 @@ class FrontendProvider implements ServiceProviderInterface
|
|||
$di[\Doctrine\ORM\EntityManager::class],
|
||||
$di[\App\Auth::class],
|
||||
$di[\App\Acl::class],
|
||||
$config->get('forms/station'),
|
||||
$di[\App\Form\StationForm::class],
|
||||
$config->get('forms/settings')
|
||||
);
|
||||
};
|
||||
|
|
|
@ -346,7 +346,7 @@ class Configuration
|
|||
}
|
||||
}
|
||||
|
||||
if ($except_station !== null) {
|
||||
if (null !== $except_station && null !== $except_station->getId()) {
|
||||
return array_filter($used_ports, function($station_reference) use ($except_station) {
|
||||
return ($station_reference['id'] !== $except_station->getId());
|
||||
});
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
<?php
|
||||
namespace App\Validator\Constraints;
|
||||
|
||||
use Symfony\Component\Validator\Constraint;
|
||||
|
||||
/**
|
||||
* @Annotation
|
||||
*/
|
||||
class StationPortChecker extends Constraint
|
||||
{
|
||||
public $message;
|
||||
|
||||
public function __construct($options = null)
|
||||
{
|
||||
$this->message = __('The port %s is in use by another station.', '{{ port }}');
|
||||
|
||||
parent::__construct($options);
|
||||
}
|
||||
|
||||
public function getTargets()
|
||||
{
|
||||
return self::CLASS_CONSTRAINT;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
<?php
|
||||
namespace App\Validator\Constraints;
|
||||
|
||||
use App\Entity;
|
||||
use App\Radio\Configuration;
|
||||
use Symfony\Component\Validator\Constraint;
|
||||
use Symfony\Component\Validator\ConstraintValidator;
|
||||
use Symfony\Component\Validator\Exception\UnexpectedTypeException;
|
||||
use Symfony\Component\Validator\Exception\UnexpectedValueException;
|
||||
|
||||
class StationPortCheckerValidator extends ConstraintValidator
|
||||
{
|
||||
/** @var Configuration */
|
||||
protected $configuration;
|
||||
|
||||
/**
|
||||
* StationPortCheckerValidator constructor.
|
||||
* @param Configuration $configuration
|
||||
*/
|
||||
public function __construct(Configuration $configuration)
|
||||
{
|
||||
$this->configuration = $configuration;
|
||||
}
|
||||
|
||||
public function validate($station, Constraint $constraint)
|
||||
{
|
||||
if (!$constraint instanceof StationPortChecker) {
|
||||
throw new UnexpectedTypeException($constraint, StationPortChecker::class);
|
||||
}
|
||||
if (!$station instanceof Entity\Station) {
|
||||
throw new UnexpectedTypeException($station, Entity\Station::class);
|
||||
}
|
||||
|
||||
$frontend_config = (array)$station->getFrontendConfig();
|
||||
$backend_config = (array)$station->getBackendConfig();
|
||||
|
||||
$ports_to_check = [
|
||||
'frontend_config_port' => $frontend_config['port'] ?? '',
|
||||
'backend_config_dj_port' => $backend_config['dj_port'] ?? '',
|
||||
'backend_config_telnet_port' => $backend_config['telnet_port'] ?? '',
|
||||
];
|
||||
|
||||
$used_ports = $this->configuration->getUsedPorts($station);
|
||||
|
||||
foreach($ports_to_check as $port_path => $value) {
|
||||
if (null === $value || '' === $value) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$port = (int)$value;
|
||||
if (isset($used_ports[$port])) {
|
||||
$this->context->buildViolation($constraint->message)
|
||||
->setParameter('{{ port }}', $port)
|
||||
->addViolation();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -8,5 +8,12 @@ use Symfony\Component\Validator\Constraint;
|
|||
*/
|
||||
class StreamerPassword extends Constraint
|
||||
{
|
||||
public $message = 'Password cannot contain the following characters: {{ chars }}';
|
||||
public $message;
|
||||
|
||||
public function __construct($options = null)
|
||||
{
|
||||
$this->message = __('Password cannot contain the following characters: %s', '{{ chars }}');
|
||||
|
||||
parent::__construct($options);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,6 +3,10 @@ $(function() {
|
|||
if ($('.tab-content').length > 0) {
|
||||
$('.tab-content').prependTo('form.form');
|
||||
|
||||
if ($('form .alert').length > 0) {
|
||||
$('form .alert').appendTo('.card-header:first');
|
||||
}
|
||||
|
||||
$('form fieldset#profile').appendTo('#tab-profile');
|
||||
|
||||
$('form fieldset#select_frontend_type').appendTo('#tab-frontend');
|
||||
|
|
|
@ -86,19 +86,18 @@ abstract class CestAbstract
|
|||
|
||||
$this->di[\App\Acl::class]->reload();
|
||||
|
||||
// Create station.
|
||||
$station_info = [
|
||||
'id' => 25,
|
||||
'name' => 'Functional Test Radio',
|
||||
'description' => 'Test radio station.',
|
||||
'frontend_type' => \App\Radio\Adapters::DEFAULT_FRONTEND,
|
||||
'backend_type' => \App\Radio\Adapters::DEFAULT_BACKEND,
|
||||
];
|
||||
|
||||
/** @var Entity\Repository\StationRepository $station_repo */
|
||||
$station_repo = $this->em->getRepository(Entity\Station::class);
|
||||
|
||||
$this->test_station = $station_repo->create($station_info);
|
||||
$test_station = new Entity\Station();
|
||||
$test_station->setName('Functional Test Radio');
|
||||
$test_station->setDescription('Test radio station.');
|
||||
$test_station->setFrontendType(\App\Radio\Adapters::DEFAULT_FRONTEND);
|
||||
$test_station->setBackendType(\App\Radio\Adapters::DEFAULT_BACKEND);
|
||||
|
||||
$station_repo->create($test_station);
|
||||
|
||||
$this->test_station = $test_station;
|
||||
|
||||
// Set settings.
|
||||
|
||||
|
|
Loading…
Reference in New Issue