Use common trait for searching across controllers; add type strictness to several $request->getParam calls on controllers.
This commit is contained in:
parent
b8e5a4bfa7
commit
4ccce52705
|
@ -12,6 +12,7 @@ use App\Exception\ValidationException;
|
|||
use App\Http\Response;
|
||||
use App\Http\ServerRequest;
|
||||
use App\Paginator;
|
||||
use App\Utilities\Types;
|
||||
use Doctrine\ORM\Query;
|
||||
use InvalidArgumentException;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
|
@ -124,7 +125,7 @@ abstract class AbstractApiCrudController
|
|||
|
||||
$return = $this->toArray($record);
|
||||
|
||||
$isInternal = ('true' === $request->getParam('internal', 'false'));
|
||||
$isInternal = Types::bool($request->getParam('internal'), false, true);
|
||||
$router = $request->getRouter();
|
||||
|
||||
if ($record instanceof IdentifiableEntityInterface) {
|
||||
|
|
|
@ -9,6 +9,7 @@ use App\Controller\SingleActionInterface;
|
|||
use App\Http\Response;
|
||||
use App\Http\ServerRequest;
|
||||
use App\Radio\Adapters;
|
||||
use App\Utilities\Types;
|
||||
use Monolog\Handler\TestHandler;
|
||||
use Monolog\Level;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
|
@ -33,7 +34,7 @@ final class TelnetAction implements SingleActionInterface
|
|||
$station = $request->getStation();
|
||||
$backend = $this->adapters->requireBackendAdapter($station);
|
||||
|
||||
$command = $request->getParam('command');
|
||||
$command = Types::string($request->getParam('command'));
|
||||
|
||||
$telnetResponse = $backend->command($station, $command);
|
||||
$this->logger->debug(
|
||||
|
|
|
@ -6,6 +6,7 @@ namespace App\Controller\Api\Admin;
|
|||
|
||||
use App\Acl;
|
||||
use App\Controller\Api\AbstractApiCrudController;
|
||||
use App\Controller\Api\Traits\CanSearchResults;
|
||||
use App\Controller\Api\Traits\CanSortResults;
|
||||
use App\Entity\Repository\RolePermissionRepository;
|
||||
use App\Entity\Role;
|
||||
|
@ -138,6 +139,7 @@ use Symfony\Component\Validator\Validator\ValidatorInterface;
|
|||
final class RolesController extends AbstractApiCrudController
|
||||
{
|
||||
use CanSortResults;
|
||||
use CanSearchResults;
|
||||
|
||||
protected string $entityClass = Role::class;
|
||||
protected string $resourceRouteName = 'api:admin:role';
|
||||
|
@ -174,11 +176,13 @@ final class RolesController extends AbstractApiCrudController
|
|||
'r.name'
|
||||
);
|
||||
|
||||
$searchPhrase = trim($request->getParam('searchPhrase', ''));
|
||||
if (!empty($searchPhrase)) {
|
||||
$qb->andWhere('(r.name LIKE :name)')
|
||||
->setParameter('name', '%' . $searchPhrase . '%');
|
||||
}
|
||||
$qb = $this->searchQueryBuilder(
|
||||
$request,
|
||||
$qb,
|
||||
[
|
||||
'r.name',
|
||||
]
|
||||
);
|
||||
|
||||
return $this->listPaginatedFromQuery($request, $response, $qb->getQuery());
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@ use App\Exception\ValidationException;
|
|||
use App\Http\Response;
|
||||
use App\Http\ServerRequest;
|
||||
use App\Service\Mail;
|
||||
use App\Utilities\Types;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Symfony\Component\Mailer\Exception\TransportException;
|
||||
use Symfony\Component\Validator\Constraints\Email;
|
||||
|
@ -30,7 +31,7 @@ final class SendTestMessageAction implements SingleActionInterface
|
|||
Response $response,
|
||||
array $params
|
||||
): ResponseInterface {
|
||||
$emailAddress = $request->getParam('email', '');
|
||||
$emailAddress = Types::string($request->getParam('email'));
|
||||
|
||||
$errors = $this->validator->validate(
|
||||
$emailAddress,
|
||||
|
|
|
@ -5,6 +5,7 @@ declare(strict_types=1);
|
|||
namespace App\Controller\Api\Admin;
|
||||
|
||||
use App\Controller\Api\AbstractApiCrudController;
|
||||
use App\Controller\Api\Traits\CanSearchResults;
|
||||
use App\Controller\Api\Traits\CanSortResults;
|
||||
use App\Entity\Repository\StationQueueRepository;
|
||||
use App\Entity\Repository\StationRepository;
|
||||
|
@ -142,6 +143,7 @@ use Throwable;
|
|||
class StationsController extends AbstractApiCrudController
|
||||
{
|
||||
use CanSortResults;
|
||||
use CanSearchResults;
|
||||
|
||||
protected string $entityClass = Station::class;
|
||||
protected string $resourceRouteName = 'api:admin:station';
|
||||
|
@ -175,11 +177,14 @@ class StationsController extends AbstractApiCrudController
|
|||
'e.name'
|
||||
);
|
||||
|
||||
$searchPhrase = trim($request->getParam('searchPhrase', ''));
|
||||
if (!empty($searchPhrase)) {
|
||||
$qb->andWhere('(e.name LIKE :name OR e.short_name LIKE :name)')
|
||||
->setParameter('name', '%' . $searchPhrase . '%');
|
||||
}
|
||||
$qb = $this->searchQueryBuilder(
|
||||
$request,
|
||||
$qb,
|
||||
[
|
||||
'e.name',
|
||||
'e.short_name',
|
||||
]
|
||||
);
|
||||
|
||||
return $this->listPaginatedFromQuery($request, $response, $qb->getQuery());
|
||||
}
|
||||
|
@ -192,7 +197,7 @@ class StationsController extends AbstractApiCrudController
|
|||
|
||||
$return = $this->toArray($record);
|
||||
|
||||
$isInternal = ('true' === $request->getParam('internal', 'false'));
|
||||
$isInternal = $request->isInternal();
|
||||
$router = $request->getRouter();
|
||||
|
||||
$return['links'] = [
|
||||
|
|
|
@ -5,6 +5,7 @@ declare(strict_types=1);
|
|||
namespace App\Controller\Api\Admin;
|
||||
|
||||
use App\Controller\Api\AbstractApiCrudController;
|
||||
use App\Controller\Api\Traits\CanSearchResults;
|
||||
use App\Controller\Api\Traits\CanSortResults;
|
||||
use App\Controller\Frontend\Account\MasqueradeAction;
|
||||
use App\Entity\Api\Error;
|
||||
|
@ -134,6 +135,7 @@ use Psr\Http\Message\ResponseInterface;
|
|||
class UsersController extends AbstractApiCrudController
|
||||
{
|
||||
use CanSortResults;
|
||||
use CanSearchResults;
|
||||
|
||||
protected string $entityClass = User::class;
|
||||
protected string $resourceRouteName = 'api:admin:user';
|
||||
|
@ -156,11 +158,14 @@ class UsersController extends AbstractApiCrudController
|
|||
'e.name'
|
||||
);
|
||||
|
||||
$searchPhrase = trim($request->getParam('searchPhrase', ''));
|
||||
if (!empty($searchPhrase)) {
|
||||
$qb->andWhere('(e.name LIKE :name OR e.email LIKE :name)')
|
||||
->setParameter('name', '%' . $searchPhrase . '%');
|
||||
}
|
||||
$qb = $this->searchQueryBuilder(
|
||||
$request,
|
||||
$qb,
|
||||
[
|
||||
'e.name',
|
||||
'e.email',
|
||||
]
|
||||
);
|
||||
|
||||
return $this->listPaginatedFromQuery($request, $response, $qb->getQuery());
|
||||
}
|
||||
|
@ -173,7 +178,7 @@ class UsersController extends AbstractApiCrudController
|
|||
|
||||
$return = $this->toArray($record);
|
||||
|
||||
$isInternal = ('true' === $request->getParam('internal', 'false'));
|
||||
$isInternal = $request->isInternal();
|
||||
$router = $request->getRouter();
|
||||
$csrf = $request->getCsrf();
|
||||
$currentUser = $request->getUser();
|
||||
|
|
|
@ -6,6 +6,8 @@ namespace App\Controller\Api\Frontend\Dashboard;
|
|||
|
||||
use App\Container\EntityManagerAwareTrait;
|
||||
use App\Container\SettingsAwareTrait;
|
||||
use App\Controller\Api\Traits\CanSearchResults;
|
||||
use App\Controller\Api\Traits\CanSortResults;
|
||||
use App\Controller\SingleActionInterface;
|
||||
use App\Entity\Api\Dashboard;
|
||||
use App\Entity\ApiGenerator\NowPlayingApiGenerator;
|
||||
|
@ -20,6 +22,8 @@ final class StationsAction implements SingleActionInterface
|
|||
{
|
||||
use EntityManagerAwareTrait;
|
||||
use SettingsAwareTrait;
|
||||
use CanSortResults;
|
||||
use CanSearchResults;
|
||||
|
||||
public function __construct(
|
||||
private readonly NowPlayingApiGenerator $npApiGenerator
|
||||
|
@ -69,8 +73,8 @@ final class StationsAction implements SingleActionInterface
|
|||
$viewStations[] = $row;
|
||||
}
|
||||
|
||||
$searchPhrase = trim($request->getParam('searchPhrase', ''));
|
||||
if (!empty($searchPhrase)) {
|
||||
$searchPhrase = $this->getSearchPhrase($request);
|
||||
if (null !== $searchPhrase) {
|
||||
$viewStations = array_filter(
|
||||
$viewStations,
|
||||
static function (Dashboard $row) use ($searchPhrase) {
|
||||
|
@ -79,22 +83,15 @@ final class StationsAction implements SingleActionInterface
|
|||
);
|
||||
}
|
||||
|
||||
$sort = $request->getParam('sort');
|
||||
usort(
|
||||
$viewStations = $this->sortArray(
|
||||
$request,
|
||||
$viewStations,
|
||||
static function (Dashboard $a, Dashboard $b) use ($sort) {
|
||||
if ('listeners' === $sort) {
|
||||
return $a->listeners->current <=> $b->listeners->current;
|
||||
}
|
||||
|
||||
return $a->station->name <=> $b->station->name;
|
||||
}
|
||||
[
|
||||
'listeners' => 'listeners.current',
|
||||
],
|
||||
'station.name'
|
||||
);
|
||||
|
||||
if ('desc' === strtolower($request->getParam('sortOrder', 'asc'))) {
|
||||
$viewStations = array_reverse($viewStations);
|
||||
}
|
||||
|
||||
return Paginator::fromArray($viewStations, $request)->write($response);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@ use App\Exception\PermissionDeniedException;
|
|||
use App\Http\Response;
|
||||
use App\Http\ServerRequest;
|
||||
use App\Radio\Frontend\Blocklist\BlocklistParser;
|
||||
use App\Utilities\Types;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
|
||||
final class ListenerAuthAction implements SingleActionInterface
|
||||
|
@ -46,7 +47,7 @@ final class ListenerAuthAction implements SingleActionInterface
|
|||
}
|
||||
|
||||
$station = $request->getStation();
|
||||
$listenerIp = $request->getParam('ip') ?? '';
|
||||
$listenerIp = Types::string($request->getParam('ip'));
|
||||
|
||||
if ($this->blocklistParser->isAllowed($station, $listenerIp)) {
|
||||
return $response->withHeader('icecast-auth-user', '1');
|
||||
|
|
|
@ -66,7 +66,7 @@ final class BatchAction implements SingleActionInterface
|
|||
|
||||
$fsMedia = $this->stationFilesystems->getMediaFilesystem($station);
|
||||
|
||||
$result = match ($request->getParam('do')) {
|
||||
$result = match (Types::string($request->getParam('do'))) {
|
||||
'delete' => $this->doDelete($request, $station, $storageLocation, $fsMedia),
|
||||
'playlist' => $this->doPlaylist($request, $station, $storageLocation, $fsMedia),
|
||||
'move' => $this->doMove($request, $station, $storageLocation, $fsMedia),
|
||||
|
@ -141,10 +141,15 @@ final class BatchAction implements SingleActionInterface
|
|||
/** @var array<int, int> $affectedPlaylistIds */
|
||||
$affectedPlaylistIds = [];
|
||||
|
||||
foreach ($request->getParam('playlists') as $playlistId) {
|
||||
/** @var string[] $requestPlaylists */
|
||||
$requestPlaylists = Types::array($request->getParam('playlists'));
|
||||
|
||||
foreach ($requestPlaylists as $playlistId) {
|
||||
if ('new' === $playlistId) {
|
||||
$playlist = new StationPlaylist($station);
|
||||
$playlist->setName($request->getParam('new_playlist_name'));
|
||||
$playlist->setName(
|
||||
Types::string($request->getParam('new_playlist_name'))
|
||||
);
|
||||
|
||||
$this->em->persist($playlist);
|
||||
$this->em->flush();
|
||||
|
@ -216,8 +221,8 @@ final class BatchAction implements SingleActionInterface
|
|||
): BatchResult {
|
||||
$result = $this->parseRequest($request, $fs);
|
||||
|
||||
$from = $request->getParam('currentDirectory', '');
|
||||
$to = $request->getParam('directory', '');
|
||||
$from = Types::string($request->getParam('currentDirectory'));
|
||||
$to = Types::string($request->getParam('directory'));
|
||||
|
||||
$toMove = [
|
||||
$this->batchUtilities->iterateMedia($storageLocation, $result->files),
|
||||
|
|
|
@ -74,7 +74,7 @@ final class ListAction implements SingleActionInterface
|
|||
|
||||
$cacheKey = implode('.', $cacheKeyParts);
|
||||
|
||||
$flushCache = Types::bool($request->getParam('flushCache'));
|
||||
$flushCache = Types::bool($request->getParam('flushCache'), false, true);
|
||||
|
||||
if (!$flushCache && $this->cache->has($cacheKey)) {
|
||||
/** @var array<int, FileList> $result */
|
||||
|
|
|
@ -10,6 +10,7 @@ use App\Entity\Api\Status;
|
|||
use App\Flysystem\StationFilesystems;
|
||||
use App\Http\Response;
|
||||
use App\Http\ServerRequest;
|
||||
use App\Utilities\Types;
|
||||
use League\Flysystem\UnableToCreateDirectory;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
|
||||
|
@ -25,8 +26,8 @@ final class MakeDirectoryAction implements SingleActionInterface
|
|||
Response $response,
|
||||
array $params
|
||||
): ResponseInterface {
|
||||
$currentDir = $request->getParam('currentDirectory', '');
|
||||
$newDirName = $request->getParam('name', '');
|
||||
$currentDir = Types::string($request->getParam('currentDirectory'));
|
||||
$newDirName = Types::string($request->getParam('name'));
|
||||
|
||||
if (empty($newDirName)) {
|
||||
return $response->withStatus(400)
|
||||
|
|
|
@ -4,6 +4,7 @@ declare(strict_types=1);
|
|||
|
||||
namespace App\Controller\Api\Stations;
|
||||
|
||||
use App\Controller\Api\Traits\CanSearchResults;
|
||||
use App\Controller\Api\Traits\CanSortResults;
|
||||
use App\Entity\Repository\StationMountRepository;
|
||||
use App\Entity\StationMount;
|
||||
|
@ -144,6 +145,7 @@ use Symfony\Component\Validator\Validator\ValidatorInterface;
|
|||
final class MountsController extends AbstractStationApiCrudController
|
||||
{
|
||||
use CanSortResults;
|
||||
use CanSearchResults;
|
||||
|
||||
protected string $entityClass = StationMount::class;
|
||||
protected string $resourceRouteName = 'api:stations:mount';
|
||||
|
@ -180,11 +182,14 @@ final class MountsController extends AbstractStationApiCrudController
|
|||
'e.display_name'
|
||||
);
|
||||
|
||||
$searchPhrase = trim($request->getParam('searchPhrase', ''));
|
||||
if (!empty($searchPhrase)) {
|
||||
$qb->andWhere('(e.name LIKE :name OR e.display_name LIKE :name)')
|
||||
->setParameter('name', '%' . $searchPhrase . '%');
|
||||
}
|
||||
$qb = $this->searchQueryBuilder(
|
||||
$request,
|
||||
$qb,
|
||||
[
|
||||
'e.name',
|
||||
'e.display_name',
|
||||
]
|
||||
);
|
||||
|
||||
return $this->listPaginatedFromQuery($request, $response, $qb->getQuery());
|
||||
}
|
||||
|
|
|
@ -12,6 +12,7 @@ use App\Entity\Repository\StationPlaylistRepository;
|
|||
use App\Exception;
|
||||
use App\Http\Response;
|
||||
use App\Http\ServerRequest;
|
||||
use App\Utilities\Types;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
|
||||
final class PutOrderAction implements SingleActionInterface
|
||||
|
@ -39,7 +40,7 @@ final class PutOrderAction implements SingleActionInterface
|
|||
throw new Exception(__('This playlist is not a sequential playlist.'));
|
||||
}
|
||||
|
||||
$order = $request->getParam('order');
|
||||
$order = Types::array($request->getParam('order'));
|
||||
|
||||
$this->spmRepo->setMediaOrder($record, $order);
|
||||
return $response->withJson($order);
|
||||
|
|
|
@ -4,6 +4,7 @@ declare(strict_types=1);
|
|||
|
||||
namespace App\Controller\Api\Stations;
|
||||
|
||||
use App\Controller\Api\Traits\CanSearchResults;
|
||||
use App\Controller\Api\Traits\CanSortResults;
|
||||
use App\Entity\Enums\PlaylistOrders;
|
||||
use App\Entity\Enums\PlaylistSources;
|
||||
|
@ -12,6 +13,7 @@ use App\Entity\StationSchedule;
|
|||
use App\Http\Response;
|
||||
use App\Http\ServerRequest;
|
||||
use App\OpenApi;
|
||||
use App\Utilities\Types;
|
||||
use Carbon\CarbonInterface;
|
||||
use Doctrine\ORM\AbstractQuery;
|
||||
use InvalidArgumentException;
|
||||
|
@ -145,6 +147,7 @@ use Symfony\Component\Serializer\Normalizer\AbstractNormalizer;
|
|||
final class PlaylistsController extends AbstractScheduledEntityController
|
||||
{
|
||||
use CanSortResults;
|
||||
use CanSearchResults;
|
||||
|
||||
protected string $entityClass = StationPlaylist::class;
|
||||
protected string $resourceRouteName = 'api:stations:playlist';
|
||||
|
@ -172,11 +175,13 @@ final class PlaylistsController extends AbstractScheduledEntityController
|
|||
'sp.name'
|
||||
);
|
||||
|
||||
$searchPhrase = trim($request->getParam('searchPhrase', ''));
|
||||
if (!empty($searchPhrase)) {
|
||||
$qb->andWhere('sp.name LIKE :name')
|
||||
->setParameter('name', '%' . $searchPhrase . '%');
|
||||
}
|
||||
$qb = $this->searchQueryBuilder(
|
||||
$request,
|
||||
$qb,
|
||||
[
|
||||
'sp.name',
|
||||
]
|
||||
);
|
||||
|
||||
return $this->listPaginatedFromQuery($request, $response, $qb->getQuery());
|
||||
}
|
||||
|
@ -261,7 +266,7 @@ final class PlaylistsController extends AbstractScheduledEntityController
|
|||
$return['num_songs'] = $songTotals['num_songs'];
|
||||
$return['total_length'] = round((float)$songTotals['total_length']);
|
||||
|
||||
$isInternal = ('true' === $request->getParam('internal', 'false'));
|
||||
$isInternal = Types::bool($request->getParam('internal'), false, true);
|
||||
$router = $request->getRouter();
|
||||
|
||||
$return['links'] = [
|
||||
|
|
|
@ -5,6 +5,7 @@ declare(strict_types=1);
|
|||
namespace App\Controller\Api\Stations;
|
||||
|
||||
use App\Controller\Api\AbstractApiCrudController;
|
||||
use App\Controller\Api\Traits\CanSearchResults;
|
||||
use App\Entity\Api\PodcastEpisode as ApiPodcastEpisode;
|
||||
use App\Entity\Api\PodcastMedia as ApiPodcastMedia;
|
||||
use App\Entity\PodcastEpisode;
|
||||
|
@ -16,6 +17,7 @@ use App\Http\Response;
|
|||
use App\Http\ServerRequest;
|
||||
use App\OpenApi;
|
||||
use App\Service\Flow\UploadedFile;
|
||||
use App\Utilities\Types;
|
||||
use InvalidArgumentException;
|
||||
use OpenApi\Attributes as OA;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
|
@ -185,6 +187,8 @@ use Symfony\Component\Validator\Validator\ValidatorInterface;
|
|||
]
|
||||
final class PodcastEpisodesController extends AbstractApiCrudController
|
||||
{
|
||||
use CanSearchResults;
|
||||
|
||||
protected string $entityClass = PodcastEpisode::class;
|
||||
protected string $resourceRouteName = 'api:stations:podcast:episode';
|
||||
|
||||
|
@ -218,11 +222,13 @@ final class PodcastEpisodesController extends AbstractApiCrudController
|
|||
->orderBy('e.created_at', 'DESC')
|
||||
->setParameter('podcast', $podcast);
|
||||
|
||||
$searchPhrase = trim($request->getParam('searchPhrase', ''));
|
||||
if (!empty($searchPhrase)) {
|
||||
$queryBuilder->andWhere('e.title LIKE :title')
|
||||
->setParameter('title', '%' . $searchPhrase . '%');
|
||||
}
|
||||
$queryBuilder = $this->searchQueryBuilder(
|
||||
$request,
|
||||
$queryBuilder,
|
||||
[
|
||||
'e.title',
|
||||
]
|
||||
);
|
||||
|
||||
return $this->listPaginatedFromQuery($request, $response, $queryBuilder->getQuery());
|
||||
}
|
||||
|
@ -299,7 +305,7 @@ final class PodcastEpisodesController extends AbstractApiCrudController
|
|||
throw new InvalidArgumentException(sprintf('Record must be an instance of %s.', $this->entityClass));
|
||||
}
|
||||
|
||||
$isInternal = ('true' === $request->getParam('internal', 'false'));
|
||||
$isInternal = Types::bool($request->getParam('internal'), false, true);
|
||||
$router = $request->getRouter();
|
||||
|
||||
$return = new ApiPodcastEpisode();
|
||||
|
|
|
@ -5,6 +5,7 @@ declare(strict_types=1);
|
|||
namespace App\Controller\Api\Stations;
|
||||
|
||||
use App\Controller\Api\AbstractApiCrudController;
|
||||
use App\Controller\Api\Traits\CanSearchResults;
|
||||
use App\Entity\Api\Podcast as ApiPodcast;
|
||||
use App\Entity\Podcast;
|
||||
use App\Entity\PodcastCategory;
|
||||
|
@ -14,6 +15,7 @@ use App\Http\Response;
|
|||
use App\Http\ServerRequest;
|
||||
use App\OpenApi;
|
||||
use App\Service\Flow\UploadedFile;
|
||||
use App\Utilities\Types;
|
||||
use InvalidArgumentException;
|
||||
use OpenApi\Attributes as OA;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
|
@ -147,6 +149,8 @@ use Symfony\Component\Validator\Validator\ValidatorInterface;
|
|||
]
|
||||
final class PodcastsController extends AbstractApiCrudController
|
||||
{
|
||||
use CanSearchResults;
|
||||
|
||||
protected string $entityClass = Podcast::class;
|
||||
protected string $resourceRouteName = 'api:stations:podcast';
|
||||
|
||||
|
@ -173,11 +177,13 @@ final class PodcastsController extends AbstractApiCrudController
|
|||
->orderBy('p.title', 'ASC')
|
||||
->setParameter('storageLocation', $station->getPodcastsStorageLocation());
|
||||
|
||||
$searchPhrase = trim($request->getParam('searchPhrase', ''));
|
||||
if (!empty($searchPhrase)) {
|
||||
$queryBuilder->andWhere('p.title LIKE :title')
|
||||
->setParameter('title', '%' . $searchPhrase . '%');
|
||||
}
|
||||
$queryBuilder = $this->searchQueryBuilder(
|
||||
$request,
|
||||
$queryBuilder,
|
||||
[
|
||||
'p.title',
|
||||
]
|
||||
);
|
||||
|
||||
return $this->listPaginatedFromQuery($request, $response, $queryBuilder->getQuery());
|
||||
}
|
||||
|
@ -228,7 +234,7 @@ final class PodcastsController extends AbstractApiCrudController
|
|||
throw new InvalidArgumentException(sprintf('Record must be an instance of %s.', $this->entityClass));
|
||||
}
|
||||
|
||||
$isInternal = ('true' === $request->getParam('internal', 'false'));
|
||||
$isInternal = Types::bool($request->getParam('internal'), false, true);
|
||||
$router = $request->getRouter();
|
||||
$station = $request->getStation();
|
||||
|
||||
|
|
|
@ -13,6 +13,7 @@ use App\Http\Response;
|
|||
use App\Http\ServerRequest;
|
||||
use App\OpenApi;
|
||||
use App\Radio\AutoDJ\Queue;
|
||||
use App\Utilities\Types;
|
||||
use InvalidArgumentException;
|
||||
use OpenApi\Attributes as OA;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
|
@ -147,7 +148,7 @@ final class QueueController extends AbstractStationApiCrudController
|
|||
$row = ($this->queueApiGenerator)($record);
|
||||
$row->resolveUrls($router->getBaseUrl());
|
||||
|
||||
$isInternal = ('true' === $request->getParam('internal', 'false'));
|
||||
$isInternal = Types::bool($request->getParam('internal'), false, true);
|
||||
|
||||
$apiResponse = new StationQueueDetailed();
|
||||
$apiResponse->fromParentObject($row);
|
||||
|
|
|
@ -4,6 +4,7 @@ declare(strict_types=1);
|
|||
|
||||
namespace App\Controller\Api\Stations;
|
||||
|
||||
use App\Controller\Api\Traits\CanSearchResults;
|
||||
use App\Controller\Api\Traits\CanSortResults;
|
||||
use App\Entity\Api\StationRemote as ApiStationRemote;
|
||||
use App\Entity\StationRemote;
|
||||
|
@ -11,6 +12,7 @@ use App\Exception\PermissionDeniedException;
|
|||
use App\Http\Response;
|
||||
use App\Http\ServerRequest;
|
||||
use App\OpenApi;
|
||||
use App\Utilities\Types;
|
||||
use InvalidArgumentException;
|
||||
use OpenApi\Attributes as OA;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
|
@ -143,6 +145,7 @@ use Psr\Http\Message\ResponseInterface;
|
|||
final class RemotesController extends AbstractStationApiCrudController
|
||||
{
|
||||
use CanSortResults;
|
||||
use CanSearchResults;
|
||||
|
||||
protected string $entityClass = StationRemote::class;
|
||||
protected string $resourceRouteName = 'api:stations:remote';
|
||||
|
@ -170,11 +173,13 @@ final class RemotesController extends AbstractStationApiCrudController
|
|||
'e.display_name'
|
||||
);
|
||||
|
||||
$searchPhrase = trim($request->getParam('searchPhrase', ''));
|
||||
if (!empty($searchPhrase)) {
|
||||
$qb->andWhere('(e.display_name LIKE :name)')
|
||||
->setParameter('name', '%' . $searchPhrase . '%');
|
||||
}
|
||||
$qb = $this->searchQueryBuilder(
|
||||
$request,
|
||||
$qb,
|
||||
[
|
||||
'e.display_name',
|
||||
]
|
||||
);
|
||||
|
||||
return $this->listPaginatedFromQuery($request, $response, $qb->getQuery());
|
||||
}
|
||||
|
@ -192,7 +197,7 @@ final class RemotesController extends AbstractStationApiCrudController
|
|||
$return = new ApiStationRemote();
|
||||
$return->fromParentObject($returnArray);
|
||||
|
||||
$isInternal = ('true' === $request->getParam('internal', 'false'));
|
||||
$isInternal = Types::bool($request->getParam('internal'), false, true);
|
||||
$router = $request->getRouter();
|
||||
|
||||
$return->is_editable = $record->isEditable();
|
||||
|
|
|
@ -5,18 +5,23 @@ declare(strict_types=1);
|
|||
namespace App\Controller\Api\Stations\Reports;
|
||||
|
||||
use App\Container\EntityManagerAwareTrait;
|
||||
use App\Controller\Api\Traits\CanSearchResults;
|
||||
use App\Controller\Api\Traits\CanSortResults;
|
||||
use App\Entity\Api\Status;
|
||||
use App\Entity\Repository\StationRequestRepository;
|
||||
use App\Entity\StationRequest;
|
||||
use App\Http\Response;
|
||||
use App\Http\ServerRequest;
|
||||
use App\Paginator;
|
||||
use App\Utilities\Types;
|
||||
use Doctrine\ORM\AbstractQuery;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
|
||||
final class RequestsController
|
||||
{
|
||||
use EntityManagerAwareTrait;
|
||||
use CanSortResults;
|
||||
use CanSearchResults;
|
||||
|
||||
public function __construct(
|
||||
private readonly StationRequestRepository $requestRepo
|
||||
|
@ -36,33 +41,35 @@ final class RequestsController
|
|||
->where('sr.station = :station')
|
||||
->setParameter('station', $station);
|
||||
|
||||
$qb = match ($request->getParam('type', 'recent')) {
|
||||
$type = Types::string($request->getParam('type', 'recent'));
|
||||
$qb = match ($type) {
|
||||
'history' => $qb->andWhere('sr.played_at != 0'),
|
||||
default => $qb->andWhere('sr.played_at = 0'),
|
||||
};
|
||||
|
||||
$queryParams = $request->getQueryParams();
|
||||
$searchPhrase = trim($queryParams['searchPhrase'] ?? '');
|
||||
$qb = $this->sortQueryBuilder(
|
||||
$request,
|
||||
$qb,
|
||||
[
|
||||
'name' => 'sm.title',
|
||||
'title' => 'sm.title',
|
||||
'artist' => 'sm.artist',
|
||||
'album' => 'sm.album',
|
||||
'genre' => 'sm.genre',
|
||||
],
|
||||
'sr.timestamp',
|
||||
'DESC'
|
||||
);
|
||||
|
||||
$sortField = (string)($queryParams['sort'] ?? '');
|
||||
$sortDirection = strtolower($queryParams['sortOrder'] ?? 'asc');
|
||||
|
||||
if (!empty($sortField)) {
|
||||
match ($sortField) {
|
||||
'name', 'title' => $qb->addOrderBy('sm.title', $sortDirection),
|
||||
'artist' => $qb->addOrderBy('sm.artist', $sortDirection),
|
||||
'album' => $qb->addOrderBy('sm.album', $sortDirection),
|
||||
'genre' => $qb->addOrderBy('sm.genre', $sortDirection),
|
||||
default => null,
|
||||
};
|
||||
} else {
|
||||
$qb->addOrderBy('sr.timestamp', 'DESC');
|
||||
}
|
||||
|
||||
if (!empty($searchPhrase)) {
|
||||
$qb->andWhere('(sm.title LIKE :query OR sm.artist LIKE :query OR sm.album LIKE :query)')
|
||||
->setParameter('query', '%' . $searchPhrase . '%');
|
||||
}
|
||||
$qb = $this->searchQueryBuilder(
|
||||
$request,
|
||||
$qb,
|
||||
[
|
||||
'sm.title',
|
||||
'sm.artist',
|
||||
'sm.album',
|
||||
]
|
||||
);
|
||||
|
||||
$query = $qb->getQuery()
|
||||
->setHydrationMode(AbstractQuery::HYDRATE_ARRAY);
|
||||
|
|
|
@ -76,7 +76,7 @@ final class BroadcastsController extends AbstractApiCrudController
|
|||
$paginator = Paginator::fromQuery($query, $request);
|
||||
|
||||
$router = $request->getRouter();
|
||||
$isInternal = ('true' === $request->getParam('internal', 'false'));
|
||||
$isInternal = $request->isInternal();
|
||||
$fsRecordings = $this->stationFilesystems->getRecordingsFilesystem($station);
|
||||
|
||||
$paginator->setPostprocessor(
|
||||
|
|
|
@ -4,6 +4,7 @@ declare(strict_types=1);
|
|||
|
||||
namespace App\Controller\Api\Stations;
|
||||
|
||||
use App\Controller\Api\Traits\CanSearchResults;
|
||||
use App\Controller\Api\Traits\CanSortResults;
|
||||
use App\Entity\Repository\StationScheduleRepository;
|
||||
use App\Entity\Repository\StationStreamerRepository;
|
||||
|
@ -14,6 +15,7 @@ use App\Http\ServerRequest;
|
|||
use App\OpenApi;
|
||||
use App\Radio\AutoDJ\Scheduler;
|
||||
use App\Service\Flow\UploadedFile;
|
||||
use App\Utilities\Types;
|
||||
use Carbon\CarbonInterface;
|
||||
use InvalidArgumentException;
|
||||
use OpenApi\Attributes as OA;
|
||||
|
@ -149,6 +151,7 @@ use Symfony\Component\Validator\Validator\ValidatorInterface;
|
|||
final class StreamersController extends AbstractScheduledEntityController
|
||||
{
|
||||
use CanSortResults;
|
||||
use CanSearchResults;
|
||||
|
||||
protected string $entityClass = StationStreamer::class;
|
||||
protected string $resourceRouteName = 'api:stations:streamer';
|
||||
|
@ -186,11 +189,14 @@ final class StreamersController extends AbstractScheduledEntityController
|
|||
'e.streamer_username'
|
||||
);
|
||||
|
||||
$searchPhrase = trim($request->getParam('searchPhrase', ''));
|
||||
if (!empty($searchPhrase)) {
|
||||
$qb->andWhere('(e.streamer_username LIKE :name OR e.display_name LIKE :name)')
|
||||
->setParameter('name', '%' . $searchPhrase . '%');
|
||||
}
|
||||
$qb = $this->searchQueryBuilder(
|
||||
$request,
|
||||
$qb,
|
||||
[
|
||||
'e.streamer_username',
|
||||
'e.display_name',
|
||||
]
|
||||
);
|
||||
|
||||
return $this->listPaginatedFromQuery($request, $response, $qb->getQuery());
|
||||
}
|
||||
|
@ -275,7 +281,7 @@ final class StreamersController extends AbstractScheduledEntityController
|
|||
$return = parent::viewRecord($record, $request);
|
||||
|
||||
$router = $request->getRouter();
|
||||
$isInternal = ('true' === $request->getParam('internal', 'false'));
|
||||
$isInternal = Types::bool($request->getParam('internal'), false, true);
|
||||
|
||||
$return['has_custom_art'] = (0 !== $record->getArtUpdatedAt());
|
||||
|
||||
|
|
|
@ -4,11 +4,13 @@ declare(strict_types=1);
|
|||
|
||||
namespace App\Controller\Api\Stations;
|
||||
|
||||
use App\Controller\Api\Traits\CanSearchResults;
|
||||
use App\Controller\Api\Traits\CanSortResults;
|
||||
use App\Entity\StationWebhook;
|
||||
use App\Http\Response;
|
||||
use App\Http\ServerRequest;
|
||||
use App\OpenApi;
|
||||
use App\Utilities\Types;
|
||||
use InvalidArgumentException;
|
||||
use OpenApi\Attributes as OA;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
|
@ -141,6 +143,7 @@ use Psr\Http\Message\ResponseInterface;
|
|||
final class WebhooksController extends AbstractStationApiCrudController
|
||||
{
|
||||
use CanSortResults;
|
||||
use CanSearchResults;
|
||||
|
||||
protected string $entityClass = StationWebhook::class;
|
||||
protected string $resourceRouteName = 'api:stations:webhook';
|
||||
|
@ -171,11 +174,13 @@ final class WebhooksController extends AbstractStationApiCrudController
|
|||
'e.name'
|
||||
);
|
||||
|
||||
$searchPhrase = trim($request->getParam('searchPhrase', ''));
|
||||
if (!empty($searchPhrase)) {
|
||||
$qb->andWhere('(e.name LIKE :name)')
|
||||
->setParameter('name', '%' . $searchPhrase . '%');
|
||||
}
|
||||
$qb = $this->searchQueryBuilder(
|
||||
$request,
|
||||
$qb,
|
||||
[
|
||||
'e.name',
|
||||
]
|
||||
);
|
||||
|
||||
return $this->listPaginatedFromQuery($request, $response, $qb->getQuery());
|
||||
}
|
||||
|
@ -188,7 +193,7 @@ final class WebhooksController extends AbstractStationApiCrudController
|
|||
|
||||
$return = $this->toArray($record);
|
||||
|
||||
$isInternal = ('true' === $request->getParam('internal', 'false'));
|
||||
$isInternal = Types::bool($request->getParam('internal'), false, true);
|
||||
$router = $request->getRouter();
|
||||
|
||||
$return['links'] = [
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Controller\Api\Traits;
|
||||
|
||||
use App\Http\ServerRequest;
|
||||
use App\Utilities\Types;
|
||||
use Doctrine\ORM\QueryBuilder;
|
||||
|
||||
trait CanSearchResults
|
||||
{
|
||||
/**
|
||||
* @param string[] $fieldsToSearch
|
||||
*/
|
||||
protected function searchQueryBuilder(
|
||||
ServerRequest $request,
|
||||
QueryBuilder $queryBuilder,
|
||||
array $fieldsToSearch,
|
||||
string $searchParam = 'searchPhrase'
|
||||
): QueryBuilder {
|
||||
$searchPhrase = $this->getSearchPhrase($request, $searchParam);
|
||||
if (null === $searchPhrase) {
|
||||
return $queryBuilder;
|
||||
}
|
||||
|
||||
$searchQuery = [];
|
||||
foreach ($fieldsToSearch as $field) {
|
||||
$searchQuery[] = $field . ' LIKE :search';
|
||||
}
|
||||
|
||||
return $queryBuilder->andWhere(
|
||||
implode(' OR ', $searchQuery)
|
||||
)->setParameter('search', '%' . $searchPhrase . '%');
|
||||
}
|
||||
|
||||
protected function getSearchPhrase(
|
||||
ServerRequest $request,
|
||||
string $searchParam = 'searchPhrase'
|
||||
): ?string {
|
||||
return Types::stringOrNull(
|
||||
$request->getParam($searchParam),
|
||||
true
|
||||
);
|
||||
}
|
||||
}
|
|
@ -12,6 +12,7 @@ use App\Exception\RateLimitExceededException;
|
|||
use App\Http\Response;
|
||||
use App\Http\ServerRequest;
|
||||
use App\RateLimit;
|
||||
use App\Utilities\Types;
|
||||
use Mezzio\Session\SessionCookiePersistenceInterface;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
|
||||
|
@ -69,14 +70,17 @@ final class LoginAction implements SingleActionInterface
|
|||
return $response->withRedirect($request->getUri()->getPath());
|
||||
}
|
||||
|
||||
$user = $auth->authenticate($request->getParam('username'), $request->getParam('password'));
|
||||
$user = $auth->authenticate(
|
||||
Types::string($request->getParam('username')),
|
||||
Types::string($request->getParam('password'))
|
||||
);
|
||||
|
||||
if ($user instanceof User) {
|
||||
$session = $request->getSession();
|
||||
|
||||
// If user selects "remember me", extend the cookie/session lifetime.
|
||||
if ($session instanceof SessionCookiePersistenceInterface) {
|
||||
$rememberMe = (bool)$request->getParam('remember', 0);
|
||||
$rememberMe = Types::bool($request->getParam('remember'), false, true);
|
||||
/** @noinspection SummerTimeUnsafeTimeManipulationInspection */
|
||||
$session->persistSessionFor(($rememberMe) ? 86400 * 14 : 0);
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@ use App\Controller\SingleActionInterface;
|
|||
use App\Entity\User;
|
||||
use App\Http\Response;
|
||||
use App\Http\ServerRequest;
|
||||
use App\Utilities\Types;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
|
||||
final class TwoFactorAction implements SingleActionInterface
|
||||
|
@ -21,7 +22,7 @@ final class TwoFactorAction implements SingleActionInterface
|
|||
|
||||
if ($request->isPost()) {
|
||||
$flash = $request->getFlash();
|
||||
$otp = $request->getParam('otp');
|
||||
$otp = Types::string($request->getParam('otp'));
|
||||
|
||||
if ($auth->verifyTwoFactor($otp)) {
|
||||
/** @var User $user */
|
||||
|
|
|
@ -13,6 +13,7 @@ use App\Enums\SupportedLocales;
|
|||
use App\Exception\InvalidRequestAttribute;
|
||||
use App\RateLimit;
|
||||
use App\Session;
|
||||
use App\Utilities\Types;
|
||||
use App\View;
|
||||
use Mezzio\Session\SessionInterface;
|
||||
use Slim\Http\ServerRequest as SlimServerRequest;
|
||||
|
@ -162,4 +163,13 @@ final class ServerRequest extends SlimServerRequest
|
|||
|
||||
return $object;
|
||||
}
|
||||
|
||||
public function isInternal(): bool
|
||||
{
|
||||
return Types::bool(
|
||||
$this->getParam('internal', false),
|
||||
false,
|
||||
true
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -126,7 +126,7 @@ final class BatchUtilities
|
|||
* @param StorageLocation $storageLocation
|
||||
* @param array $paths
|
||||
*
|
||||
* @return iterable|StationMedia[]
|
||||
* @return iterable<StationMedia>
|
||||
*/
|
||||
public function iterateMedia(StorageLocation $storageLocation, array $paths): iterable
|
||||
{
|
||||
|
@ -141,7 +141,7 @@ final class BatchUtilities
|
|||
* @param StorageLocation $storageLocation
|
||||
* @param string $dir
|
||||
*
|
||||
* @return iterable|StationMedia[]
|
||||
* @return iterable<StationMedia>
|
||||
*/
|
||||
public function iterateMediaInDirectory(StorageLocation $storageLocation, string $dir): iterable
|
||||
{
|
||||
|
@ -166,7 +166,7 @@ final class BatchUtilities
|
|||
* @param StorageLocation $storageLocation
|
||||
* @param array $paths
|
||||
*
|
||||
* @return iterable|UnprocessableMedia[]
|
||||
* @return iterable<UnprocessableMedia>
|
||||
*/
|
||||
public function iterateUnprocessableMedia(StorageLocation $storageLocation, array $paths): iterable
|
||||
{
|
||||
|
@ -181,7 +181,7 @@ final class BatchUtilities
|
|||
* @param StorageLocation $storageLocation
|
||||
* @param string $dir
|
||||
*
|
||||
* @return iterable|UnprocessableMedia[]
|
||||
* @return iterable<UnprocessableMedia>
|
||||
*/
|
||||
public function iterateUnprocessableMediaInDirectory(
|
||||
StorageLocation $storageLocation,
|
||||
|
@ -204,7 +204,7 @@ final class BatchUtilities
|
|||
* @param StorageLocation $storageLocation
|
||||
* @param string $dir
|
||||
*
|
||||
* @return iterable|StationPlaylistFolder[]
|
||||
* @return iterable<StationPlaylistFolder>
|
||||
*/
|
||||
public function iteratePlaylistFoldersInDirectory(
|
||||
StorageLocation $storageLocation,
|
||||
|
|
Loading…
Reference in New Issue