Architecture change: Integrate common AzuraCore code (#944)

* Update composer.json/lock
* Update CLI console commands
* All AzuraCore refactors except folder moves.
* MVP for all core functionality.
* Working unit/functional tests
* Remove DB dump from util/, replace with direct migration.
* Update AzuraCore, define testing mode earlier.
This commit is contained in:
Buster "Silver Eagle" Neece 2018-11-12 10:59:15 -06:00 committed by GitHub
parent 83ebaab952
commit 854cb93261
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
181 changed files with 896 additions and 5096 deletions

View File

@ -1,88 +0,0 @@
<?php
/**
* Global bootstrap file.
*/
// Security settings
define('APP_IS_COMMAND_LINE', PHP_SAPI === 'cli');
define('APP_IS_SECURE', !APP_IS_COMMAND_LINE && (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === "on"));
if (!defined('APP_TESTING_MODE')) {
define('APP_TESTING_MODE', false);
}
// General includes
define('APP_INCLUDE_BASE', __DIR__);
define('APP_INCLUDE_ROOT', dirname(__DIR__));
define('APP_INCLUDE_WEB', APP_INCLUDE_ROOT . '/web');
define('APP_INCLUDE_STATIC', APP_INCLUDE_WEB . '/static');
// Detect Docker containerization
define('APP_INSIDE_DOCKER', file_exists(APP_INCLUDE_ROOT.'/../.docker'));
define('APP_INCLUDE_VENDOR', APP_INCLUDE_ROOT . '/vendor');
define('APP_INCLUDE_TEMP', dirname(APP_INCLUDE_ROOT) . '/www_tmp');
define('APP_INCLUDE_CACHE', APP_INCLUDE_TEMP . '/cache');
// Set up application environment.
if (APP_INSIDE_DOCKER) {
$_ENV = getenv();
} else if (file_exists(APP_INCLUDE_ROOT.'/env.ini')) {
$_ENV = array_merge($_ENV, parse_ini_file(APP_INCLUDE_ROOT.'/env.ini'));
} else if (file_exists(APP_INCLUDE_ROOT.'/app/env.ini')) {
// Legacy file location.
$_ENV = array_merge($_ENV, parse_ini_file(APP_INCLUDE_ROOT.'/app/env.ini'));
}
// Application environment.
define('APP_APPLICATION_ENV', $_ENV['application_env'] ?? $_ENV['APPLICATION_ENV'] ?? 'production');
define('APP_IN_PRODUCTION', APP_APPLICATION_ENV === 'production');
define('APP_DOCKER_REVISION', $_ENV['AZURACAST_DC_REVISION'] ?? 1);
if (isset($_SERVER['HTTP_X_FORWARDED_PROTO'])) {
$_SERVER['HTTPS'] = (strtolower($_SERVER['HTTP_X_FORWARDED_PROTO']) === 'https');
}
// Apply PHP settings.
ini_set('display_startup_errors', !APP_IN_PRODUCTION ? 1 : 0);
ini_set('display_errors', !APP_IN_PRODUCTION ? 1 : 0);
ini_set('log_errors', 1);
ini_set('error_log', (APP_INSIDE_DOCKER) ? '/dev/stderr' : APP_INCLUDE_TEMP.'/php_errors.log');
ini_set('error_reporting', E_ALL & ~E_NOTICE & ~E_WARNING & ~E_STRICT);
ini_set('session.use_only_cookies', 1);
ini_set('session.cookie_httponly', 1);
ini_set('session.cookie_lifetime', 86400);
ini_set('session.use_strict_mode', 1);
// Disable sessions sending their own Cache-Control/Expires headers.
session_cache_limiter('');
// Composer autoload.
$autoloader = require(APP_INCLUDE_VENDOR . '/autoload.php');
// Initialize plugins
$plugins = new \App\Plugins(APP_INCLUDE_ROOT.'/plugins');
$plugins->registerAutoloaders($autoloader);
// Set up DI container.
$di = new \Slim\Container([
'settings' => [
'outputBuffering' => false,
'displayErrorDetails' => !APP_IN_PRODUCTION,
'addContentLengthHeader' => false,
'routerCacheFile' => (APP_IN_PRODUCTION) ? APP_INCLUDE_TEMP . '/app_routes.cache.php' : false,
'determineRouteBeforeAppMiddleware' => true,
]
]);
$di[\App\Plugins::class] = $plugins;
// Define services.
$settings = require(dirname(__DIR__).'/config/settings.php');
call_user_func(include(dirname(__DIR__).'/config/services.php'), $di, $settings);
$plugins->registerServices($di, $settings);
return $di;

View File

@ -1,27 +0,0 @@
<?php
error_reporting(E_ALL & ~E_NOTICE & ~E_STRICT);
ini_set('display_errors', 1);
$di = require __DIR__.'/app.php';
// Load app, to generate routes, etc.
$di->get('app');
// Placeholder locale functions
$translator = new \Gettext\Translator();
$translator->register();
/** @var \Doctrine\ORM\EntityManager $em */
$em = $di[\Doctrine\ORM\EntityManager::class];
/** @var \App\Version $version */
$version = $di[\App\Version::class];
$helperSet = \Doctrine\ORM\Tools\Console\ConsoleRunner::createHelperSet($em);
$helperSet->set(new \Symfony\Component\Console\Helper\QuestionHelper, 'dialog');
$cli = new \App\Console\Application($settings['name'].' Command Line Tools ('.APP_APPLICATION_ENV.')', $version->getVersion());
$cli->setContainer($di);
$cli->setHelperSet($helperSet);
return $cli;

View File

@ -19,24 +19,13 @@ coverage:
include:
- src/*
exclude:
# Loaded before tests
- src/Tests/Module.php
- src/Doctrine/EntityManagerFactory.php
# Not used in entire application
- src/Doctrine/Platform/*.php
- src/Doctrine/Paginate/*.php
# Used in application, but not used in tests
- src/Radio/Frontend/ShoutCast2.php
- src/Console/Command/*.php
- src/Doctrine/Logger/EchoSQL.php
- src/Session/Temporary.php
- src/Console/Command/*.php
- src/Entity/Fixture/*
- src/Entity/Migration/*
# Exceptions
- src/Exception.php
- src/Exception/*.php
- src/Mvc/ErrorHandler.php

View File

@ -1,72 +1,57 @@
{
"name": "AzuraCast/AzuraCast",
"description": "The AzuraCast standalone radio station management tool.",
"license": "Apache-2.0",
"require": {
"php": ">=7.2",
"ext-fileinfo": "*",
"ext-gd": "*",
"ext-intl": "*",
"ext-json": "*",
"ext-PDO": "*",
"ext-redis": "*",
"ext-simplexml": "*",
"ext-xml": "*",
"ext-xmlwriter": "*",
"name": "AzuraCast/AzuraCast",
"description": "The AzuraCast standalone radio station management suite.",
"license": "Apache-2.0",
"require": {
"php": ">=7.2",
"ext-fileinfo": "*",
"ext-gd": "*",
"ext-intl": "*",
"ext-simplexml": "*",
"ext-xml": "*",
"ext-xmlwriter": "*",
"azuracast/azuraforms": "dev-master",
"azuracast/nowplaying": "dev-master",
"cakephp/chronos": "^1.1",
"doctrine/data-fixtures": "^1.3",
"doctrine/dbal": "^2.8",
"doctrine/migrations": "^1.5",
"doctrine/orm": "~2.6",
"filp/whoops": "2.*",
"gettext/gettext": "^4.4",
"guzzlehttp/guzzle": ">6.0",
"guzzlehttp/oauth-subscriber": "^0.3.0",
"influxdb/influxdb-php": "^1.14.3",
"james-heinrich/getid3": "dev-master",
"league/plates": "^3.1",
"lstrojny/fxmlrpc": "^0.14.0",
"maxmind-db/reader": "~1.0",
"mobiledetect/mobiledetectlib": "^2.8",
"monolog/monolog": "^1.23",
"paragonie/certainty": "^2",
"php-http/socket-client": "^1.2",
"php-http/message": "^1.4",
"php-http/guzzle6-adapter": "^1.1",
"slim/slim": "^3.0",
"studio24/rotate": "^1.0",
"supervisorphp/supervisor": "^3.0",
"symfony/console": "^4.1",
"symfony/event-dispatcher": "^4.1",
"symfony/finder": "^4.1",
"symfony/process": "^4.1",
"zendframework/zend-config": "^3.1.0"
},
"require-dev": {
"codeception/codeception": "^2.2",
"flow/jsonpath": "^0.3.4",
"mockery/mockery": "^1.0",
"roave/security-advisories": "dev-master",
"squizlabs/php_codesniffer": "3.*",
"zircote/swagger-php": "^3.0"
},
"authors": [
{
"name": "Buster Neece",
"email": "buster@busterneece.com"
}
],
"autoload": {
"psr-4": {
"App\\": "src"
}
},
"config": {
"preferred-install": "dist"
},
"prefer-stable": true,
"minimum-stability": "dev"
"azuracast/azuracore": "dev-master",
"azuracast/azuraforms": "dev-master",
"azuracast/nowplaying": "dev-master",
"cakephp/chronos": "^1.1",
"gettext/gettext": "^4.4",
"guzzlehttp/oauth-subscriber": "^0.3.0",
"influxdb/influxdb-php": "^1.14.3",
"james-heinrich/getid3": "dev-master",
"lstrojny/fxmlrpc": "^0.14.0",
"maxmind-db/reader": "~1.0",
"mobiledetect/mobiledetectlib": "^2.8",
"php-http/socket-client": "^1.2",
"php-http/message": "^1.4",
"php-http/guzzle6-adapter": "^1.1",
"studio24/rotate": "^1.0",
"supervisorphp/supervisor": "^3.0",
"symfony/finder": "^4.1",
"symfony/process": "^4.1"
},
"require-dev": {
"codeception/codeception": "^2.2",
"flow/jsonpath": "^0.3.4",
"mockery/mockery": "^1.0",
"roave/security-advisories": "dev-master",
"squizlabs/php_codesniffer": "3.*",
"zircote/swagger-php": "^3.0"
},
"authors": [
{
"name": "Buster Neece",
"email": "buster@busterneece.com"
}
],
"autoload": {
"psr-4": {
"App\\": "src"
}
},
"config": {
"preferred-install": "dist"
},
"prefer-stable": true,
"minimum-stability": "dev"
}

438
composer.lock generated
View File

@ -4,8 +4,66 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "988f73420ee8c51c179ed07afb9a0a97",
"content-hash": "2580b6ed9dfc5dcaaa2846f267386636",
"packages": [
{
"name": "azuracast/azuracore",
"version": "dev-master",
"source": {
"type": "git",
"url": "https://github.com/AzuraCast/azuracore.git",
"reference": "4605cd9dc87269463aac78ee557cd489baa84f88"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/AzuraCast/azuracore/zipball/4605cd9dc87269463aac78ee557cd489baa84f88",
"reference": "4605cd9dc87269463aac78ee557cd489baa84f88",
"shasum": ""
},
"require": {
"doctrine/data-fixtures": "^1.3",
"doctrine/dbal": "^2.8",
"doctrine/migrations": "^1.5",
"doctrine/orm": "~2.6",
"ext-json": "*",
"ext-pdo": "*",
"ext-redis": "*",
"filp/whoops": "2.*",
"guzzlehttp/guzzle": ">6.0",
"league/plates": "^3.1",
"monolog/monolog": "^1.23",
"paragonie/certainty": "^2",
"php": ">=7.2",
"slim/slim": "^3.0",
"symfony/console": "^4.1",
"symfony/event-dispatcher": "^4.1",
"symfony/var-dumper": "^4.1",
"zendframework/zend-config": "^3.1.0"
},
"require-dev": {
"codeception/codeception": "^2.2",
"mockery/mockery": "^1.0",
"roave/security-advisories": "dev-master"
},
"type": "library",
"autoload": {
"psr-4": {
"Azura\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"Apache-2.0"
],
"authors": [
{
"name": "Buster Neece",
"email": "buster@busterneece.com"
}
],
"description": "A lightweight core application framework.",
"time": "2018-11-12T07:55:59+00:00"
},
{
"name": "azuracast/azuraforms",
"version": "dev-master",
@ -95,16 +153,16 @@
},
{
"name": "cakephp/chronos",
"version": "1.2.2",
"version": "1.2.3",
"source": {
"type": "git",
"url": "https://github.com/cakephp/chronos.git",
"reference": "30f5b26bcf76a5e53ecc274700ad1ec49dc05567"
"reference": "395110125ff577f080fa064dca5c5608a4e77ee1"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/cakephp/chronos/zipball/30f5b26bcf76a5e53ecc274700ad1ec49dc05567",
"reference": "30f5b26bcf76a5e53ecc274700ad1ec49dc05567",
"url": "https://api.github.com/repos/cakephp/chronos/zipball/395110125ff577f080fa064dca5c5608a4e77ee1",
"reference": "395110125ff577f080fa064dca5c5608a4e77ee1",
"shasum": ""
},
"require": {
@ -148,7 +206,7 @@
"datetime",
"time"
],
"time": "2018-07-11T18:51:56+00:00"
"time": "2018-10-18T22:02:21+00:00"
},
{
"name": "clue/stream-filter",
@ -1225,16 +1283,16 @@
},
{
"name": "filp/whoops",
"version": "2.2.1",
"version": "2.3.1",
"source": {
"type": "git",
"url": "https://github.com/filp/whoops.git",
"reference": "e79cd403fb77fc8963a99ecc30e80ddd885b3311"
"reference": "bc0fd11bc455cc20ee4b5edabc63ebbf859324c7"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/filp/whoops/zipball/e79cd403fb77fc8963a99ecc30e80ddd885b3311",
"reference": "e79cd403fb77fc8963a99ecc30e80ddd885b3311",
"url": "https://api.github.com/repos/filp/whoops/zipball/bc0fd11bc455cc20ee4b5edabc63ebbf859324c7",
"reference": "bc0fd11bc455cc20ee4b5edabc63ebbf859324c7",
"shasum": ""
},
"require": {
@ -1282,7 +1340,7 @@
"throwable",
"whoops"
],
"time": "2018-06-30T13:14:06+00:00"
"time": "2018-10-23T09:00:00+00:00"
},
{
"name": "gettext/gettext",
@ -1705,12 +1763,12 @@
"source": {
"type": "git",
"url": "https://github.com/JamesHeinrich/getID3.git",
"reference": "dd1099bcef91ee9d750bba4eb3a0c93a73edebb8"
"reference": "50db613408bf3ffee85e4bab49fd756c4db97b98"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/JamesHeinrich/getID3/zipball/dd1099bcef91ee9d750bba4eb3a0c93a73edebb8",
"reference": "dd1099bcef91ee9d750bba4eb3a0c93a73edebb8",
"url": "https://api.github.com/repos/JamesHeinrich/getID3/zipball/50db613408bf3ffee85e4bab49fd756c4db97b98",
"reference": "50db613408bf3ffee85e4bab49fd756c4db97b98",
"shasum": ""
},
"require": {
@ -1760,7 +1818,7 @@
"php",
"tags"
],
"time": "2018-09-28T23:48:50+00:00"
"time": "2018-11-11T03:13:18+00:00"
},
{
"name": "league/plates",
@ -2001,16 +2059,16 @@
},
{
"name": "monolog/monolog",
"version": "1.23.0",
"version": "1.24.0",
"source": {
"type": "git",
"url": "https://github.com/Seldaek/monolog.git",
"reference": "fd8c787753b3a2ad11bc60c063cff1358a32a3b4"
"reference": "bfc9ebb28f97e7a24c45bdc3f0ff482e47bb0266"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/Seldaek/monolog/zipball/fd8c787753b3a2ad11bc60c063cff1358a32a3b4",
"reference": "fd8c787753b3a2ad11bc60c063cff1358a32a3b4",
"url": "https://api.github.com/repos/Seldaek/monolog/zipball/bfc9ebb28f97e7a24c45bdc3f0ff482e47bb0266",
"reference": "bfc9ebb28f97e7a24c45bdc3f0ff482e47bb0266",
"shasum": ""
},
"require": {
@ -2075,7 +2133,7 @@
"logging",
"psr-3"
],
"time": "2017-06-19T01:22:40+00:00"
"time": "2018-11-05T09:00:11+00:00"
},
{
"name": "nikic/fast-route",
@ -2244,16 +2302,16 @@
},
{
"name": "paragonie/certainty",
"version": "v2.1.0",
"version": "v2.2.0",
"source": {
"type": "git",
"url": "https://github.com/paragonie/certainty.git",
"reference": "30364451a3615d4b3813bb0857b85b10815b4487"
"reference": "d3e2777e1ca2b1401329a49c7d56d112e6414f23"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/paragonie/certainty/zipball/30364451a3615d4b3813bb0857b85b10815b4487",
"reference": "30364451a3615d4b3813bb0857b85b10815b4487",
"url": "https://api.github.com/repos/paragonie/certainty/zipball/d3e2777e1ca2b1401329a49c7d56d112e6414f23",
"reference": "d3e2777e1ca2b1401329a49c7d56d112e6414f23",
"shasum": ""
},
"require": {
@ -2263,6 +2321,7 @@
"php": "^5.5|^7"
},
"require-dev": {
"composer/composer": "^1",
"phpunit/phpunit": "^4|^5|^6"
},
"bin": [
@ -2299,7 +2358,7 @@
"ssl",
"tls"
],
"time": "2018-05-03T17:31:41+00:00"
"time": "2018-10-30T01:14:53+00:00"
},
{
"name": "paragonie/constant_time_encoding",
@ -2670,16 +2729,16 @@
},
{
"name": "php-http/message",
"version": "1.7.0",
"version": "1.7.2",
"source": {
"type": "git",
"url": "https://github.com/php-http/message.git",
"reference": "741f2266a202d59c4ed75436671e1b8e6f475ea3"
"reference": "b159ffe570dffd335e22ef0b91a946eacb182fa1"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/php-http/message/zipball/741f2266a202d59c4ed75436671e1b8e6f475ea3",
"reference": "741f2266a202d59c4ed75436671e1b8e6f475ea3",
"url": "https://api.github.com/repos/php-http/message/zipball/b159ffe570dffd335e22ef0b91a946eacb182fa1",
"reference": "b159ffe570dffd335e22ef0b91a946eacb182fa1",
"shasum": ""
},
"require": {
@ -2738,7 +2797,7 @@
"message",
"psr-7"
],
"time": "2018-08-15T06:37:30+00:00"
"time": "2018-11-01T09:32:41+00:00"
},
{
"name": "php-http/message-factory",
@ -3270,16 +3329,16 @@
},
{
"name": "symfony/console",
"version": "v4.1.4",
"version": "v4.1.7",
"source": {
"type": "git",
"url": "https://github.com/symfony/console.git",
"reference": "ca80b8ced97cf07390078b29773dc384c39eee1f"
"reference": "432122af37d8cd52fba1b294b11976e0d20df595"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/console/zipball/ca80b8ced97cf07390078b29773dc384c39eee1f",
"reference": "ca80b8ced97cf07390078b29773dc384c39eee1f",
"url": "https://api.github.com/repos/symfony/console/zipball/432122af37d8cd52fba1b294b11976e0d20df595",
"reference": "432122af37d8cd52fba1b294b11976e0d20df595",
"shasum": ""
},
"require": {
@ -3334,20 +3393,20 @@
],
"description": "Symfony Console Component",
"homepage": "https://symfony.com",
"time": "2018-07-26T11:24:31+00:00"
"time": "2018-10-31T09:30:44+00:00"
},
{
"name": "symfony/event-dispatcher",
"version": "v4.1.4",
"version": "v4.1.7",
"source": {
"type": "git",
"url": "https://github.com/symfony/event-dispatcher.git",
"reference": "bfb30c2ad377615a463ebbc875eba64a99f6aa3e"
"reference": "552541dad078c85d9414b09c041ede488b456cd5"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/bfb30c2ad377615a463ebbc875eba64a99f6aa3e",
"reference": "bfb30c2ad377615a463ebbc875eba64a99f6aa3e",
"url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/552541dad078c85d9414b09c041ede488b456cd5",
"reference": "552541dad078c85d9414b09c041ede488b456cd5",
"shasum": ""
},
"require": {
@ -3397,20 +3456,20 @@
],
"description": "Symfony EventDispatcher Component",
"homepage": "https://symfony.com",
"time": "2018-07-26T09:10:45+00:00"
"time": "2018-10-10T13:52:42+00:00"
},
{
"name": "symfony/finder",
"version": "v4.1.4",
"version": "v4.1.7",
"source": {
"type": "git",
"url": "https://github.com/symfony/finder.git",
"reference": "e162f1df3102d0b7472805a5a9d5db9fcf0a8068"
"reference": "1f17195b44543017a9c9b2d437c670627e96ad06"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/finder/zipball/e162f1df3102d0b7472805a5a9d5db9fcf0a8068",
"reference": "e162f1df3102d0b7472805a5a9d5db9fcf0a8068",
"url": "https://api.github.com/repos/symfony/finder/zipball/1f17195b44543017a9c9b2d437c670627e96ad06",
"reference": "1f17195b44543017a9c9b2d437c670627e96ad06",
"shasum": ""
},
"require": {
@ -3446,20 +3505,20 @@
],
"description": "Symfony Finder Component",
"homepage": "https://symfony.com",
"time": "2018-07-26T11:24:31+00:00"
"time": "2018-10-03T08:47:56+00:00"
},
{
"name": "symfony/options-resolver",
"version": "v4.1.4",
"version": "v4.1.7",
"source": {
"type": "git",
"url": "https://github.com/symfony/options-resolver.git",
"reference": "1913f1962477cdbb13df951f8147d5da1fe2412c"
"reference": "40f0e40d37c1c8a762334618dea597d64bbb75ff"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/options-resolver/zipball/1913f1962477cdbb13df951f8147d5da1fe2412c",
"reference": "1913f1962477cdbb13df951f8147d5da1fe2412c",
"url": "https://api.github.com/repos/symfony/options-resolver/zipball/40f0e40d37c1c8a762334618dea597d64bbb75ff",
"reference": "40f0e40d37c1c8a762334618dea597d64bbb75ff",
"shasum": ""
},
"require": {
@ -3500,20 +3559,20 @@
"configuration",
"options"
],
"time": "2018-07-26T08:55:25+00:00"
"time": "2018-09-18T12:45:12+00:00"
},
{
"name": "symfony/polyfill-mbstring",
"version": "v1.9.0",
"version": "v1.10.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-mbstring.git",
"reference": "d0cd638f4634c16d8df4508e847f14e9e43168b8"
"reference": "c79c051f5b3a46be09205c73b80b346e4153e494"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/d0cd638f4634c16d8df4508e847f14e9e43168b8",
"reference": "d0cd638f4634c16d8df4508e847f14e9e43168b8",
"url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/c79c051f5b3a46be09205c73b80b346e4153e494",
"reference": "c79c051f5b3a46be09205c73b80b346e4153e494",
"shasum": ""
},
"require": {
@ -3559,20 +3618,75 @@
"portable",
"shim"
],
"time": "2018-08-06T14:22:27+00:00"
"time": "2018-09-21T13:07:52+00:00"
},
{
"name": "symfony/process",
"version": "v4.1.6",
"name": "symfony/polyfill-php72",
"version": "v1.10.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/process.git",
"reference": "ee33c0322a8fee0855afcc11fff81e6b1011b529"
"url": "https://github.com/symfony/polyfill-php72.git",
"reference": "9050816e2ca34a8e916c3a0ae8b9c2fccf68b631"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/process/zipball/ee33c0322a8fee0855afcc11fff81e6b1011b529",
"reference": "ee33c0322a8fee0855afcc11fff81e6b1011b529",
"url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/9050816e2ca34a8e916c3a0ae8b9c2fccf68b631",
"reference": "9050816e2ca34a8e916c3a0ae8b9c2fccf68b631",
"shasum": ""
},
"require": {
"php": ">=5.3.3"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.9-dev"
}
},
"autoload": {
"psr-4": {
"Symfony\\Polyfill\\Php72\\": ""
},
"files": [
"bootstrap.php"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Nicolas Grekas",
"email": "p@tchwork.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Symfony polyfill backporting some PHP 7.2+ features to lower PHP versions",
"homepage": "https://symfony.com",
"keywords": [
"compatibility",
"polyfill",
"portable",
"shim"
],
"time": "2018-09-21T13:07:52+00:00"
},
{
"name": "symfony/process",
"version": "v4.1.7",
"source": {
"type": "git",
"url": "https://github.com/symfony/process.git",
"reference": "3e83acef94d979b1de946599ef86b3a352abcdc9"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/process/zipball/3e83acef94d979b1de946599ef86b3a352abcdc9",
"reference": "3e83acef94d979b1de946599ef86b3a352abcdc9",
"shasum": ""
},
"require": {
@ -3608,7 +3722,82 @@
],
"description": "Symfony Process Component",
"homepage": "https://symfony.com",
"time": "2018-10-02T12:40:59+00:00"
"time": "2018-10-14T20:48:13+00:00"
},
{
"name": "symfony/var-dumper",
"version": "v4.1.7",
"source": {
"type": "git",
"url": "https://github.com/symfony/var-dumper.git",
"reference": "60319b45653580b0cdacca499344577d87732f16"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/var-dumper/zipball/60319b45653580b0cdacca499344577d87732f16",
"reference": "60319b45653580b0cdacca499344577d87732f16",
"shasum": ""
},
"require": {
"php": "^7.1.3",
"symfony/polyfill-mbstring": "~1.0",
"symfony/polyfill-php72": "~1.5"
},
"conflict": {
"phpunit/phpunit": "<4.8.35|<5.4.3,>=5.0",
"symfony/console": "<3.4"
},
"require-dev": {
"ext-iconv": "*",
"symfony/process": "~3.4|~4.0",
"twig/twig": "~1.34|~2.4"
},
"suggest": {
"ext-iconv": "To convert non-UTF-8 strings to UTF-8 (or symfony/polyfill-iconv in case ext-iconv cannot be used).",
"ext-intl": "To show region name in time zone dump",
"symfony/console": "To use the ServerDumpCommand and/or the bin/var-dump-server script"
},
"bin": [
"Resources/bin/var-dump-server"
],
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "4.1-dev"
}
},
"autoload": {
"files": [
"Resources/functions/dump.php"
],
"psr-4": {
"Symfony\\Component\\VarDumper\\": ""
},
"exclude-from-classmap": [
"/Tests/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Nicolas Grekas",
"email": "p@tchwork.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Symfony mechanism for exploring and dumping PHP variables",
"homepage": "https://symfony.com",
"keywords": [
"debug",
"dump"
],
"time": "2018-10-02T16:36:10+00:00"
},
{
"name": "zendframework/zend-code",
@ -3886,16 +4075,16 @@
},
{
"name": "codeception/codeception",
"version": "2.5.0",
"version": "2.5.1",
"source": {
"type": "git",
"url": "https://github.com/Codeception/Codeception.git",
"reference": "dee493561daf644134c95cf176fd2c25aff59ea9"
"reference": "e0a658c64e98811a6fd4f6aa7c3222e0609066a9"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/Codeception/Codeception/zipball/dee493561daf644134c95cf176fd2c25aff59ea9",
"reference": "dee493561daf644134c95cf176fd2c25aff59ea9",
"url": "https://api.github.com/repos/Codeception/Codeception/zipball/e0a658c64e98811a6fd4f6aa7c3222e0609066a9",
"reference": "e0a658c64e98811a6fd4f6aa7c3222e0609066a9",
"shasum": ""
},
"require": {
@ -3974,20 +4163,20 @@
"functional testing",
"unit testing"
],
"time": "2018-09-24T09:33:01+00:00"
"time": "2018-10-29T21:23:19+00:00"
},
{
"name": "codeception/phpunit-wrapper",
"version": "7.3.1",
"version": "7.3.2",
"source": {
"type": "git",
"url": "https://github.com/Codeception/phpunit-wrapper.git",
"reference": "1967be130082effb3c30510cf8f8397fbd9d8b84"
"reference": "a5633c736e0e0022bc5065b27c63f2d1aa97b69f"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/Codeception/phpunit-wrapper/zipball/1967be130082effb3c30510cf8f8397fbd9d8b84",
"reference": "1967be130082effb3c30510cf8f8397fbd9d8b84",
"url": "https://api.github.com/repos/Codeception/phpunit-wrapper/zipball/a5633c736e0e0022bc5065b27c63f2d1aa97b69f",
"reference": "a5633c736e0e0022bc5065b27c63f2d1aa97b69f",
"shasum": ""
},
"require": {
@ -4017,7 +4206,7 @@
}
],
"description": "PHPUnit classes used by Codeception",
"time": "2018-09-28T15:36:26+00:00"
"time": "2018-10-07T21:30:01+00:00"
},
{
"name": "codeception/stub",
@ -4200,16 +4389,16 @@
},
{
"name": "mockery/mockery",
"version": "1.1.0",
"version": "1.2.0",
"source": {
"type": "git",
"url": "https://github.com/mockery/mockery.git",
"reference": "99e29d3596b16dabe4982548527d5ddf90232e99"
"reference": "100633629bf76d57430b86b7098cd6beb996a35a"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/mockery/mockery/zipball/99e29d3596b16dabe4982548527d5ddf90232e99",
"reference": "99e29d3596b16dabe4982548527d5ddf90232e99",
"url": "https://api.github.com/repos/mockery/mockery/zipball/100633629bf76d57430b86b7098cd6beb996a35a",
"reference": "100633629bf76d57430b86b7098cd6beb996a35a",
"shasum": ""
},
"require": {
@ -4218,8 +4407,7 @@
"php": ">=5.6.0"
},
"require-dev": {
"phpdocumentor/phpdocumentor": "^2.9",
"phpunit/phpunit": "~5.7.10|~6.5"
"phpunit/phpunit": "~5.7.10|~6.5|~7.0"
},
"type": "library",
"extra": {
@ -4262,7 +4450,7 @@
"test double",
"testing"
],
"time": "2018-05-08T08:54:48+00:00"
"time": "2018-10-02T21:52:37+00:00"
},
{
"name": "myclabs/deep-copy",
@ -4631,16 +4819,16 @@
},
{
"name": "phpunit/php-code-coverage",
"version": "6.0.7",
"version": "6.1.4",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/php-code-coverage.git",
"reference": "865662550c384bc1db7e51d29aeda1c2c161d69a"
"reference": "807e6013b00af69b6c5d9ceb4282d0393dbb9d8d"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/865662550c384bc1db7e51d29aeda1c2c161d69a",
"reference": "865662550c384bc1db7e51d29aeda1c2c161d69a",
"url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/807e6013b00af69b6c5d9ceb4282d0393dbb9d8d",
"reference": "807e6013b00af69b6c5d9ceb4282d0393dbb9d8d",
"shasum": ""
},
"require": {
@ -4651,7 +4839,7 @@
"phpunit/php-text-template": "^1.2.1",
"phpunit/php-token-stream": "^3.0",
"sebastian/code-unit-reverse-lookup": "^1.0.1",
"sebastian/environment": "^3.1",
"sebastian/environment": "^3.1 || ^4.0",
"sebastian/version": "^2.0.1",
"theseer/tokenizer": "^1.1"
},
@ -4664,7 +4852,7 @@
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "6.0-dev"
"dev-master": "6.1-dev"
}
},
"autoload": {
@ -4690,7 +4878,7 @@
"testing",
"xunit"
],
"time": "2018-06-01T07:51:50+00:00"
"time": "2018-10-31T16:06:48+00:00"
},
{
"name": "phpunit/php-file-iterator",
@ -4834,16 +5022,16 @@
},
{
"name": "phpunit/php-token-stream",
"version": "3.0.0",
"version": "3.0.1",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/php-token-stream.git",
"reference": "21ad88bbba7c3d93530d93994e0a33cd45f02ace"
"reference": "c99e3be9d3e85f60646f152f9002d46ed7770d18"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/21ad88bbba7c3d93530d93994e0a33cd45f02ace",
"reference": "21ad88bbba7c3d93530d93994e0a33cd45f02ace",
"url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/c99e3be9d3e85f60646f152f9002d46ed7770d18",
"reference": "c99e3be9d3e85f60646f152f9002d46ed7770d18",
"shasum": ""
},
"require": {
@ -4879,7 +5067,7 @@
"keywords": [
"tokenizer"
],
"time": "2018-02-01T13:16:43+00:00"
"time": "2018-10-30T05:52:18+00:00"
},
{
"name": "phpunit/phpunit",
@ -4971,6 +5159,7 @@
"conflict": {
"3f/pygmentize": "<1.2",
"adodb/adodb-php": "<5.20.12",
"alterphp/easyadmin-extension-bundle": ">=1.2,<1.2.11|>=1.3,<1.3.1",
"amphp/artax": "<1.0.6|>=2,<2.0.6",
"amphp/http": "<1.0.1",
"asymmetricrypt/asymmetricrypt": ">=0,<9.9.99",
@ -4998,12 +5187,14 @@
"doctrine/mongodb-odm-bundle": ">=2,<3.0.1",
"doctrine/orm": ">=2,<2.4.8|>=2.5,<2.5.1",
"dompdf/dompdf": ">=0.6,<0.6.2",
"drupal/core": ">=7,<7.59|>=8,<8.4.8|>=8.5,<8.5.3",
"drupal/drupal": ">=7,<7.59|>=8,<8.4.8|>=8.5,<8.5.3",
"drupal/core": ">=7,<7.60|>=8,<8.5.8|>=8.6,<8.6.2",
"drupal/drupal": ">=7,<7.60|>=8,<8.5.8|>=8.6,<8.6.2",
"erusev/parsedown": "<1.7",
"ezsystems/ezpublish-legacy": ">=5.3,<5.3.12.3|>=5.4,<5.4.11.3|>=2017.8,<2017.8.1.1|>=2017.12,<2017.12.2.1",
"ezsystems/ezpublish-legacy": ">=5.3,<5.3.12.5|>=5.4,<5.4.12.2|>=2017.8,<2017.8.1.1|>=2017.12,<2017.12.4.2|>=2018.6,<2018.6.1.3|>=2018.9,<2018.9.1.2",
"ezyang/htmlpurifier": "<4.1.1",
"firebase/php-jwt": "<2",
"fooman/tcpdf": "<6.2.22",
"fossar/tcpdf-parser": "<6.2.22",
"friendsofsymfony/rest-bundle": ">=1.2,<1.2.2",
"friendsofsymfony/user-bundle": ">=1.2,<1.3.5",
"fuel/core": "<1.8.1",
@ -5020,11 +5211,12 @@
"jsmitty12/phpwhois": "<5.1",
"kazist/phpwhois": "<=4.2.6",
"kreait/firebase-php": ">=3.2,<3.8.1",
"la-haute-societe/tcpdf": "<6.2.22",
"laravel/framework": ">=4,<4.0.99|>=4.1,<=4.1.31|>=4.2,<=4.2.22|>=5,<=5.0.35|>=5.1,<=5.1.46|>=5.2,<=5.2.45|>=5.3,<=5.3.31|>=5.4,<=5.4.36|>=5.5,<5.5.42|>=5.6,<5.6.30",
"laravel/socialite": ">=1,<1.0.99|>=2,<2.0.10",
"magento/magento1ce": "<1.9.3.9",
"magento/magento1ee": ">=1.9,<1.14.3.2",
"magento/product-community-edition": ">=2,<2.2.5",
"magento/product-community-edition": ">=2,<2.2.6",
"monolog/monolog": ">=1.8,<1.12",
"namshi/jose": "<2.2",
"onelogin/php-saml": "<2.10.4",
@ -5042,6 +5234,7 @@
"propel/propel": ">=2.0.0-alpha1,<=2.0.0-alpha7",
"propel/propel1": ">=1,<=1.7.1",
"pusher/pusher-php-server": "<2.2.1",
"robrichards/xmlseclibs": ">=1,<3.0.2",
"sabre/dav": ">=1.6,<1.6.99|>=1.7,<1.7.11|>=1.8,<1.8.9",
"sensiolabs/connect": "<4.2.3",
"serluck/phpwhois": "<=4.2.6",
@ -5057,6 +5250,7 @@
"slim/slim": "<2.6",
"smarty/smarty": "<3.1.33",
"socalnick/scn-social-auth": "<1.15.2",
"spoonity/tcpdf": "<6.2.22",
"squizlabs/php_codesniffer": ">=1,<2.8.1|>=3,<3.0.1",
"stormpath/sdk": ">=0,<9.9.99",
"swiftmailer/swiftmailer": ">=4,<5.4.5",
@ -5068,6 +5262,8 @@
"symfony/http-foundation": ">=2,<2.7.49|>=2.8,<2.8.44|>=3,<3.3.18|>=3.4,<3.4.14|>=4,<4.0.14|>=4.1,<4.1.3",
"symfony/http-kernel": ">=2,<2.3.29|>=2.4,<2.5.12|>=2.6,<2.6.8",
"symfony/intl": ">=2.7,<2.7.38|>=2.8,<2.8.31|>=3,<3.2.14|>=3.3,<3.3.13",
"symfony/polyfill": ">=1,<1.10",
"symfony/polyfill-php55": ">=1,<1.10",
"symfony/routing": ">=2,<2.0.19",
"symfony/security": ">=2,<2.7.48|>=2.8,<2.8.41|>=3,<3.3.17|>=3.4,<3.4.11|>=4,<4.0.11",
"symfony/security-bundle": ">=2,<2.7.48|>=2.8,<2.8.41|>=3,<3.3.17|>=3.4,<3.4.11|>=4,<4.0.11",
@ -5081,6 +5277,7 @@
"symfony/validator": ">=2,<2.0.24|>=2.1,<2.1.12|>=2.2,<2.2.5|>=2.3,<2.3.3",
"symfony/web-profiler-bundle": ">=2,<2.3.19|>=2.4,<2.4.9|>=2.5,<2.5.4",
"symfony/yaml": ">=2,<2.0.22|>=2.1,<2.1.7",
"tecnickcom/tcpdf": "<6.2.22",
"thelia/backoffice-default-template": ">=2.1,<2.1.2",
"thelia/thelia": ">=2.1.0-beta1,<2.1.3|>=2.1,<2.1.2",
"theonedemon/phpwhois": "<=4.2.5",
@ -5091,6 +5288,7 @@
"typo3/cms-core": ">=8,<8.7.17|>=9,<9.3.2",
"typo3/flow": ">=1,<1.0.4|>=1.1,<1.1.1|>=2,<2.0.1|>=2.3,<2.3.16|>=3,<3.0.10|>=3.1,<3.1.7|>=3.2,<3.2.7|>=3.3,<3.3.5",
"typo3/neos": ">=1.1,<1.1.3|>=1.2,<1.2.13|>=2,<2.0.4",
"wallabag/tcpdf": "<6.2.22",
"willdurand/js-translation-bundle": "<2.1.1",
"yiisoft/yii": ">=1.1.14,<1.1.15",
"yiisoft/yii2": "<2.0.15",
@ -5138,7 +5336,7 @@
}
],
"description": "Prevents installation of composer packages with known security vulnerabilities: no API, simply require it",
"time": "2018-09-17T20:20:31+00:00"
"time": "2018-11-01T18:39:28+00:00"
},
{
"name": "sebastian/code-unit-reverse-lookup",
@ -5756,7 +5954,7 @@
},
{
"name": "symfony/browser-kit",
"version": "v4.1.4",
"version": "v4.1.7",
"source": {
"type": "git",
"url": "https://github.com/symfony/browser-kit.git",
@ -5813,16 +6011,16 @@
},
{
"name": "symfony/css-selector",
"version": "v4.1.4",
"version": "v4.1.7",
"source": {
"type": "git",
"url": "https://github.com/symfony/css-selector.git",
"reference": "2a4df7618f869b456f9096781e78c57b509d76c7"
"reference": "d67de79a70a27d93c92c47f37ece958bf8de4d8a"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/css-selector/zipball/2a4df7618f869b456f9096781e78c57b509d76c7",
"reference": "2a4df7618f869b456f9096781e78c57b509d76c7",
"url": "https://api.github.com/repos/symfony/css-selector/zipball/d67de79a70a27d93c92c47f37ece958bf8de4d8a",
"reference": "d67de79a70a27d93c92c47f37ece958bf8de4d8a",
"shasum": ""
},
"require": {
@ -5862,20 +6060,20 @@
],
"description": "Symfony CssSelector Component",
"homepage": "https://symfony.com",
"time": "2018-07-26T09:10:45+00:00"
"time": "2018-10-02T16:36:10+00:00"
},
{
"name": "symfony/dom-crawler",
"version": "v4.1.4",
"version": "v4.1.7",
"source": {
"type": "git",
"url": "https://github.com/symfony/dom-crawler.git",
"reference": "1c4519d257e652404c3aa550207ccd8ada66b38e"
"reference": "80e60271bb288de2a2259662cff125cff4f93f95"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/dom-crawler/zipball/1c4519d257e652404c3aa550207ccd8ada66b38e",
"reference": "1c4519d257e652404c3aa550207ccd8ada66b38e",
"url": "https://api.github.com/repos/symfony/dom-crawler/zipball/80e60271bb288de2a2259662cff125cff4f93f95",
"reference": "80e60271bb288de2a2259662cff125cff4f93f95",
"shasum": ""
},
"require": {
@ -5919,11 +6117,11 @@
],
"description": "Symfony DomCrawler Component",
"homepage": "https://symfony.com",
"time": "2018-07-26T11:00:49+00:00"
"time": "2018-10-02T12:40:59+00:00"
},
{
"name": "symfony/polyfill-ctype",
"version": "v1.9.0",
"version": "v1.10.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-ctype.git",
@ -5981,16 +6179,16 @@
},
{
"name": "symfony/yaml",
"version": "v4.1.4",
"version": "v4.1.7",
"source": {
"type": "git",
"url": "https://github.com/symfony/yaml.git",
"reference": "b832cc289608b6d305f62149df91529a2ab3c314"
"reference": "367e689b2fdc19965be435337b50bc8adf2746c9"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/yaml/zipball/b832cc289608b6d305f62149df91529a2ab3c314",
"reference": "b832cc289608b6d305f62149df91529a2ab3c314",
"url": "https://api.github.com/repos/symfony/yaml/zipball/367e689b2fdc19965be435337b50bc8adf2746c9",
"reference": "367e689b2fdc19965be435337b50bc8adf2746c9",
"shasum": ""
},
"require": {
@ -6036,7 +6234,7 @@
],
"description": "Symfony Yaml Component",
"homepage": "https://symfony.com",
"time": "2018-08-18T16:52:46+00:00"
"time": "2018-10-02T16:36:10+00:00"
},
{
"name": "theseer/tokenizer",
@ -6130,16 +6328,16 @@
},
{
"name": "zircote/swagger-php",
"version": "3.0.0",
"version": "3.0.1",
"source": {
"type": "git",
"url": "https://github.com/zircote/swagger-php.git",
"reference": "67f517375c0c8180bf2c09dbad6f305524cff2ab"
"reference": "8fc3bc059a7f71b3f100bcfd84a96b5b8fcf6fcf"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/zircote/swagger-php/zipball/67f517375c0c8180bf2c09dbad6f305524cff2ab",
"reference": "67f517375c0c8180bf2c09dbad6f305524cff2ab",
"url": "https://api.github.com/repos/zircote/swagger-php/zipball/8fc3bc059a7f71b3f100bcfd84a96b5b8fcf6fcf",
"reference": "8fc3bc059a7f71b3f100bcfd84a96b5b8fcf6fcf",
"shasum": ""
},
"require": {
@ -6189,12 +6387,13 @@
"rest",
"service discovery"
],
"time": "2018-08-16T06:06:29+00:00"
"time": "2018-09-30T12:19:07+00:00"
}
],
"aliases": [],
"minimum-stability": "dev",
"stability-flags": {
"azuracast/azuracore": 20,
"azuracast/azuraforms": 20,
"azuracast/nowplaying": 20,
"james-heinrich/getid3": 20,
@ -6207,9 +6406,6 @@
"ext-fileinfo": "*",
"ext-gd": "*",
"ext-intl": "*",
"ext-json": "*",
"ext-pdo": "*",
"ext-redis": "*",
"ext-simplexml": "*",
"ext-xml": "*",
"ext-xmlwriter": "*"

View File

@ -1,68 +1,23 @@
<?php
use App\Event;
use App\Middleware;
use App\Console\Command;
return function (\App\EventDispatcher $dispatcher)
return function (\Azura\EventDispatcher $dispatcher)
{
// Build default routes and middleware
$dispatcher->addListener(Event\BuildRoutes::NAME, function(Event\BuildRoutes $event) {
$dispatcher->addListener(Azura\Event\BuildRoutes::NAME, function(Azura\Event\BuildRoutes $event) {
$app = $event->getApp();
// Get the current user entity object and assign it into the request if it exists.
$app->add(Middleware\GetCurrentUser::class);
// Inject the application router into the request object.
$app->add(Middleware\EnableRouter::class);
// Inject the session manager into the request object.
$app->add(Middleware\EnableSession::class);
// Check HTTPS setting and enforce Content Security Policy accordingly.
$app->add(Middleware\EnforceSecurity::class);
// Remove trailing slash from all URLs when routing.
$app->add(Middleware\RemoveSlashes::class);
}, 1);
$dispatcher->addListener(Event\BuildRoutes::NAME, function(Event\BuildRoutes $event) {
call_user_func(include(__DIR__.'/routes.php'), $event->getApp());
}, 0);
}, 2);
// Build CLI commands
$dispatcher->addListener(Event\BuildConsoleCommands::NAME, function(Event\BuildConsoleCommands $event) {
$em = $event->getConsole()->getService(\Doctrine\ORM\EntityManager::class);
// Doctrine ORM/DBAL
\Doctrine\ORM\Tools\Console\ConsoleRunner::addCommands($event->getConsole());
// Doctrine Migrations
$migrate_config = new \Doctrine\DBAL\Migrations\Configuration\Configuration($em->getConnection());
$migrate_config->setMigrationsTableName('app_migrations');
$migrate_config->setMigrationsDirectory(dirname(__DIR__).'/src/Entity/Migration');
$migrate_config->setMigrationsNamespace('App\Entity\Migration');
$output = new \Symfony\Component\Console\Output\ConsoleOutput;
$migrate_config->setOutputWriter(new \Doctrine\DBAL\Migrations\OutputWriter(function($message) use ($output) {
$output->writeln($message);
}));
$migration_commands = [
new \Doctrine\DBAL\Migrations\Tools\Console\Command\DiffCommand,
new \Doctrine\DBAL\Migrations\Tools\Console\Command\ExecuteCommand,
new \Doctrine\DBAL\Migrations\Tools\Console\Command\GenerateCommand,
new \Doctrine\DBAL\Migrations\Tools\Console\Command\MigrateCommand,
new \Doctrine\DBAL\Migrations\Tools\Console\Command\StatusCommand,
new \Doctrine\DBAL\Migrations\Tools\Console\Command\VersionCommand
];
foreach($migration_commands as $cmd) {
$cmd->setMigrationConfiguration($migrate_config);
$event->getConsole()->add($cmd);
}
}, 1);
$dispatcher->addListener(Event\BuildConsoleCommands::NAME, function(Event\BuildConsoleCommands $event) {
$dispatcher->addListener(Azura\Event\BuildConsoleCommands::NAME, function(Azura\Event\BuildConsoleCommands $event) {
$event->getConsole()->addCommands([
// Liquidsoap Internal CLI Commands
new Command\NextSong,
@ -81,7 +36,6 @@ return function (\App\EventDispatcher $dispatcher)
new Command\Setup,
// Maintenance
new Command\ClearCache,
new Command\RestartRadio,
new Command\Sync,
new Command\ReprocessMedia,

View File

@ -3,7 +3,7 @@ use \App\Entity\StationPlaylist;
/** @var \App\Customization $customization */
$local_time_offset = \App\Timezone::getOffsetMinutes(null);
$local_time_offset = \Azura\Timezone::getOffsetMinutes(null);
$local_time_hours = floor($local_time_offset / 60);
$local_time_mins = $local_time_offset % 60;

View File

@ -81,7 +81,7 @@ return [
'label' => __('Time Zone'),
'description' => __('All times displayed on the site will be based on this time zone.') . '<br>' . sprintf(__('Current server time is <b>%s</b>.'),
date('g:ia')),
'options' => \App\Timezone::fetchSelect(),
'options' => \Azura\Timezone::fetchSelect(),
'default' => 'UTC',
]
],

View File

@ -31,7 +31,7 @@ return [
[
'label' => __('System Default Time Zone'),
'description' => __('For users who have not customized their time zone, all times displayed on the site will be based on this time zone.'),
'options' => \App\Timezone::fetchSelect(),
'options' => \Azura\Timezone::fetchSelect(),
'default' => 'UTC',
],
],

View File

@ -1,6 +1,7 @@
<?php
use App\Controller;
use App\Middleware;
use Azura\Middleware as AzuraMiddleware;
return function(\Slim\App $app)
{
@ -115,7 +116,7 @@ return function(\Slim\App $app)
})
->add(Middleware\Module\Admin::class)
->add(Middleware\EnableView::class)
->add(AzuraMiddleware\EnableView::class)
->add([Middleware\Permissions::class, 'view administration'])
->add(Middleware\RequireLogin::class);
@ -167,13 +168,13 @@ return function(\Slim\App $app)
$this->get('/stations', Controller\Api\Stations\IndexController::class.':listAction')
->setName('api:stations:list')
->add([Middleware\RateLimit::class, 'api', 5, 2]);
->add([AzuraMiddleware\RateLimit::class, 'api', 5, 2]);
$this->group('/station/{station}', function () {
$this->get('', Controller\Api\Stations\IndexController::class.':indexAction')
->setName('api:stations:index')
->add([Middleware\RateLimit::class, 'api', 5, 2]);
->add([AzuraMiddleware\RateLimit::class, 'api', 5, 2]);
$this->get('/nowplaying', Controller\Api\NowplayingController::class.':indexAction');
@ -188,7 +189,7 @@ return function(\Slim\App $app)
$this->map(['GET', 'POST'], '/request/{media_id}', Controller\Api\RequestsController::class.':submitAction')
->setName('api:requests:submit')
->add([Middleware\RateLimit::class, 'api', 5, 2]);
->add([AzuraMiddleware\RateLimit::class, 'api', 5, 2]);
$this->get('/listeners', Controller\Api\ListenersController::class.':indexAction')
->setName('api:listeners:index')
@ -257,12 +258,12 @@ return function(\Slim\App $app)
}
})
->add(Middleware\EnableView::class)
->add(AzuraMiddleware\EnableView::class)
->add(Middleware\RequireLogin::class);
$app->map(['GET', 'POST'], '/login', Controller\Frontend\AccountController::class.':loginAction')
->setName('account:login')
->add(Middleware\EnableView::class);
->add(AzuraMiddleware\EnableView::class);
$app->group('/setup', function () {
@ -282,7 +283,7 @@ return function(\Slim\App $app)
->setName('setup:settings');
})
->add(Middleware\EnableView::class);
->add(AzuraMiddleware\EnableView::class);
$app->group('/public/{station}', function () {
@ -300,7 +301,7 @@ return function(\Slim\App $app)
})
->add(Middleware\GetStation::class)
->add(Middleware\EnableView::class);
->add(AzuraMiddleware\EnableView::class);
$app->group('/station/{station}', function () {
@ -498,7 +499,7 @@ return function(\Slim\App $app)
->add(Middleware\Module\Stations::class)
->add([Middleware\Permissions::class, 'view station management', true])
->add(Middleware\GetStation::class)
->add(Middleware\EnableView::class)
->add(AzuraMiddleware\EnableView::class)
->add(Middleware\RequireLogin::class);
};

View File

@ -1,148 +1,48 @@
<?php
return function (\Slim\Container $di, $settings)
return function (\Azura\Container $di)
{
$di['app_settings'] = $settings;
// Override Slim handlers.
$di['request'] = function (\Slim\Container $di) {
$di['request'] = function (\Azura\Container $di) {
return \App\Http\Request::createFromEnvironment($di->get('environment'));
};
$di['response'] = function (\Slim\Container $di) {
$di['response'] = function (\Azura\Container $di) {
$headers = new \Slim\Http\Headers(['Content-Type' => 'text/html; charset=UTF-8']);
$response = new \App\Http\Response(200, $headers, null);
return $response->withProtocolVersion($di->get('settings')['httpVersion']);
};
$di['router'] = function(\Slim\Container $di) {
$routerCacheFile = $di['settings']['routerCacheFile'];
$router = (new \App\Http\Router())->setCacheFile($routerCacheFile);
$router->setContainer($di);
$di['router'] = function(\Azura\Container $container) {
$routerCacheFile = $container->get('settings')[\Azura\Settings::SLIM_ROUTER_CACHE_FILE];
$router = new \App\Http\Router();
$router->setCacheFile($routerCacheFile);
$router->setContainer($container);
return $router;
};
$di['callableResolver'] = function ($di) {
return new \App\Http\Resolver($di);
};
$di['errorHandler'] = function ($di) {
return $di[\App\Http\ErrorHandler::class];
};
$di['phpErrorHandler'] = function($di) {
return $di[\App\Http\ErrorHandler::class];
$di[\App\Http\ErrorHandler::class] = function($di) {
return new \App\Http\ErrorHandler(
$di[\App\Acl::class],
$di[\Monolog\Logger::class],
$di['router'],
$di[\Azura\Session::class],
$di[\Azura\View::class]
);
};
$di->addAlias('phpErrorHandler', \App\Http\ErrorHandler::class);
$di->addAlias('errorHandler', \App\Http\ErrorHandler::class);
$di['notFoundHandler'] = function ($di) {
return function (\App\Http\Request $request, \App\Http\Response $response) use ($di) {
/** @var \App\View $view */
$view = $di[App\View::class];
/** @var \Azura\View $view */
$view = $di[\Azura\View::class];
return $view->renderToResponse($response->withStatus(404), 'system/error_pagenotfound');
};
};
$di['foundHandler'] = function() {
return new \Slim\Handlers\Strategies\RequestResponseArgs();
};
$di[\App\Config::class] = function () {
return new \App\Config(__DIR__);
};
$di[\Doctrine\ORM\EntityManager::class] = function ($di) {
try {
$options = [
'autoGenerateProxies' => !APP_IN_PRODUCTION,
'proxyNamespace' => 'AppProxy',
'proxyPath' => APP_INCLUDE_TEMP . '/proxies',
'modelPath' => APP_INCLUDE_ROOT . '/src/Entity',
'conn' => [
'driver' => 'pdo_mysql',
'charset' => 'utf8mb4',
'defaultTableOptions' => [
'charset' => 'utf8mb4',
'collate' => 'utf8mb4_general_ci',
],
'driverOptions' => [
\PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8mb4 COLLATE utf8mb4_general_ci',
],
'platform' => new \Doctrine\DBAL\Platforms\MariaDb1027Platform(),
]
];
if (APP_INSIDE_DOCKER) {
$options['conn']['host'] = $_ENV['MYSQL_HOST'] ?? 'mariadb';
$options['conn']['port'] = $_ENV['MYSQL_PORT'] ?? 3306;
$options['conn']['dbname'] = $_ENV['MYSQL_DATABASE'] ?? 'azuracast';
$options['conn']['user'] = $_ENV['MYSQL_USER'] ?? 'azuracast';
$options['conn']['password'] = $_ENV['MYSQL_PASSWORD'] ?? 'azur4c457';
} else {
$options['conn']['host'] = $_ENV['db_host'] ?? 'localhost';
$options['conn']['port'] = $_ENV['db_port'] ?? '3306';
$options['conn']['dbname'] = $_ENV['db_name'] ?? 'azuracast';
$options['conn']['user'] = $_ENV['db_username'] ?? 'azuracast';
$options['conn']['password'] = $_ENV['db_password'];
}
// Fetch and store entity manager.
$config = new \Doctrine\ORM\Configuration;
$metadata_driver = $config->newDefaultAnnotationDriver($options['modelPath']);
$config->setMetadataDriverImpl($metadata_driver);
$repo_factory = new \App\Doctrine\RepositoryFactory($di);
$config->setRepositoryFactory($repo_factory);
if (APP_IN_PRODUCTION) {
/** @var \Redis $redis */
$redis = $di[\Redis::class];
$redis->select(2);
$cache = new \App\Doctrine\Cache\Redis;
$cache->setRedis($redis);
} else {
$cache = new \Doctrine\Common\Cache\ArrayCache;
}
$config->setMetadataCacheImpl($cache);
$config->setQueryCacheImpl($cache);
$config->setResultCacheImpl($cache);
// Disable second-level cache for unit testing purposes, as it causes data to be out of date on pages.
if (APP_TESTING_MODE) {
$config->setSecondLevelCacheEnabled(false);
}
$config->setProxyDir($options['proxyPath']);
$config->setProxyNamespace($options['proxyNamespace']);
$config->setAutoGenerateProxyClasses(\Doctrine\Common\Proxy\AbstractProxyFactory::AUTOGENERATE_FILE_NOT_EXISTS);
$config->setDefaultRepositoryClassName(\App\Entity\Repository\BaseRepository::class);
if (isset($options['conn']['debug']) && $options['conn']['debug']) {
$config->setSQLLogger(new \Doctrine\DBAL\Logging\EchoSQLLogger);
}
$config->addCustomNumericFunction('RAND', '\App\Doctrine\Functions\Rand');
$config->addCustomStringFunction('FIELD', 'DoctrineExtensions\Query\Mysql\Field');
$config->addCustomStringFunction('IF', 'DoctrineExtensions\Query\Mysql\IfElse');
$em = \Doctrine\ORM\EntityManager::create($options['conn'], $config, new \Doctrine\Common\EventManager);
return $em;
} catch (\Exception $e) {
throw new \App\Exception\Bootstrap($e->getMessage());
}
};
$di[\Doctrine\DBAL\Connection::class] = function ($di) {
/** @var \Doctrine\ORM\EntityManager $em */
$em = $di[\Doctrine\ORM\EntityManager::class];
return $em->getConnection();
};
$di[\App\Entity\Repository\SettingsRepository::class] = function($di) {
/** @var \Doctrine\ORM\EntityManager $em */
$em = $di[\Doctrine\ORM\EntityManager::class];
@ -160,7 +60,7 @@ return function (\Slim\Container $di, $settings)
return new \App\Entity\Repository\StationPlaylistMediaRepository(
$em,
$em->getClassMetadata(\App\Entity\StationPlaylistMedia::class),
$di[\App\Cache::class]
$di[\Azura\Cache::class]
);
};
@ -171,7 +71,7 @@ return function (\Slim\Container $di, $settings)
/** @var App\Entity\Repository\UserRepository $user_repo */
$user_repo = $em->getRepository(App\Entity\User::class);
return new \App\Auth($di[\App\Session::class], $user_repo);
return new \App\Auth($di[\Azura\Session::class], $user_repo);
};
$di[\App\Acl::class] = function ($di) {
@ -184,36 +84,6 @@ return function (\Slim\Container $di, $settings)
return new \App\Acl($permissions_repo);
};
$di[\Redis::class] = $di->factory(function ($di) {
$redis_host = (APP_INSIDE_DOCKER) ? 'redis' : 'localhost';
$redis = new \Redis();
$redis->connect($redis_host, 6379, 15);
return $redis;
});
$di[\App\Cache::class] = function ($di) {
/** @var \Redis $redis */
$redis = $di[\Redis::class];
$redis->select(0);
return new \App\Cache($redis);
};
$di[\App\Session::class] = function ($di) {
if (!APP_TESTING_MODE) {
ini_set('session.gc_maxlifetime', 86400);
ini_set('session.gc_probability', 1);
ini_set('session.gc_divisor', 100);
$redis_server = (APP_INSIDE_DOCKER) ? 'redis' : 'localhost';
ini_set('session.save_handler', 'redis');
ini_set('session.save_path', 'tcp://' . $redis_server . ':6379?database=1');
}
return new \App\Session;
};
$di[\InfluxDB\Database::class] = function ($di) {
$opts = [
'host' => (APP_INSIDE_DOCKER) ? 'influxdb' : 'localhost',
@ -239,31 +109,18 @@ return function (\Slim\Container $di, $settings)
$supervisor = new \Supervisor\Supervisor($connector);
if (!$supervisor->isConnected()) {
throw new \App\Exception(sprintf('Could not connect to supervisord.'));
throw new \Azura\Exception(sprintf('Could not connect to supervisord.'));
}
return $supervisor;
};
$di[\App\View::class] = $di->factory(function(\Slim\Container $di) {
$view = new App\View(dirname(__DIR__) . '/resources/templates');
$view->setFileExtension('phtml');
$view->registerFunction('service', function($service) {
return $this->di->get($service);
});
$view->registerFunction('escapeJs', function($string) {
return json_encode($string);
});
$di->extend(\Azura\View::class, function(\Azura\View $view, \Azura\Container $di) {
$view->registerFunction('mailto', function ($address, $link_text = null) {
$address = substr(chunk_split(bin2hex(" $address"), 2, ";&#x"), 3, -3);
$link_text = $link_text ?? $address;
return '<a href="mailto:' . $address . '">' . $link_text . '</a>';
});
$view->registerFunction('pluralize', function ($word, $num = 0) {
if ((int)$num === 1) {
return $word;
@ -271,106 +128,37 @@ return function (\Slim\Container $di, $settings)
return \Doctrine\Common\Inflector\Inflector::pluralize($word);
}
});
$view->registerFunction('truncate', function ($text, $length = 80) {
return \App\Utilities::truncate_text($text, $length);
});
/** @var \App\Session $session */
$session = $di[\App\Session::class];
$view->addData([
'app_settings' => $di['app_settings'],
'router' => $di['router'],
'request' => $di['request'],
'assets' => $di[\App\Assets::class],
'assets' => $di[\Azura\Assets::class],
'auth' => $di[\App\Auth::class],
'acl' => $di[\App\Acl::class],
'flash' => $session->getFlash(),
'customization' => $di[\App\Customization::class],
'version' => $di[\App\Version::class],
]);
/** @var \App\EventDispatcher $dispatcher */
$dispatcher = $di[\App\EventDispatcher::class];
$dispatcher->dispatch(\App\Event\BuildView::NAME, new \App\Event\BuildView($view));
return $view;
});
$di[\App\Http\ErrorHandler::class] = function($di) {
return new \App\Http\ErrorHandler(
$di[\App\Acl::class],
$di[\Monolog\Logger::class],
$di['router'],
$di[\App\Session::class],
$di[\App\View::class]
);
};
$di[\Monolog\Logger::class] = function($di) use ($settings) {
$logger = new Monolog\Logger($settings['name']);
$logging_level = (APP_IN_PRODUCTION) ? \Monolog\Logger::INFO : \Monolog\Logger::DEBUG;
if (APP_INSIDE_DOCKER || APP_IS_COMMAND_LINE) {
$log_stderr = new \Monolog\Handler\StreamHandler('php://stderr', $logging_level, true);
$logger->pushHandler($log_stderr);
}
$log_file = new \Monolog\Handler\StreamHandler(APP_INCLUDE_TEMP.'/azuracast.log', $logging_level, true);
$logger->pushHandler($log_file);
return $logger;
};
$di[\MaxMind\Db\Reader::class] = function($di) {
$mmdb_path = dirname(APP_INCLUDE_ROOT).'/geoip/GeoLite2-City.mmdb';
return new \MaxMind\Db\Reader($mmdb_path);
};
$di[\GuzzleHttp\Client::class] = function($di) {
$stack = \GuzzleHttp\HandlerStack::create();
$di->extend(\Azura\EventDispatcher::class, function(\Azura\EventDispatcher $dispatcher, \Azura\Container $di) {
if (isset($di[\App\Plugins::class])) {
/** @var \App\Plugins $plugins */
$plugins = $di[\App\Plugins::class];
$stack->unshift(function (callable $handler) use ($di) {
return function (\Psr\Http\Message\RequestInterface $request, array $options) use ($handler, $di) {
if ($request->getUri()->getScheme() === 'https') {
$fetcher = new \ParagonIE\Certainty\RemoteFetch(APP_INCLUDE_TEMP);
$latestCACertBundle = $fetcher->getLatestBundle();
$options['verify'] = $latestCACertBundle->getFilePath();
}
return $handler($request, $options);
};
}, 'ssl_verify');
$stack->push(\GuzzleHttp\Middleware::log(
$di[\Monolog\Logger::class],
new \GuzzleHttp\MessageFormatter('HTTP client {method} call to {uri} produced response {code}'),
\Monolog\Logger::DEBUG
));
return new \GuzzleHttp\Client([
'handler' => $stack,
'http_errors' => false,
'timeout' => 3.0,
]);
};
$di[\App\EventDispatcher::class] = function($di) use ($settings) {
$dispatcher = new \App\EventDispatcher($di);
// Register application default events.
call_user_func(include(__DIR__.'/events.php'), $dispatcher);
/** @var \App\Plugins $plugins */
$plugins = $di[\App\Plugins::class];
// Register plugin-provided events.
$plugins->registerEvents($dispatcher);
// Register plugin-provided events.
$plugins->registerEvents($dispatcher);
}
return $dispatcher;
};
});
//
// AzuraCast-specific dependencies
@ -384,39 +172,31 @@ return function (\Slim\Container $di, $settings)
);
};
$di[\App\Assets::class] = function ($di) {
/** @var \App\Config $config */
$config = $di[\App\Config::class];
$di[\Azura\Assets::class] = function ($di) {
/** @var \Azura\Config $config */
$config = $di[\Azura\Config::class];
$libraries = $config->get('assets');
$versioned_files = [];
$assets_file = APP_INCLUDE_STATIC . '/assets.json';
$assets_file = APP_INCLUDE_ROOT.'/web/static/assets.json';
if (file_exists($assets_file)) {
$versioned_files = json_decode(file_get_contents($assets_file), true);
}
return new \App\Assets($libraries, $versioned_files);
return new \Azura\Assets($libraries, $versioned_files);
};
$di[\App\Customization::class] = function ($di) {
return new \App\Customization(
$di['app_settings'],
$di['settings'],
$di[\App\Entity\Repository\SettingsRepository::class]
);
};
$di[\App\RateLimit::class] = function($di) {
/** @var \Redis $redis */
$redis = $di[\Redis::class];
$redis->select(3);
return new \App\RateLimit($redis);
};
$di[\App\Version::class] = function($di) {
return new \App\Version(
$di[\App\Cache::class]
$di[\Azura\Cache::class]
);
};
@ -438,18 +218,5 @@ return function (\Slim\Container $di, $settings)
$di->register(new \App\Provider\FrontendProvider);
$di->register(new \App\Provider\StationsProvider);
// Main Slim Application
$di['app'] = function ($di) {
$app = new \Slim\App($di);
/** @var \App\EventDispatcher $dispatcher */
$dispatcher = $di[\App\EventDispatcher::class];
$dispatcher->dispatch(\App\Event\BuildRoutes::NAME, new \App\Event\BuildRoutes($app));
return $app;
};
return $di;
};

View File

@ -1,5 +1,5 @@
<?php
/** @var \App\Assets $assets */
/** @var \Azura\Assets $assets */
$assets
->load('codemirror_css')
->addInlineJs($this->fetch('admin/branding/index.js'), 99);

View File

@ -12,7 +12,7 @@ $this->layout('minimal', [
<?php if ($customization->hideProductName()): ?>
<?=__('Welcome!') ?>
<?php else: ?>
<?=sprintf(__('Welcome to %s!'), $app_settings['name']) ?>
<?=sprintf(__('Welcome to %s!'), $settings['name']) ?>
<?php endif; ?>
<?php if (!empty($customization->getInstanceName())): ?>
<small class="text-muted"><?=$this->e($customization->getInstanceName()) ?></small>

View File

@ -2,7 +2,7 @@
<?php
/** @var \App\Customization $customization */
/** @var \App\Assets $assets */
/** @var \Azura\Assets $assets */
$assets
->load('vue')
->load('radio')

View File

@ -1,7 +1,7 @@
$(function () {
Highcharts.setOptions({
global: {
timezoneOffset: <?=\App\Timezone::getOffsetMinutes() ?>,
timezoneOffset: <?=\Azura\Timezone::getOffsetMinutes() ?>,
useUTC: false
}
});

View File

@ -7,7 +7,7 @@ $this->layout('minimal', [
'hide_footer' => true
]);
/** @var \App\Assets $assets */
/** @var \Azura\Assets $assets */
$assets
->load('vue')
->load('radio')

View File

@ -7,7 +7,7 @@ $this->layout('minimal', [
'hide_footer' => true
]);
/** @var \App\Assets $assets */
/** @var \Azura\Assets $assets */
$assets
->load('bootgrid')
->addInlineJs($this->fetch('frontend/public/embedrequests.js', ['station' => $station]));

View File

@ -6,7 +6,7 @@ $this->layout('minimal', [
'title' => $this->e($station->getName()),
]);
/** @var \App\Assets $assets */
/** @var \Azura\Assets $assets */
$assets
->load('vue')
->load('radio')

View File

@ -1,5 +1,5 @@
<?php
/** @var \App\Assets $assets */
/** @var \Azura\Assets $assets */
$assets->addInlineJs($this->fetch('frontend/public/player_component.js'), 95);
?>
<div class="media media-left" id="station-nowplaying">

View File

@ -4,7 +4,7 @@ $this->layout('minimal', [
'page_class' => 'login-content'
]);
/** @var \App\Assets $assets */
/** @var \Azura\Assets $assets */
$assets->load('zxcvbn');
?>

View File

@ -4,7 +4,7 @@ $this->layout('main', [
'manual' => true
]);
/** @var \App\Assets $assets */
/** @var \Azura\Assets $assets */
$assets
->addInlineJs($this->fetch('partials/station_form.js'), 99);
?>

View File

@ -5,12 +5,15 @@
* @var \App\Auth $auth
* @var \App\Acl $acl
* @var \App\Http\Router $router
* @var \App\Session\Flash $flash
* @var \Azura\Session\Flash $flash
* @var \App\Customization $customization
* @var \App\Assets $assets
* @var \Azura\Assets $assets
* @var \App\Version $version
* @var \App\Http\Request $request
* @var array $app_settings
*/
$user = $request->getUser();
?>
<html>
<head>
@ -131,7 +134,7 @@ echo $assets->js();
</section>
<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">'.$app_settings['name'].'</a> &bull; '.$version->getVersionText().' &bull; '.(APP_INSIDE_DOCKER ? 'Docker' : 'Traditional').' &bull; PHP '.\PHP_MAJOR_VERSION.'.'.\PHP_MINOR_VERSION) ?><br>
<?=__('Powered by %s', '<a href="https://www.azuracast.com/" target="_blank">'.$settings['name'].'</a> &bull; '.$version->getVersionText().' &bull; '.(APP_INSIDE_DOCKER ? 'Docker' : 'Traditional').' &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') ?>
</footer>

View File

@ -5,9 +5,11 @@
* @var \App\Auth $auth
* @var \App\Acl $acl
* @var \App\Http\Router $router
* @var \App\Session\Flash $flash
* @var \Azura\Session\Flash $flash
* @var \App\Customization $customization
* @var \App\Assets $assets
* @var \Azura\Assets $assets
* @var \App\Version $version
* @var \App\Http\Request $request
* @var array $app_settings
*/
?>
@ -40,7 +42,7 @@ echo $assets->js();
<?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">'.$app_settings['name'].'</a>'.' ') ?><br>
<?=sprintf(__('Powered by %s'), '<a href="https://azuracast.com/" target="_blank">'.$settings['name'].'</a>'.' ') ?><br>
<?=sprintf(__('Mascot designed by %s'), '<a href="https://tysontan.deviantart.com/" target="_blank">Tyson Tan</a>') ?>
</footer>
<?php endif; ?>

View File

@ -1,5 +1,5 @@
<?php
/** @var \App\Assets $assets */
/** @var \Azura\Assets $assets */
$assets
->addInlineJs($this->fetch('partials/station_form.js'), 99);

View File

@ -1,7 +1,7 @@
<?php $this->layout('main', ['title' => __('Music Files'), 'manual' => true, 'page_class' => 'page-file-manager']) ?>
<?php
/** @var \App\Assets $assets */
/** @var \Azura\Assets $assets */
$assets
->load('vue')
->load('radio')

View File

@ -4,7 +4,7 @@ $this->layout('main', [
'manual' => true
]);
/** @var \App\Assets $assets */
/** @var \Azura\Assets $assets */
$assets
->addInlineJs($this->fetch('stations/logs/index.js'), 99);
?>

View File

@ -1,5 +1,5 @@
<?php
/** @var \App\Assets $assets */
/** @var \Azura\Assets $assets */
$assets
->addInlineJs($this->fetch('stations/playlists/edit.js'), 99);

View File

@ -3,7 +3,7 @@ use App\Entity\StationPlaylist;
$this->layout('main', ['title' => __('Playlists'), 'manual' => true]);
/** @var \App\Assets $assets */
/** @var \Azura\Assets $assets */
$assets->load('fullcalendar')
->addInlineJs($this->fetch('stations/playlists/index.js', [
'schedule_now' => $schedule_now,

View File

@ -3,7 +3,7 @@ use Entity\StationPlaylist;
$this->layout('main', ['title' => __('Reorder Playlist'), 'manual' => true]);
/** @var \App\Assets $assets */
/** @var \Azura\Assets $assets */
$assets->load('jquery-sortable');
?>

View File

@ -7,12 +7,14 @@ $this->layout('main', [
'sidebar_tab' => 'profile',
]);
/** @var \App\Assets $assets */
/** @var \Azura\Assets $assets */
$assets
->load('vue')
->load('radio')
->load('clipboard')
->addInlineJs($this->fetch('stations/profile/index.js', ['nowplaying' => $nowplaying]), 99);
$user = $request->getUser();
?>
<div class="row">

View File

@ -1,5 +1,5 @@
<?php
/** @var \App\Assets $assets */
/** @var \Azura\Assets $assets */
$assets
->addInlineJs($this->fetch('stations/remotes/edit.js'), 99);

View File

@ -1,7 +1,7 @@
<?php $this->layout('main', ['title' => __('Listeners'), 'manual' => true]) ?>
<?php
/** @var \App\Assets $assets */
/** @var \Azura\Assets $assets */
$assets
->load('vue')
->load('daterangepicker')

View File

@ -1,7 +1,7 @@
$(function () {
Highcharts.setOptions({
global: {
timezoneOffset: <?=\App\Timezone::getOffsetMinutes() ?>,
timezoneOffset: <?=\Azura\Timezone::getOffsetMinutes() ?>,
useUTC: false
}
});

View File

@ -1,7 +1,7 @@
<?php $this->layout('main', ['title' => __('Statistics Overview'), 'manual' => true]) ?>
<?php
/** @var \App\Assets $assets */
/** @var \Azura\Assets $assets */
$assets
->load('highcharts')
->load('highcharts_theme_'.$customization->getTheme())

View File

@ -1,7 +1,7 @@
<?php $this->layout('main', ['title' => __('Song Listener Impact'), 'manual' => true]) ?>
<?php
/** @var \App\Assets $assets */
/** @var \Azura\Assets $assets */
$assets
->load('bootgrid')
->addInlineJs($this->fetch('stations/reports/performance.js'));

View File

@ -1,7 +1,7 @@
<?php $this->layout('main', ['title' => __('Song Requests'), 'manual' => true]) ?>
<?php
/** @var \App\Assets $assets */
/** @var \Azura\Assets $assets */
$assets
->load('bootgrid');
?>

View File

@ -1,7 +1,7 @@
<?php $this->layout('main', ['title' => __('Song Playback Timeline'), 'manual' => true]) ?>
<?php
/** @var \App\Assets $assets */
/** @var \Azura\Assets $assets */
$assets
->load('daterangepicker')
->load('bootgrid')

View File

@ -6,6 +6,8 @@
/** @var $assets \App\Assets */
$assets->addInlineJs($this->fetch('stations/sidebar.js'));
$user = $request->getUser();
?>
<div class="navdrawer-header">
<a class="navbar-brand px-0" href="<?=$router->fromHere('stations:profile:index') ?>">

View File

@ -1,5 +1,5 @@
<?php
/** @var \App\Assets $assets */
/** @var \Azura\Assets $assets */
$assets
->load('chosen')
->load('zxcvbn')

62
src/App.php Normal file
View File

@ -0,0 +1,62 @@
<?php
namespace App;
use Azura\Settings;
use Azura\Exception;
class App extends \Azura\App
{
public static function create(array $values): \Azura\App
{
$settings = $values['settings'] ?? [];
if (!isset($settings[Settings::BASE_DIR])) {
throw new Exception\Bootstrap('No base directory specified!');
}
$settings[Settings::TEMP_DIR] = dirname($settings[Settings::BASE_DIR]) . '/www_tmp';
$settings[Settings::VIEWS_DIR] = $settings[Settings::BASE_DIR] . '/resources/templates';
// Define the "helper" constants used by AzuraCast.
define('APP_IS_COMMAND_LINE', PHP_SAPI === 'cli');
define('APP_INCLUDE_ROOT', $settings[Settings::BASE_DIR]);
define('APP_INCLUDE_TEMP', $settings[Settings::TEMP_DIR]);
define('APP_INSIDE_DOCKER', file_exists(dirname($settings[Settings::BASE_DIR]) . '/.docker'));
define('APP_DOCKER_REVISION', getenv('AZURACAST_DC_REVISION') ?? 1);
$settings[Settings::IS_DOCKER] = APP_INSIDE_DOCKER;
define('APP_TESTING_MODE', (isset($settings[Settings::APP_ENV]) && Settings::ENV_TESTING === $settings[Settings::APP_ENV]));
// Register the plugins engine.
if (isset($values['autoloader'])) {
$autoloader = $values['autoloader'];
$plugins = new Plugins($settings[Settings::BASE_DIR] . '/plugins');
$plugins->registerAutoloaders($autoloader);
$values[Plugins::class] = $plugins;
} else {
$plugins = null;
}
$values['settings'] = $settings;
$app = \Azura\App::create($values);
$di = $app->getContainer();
/** @var Settings $settings */
$settings = $di['settings'];
define('APP_APPLICATION_ENV', $settings[Settings::APP_ENV]);
define('APP_IN_PRODUCTION', $settings->isProduction());
if (null !== $plugins) {
$plugins->registerServices($di);
}
return $app;
}
}

View File

@ -1,382 +0,0 @@
<?php
namespace App;
use App\Http\Request;
/**
* Asset management class for AzuraCast.
* Inspired by Asseter by Adam Banaszkiewicz: https://github.com/requtize
*
* @link https://github.com/requtize/assetter
*/
class Assets
{
/** @var array Known libraries loaded in initialization. */
protected $libraries = [];
/** @var array An optional array lookup for versioned files. */
protected $versioned_files = [];
/** @var array Loaded libraries. */
protected $loaded = [];
/** @var bool Whether the current loaded libraries have been sorted by order. */
protected $is_sorted = true;
/** @var string A randomly generated number-used-once (nonce) for inline CSP. */
protected $csp_nonce;
/** @var array The loaded domains that should be included in the CSP header. */
protected $csp_domains;
/**
* Assets constructor.
*
* @param array $libraries
* @param array $versioned_files
* @throws \Exception
*/
public function __construct(array $libraries = [], array $versioned_files = [])
{
foreach($libraries as $library_name => $library) {
$this->addLibrary($library, $library_name);
}
$this->versioned_files = $versioned_files;
$this->csp_nonce = \preg_replace('/[^A-Za-z0-9\+\/=]/', '', \base64_encode(\random_bytes(18)));
$this->csp_domains = [];
}
/**
* Returns the randomly generated nonce for inline CSP for this request.
*
* @return string
*/
public function getCspNonce(): string
{
return $this->csp_nonce;
}
/**
* Returns the list of approved domains for CSP header inclusion.
*
* @return array
*/
public function getCspDomains(): array
{
return $this->csp_domains;
}
/**
* Add a library to the collection.
*
* @param array $data Array with asset data.
* @param string|null $library_name
* @return $this
*/
public function addLibrary(array $data, $library_name = null): self
{
$library_name = $library_name ?? uniqid('', false);
$this->libraries[$library_name] = [
'name' => $library_name,
'order' => $data['order'] ?? 0,
'files' => $data['files'] ?? [],
'inline' => $data['inline'] ?? [],
'require' => $data['require'] ?? [],
];
return $this;
}
/**
* Loads assets from given name or array definition.
*
* @param mixed $data Name or array definition of library/asset.
* @return self
*/
public function load($data): self
{
if (\is_array($data)) {
$item = [
'name' => $data['name'] ?? uniqid('', false),
'order' => $data['order'] ?? 0,
'files' => $data['files'] ?? [],
'inline' => $data['inline'] ?? [],
'require' => $data['require'] ?? []
];
} elseif (isset($this->libraries[$data])) {
$item = $this->libraries[$data];
} else {
throw new \InvalidArgumentException(sprintf('Library %s not found!', $data));
}
$name = $item['name'];
if (!isset($this->loaded[$name])) {
if (!empty($item['require'])) {
foreach($item['require'] as $require_name) {
$this->load($require_name);
}
}
$this->loaded[$name] = $item;
$this->is_sorted = false;
}
return $this;
}
/**
* Add a single javascript file.
*
* @param $js_script
* @return $this
*/
public function addJs($js_script): self
{
$this->load([
'order' => 100,
'files' => [
'js' => [
(\is_array($js_script)) ? $js_script : ['src' => $js_script]
],
],
]);
return $this;
}
/**
* Add a single javascript inline script.
*
* @param $js_script
* @return $this
*/
public function addInlineJs($js_script, $order = 100): self
{
$this->load([
'order' => $order,
'inline' => [
'js' => (is_array($js_script)) ? $js_script : array($js_script),
],
]);
return $this;
}
/**
* Add a single CSS file.
*
* @param $css_script
* @return $this
*/
public function addCss($css_script, $order = 100): self
{
$this->load([
'order' => $order,
'files' => [
'css' => [
(\is_array($css_script)) ? $css_script : ['src' => $css_script]
],
],
]);
return $this;
}
/**
* Add a single inline CSS file[s].
*
* @param $css_script
* @return $this
*/
public function addInlineCss($css_script): self
{
$this->load([
'order' => 100,
'inline' => [
'css' => (is_array($css_script)) ? $css_script : array($css_script),
],
]);
return $this;
}
/**
* Returns all CSS includes and inline styles.
*
* @return string HTML tags as string.
*/
public function css()
{
$this->_sort();
$result = [];
foreach($this->loaded as $item)
{
if (!empty($item['files']['css'])) {
foreach($item['files']['css'] as $file) {
$compiled_attributes = $this->_compileAttributes($file, [
'rel' => 'stylesheet',
'type' => 'text/css',
]);
$result[] = '<link '.implode(' ', $compiled_attributes).' />';
}
}
if (!empty($item['inline']['css'])) {
foreach($item['inline']['css'] as $inline) {
if (!empty($inline)) {
$result[] = '<style type="text/css" nonce="'.$this->csp_nonce.'">'."\n".$inline.'</style>';
}
}
}
}
return implode("\n", $result)."\n";
}
/**
* Returns all script include tags.
*
* @return string HTML tags as string.
*/
public function js()
{
$this->_sort();
$result = [];
foreach($this->loaded as $item)
{
if (!empty($item['files']['js'])) {
foreach($item['files']['js'] as $file) {
$compiled_attributes = $this->_compileAttributes($file, [
'type' => 'text/javascript',
]);
$result[] = '<script '.implode(' ', $compiled_attributes).'></script>';
}
}
}
return implode("\n", $result)."\n";
}
/**
* Return any inline JavaScript.
* @param Request $request
* @return string
*/
public function inlineJs(Request $request): string
{
$this->_sort();
$result = [];
foreach($this->loaded as $item)
{
if (!empty($item['inline']['js'])) {
foreach($item['inline']['js'] as $inline) {
if (\is_callable($inline)) {
$inline = $inline($request);
}
if (!empty($inline)) {
$result[] = '<script type="text/javascript" nonce="'.$this->csp_nonce.'">'."\n".$inline.'</script>';
}
}
}
}
return implode("\n", $result)."\n";
}
/**
* Build the proper include tag for a JS/CSS include.
*
* @param array $file
* @param array $defaults
* @return array
*/
protected function _compileAttributes(array $file, array $defaults = [])
{
if (isset($file['src'])) {
$defaults['src'] = $this->_getUrl($file['src']);
unset($file['src']);
}
if (isset($file['href'])) {
$defaults['href'] = $this->_getUrl($file['href']);
unset($file['href']);
}
if (isset($file['integrity'])) {
$defaults['crossorigin'] = 'anonymous';
}
$attributes = array_merge($defaults, $file);
$compiled_attributes = [];
foreach($attributes as $attr_key => $attr_val) {
// Check for attributes like "defer"
if ($attr_val === true) {
$compiled_attributes[] = $attr_key;
} else {
$compiled_attributes[] = $attr_key.'="'.$attr_val.'"';
}
}
return $compiled_attributes;
}
/**
* Resolve the URI of the resource, whether local or remote/CDN-based.
*
* @param $resource_uri
* @return string The resolved resource URL.
*/
protected function _getUrl($resource_uri)
{
if (isset($this->versioned_files[$resource_uri])) {
$resource_uri = $this->versioned_files[$resource_uri];
}
if (preg_match('/^(https?:)?\/\//', $resource_uri)) {
$this->_addDomainToCsp($resource_uri);
return $resource_uri;
} else {
return '/static/'.$resource_uri;
}
}
/**
* Add the loaded domain to the full list of CSP-approved domains.
*
* @param $files
*/
protected function _addDomainToCsp($src)
{
$src_parts = parse_url($src);
$domain = $src_parts['scheme'].'://'.$src_parts['host'];
if (!isset($this->csp_domains[$domain])) {
$this->csp_domains[$domain] = $domain;
}
}
/**
* Sort the list of loaded libraries.
*/
protected function _sort()
{
if (!$this->is_sorted) {
uasort($this->loaded, function($a, $b) {
return $a['order'] <=> $b['order']; // SPACESHIP!
});
$this->is_sorted = true;
}
}
}

View File

@ -3,7 +3,8 @@ namespace App;
use App\Entity\Repository\UserRepository;
use App\Entity\User;
use App\Session\NamespaceInterface;
use Azura\Session;
use Azura\Session\NamespaceInterface;
class Auth
{
@ -78,7 +79,7 @@ class Auth
*
* @param bool $real_user_only
* @return bool|User|null|object
* @throws Exception
* @throws \Azura\Exception
*/
public function getLoggedInUser($real_user_only = false)
{
@ -93,7 +94,7 @@ class Auth
* Get the authenticated user entity.
*
* @return bool|User|null
* @throws Exception
* @throws \Azura\Exception
*/
public function getUser()
{
@ -114,7 +115,7 @@ class Auth
$this->_user = false;
$this->logout();
throw new Exception('Invalid user!');
throw new \Azura\Exception('Invalid user!');
}
}

View File

@ -1,170 +0,0 @@
<?php
namespace App;
class Cache
{
/** @var \Redis */
protected $redis;
/** @var int Default length of time to keep cached items. */
protected $default_ttl = 3600;
public function __construct(\Redis $redis, $default_ttl = null)
{
$this->redis = $redis;
if ($default_ttl !== null) {
$this->default_ttl = $default_ttl;
}
}
/**
* Properly close the connection.
*/
public function __destruct()
{
if ($this->redis instanceof \Redis) {
try {
$this->redis->close();
} catch (\RedisException $e) {
/*
* \Redis::close will throw a \RedisException("Redis server went away") exception if
* we haven't previously been able to connect to Redis or the connection has severed.
*/
}
}
}
/**
* Attempt to load an item from cache, or return default value if not found.
*
* @param $id
* @param null $default
* @return mixed|null
*/
public function load($id, $default = null)
{
$result = $this->redis->get($this->_filterId($id));
if ($result === false) {
return (is_callable($default)) ? $default() : $default;
}
return unserialize($result);
}
/**
* Alias of the "load" function.
*
* @param $id
* @param null $default
* @return mixed|null
*/
public function get($id, $default = null)
{
return $this->load($id, $default);
}
/**
* Test whether an ID is present in the cache.
*
* @param $id
* @return bool
*/
public function test($id)
{
$result = $this->redis->get($this->_filterId($id));
return ($result !== false);
}
/**
* Save an item to the cache.
*
* @param $data
* @param $id
* @param int|null $ttl
*/
public function save($data, $id, $ttl = null)
{
if ($ttl === null || !is_numeric($ttl)) {
$ttl = $this->default_ttl;
}
if ($ttl < 0) {
$ttl = 0.1;
}
$this->redis->setex($this->_filterId($id), $ttl, serialize($data));
}
/**
* Alias for the "save" function.
*
* @param $data
* @param $id
* @param int|null $ttl
*/
public function set($data, $id, $ttl = null)
{
$this->save($data, $id, $ttl);
}
/**
* Combination of the "get" and "set" functions to return an existing cache
* item or set it if one doesn't exist.
*
* @param $id
* @param null $default
* @param bool|false $ttl
* @return mixed|null
*/
public function getOrSet($id, $default = null, $ttl = false)
{
$result = $this->redis->get($this->_filterId($id));
if ($result === false) {
if ($ttl === null || !is_numeric($ttl)) {
$ttl = $this->default_ttl;
}
if ($ttl < 0) {
$ttl = 0.1;
}
$data = (is_callable($default)) ? $default() : $default;
$this->redis->setex($this->_filterId($id), $ttl, serialize($data));
return $data;
}
return unserialize($result);
}
/**
* Delete an item from the cache.
*
* @param $id
* @return void
*/
public function remove($id)
{
$this->redis->delete($this->_filterId($id));
}
/**
* Clean the cache of all items.
*
* @return bool
*/
public function clean()
{
return true; // Not used with Redis
}
protected function _filterId($id)
{
return str_replace('/', ':', ltrim($id, '/'));
}
}

View File

@ -1,58 +0,0 @@
<?php
namespace App;
class Config
{
protected $_base_folder;
public function __construct($base_folder)
{
if (!is_dir($base_folder)) {
throw new Exception("Invalid base folder for configurations.");
}
$this->_base_folder = $base_folder;
}
/**
* @param string $name
* @param array $inject_vars Variables to pass into the scope of the configuration.
* @return array
*/
public function get($name, $inject_vars = []): array
{
$path = $this->_getPath($name);
if (file_exists($path)) {
unset($name);
extract($inject_vars, \EXTR_OVERWRITE);
unset($inject_vars);
return require $path;
}
return [];
}
/**
* Indicate whether a given configuration file name exists.
*
* @param $name
* @return bool
*/
public function has($name): bool
{
return file_exists($this->_getPath($name));
}
/**
* Return the configuration path resolved by the specified name.
*
* @param $name
* @return string
*/
public function _getPath($name)
{
return $this->_base_folder.DIRECTORY_SEPARATOR.str_replace(['.', '..'], ['', ''], $name).'.php';
}
}

View File

@ -1,60 +0,0 @@
<?php
namespace App\Console;
use App\Event\BuildConsoleCommands;
use App\EventDispatcher;
use Slim\Container;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
class Application extends \Symfony\Component\Console\Application
{
/** @var Container */
protected $di;
/**
* @param Container $di
*/
public function setContainer(Container $di)
{
$this->di = $di;
}
/**
* @return Container
*/
public function getContainer(): Container
{
return $this->di;
}
/**
* @param $service_name
* @return mixed
* @throws \App\Exception
* @throws \Interop\Container\Exception\ContainerException
*/
public function getService($service_name)
{
if ($this->di->has($service_name)) {
return $this->di->get($service_name);
} else {
throw new \App\Exception(sprintf('Service "%s" not found.', $service_name));
}
}
/**
* @inheritdoc
*/
public function run(InputInterface $input = null, OutputInterface $output = null)
{
/** @var EventDispatcher $dispatcher */
$dispatcher = $this->di[EventDispatcher::class];
// Trigger an event for the core app and all plugins to build their CLI commands.
$event = new BuildConsoleCommands($this);
$dispatcher->dispatch(BuildConsoleCommands::NAME, $event);
return parent::run($input, $output);
}
}

View File

@ -1,44 +0,0 @@
<?php
namespace App\Console\Command;
use Doctrine\ORM\EntityManager;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
class ClearCache extends \App\Console\Command\CommandAbstract
{
/**
* {@inheritdoc}
*/
protected function configure()
{
$this->setName('cache:clear')
->setDescription('Clear all application caches.');
}
/**
* {@inheritdoc}
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
// Flush route cache.
$app_settings = $this->get('settings');
if (!empty($app_settings['routerCacheFile'])) {
@unlink($app_settings['routerCacheFile']);
}
$output->writeln('Router cache file cleared.');
// Flush all Redis databases
/** @var \Redis $redis */
$redis = $this->get(\Redis::class);
for($i = 0; $i < 14; $i++) {
$redis->select($i);
$redis->flushAll();
}
$output->writeln('Local cache flushed.');
return 0;
}
}

View File

@ -1,42 +0,0 @@
<?php
namespace App\Console\Command;
use App\Console\Application;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\ArrayInput;
use Symfony\Component\Console\Output\OutputInterface;
abstract class CommandAbstract extends Command
{
/**
* Return a Dependency Injection service.
*
* @param $service_name
* @return mixed
* @throws \App\Exception
* @throws \Interop\Container\Exception\ContainerException
*/
public function get($service_name)
{
/** @var Application $application */
$application = self::getApplication();
return $application->getService($service_name);
}
/**
* @param OutputInterface $output
* @param $command_name
* @param array $command_args
* @throws \Exception
*/
protected function runCommand(OutputInterface $output, $command_name, $command_args = [])
{
$command = $this->getApplication()->find($command_name);
$input = new ArrayInput(['command' => $command_name] + $command_args);
$input->setInteractive(false);
$command->run($input, $output);
}
}

View File

@ -3,6 +3,7 @@ namespace App\Console\Command;
use App\Radio\Adapters;
use App\Radio\Backend\Liquidsoap;
use Azura\Console\Command\CommandAbstract;
use Doctrine\ORM\EntityManager;
use App\Entity;
use Symfony\Component\Console\Input\InputArgument;
@ -10,7 +11,7 @@ use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
class DjAuth extends \App\Console\Command\CommandAbstract
class DjAuth extends CommandAbstract
{
/**
* {@inheritdoc}

View File

@ -3,13 +3,14 @@ namespace App\Console\Command;
use App\Radio\Adapters;
use App\Radio\Backend\Liquidsoap;
use Azura\Console\Command\CommandAbstract;
use Doctrine\ORM\EntityManager;
use App\Entity;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
class DjOff extends \App\Console\Command\CommandAbstract
class DjOff extends CommandAbstract
{
/**
* {@inheritdoc}

View File

@ -3,13 +3,14 @@ namespace App\Console\Command;
use App\Radio\Adapters;
use App\Radio\Backend\Liquidsoap;
use Azura\Console\Command\CommandAbstract;
use Doctrine\ORM\EntityManager;
use App\Entity;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
class DjOn extends \App\Console\Command\CommandAbstract
class DjOn extends CommandAbstract
{
/**
* {@inheritdoc}

View File

@ -3,10 +3,11 @@
namespace App\Console\Command;
use App\Entity;
use Azura\Console\Command\CommandAbstract;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
class GenerateApiDocs extends \App\Console\Command\CommandAbstract
class GenerateApiDocs extends CommandAbstract
{
/**
* {@inheritdoc}
@ -37,7 +38,7 @@ class GenerateApiDocs extends \App\Console\Command\CommandAbstract
],
]);
$yaml_path = APP_INCLUDE_STATIC.'/api/openapi.yml';
$yaml_path = APP_INCLUDE_ROOT.'/web/static/api/openapi.yml';
$yaml = $oa->toYaml();
file_put_contents($yaml_path, $yaml);

View File

@ -3,13 +3,14 @@ namespace App\Console\Command;
use App\Entity;
use App\Utilities;
use Azura\Console\Command\CommandAbstract;
use Doctrine\ORM\EntityManager;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
class ListSettings extends \App\Console\Command\CommandAbstract
class ListSettings extends CommandAbstract
{
/**
* {@inheritdoc}

View File

@ -1,6 +1,7 @@
<?php
namespace App\Console\Command;
use Azura\Console\Command\CommandAbstract;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;

View File

@ -1,6 +1,7 @@
<?php
namespace App\Console\Command;
use Azura\Console\Command\CommandAbstract;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
@ -20,7 +21,7 @@ class LocaleImport extends CommandAbstract
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
$app_settings = $this->get('app_settings');
$app_settings = $this->get('settings');
$locales = $app_settings['locale']['supported'];
$locale_base = APP_INCLUDE_ROOT.'/resources/locale';

View File

@ -2,11 +2,12 @@
namespace App\Console\Command;
use App\Entity;
use Azura\Console\Command\CommandAbstract;
use Doctrine\ORM\EntityManager;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
class MigrateConfig extends \App\Console\Command\CommandAbstract
class MigrateConfig extends CommandAbstract
{
/**
* {@inheritdoc}

View File

@ -4,12 +4,13 @@ namespace App\Console\Command;
use App\Radio\Adapters;
use App\Radio\Backend\Liquidsoap;
use App\Entity;
use Azura\Console\Command\CommandAbstract;
use Doctrine\ORM\EntityManager;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
class NextSong extends \App\Console\Command\CommandAbstract
class NextSong extends CommandAbstract
{
/**
* {@inheritdoc}

View File

@ -1,12 +1,13 @@
<?php
namespace App\Console\Command;
use Azura\Console\Command\CommandAbstract;
use Doctrine\ORM\EntityManager;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use App\Entity;
class ReprocessMedia extends \App\Console\Command\CommandAbstract
class ReprocessMedia extends CommandAbstract
{
/**
* {@inheritdoc}

View File

@ -2,13 +2,14 @@
namespace App\Console\Command;
use App\Entity;
use Azura\Console\Command\CommandAbstract;
use Doctrine\ORM\EntityManager;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
class ResetPassword extends \App\Console\Command\CommandAbstract
class ResetPassword extends CommandAbstract
{
/**
* {@inheritdoc}

View File

@ -2,12 +2,13 @@
namespace App\Console\Command;
use App\Radio\Configuration;
use Azura\Console\Command\CommandAbstract;
use Doctrine\ORM\EntityManager;
use App\Entity\Station;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
class RestartRadio extends \App\Console\Command\CommandAbstract
class RestartRadio extends CommandAbstract
{
/**
* {@inheritdoc}

View File

@ -2,13 +2,14 @@
namespace App\Console\Command;
use App\Entity;
use Azura\Console\Command\CommandAbstract;
use Doctrine\ORM\EntityManager;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
class SetSetting extends \App\Console\Command\CommandAbstract
class SetSetting extends CommandAbstract
{
/**
* {@inheritdoc}

View File

@ -2,12 +2,13 @@
namespace App\Console\Command;
use App\Utilities;
use Azura\Console\Command\CommandAbstract;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
class Setup extends \App\Console\Command\CommandAbstract
class Setup extends CommandAbstract
{
/**
* {@inheritdoc}

View File

@ -1,6 +1,7 @@
<?php
namespace App\Console\Command;
use Azura\Console\Command\CommandAbstract;
use Doctrine\Common\DataFixtures\Executor\ORMExecutor;
use Doctrine\Common\DataFixtures\Loader;
use Doctrine\Common\DataFixtures\Purger\ORMPurger;
@ -9,7 +10,7 @@ use Symfony\Component\Console\Input\ArrayInput;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
class SetupFixtures extends \App\Console\Command\CommandAbstract
class SetupFixtures extends CommandAbstract
{
/**
* {@inheritdoc}

View File

@ -1,11 +1,12 @@
<?php
namespace App\Console\Command;
use Azura\Console\Command\CommandAbstract;
use InfluxDB\Database;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
class SetupInflux extends \App\Console\Command\CommandAbstract
class SetupInflux extends CommandAbstract
{
/**
* {@inheritdoc}

View File

@ -3,11 +3,12 @@ namespace App\Console\Command;
use App;
use App\Sync\Runner;
use Azura\Console\Command\CommandAbstract;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
class Sync extends \App\Console\Command\CommandAbstract
class Sync extends CommandAbstract
{
/**
* {@inheritdoc}

View File

@ -2,13 +2,14 @@
namespace App\Console\Command;
use App;
use Azura\Console\Command\CommandAbstract;
use Doctrine\ORM\EntityManager;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
class UptimeWait extends \App\Console\Command\CommandAbstract
class UptimeWait extends CommandAbstract
{
/**
* {@inheritdoc}

View File

@ -11,7 +11,7 @@ class ApiController
/** @var EntityManager */
protected $em;
/** @var Entity\Repository\BaseRepository */
/** @var \Azura\Doctrine\Repository */
protected $record_repo;
/** @var array */

View File

@ -14,7 +14,7 @@ class CustomFieldsController
/** @var array */
protected $form_config;
/** @var Entity\Repository\BaseRepository */
/** @var \Azura\Doctrine\Repository */
protected $record_repo;
/** @var string */

View File

@ -65,7 +65,7 @@ class PermissionsController
public function editAction(Request $request, Response $response, $id = null): Response
{
/** @var Entity\Repository\BaseRepository $role_repo */
/** @var \Azura\Doctrine\Repository $role_repo */
$role_repo = $this->em->getRepository(Entity\Role::class);
/** @var Entity\Repository\RolePermissionRepository $permission_repo */
@ -114,7 +114,7 @@ class PermissionsController
{
$request->getSession()->getCsrf()->verify($csrf_token, $this->csrf_namespace);
/** @var Entity\Repository\BaseRepository $role_repo */
/** @var \Azura\Doctrine\Repository $role_repo */
$role_repo = $this->em->getRepository(Entity\Role::class);
$record = $role_repo->find((int)$id);

View File

@ -1,7 +1,7 @@
<?php
namespace App\Controller\Admin;
use App\Cache;
use Azura\Cache;
use App\Radio\Adapters;
use App\Radio\Configuration;
use Doctrine\ORM\EntityManager;

View File

@ -46,7 +46,7 @@ class IndexController
*/
public function timeAction(Request $request, Response $response): Response
{
$tz_info = \App\Timezone::getInfo();
$tz_info = \Azura\Timezone::getInfo();
return $response->withJson(new Entity\Api\Time($tz_info));
}
}

View File

@ -93,11 +93,6 @@ class InternalController
$payload = $request->getBody()->getContents();
if (!APP_IN_PRODUCTION) {
$log = date('Y-m-d g:i:s')."\n".$station->getName()."\n".$payload."\n\n";
file_put_contents(APP_INCLUDE_TEMP.'/notify.log', $log, \FILE_APPEND);
}
$this->sync_nowplaying->processStation($station, $payload);
return $response->write('received');
@ -105,7 +100,7 @@ class InternalController
/**
* @param Request $request
* @throws \App\Exception
* @throws \Azura\Exception
* @throws \App\Exception\PermissionDenied
*/
protected function _checkStationAuth(Request $request): void

View File

@ -1,7 +1,7 @@
<?php
namespace App\Controller\Api;
use App\Cache;
use Azura\Cache;
use Doctrine\ORM\EntityManager;
use App\Entity;
use App\Http\Request;

View File

@ -1,9 +1,9 @@
<?php
namespace App\Controller\Api;
use App\Cache;
use Azura\Cache;
use App\Event\Radio\LoadNowPlaying;
use App\EventDispatcher;
use Azura\EventDispatcher;
use Doctrine\ORM\EntityManager;
use App\Entity;
use App\Http\Request;

View File

@ -1,7 +1,7 @@
<?php
namespace App\Controller\Api;
use App\Doctrine\Paginator;
use Azura\Doctrine\Paginator;
use App\Utilities;
use App\ApiUtilities;
use Doctrine\ORM\EntityManager;
@ -155,7 +155,7 @@ class RequestsController
$request_repo->submit($station, $media_id);
return $response->withJson('Request submitted successfully.');
} catch (\App\Exception $e) {
} catch (\Azura\Exception $e) {
return $response->withJson($e->getMessage(), 400);
}
}

View File

@ -2,7 +2,7 @@
namespace App\Controller\Api\Stations;
use App;
use App\Doctrine\Paginator;
use Azura\Doctrine\Paginator;
use Doctrine\ORM\EntityManager;
use App\Entity;
use App\Http\Request;

View File

@ -5,7 +5,7 @@ use App\Acl;
use App\Auth;
use App\Entity\Repository\SettingsRepository;
use App\Entity\User;
use App\RateLimit;
use Azura\RateLimit;
use Doctrine\ORM\EntityManager;
use App\Entity\Settings;
use App\Http\Request;
@ -68,7 +68,7 @@ class AccountController
if (!empty($_POST['username']) && !empty($_POST['password'])) {
try {
$this->rate_limit->checkRateLimit('login', 30, 5);
} catch(\App\Exception\RateLimitExceeded $e) {
} catch(\Azura\Exception\RateLimitExceeded $e) {
$session->flash('<b>' . __('Too many login attempts') . '</b><br>' . __('You have attempted to log in too many times. Please wait 30 seconds and try again.'),
'red');

View File

@ -2,9 +2,8 @@
namespace App\Controller\Frontend;
use App\Acl;
use App\Cache;
use Azura\Cache;
use App\Http\Router;
use App\Url;
use App\Radio\Adapters;
use Doctrine\ORM\EntityManager;
use App\Entity;

View File

@ -40,6 +40,7 @@ class ProfileController
$customization_form = new \AzuraForms\Form($this->form_config['groups']['customization'], $user_profile);
return $request->getView()->renderToResponse($response, 'frontend/profile/index', [
'user' => $request->getUser(),
'account_info_form' => $account_info_form,
'customization_form' => $customization_form,
]);

View File

@ -34,7 +34,7 @@ class PublicController
$station = $request->getStation();
if (!$station->getEnablePublicPage()) {
throw new \App\Exception(__('Station not found!'));
throw new \Azura\Exception(__('Station not found!'));
}
$frontend_adapter = $request->getStationFrontend();

View File

@ -29,7 +29,7 @@ class EditController extends FilesControllerAbstract
// Add custom fields to form configuration.
/** @var Entity\Repository\BaseRepository $custom_fields_repo */
/** @var \Azura\Doctrine\Repository $custom_fields_repo */
$custom_fields_repo = $this->em->getRepository(Entity\CustomField::class);
$custom_fields = $custom_fields_repo->fetchArray();
@ -79,7 +79,7 @@ class EditController extends FilesControllerAbstract
$art_resource = imagecreatefromstring($file->getStream()->getContents());
$media->setArt($art_resource);
} else if ($file->getError() !== UPLOAD_ERR_NO_FILE) {
throw new \App\Exception('Error ' . $file->getError() . ' in uploaded file!');
throw new \Azura\Exception('Error ' . $file->getError() . ' in uploaded file!');
}
}

View File

@ -130,7 +130,7 @@ class FilesController extends FilesControllerAbstract
$search_phrase = trim($request->getParam('searchPhrase') ?? '');
if (!is_dir($file_path)) {
throw new \App\Exception(__('Path "%s" is not a folder.', $file));
throw new \Azura\Exception(__('Path "%s" is not a folder.', $file));
}
$media_query = $this->em->createQueryBuilder()
@ -293,7 +293,7 @@ class FilesController extends FilesControllerAbstract
{
try {
$request->getSession()->getCsrf()->verify($request->getParam('csrf'), $this->csrf_namespace);
} catch(\App\Exception\CsrfValidation $e) {
} catch(\Azura\Exception\CsrfValidation $e) {
return $response->withStatus(403)
->withJson(['error' => ['code' => 403, 'msg' => 'CSRF Failure: '.$e->getMessage()]]);
}
@ -449,7 +449,7 @@ class FilesController extends FilesControllerAbstract
{
try {
$request->getSession()->getCsrf()->verify($request->getParam('csrf'), $this->csrf_namespace);
} catch(\App\Exception\CsrfValidation $e) {
} catch(\Azura\Exception\CsrfValidation $e) {
return $response->withStatus(403)
->withJson(['error' => ['code' => 403, 'msg' => 'CSRF Failure: '.$e->getMessage()]]);
}
@ -474,7 +474,7 @@ class FilesController extends FilesControllerAbstract
{
try {
$request->getSession()->getCsrf()->verify($request->getParam('csrf'), $this->csrf_namespace);
} catch(\App\Exception\CsrfValidation $e) {
} catch(\Azura\Exception\CsrfValidation $e) {
return $response->withStatus(403)
->withJson(['error' => ['code' => 403, 'msg' => 'CSRF Failure: '.$e->getMessage()]]);
}

View File

@ -1,7 +1,7 @@
<?php
namespace App\Controller\Stations\Files;
use App\Cache;
use Azura\Cache;
use App\Http\Router;
use Doctrine\ORM\EntityManager;
use App\Entity;

View File

@ -2,7 +2,7 @@
namespace App\Controller\Stations;
use App\Entity;
use App\Exception;
use Azura\Exception;
use App\Http\Request;
use App\Http\Response;
use App\Radio\Adapters;

View File

@ -35,7 +35,7 @@ class MountsController
$frontend = $request->getStationFrontend();
if (!$frontend::supportsMounts()) {
throw new \App\Exception(__('This feature is not currently supported on this station.'));
throw new \Azura\Exception(__('This feature is not currently supported on this station.'));
}
return $request->getView()->renderToResponse($response, 'stations/mounts/index', [

View File

@ -23,7 +23,7 @@ class PlaylistsController
/** @var array */
protected $form_config;
/** @var Entity\Repository\BaseRepository */
/** @var \Azura\Doctrine\Repository */
protected $playlist_repo;
/** @var Entity\Repository\StationPlaylistMediaRepository */
@ -52,7 +52,7 @@ class PlaylistsController
$backend = $request->getStationBackend();
if (!$backend::supportsMedia()) {
throw new \App\Exception(__('This feature is not currently supported on this station.'));
throw new \Azura\Exception(__('This feature is not currently supported on this station.'));
}
/** @var Entity\StationPlaylist[] $all_playlists */
@ -167,13 +167,13 @@ class PlaylistsController
if ($record->getSource() !== Entity\StationPlaylist::SOURCE_SONGS
|| $record->getOrder() !== Entity\StationPlaylist::ORDER_SEQUENTIAL) {
throw new \App\Exception(__('This playlist is not a sequential playlist.'));
throw new \Azura\Exception(__('This playlist is not a sequential playlist.'));
}
if ($request->isPost()) {
try {
$request->getSession()->getCsrf()->verify($request->getParam('csrf'), $this->csrf_namespace);
} catch(\App\Exception\CsrfValidation $e) {
} catch(\Azura\Exception\CsrfValidation $e) {
return $response->withStatus(403)
->withJson(['error' => ['code' => 403, 'msg' => 'CSRF Failure: '.$e->getMessage()]]);
}

View File

@ -1,10 +1,8 @@
<?php
namespace App\Controller\Stations\Profile;
use App\Cache;
use App\Radio\Backend\BackendAbstract;
use Azura\Cache;
use App\Radio\Configuration;
use App\Radio\Frontend\FrontendAbstract;
use AzuraForms\Field\AbstractField;
use Doctrine\ORM\EntityManager;
use App\Entity;

View File

@ -42,7 +42,7 @@ class RemotesController
{
$station = $request->getStation();
/** @var Entity\Repository\BaseRepository $remote_repo */
/** @var \Azura\Doctrine\Repository $remote_repo */
$remote_repo = $this->em->getRepository(Entity\StationRemote::class);
$form = new \AzuraForms\Form($this->form_config);

View File

@ -1,7 +1,7 @@
<?php
namespace App\Controller\Stations\Reports;
use App\Cache;
use Azura\Cache;
use Doctrine\ORM\EntityManager;
use App\Entity;
use App\Http\Request;

View File

@ -40,7 +40,7 @@ class StreamersController
$backend = $request->getStationBackend();
if (!$backend::supportsStreamers()) {
throw new \App\Exception(__('This feature is not currently supported on this station.'));
throw new \Azura\Exception(__('This feature is not currently supported on this station.'));
}
$view = $request->getView();

View File

@ -85,7 +85,7 @@ class WebhooksController
if (!empty($_POST) && $form->isValid($_POST)) {
$data = $form->getValues();
/** @var Entity\Repository\BaseRepository $record_repo */
/** @var \Azura\Doctrine\Repository $record_repo */
$record_repo = $this->em->getRepository(Entity\StationWebhook::class);
$record = new Entity\StationWebhook($station, $type);
@ -112,7 +112,7 @@ class WebhooksController
{
$station = $request->getStation();
/** @var Entity\Repository\BaseRepository $record_repo */
/** @var \Azura\Doctrine\Repository $record_repo */
$record_repo = $this->em->getRepository(Entity\StationWebhook::class);
$record = $record_repo->findOneBy([

View File

@ -1,92 +0,0 @@
<?php
namespace App\Doctrine\Cache;
/**
* Overrider of the Doctrine Redis Cache functionality
*
* Dev Note: This entire class basically only exists because \Doctrine\Common\Cache\CacheProvider::getNamespacedId
* is a private function that can't be overridden. Thus, instead, to remove Doctrine's flavor of namespacing
* from Redis caching, one must reimplement *everything* that calls that.
*
* @package App\Doctrine\Cache
*/
class Redis extends \Doctrine\Common\Cache\RedisCache
{
/**
* {@inheritdoc}
*/
public function fetch($id)
{
return $this->doFetch($id);
}
/**
* {@inheritdoc}
*/
public function fetchMultiple(array $keys)
{
if (empty($keys)) {
return array();
}
// note: the array_combine() is in place to keep an association between our $keys and the $namespacedKeys
$namespacedKeys = array_combine($keys, $keys);
$items = $this->doFetchMultiple($namespacedKeys);
$foundItems = array();
// no internal array function supports this sort of mapping: needs to be iterative
// this filters and combines keys in one pass
foreach ($namespacedKeys as $requestedKey => $namespacedKey) {
if (isset($items[$namespacedKey]) || array_key_exists($namespacedKey, $items)) {
$foundItems[$requestedKey] = $items[$namespacedKey];
}
}
return $foundItems;
}
/**
* {@inheritdoc}
*/
public function saveMultiple(array $keysAndValues, $lifetime = 0)
{
return $this->doSaveMultiple($keysAndValues, $lifetime);
}
/**
* {@inheritdoc}
*/
public function contains($id)
{
return $this->doContains($id);
}
/**
* {@inheritdoc}
*/
public function save($id, $data, $lifeTime = 0)
{
return $this->doSave($id, $data, $lifeTime);
}
/**
* {@inheritdoc}
*/
public function delete($id)
{
return $this->doDelete($id);
}
/**
* {@inheritDoc}
*/
public function deleteAll()
{
$this->getRedis()->flushDB();
}
protected function getSerializerValue()
{
return \Redis::SERIALIZER_PHP;
}
}

View File

@ -1,25 +0,0 @@
<?php
namespace App\Doctrine\Functions;
use Doctrine\ORM\Query\AST\Functions\FunctionNode;
use Doctrine\ORM\Query\Lexer;
use Doctrine\ORM\Query\Parser;
use Doctrine\ORM\Query\SqlWalker;
/**
* RandFunction ::= "RAND" "(" ")"
*/
class Rand extends FunctionNode
{
public function parse(Parser $parser)
{
$parser->match(Lexer::T_IDENTIFIER);
$parser->match(Lexer::T_OPEN_PARENTHESIS);
$parser->match(Lexer::T_CLOSE_PARENTHESIS);
}
public function getSql(SqlWalker $sqlWalker)
{
return 'RAND()';
}
}

View File

@ -1,68 +0,0 @@
<?php
/**
* DoctrineExtensions Paginate
*
* LICENSE
*
* This source file is subject to the new BSD license that is bundled
* with this package in the file LICENSE.txt.
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to kontakt@beberlei.de so I can send you a copy immediately.
*/
namespace App\Doctrine\Paginate;
use Doctrine\ORM\Query\AST\AggregateExpression;
use Doctrine\ORM\Query\AST\PathExpression;
use Doctrine\ORM\Query\AST\SelectExpression;
use Doctrine\ORM\Query\AST\SelectStatement;
use Doctrine\ORM\Query\TreeWalkerAdapter;
class CountWalker extends TreeWalkerAdapter
{
/**
* Walks down a SelectStatement AST node, modifying it to retrieve a COUNT
*
* @param SelectStatement $AST
* @return void
*/
public function walkSelectStatement(SelectStatement $AST)
{
$parent = null;
$parentName = null;
foreach ($this->_getQueryComponents() AS $dqlAlias => $qComp) {
// skip mixed data in query
if (isset($qComp['resultVariable'])) {
continue;
}
if ($qComp['parent'] === null && $qComp['nestingLevel'] == 0) {
$parent = $qComp;
$parentName = $dqlAlias;
break;
}
}
$pathExpression = new PathExpression(
PathExpression::TYPE_STATE_FIELD | PathExpression::TYPE_SINGLE_VALUED_ASSOCIATION, $parentName,
$parent['metadata']->getSingleIdentifierFieldName()
);
$pathExpression->type = PathExpression::TYPE_STATE_FIELD;
$AST->selectClause->selectExpressions = [
new SelectExpression(
new AggregateExpression('count', $pathExpression, true), null
)
];
// ORDER BY is not needed, only increases query execution through unnecessary sorting.
$AST->orderByClause = null;
// GROUP BY will break things, we are trying to get a count of all
$AST->groupByClause = null;
}
}

View File

@ -1,187 +0,0 @@
<?php
namespace App\Doctrine;
use App\Http\Request;
use App\Http\Response;
use App\Http\Router;
use Doctrine\ORM\Query;
use Doctrine\ORM\QueryBuilder;
use Doctrine\ORM\Tools\Pagination\Paginator as DoctrinePaginator;
class Paginator
{
/** @var Query */
protected $query;
/** @var Router */
protected $router;
/** @var int */
protected $current_page = 1;
/** @var int */
protected $per_page = 15;
/** @var int */
protected $max_per_page = 50;
/** @var bool Whether the current request is from jQuery Bootgrid */
protected $is_bootgrid;
/** @var callable|null A callable postprocessor that can be run on each result. */
protected $postprocessor;
public function __construct($query)
{
if ($query instanceof QueryBuilder) {
$query = $query->getQuery();
}
if (!($query instanceof Query)) {
throw new \InvalidArgumentException('Query specified is not a Doctrine query.');
}
$this->query = $query;
}
public function getQuery(): Query
{
return $this->query;
}
public function setCurrentPage(int $current_page): void
{
$this->current_page = ($current_page > 0) ? $current_page : 1;
}
public function getCurrentPage(): int
{
return $this->current_page;
}
public function setMaxPerPage(int $max_per_page): void
{
$this->max_per_page = ($max_per_page > 0) ? $max_per_page : 1;
}
public function setPerPage(int $per_page): void
{
if ($per_page > 0) {
$this->per_page = ($per_page <= $this->max_per_page) ? $per_page : $this->max_per_page;
} else {
$this->per_page = -1;
}
}
public function getPerPage(): int
{
return $this->per_page;
}
public function setRouter(Router $router): void
{
$this->router = $router;
}
public function getRouter(): Router
{
return $this->router;
}
public function isFromBootgrid(): bool
{
return $this->is_bootgrid;
}
public function setFromRequest(Request $request)
{
$this->is_bootgrid = $request->hasParam('rowCount') || $request->hasParam('searchPhrase');
if ($this->is_bootgrid) {
$this->setCurrentPage((int)$request->getParam('current'));
$this->setPerPage((int)$request->getParam('rowCount'));
} else {
if ($request->hasParam('page')) {
$this->setCurrentPage($request->getParam('page'));
}
if ($request->hasParam('per_page')) {
$this->setPerPage($request->getParam('per_page'));
}
}
$this->setRouter($request->getRouter());
}
public function setPostprocessor(callable $postprocessor)
{
$this->postprocessor = $postprocessor;
}
public function getPaginator()
{
static $paginator;
if (!$paginator) {
if ($this->per_page !== -1) {
$offset = ($this->current_page - 1) * $this->per_page;
$this->query->setFirstResult($offset);
$this->query->setMaxResults($this->per_page);
}
$paginator = new DoctrinePaginator($this->query);
}
return $paginator;
}
public function write(Response $response): Response
{
$paginator = $this->getPaginator();
$total = count($paginator);
$total_pages = ($this->per_page === -1)
? 1
: ceil($total / $this->per_page);
if ($this->postprocessor) {
$results = [];
$postprocessor = $this->postprocessor;
foreach($paginator as $result) {
$results[] = $postprocessor($result);
}
} else {
$results = iterator_to_array($paginator);
}
if ($this->is_bootgrid) {
return $response->withJson([
'current' => $this->current_page,
'rowCount' => $this->per_page,
'total' => $paginator->count(),
'rows' => $results,
]);
}
$page_links = [];
if ($this->router instanceof Router) {
$page_links['first'] = $this->router->fromHereWithQuery(null, [], ['page' => 1]);
$prev_page = ($this->current_page > 1) ? $this->current_page - 1 : 1;
$page_links['previous'] = $this->router->fromHereWithQuery(null, [], ['page' => $prev_page]);
$next_page = ($this->current_page < $total_pages) ? $this->current_page + 1 : $total_pages;
$page_links['next'] = $this->router->fromHereWithQuery(null, [], ['page' => $next_page]);
$page_links['last'] = $this->router->fromHereWithQuery(null, [], ['page' => $total_pages]);
}
return $response->withJson([
'page' => $this->current_page,
'per_page' => $this->per_page,
'total' => $paginator->count(),
'total_pages' => $total_pages,
'links' => $page_links,
'rows' => $results,
]);
}
}

View File

@ -1,22 +0,0 @@
<?php
/**
* Custom MySQL platform that supports utf8mb4 charset and encoding by default for tables.
*/
namespace App\Doctrine\Platform;
class MysqlUnicode extends \Doctrine\DBAL\Platforms\MySqlPlatform
{
protected function _getCreateTableSQL($tableName, array $columns, array $options = [])
{
if (!isset($options['charset'])) {
$options['charset'] = 'utf8mb4';
}
if (!isset($options['collate'])) {
$options['collate'] = 'utf8mb4_unicode_ci';
}
return parent::_getCreateTableSQL($tableName, $columns, $options);
}
}

View File

@ -1,383 +0,0 @@
<?php
namespace App\Doctrine;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\EntityRepository;
class Repository extends EntityRepository
{
/**
* Generate an array result of all records.
*
* @param bool $cached
* @param null $order_by
* @param string $order_dir
* @return array
*/
public function fetchArray($cached = true, $order_by = null, $order_dir = 'ASC')
{
$qb = $this->_em->createQueryBuilder()
->select('e')
->from($this->_entityName, 'e');
if ($order_by) {
$qb->orderBy('e.' . str_replace('e.', '', $order_by), $order_dir);
}
return $qb->getQuery()->getArrayResult();
}
/**
* Generic dropdown builder function (can be overridden for specialized use cases).
*
* @param bool $add_blank
* @param \Closure|NULL $display
* @param string $pk
* @param string $order_by
* @return array
*/
public function fetchSelect($add_blank = false, \Closure $display = null, $pk = 'id', $order_by = 'name')
{
$select = [];
// Specify custom text in the $add_blank parameter to override.
if ($add_blank !== false) {
$select[''] = ($add_blank === true) ? 'Select...' : $add_blank;
}
// Build query for records.
$qb = $this->_em->createQueryBuilder()->from($this->_entityName, 'e');
if ($display === null) {
$qb->select('e.' . $pk)->addSelect('e.name')->orderBy('e.' . $order_by, 'ASC');
} else {
$qb->select('e')->orderBy('e.' . $order_by, 'ASC');
}
$results = $qb->getQuery()->getArrayResult();
// Assemble select values and, if necessary, call $display callback.
foreach ((array)$results as $result) {
$key = $result[$pk];
$value = ($display === null) ? $result['name'] : $display($result);
$select[$key] = $value;
}
return $select;
}
/**
* FromArray (A Doctrine 1 Classic)
*
* @param object|Entity $entity
* @param array $source
*/
public function fromArray($entity, array $source)
{
$metadata = $this->_getMetadata($entity);
$meta = $metadata['meta'];
$mappings = $metadata['mappings'];
foreach ((array)$source as $field => $value) {
if (isset($mappings[$field])) {
$mapping = $mappings[$field];
switch ($mapping['type']) {
case "one_id":
$entity_field = $mapping['name'];
$entity_id = $mapping['ids'][0];
if (empty($value)) {
$this->_set($entity, $field, null);
$this->_set($entity, $entity_field, null);
} else {
if ($value != $this->_get($entity, $field)) {
$obj_class = $mapping['entity'];
$obj = $this->_em->find($obj_class, $value);
if ($obj instanceof $obj_class) {
$this->_set($entity, $field, $this->_get($obj, $entity_id));
$this->_set($entity, $entity_field, $obj);
}
}
}
break;
case "one_entity":
$entity_id = $mapping['ids'][0];
$id_field = $mapping['name'];
if (empty($value)) {
$this->_set($entity, $field, null);
$this->_set($entity, $id_field, null);
} else {
if ($this->_get($value, $entity_id) != $this->_get($entity, $field)) {
$this->_set($entity, $field, $value);
$this->_set($entity, $id_field, $this->_get($value, $entity_id));
}
}
break;
case "many":
$obj_class = $mapping['entity'];
if ($mapping['is_owning_side']) {
/** @var Collection $collection */
$collection = $this->_get($entity, $field);
$collection->clear();
if ($value) {
foreach ((array)$value as $field_id) {
if (($field_item = $this->_em->find($obj_class, (int)$field_id)) instanceof $obj_class) {
$collection->add($field_item);
}
}
}
} else {
$foreign_field = $mapping['mappedBy'];
if (count($this->_get($entity, $field)) > 0) {
foreach ($this->_get($entity, $field) as $record) {
/** @var Collection $collection */
$collection = $this->_get($record, $foreign_field);
$collection->removeElement($entity);
$this->_em->persist($record);
}
}
foreach ((array)$value as $field_id) {
$record = $this->_em->find($obj_class, (int)$field_id);
if ($record instanceof $obj_class) {
/** @var Collection $collection */
$collection = $this->_get($record, $foreign_field);
$collection->add($entity);
$this->_em->persist($record);
}
}
$this->_em->flush();
}
break;
}
} else {
if (!isset($meta->fieldMappings[$field])) {
$field_info = [];
} else {
$field_info = $meta->fieldMappings[$field];
}
switch ($field_info['type']) {
case "datetime":
case "date":
if (!($value instanceof \DateTime)) {
if ($value) {
if (!is_numeric($value)) {
$value = strtotime($value . ' UTC');
}
$value = \DateTime::createFromFormat(\DateTime::ISO8601,
gmdate(\DateTime::ISO8601, (int)$value));
} else {
$value = null;
}
}
break;
case "string":
if ($field_info['length'] && strlen($value) > $field_info['length']) {
$value = substr($value, 0, $field_info['length']);
}
break;
case "decimal":
case "float":
if ($value !== null) {
if (is_numeric($value)) {
$value = (float)$value;
} elseif (empty($value)) {
$value = ($field_info['nullable']) ? NULL : 0.0;
}
}
break;
case "integer":
case "smallint":
case "bigint":
if ($value !== null) {
$value = (int)$value;
}
break;
case "boolean":
if ($value !== null) {
$value = (bool)$value;
}
break;
}
$this->_set($entity, $field, $value);
}
}
}
/**
* ToArray (A Doctrine 1 Classic)
*
* @param object|Entity $entity
* @param bool $deep Iterate through collections associated with this item.
* @param bool $form_mode Return values in a format suitable for ZendForm setDefault function.
* @return array
*/
public function toArray($entity, $deep = false, $form_mode = false)
{
$return_arr = [];
$class_meta = $this->_em->getClassMetadata(get_class($entity));
$reflect = new \ReflectionClass($entity);
$props = $reflect->getProperties(\ReflectionProperty::IS_PUBLIC | \ReflectionProperty::IS_PROTECTED);
if ($props) {
foreach ($props as $property) {
$property->setAccessible(true);
$prop_name = $property->getName();
$prop_val = $property->getValue($entity);
if (isset($class_meta->fieldMappings[$prop_name])) {
$prop_info = $class_meta->fieldMappings[$prop_name];
} else {
$prop_info = [];
}
if (is_array($prop_val)) {
$return_arr[$prop_name] = $prop_val;
} else {
if (!is_object($prop_val)) {
if ($prop_info['type'] == "array") {
$return_arr[$prop_name] = (array)$prop_val;
} else {
$return_arr[$prop_name] = (string)$prop_val;
}
} else {
if ($prop_val instanceof \DateTime) {
$return_arr[$prop_name] = $prop_val->getTimestamp();
} else {
if ($deep) {
if ($prop_val instanceof \Doctrine\Common\Collections\Collection) {
$return_val = [];
if (count($prop_val) > 0) {
foreach ($prop_val as $val_obj) {
if ($form_mode) {
$obj_meta = $this->_em->getClassMetadata(get_class($val_obj));
$id_field = $obj_meta->identifier;
if ($id_field && count($id_field) == 1) {
$return_val[] = $this->_get($val_obj, $id_field[0]);
}
} else {
$return_val[] = $this->toArray($val_obj, false);
}
}
}
$return_arr[$prop_name] = $return_val;
} else {
$return_arr[$prop_name] = $this->toArray($prop_val, false);
}
}
}
}
}
}
}
return $return_arr;
}
/**
* Internal function for pulling metadata, used in toArray and fromArray
*
* @param object $source_entity
* @return array
*/
protected function _getMetadata($source_entity)
{
$class = get_class($source_entity);
$meta_result = [];
$meta_result['em'] = $this->_em;
$meta_result['factory'] = $this->_em->getMetadataFactory();
$meta_result['meta'] = $meta_result['factory']->getMetadataFor($class);
$meta_result['mappings'] = [];
if ($meta_result['meta']->associationMappings) {
foreach ($meta_result['meta']->associationMappings as $mapping_name => $mapping_info) {
$entity = $mapping_info['targetEntity'];
if (isset($mapping_info['joinTable'])) {
$meta_result['mappings'][$mapping_info['fieldName']] = [
'type' => 'many',
'entity' => $entity,
'is_owning_side' => ($mapping_info['isOwningSide'] == 1),
'mappedBy' => $mapping_info['mappedBy'],
];
} else {
if (isset($mapping_info['joinColumns'])) {
foreach ($mapping_info['joinColumns'] as $col) {
$join_meta = $meta_result['factory']->getMetadataFor($entity);
$join_ids = $join_meta->getIdentifierFieldNames();
$col_name = $col['name'];
$col_name = (isset($meta_result['meta']->fieldNames[$col_name])) ? $meta_result['meta']->fieldNames[$col_name] : $col_name;
$meta_result['mappings'][$col_name] = [
'name' => $mapping_name,
'type' => 'one_id',
'entity' => $entity,
'ids' => $join_ids,
];
$meta_result['mappings'][$mapping_name] = [
'name' => $col_name,
'type' => 'one_entity',
'entity' => $entity,
'ids' => $join_ids,
];
}
}
}
}
}
return $meta_result;
}
protected function _get($entity, $key)
{
$method_name = $this->_getMethodName($key, 'get');
return (method_exists($entity, $method_name))
? $entity->$method_name()
: null;
}
protected function _set($entity, $key, $value)
{
$method_name = $this->_getMethodName($key, 'set');
return (method_exists($entity, $method_name))
? $entity->$method_name($value)
: null;
}
// Converts "getvar_name_blah" to "getVarNameBlah".
protected function _getMethodName($var, $prefix = '')
{
return $prefix . str_replace(" ", "", ucwords(strtr($var, "_-", " ")));
}
}

View File

@ -1,62 +0,0 @@
<?php
namespace App\Doctrine;
use Doctrine\Common\Persistence\ObjectRepository;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\Mapping\ClassMetadata;
use Psr\Container\ContainerInterface;
/**
* A dependency-injection aware repository locator.
*
* @package App\Doctrine
*/
class RepositoryFactory implements \Doctrine\ORM\Repository\RepositoryFactory
{
/** @var ObjectRepository[] */
protected $repository_list = [];
/** @var ContainerInterface */
protected $di;
public function __construct(ContainerInterface $di)
{
$this->di = $di;
}
/**
* {@inheritdoc}
*/
public function getRepository(EntityManagerInterface $em, $entity_name)
{
$entity_meta = $em->getClassMetadata($entity_name);
$repo_name = $entity_meta->getName();
if (isset($this->repository_list[$repo_name])) {
return $this->repository_list[$repo_name];
}
return $this->repository_list[$repo_name] = $this->createRepository($em, $entity_meta);
}
/**
* Create a new repository instance for an entity class.
*
* @param EntityManagerInterface $entityManager
* @param ClassMetadata $entity_meta
* @return ObjectRepository
*/
protected function createRepository(
EntityManagerInterface $entityManager,
ClassMetadata $entity_meta): ObjectRepository
{
$repo_class = $entity_meta->customRepositoryClassName
?: $entityManager->getConfiguration()->getDefaultRepositoryClassName();
if ($this->di->has($repo_class)) {
return $this->di->get($repo_class);
}
return new $repo_class($entityManager, $entity_meta);
}
}

View File

@ -19,21 +19,38 @@ final class Version20161003041904 extends AbstractMigration
$this->abortIf($this->connection->getDatabasePlatform()->getName() != 'mysql',
'Migration can only be executed safely on \'mysql\'.');
$db_dump_file = APP_INCLUDE_ROOT.'/util/azuracast_db.sql';
$db_dump = file_get_contents($db_dump_file);
// Apply the original database schema.
$this->addSql([
'SET NAMES utf8mb4',
'SET FOREIGN_KEY_CHECKS=0',
foreach(explode("\n", $db_dump) as $db_dump_line) {
$this->addSql($db_dump_line);
}
'CREATE TABLE IF NOT EXISTS `action` (`id` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(100) COLLATE utf8_unicode_ci DEFAULT NULL, PRIMARY KEY (`id`)) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci',
'CREATE TABLE IF NOT EXISTS `analytics` (`id` int(11) NOT NULL AUTO_INCREMENT, `station_id` int(11) DEFAULT NULL, `type` varchar(15) COLLATE utf8_unicode_ci NOT NULL, `timestamp` int(11) NOT NULL, `number_min` int(11) NOT NULL, `number_max` int(11) NOT NULL, `number_avg` int(11) NOT NULL, PRIMARY KEY (`id`), KEY `IDX_EAC2E68821BDB235` (`station_id`), KEY `search_idx` (`type`,`timestamp`), CONSTRAINT `FK_EAC2E68821BDB235` FOREIGN KEY (`station_id`) REFERENCES `station` (`id`) ON DELETE CASCADE) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci',
'CREATE TABLE IF NOT EXISTS `api_keys` (`id` varchar(50) COLLATE utf8_unicode_ci NOT NULL, `owner` varchar(150) COLLATE utf8_unicode_ci DEFAULT NULL, `calls_made` int(11) NOT NULL, `created` int(11) NOT NULL, PRIMARY KEY (`id`)) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci',
'CREATE TABLE IF NOT EXISTS `role` (`id` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(100) COLLATE utf8_unicode_ci NOT NULL, PRIMARY KEY (`id`)) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci',
'CREATE TABLE IF NOT EXISTS `role_has_action` (`role_id` int(11) NOT NULL, `action_id` int(11) NOT NULL, PRIMARY KEY (`role_id`,`action_id`), KEY `IDX_E4DAF125D60322AC` (`role_id`), KEY `IDX_E4DAF1259D32F035` (`action_id`), CONSTRAINT `FK_E4DAF1259D32F035` FOREIGN KEY (`action_id`) REFERENCES `action` (`id`) ON DELETE CASCADE, CONSTRAINT `FK_E4DAF125D60322AC` FOREIGN KEY (`role_id`) REFERENCES `role` (`id`) ON DELETE CASCADE) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci',
'CREATE TABLE IF NOT EXISTS `settings` (`setting_key` varchar(64) COLLATE utf8_unicode_ci NOT NULL, `setting_value` longtext COLLATE utf8_unicode_ci COMMENT \'(DC2Type:json)\', PRIMARY KEY (`setting_key`)) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci',
'CREATE TABLE IF NOT EXISTS `songs` (`id` varchar(50) COLLATE utf8_unicode_ci NOT NULL, `text` varchar(150) COLLATE utf8_unicode_ci DEFAULT NULL, `artist` varchar(150) COLLATE utf8_unicode_ci DEFAULT NULL, `title` varchar(150) COLLATE utf8_unicode_ci DEFAULT NULL, `created` int(11) NOT NULL, `play_count` int(11) NOT NULL, `last_played` int(11) NOT NULL, PRIMARY KEY (`id`), KEY `search_idx` (`text`,`artist`,`title`)) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci',
'CREATE TABLE IF NOT EXISTS `song_history` (`id` int(11) NOT NULL AUTO_INCREMENT, `song_id` varchar(50) COLLATE utf8_unicode_ci NOT NULL, `station_id` int(11) NOT NULL, `timestamp_start` int(11) NOT NULL, `listeners_start` int(11) DEFAULT NULL, `timestamp_end` int(11) NOT NULL, `listeners_end` smallint(6) DEFAULT NULL, `delta_total` smallint(6) NOT NULL, `delta_positive` smallint(6) NOT NULL, `delta_negative` smallint(6) NOT NULL, `delta_points` longtext COLLATE utf8_unicode_ci COMMENT \'(DC2Type:json)\', PRIMARY KEY (`id`), KEY `IDX_2AD16164A0BDB2F3` (`song_id`), KEY `IDX_2AD1616421BDB235` (`station_id`), KEY `sort_idx` (`timestamp_start`), CONSTRAINT `FK_2AD1616421BDB235` FOREIGN KEY (`station_id`) REFERENCES `station` (`id`) ON DELETE CASCADE, CONSTRAINT `FK_2AD16164A0BDB2F3` FOREIGN KEY (`song_id`) REFERENCES `songs` (`id`) ON DELETE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci',
'CREATE TABLE IF NOT EXISTS `station` ( `id` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(100) COLLATE utf8_unicode_ci DEFAULT NULL, `frontend_type` varchar(100) COLLATE utf8_unicode_ci DEFAULT NULL, `frontend_config` longtext COLLATE utf8_unicode_ci COMMENT \'(DC2Type:json)\', `backend_type` varchar(100) COLLATE utf8_unicode_ci DEFAULT NULL, `backend_config` longtext COLLATE utf8_unicode_ci COMMENT \'(DC2Type:json)\', `description` longtext COLLATE utf8_unicode_ci, `radio_base_dir` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, `nowplaying_data` longtext COLLATE utf8_unicode_ci COMMENT \'(DC2Type:json)\', `automation_settings` longtext COLLATE utf8_unicode_ci COMMENT \'(DC2Type:json)\', `automation_timestamp` int(11) DEFAULT NULL, `enable_requests` tinyint(1) NOT NULL, `request_delay` int(11) DEFAULT NULL, `enable_streamers` tinyint(1) NOT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci',
'CREATE TABLE IF NOT EXISTS `station_media` ( `id` int(11) NOT NULL AUTO_INCREMENT, `station_id` int(11) NOT NULL, `song_id` varchar(50) COLLATE utf8_unicode_ci DEFAULT NULL, `title` varchar(200) COLLATE utf8_unicode_ci DEFAULT NULL, `artist` varchar(200) COLLATE utf8_unicode_ci DEFAULT NULL, `album` varchar(200) COLLATE utf8_unicode_ci DEFAULT NULL, `length` smallint(6) NOT NULL, `length_text` varchar(10) COLLATE utf8_unicode_ci DEFAULT NULL, `path` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, `mtime` int(11) DEFAULT NULL, PRIMARY KEY (`id`), UNIQUE KEY `path_unique_idx` (`path`), KEY `IDX_32AADE3A21BDB235` (`station_id`), KEY `IDX_32AADE3AA0BDB2F3` (`song_id`), KEY `search_idx` (`title`,`artist`,`album`), CONSTRAINT `FK_32AADE3A21BDB235` FOREIGN KEY (`station_id`) REFERENCES `station` (`id`) ON DELETE CASCADE, CONSTRAINT `FK_32AADE3AA0BDB2F3` FOREIGN KEY (`song_id`) REFERENCES `songs` (`id`) ON DELETE SET NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci',
'CREATE TABLE IF NOT EXISTS `station_playlists` ( `id` int(11) NOT NULL AUTO_INCREMENT, `station_id` int(11) NOT NULL, `name` varchar(200) COLLATE utf8_unicode_ci NOT NULL, `type` varchar(50) COLLATE utf8_unicode_ci NOT NULL, `is_enabled` tinyint(1) NOT NULL, `play_per_songs` smallint(6) NOT NULL, `play_per_minutes` smallint(6) NOT NULL, `schedule_start_time` smallint(6) NOT NULL, `schedule_end_time` smallint(6) NOT NULL, `play_once_time` smallint(6) NOT NULL, `weight` smallint(6) NOT NULL, `include_in_automation` tinyint(1) NOT NULL, PRIMARY KEY (`id`), KEY `IDX_DC827F7421BDB235` (`station_id`), CONSTRAINT `FK_DC827F7421BDB235` FOREIGN KEY (`station_id`) REFERENCES `station` (`id`) ON DELETE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci',
'CREATE TABLE IF NOT EXISTS `station_playlist_has_media` ( `media_id` int(11) NOT NULL, `playlists_id` int(11) NOT NULL, PRIMARY KEY (`media_id`,`playlists_id`), KEY `IDX_668E6486EA9FDD75` (`media_id`), KEY `IDX_668E64869F70CF56` (`playlists_id`), CONSTRAINT `FK_668E64869F70CF56` FOREIGN KEY (`playlists_id`) REFERENCES `station_playlists` (`id`) ON DELETE CASCADE, CONSTRAINT `FK_668E6486EA9FDD75` FOREIGN KEY (`media_id`) REFERENCES `station_media` (`id`) ON DELETE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci',
'CREATE TABLE IF NOT EXISTS `station_requests` ( `id` int(11) NOT NULL AUTO_INCREMENT, `station_id` int(11) NOT NULL, `track_id` int(11) NOT NULL, `timestamp` int(11) NOT NULL, `played_at` int(11) NOT NULL, `ip` varchar(40) COLLATE utf8_unicode_ci NOT NULL, PRIMARY KEY (`id`), KEY `IDX_F71F0C0721BDB235` (`station_id`), KEY `IDX_F71F0C075ED23C43` (`track_id`), CONSTRAINT `FK_F71F0C0721BDB235` FOREIGN KEY (`station_id`) REFERENCES `station` (`id`) ON DELETE CASCADE, CONSTRAINT `FK_F71F0C075ED23C43` FOREIGN KEY (`track_id`) REFERENCES `station_media` (`id`) ON DELETE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci',
'CREATE TABLE IF NOT EXISTS `station_streamers` ( `id` int(11) NOT NULL AUTO_INCREMENT, `station_id` int(11) NOT NULL, `streamer_username` varchar(50) COLLATE utf8_unicode_ci NOT NULL, `streamer_password` varchar(50) COLLATE utf8_unicode_ci NOT NULL, `comments` longtext COLLATE utf8_unicode_ci, `is_active` tinyint(1) NOT NULL, PRIMARY KEY (`id`), KEY `IDX_5170063E21BDB235` (`station_id`), CONSTRAINT `FK_5170063E21BDB235` FOREIGN KEY (`station_id`) REFERENCES `station` (`id`) ON DELETE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci',
'CREATE TABLE IF NOT EXISTS `users` ( `uid` int(11) NOT NULL AUTO_INCREMENT, `email` varchar(100) COLLATE utf8_unicode_ci DEFAULT NULL, `auth_password` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, `auth_last_login_time` int(11) DEFAULT NULL, `auth_recovery_code` varchar(50) COLLATE utf8_unicode_ci DEFAULT NULL, `name` varchar(100) COLLATE utf8_unicode_ci DEFAULT NULL, `gender` varchar(1) COLLATE utf8_unicode_ci DEFAULT NULL, `customization` longtext COLLATE utf8_unicode_ci COMMENT \'(DC2Type:json)\', PRIMARY KEY (`uid`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci',
'CREATE TABLE IF NOT EXISTS `user_has_role` ( `user_id` int(11) NOT NULL, `role_id` int(11) NOT NULL, PRIMARY KEY (`user_id`,`role_id`), KEY `IDX_EAB8B535A76ED395` (`user_id`), KEY `IDX_EAB8B535D60322AC` (`role_id`), CONSTRAINT `FK_EAB8B535A76ED395` FOREIGN KEY (`user_id`) REFERENCES `users` (`uid`) ON DELETE CASCADE, CONSTRAINT `FK_EAB8B535D60322AC` FOREIGN KEY (`role_id`) REFERENCES `role` (`id`) ON DELETE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci',
'CREATE TABLE IF NOT EXISTS `user_manages_station` ( `user_id` int(11) NOT NULL, `station_id` int(11) NOT NULL, PRIMARY KEY (`user_id`,`station_id`), KEY `IDX_2453B56BA76ED395` (`user_id`), KEY `IDX_2453B56B21BDB235` (`station_id`), CONSTRAINT `FK_2453B56B21BDB235` FOREIGN KEY (`station_id`) REFERENCES `station` (`id`) ON DELETE CASCADE, CONSTRAINT `FK_2453B56BA76ED395` FOREIGN KEY (`user_id`) REFERENCES `users` (`uid`) ON DELETE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci',
$this->addSql('CREATE TABLE role_has_actions (id INT AUTO_INCREMENT NOT NULL, station_id INT DEFAULT NULL, role_id INT NOT NULL, action_id INT NOT NULL, INDEX IDX_50EEC1BDD60322AC (role_id), INDEX IDX_50EEC1BD21BDB235 (station_id), UNIQUE INDEX role_action_unique_idx (role_id, action_id, station_id), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci ENGINE = InnoDB');
$this->addSql('ALTER TABLE role_has_actions ADD CONSTRAINT FK_50EEC1BD21BDB235 FOREIGN KEY (station_id) REFERENCES station (id) ON DELETE CASCADE');
'CREATE TABLE role_has_actions (id INT AUTO_INCREMENT NOT NULL, station_id INT DEFAULT NULL, role_id INT NOT NULL, action_id INT NOT NULL, INDEX IDX_50EEC1BDD60322AC (role_id), INDEX IDX_50EEC1BD21BDB235 (station_id), UNIQUE INDEX role_action_unique_idx (role_id, action_id, station_id), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci ENGINE = InnoDB',
'ALTER TABLE role_has_actions ADD CONSTRAINT FK_50EEC1BD21BDB235 FOREIGN KEY (station_id) REFERENCES station (id) ON DELETE CASCADE',
'INSERT INTO role_has_actions (role_id, action_id) SELECT role_id, action_id FROM role_has_action',
'DROP TABLE role_has_action',
'ALTER TABLE action ADD is_global TINYINT(1) NOT NULL',
'UPDATE action SET is_global=1',
$this->addSql('INSERT INTO role_has_actions (role_id, action_id) SELECT role_id, action_id FROM role_has_action');
$this->addSql('DROP TABLE role_has_action');
$this->addSql('ALTER TABLE action ADD is_global TINYINT(1) NOT NULL');
$this->addSql('UPDATE action SET is_global=1');
'SET FOREIGN_KEY_CHECKS=1',
]);
$actions = [
['view administration', 1],

View File

@ -2,8 +2,9 @@
namespace App\Entity\Repository;
use App\Entity;
use Azura\Doctrine\Repository;
class ApiKeyRepository extends BaseRepository
class ApiKeyRepository extends Repository
{
/**
* Given an API key string in the format `identifier:verifier`, find and authenticate an API key.

View File

@ -1,415 +0,0 @@
<?php
namespace App\Entity\Repository;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\EntityRepository;
class BaseRepository extends EntityRepository
{
/**
* Combination of find() and toArray() helper functions.
*
* @param $id
* @param bool $deep
* @param bool $form_mode
* @return array|null
*/
public function findAsArray($id, $deep = false, $form_mode = false)
{
$record = $this->_em->find($this->_entityName, $id);
return ($record === null)
? null
: $this->toArray($record, $deep, $form_mode);
}
/**
* Generate an array result of all records.
*
* @param bool $cached
* @param null $order_by
* @param string $order_dir
* @return array
*/
public function fetchArray($cached = true, $order_by = null, $order_dir = 'ASC')
{
$qb = $this->_em->createQueryBuilder()
->select('e')
->from($this->_entityName, 'e');
if ($order_by) {
$qb->orderBy('e.' . str_replace('e.', '', $order_by), $order_dir);
}
return $qb->getQuery()->getArrayResult();
}
/**
* Generic dropdown builder function (can be overridden for specialized use cases).
*
* @param bool $add_blank
* @param \Closure|NULL $display
* @param string $pk
* @param string $order_by
* @return array
*/
public function fetchSelect($add_blank = false, \Closure $display = null, $pk = 'id', $order_by = 'name')
{
$select = [];
// Specify custom text in the $add_blank parameter to override.
if ($add_blank !== false) {
$select[''] = ($add_blank === true) ? 'Select...' : $add_blank;
}
// Build query for records.
$qb = $this->_em->createQueryBuilder()->from($this->_entityName, 'e');
if ($display === null) {
$qb->select('e.' . $pk)->addSelect('e.name')->orderBy('e.' . $order_by, 'ASC');
} else {
$qb->select('e')->orderBy('e.' . $order_by, 'ASC');
}
$results = $qb->getQuery()->getArrayResult();
// Assemble select values and, if necessary, call $display callback.
foreach ((array)$results as $result) {
$key = $result[$pk];
$value = ($display === null) ? $result['name'] : $display($result);
$select[$key] = $value;
}
return $select;
}
/**
* FromArray (A Doctrine 1 Classic)
*
* @param object $entity
* @param array $source
*/
public function fromArray($entity, array $source)
{
$metadata = $this->_getMetadata($entity);
$meta = $metadata['meta'];
$mappings = $metadata['mappings'];
foreach ((array)$source as $field => $value) {
if (isset($mappings[$field])) {
$mapping = $mappings[$field];
switch ($mapping['type']) {
case "one_id":
$entity_field = $mapping['name'];
$entity_id = $mapping['ids'][0];
if (empty($value)) {
$this->_set($entity, $field, null);
$this->_set($entity, $entity_field, null);
} else {
if ($value != $this->_get($entity, $field)) {
$obj_class = $mapping['entity'];
$obj = $this->_em->find($obj_class, $value);
if ($obj instanceof $obj_class) {
$this->_set($entity, $field, $this->_get($obj, $entity_id));
$this->_set($entity, $entity_field, $obj);
}
}
}
break;
case "one_entity":
$entity_id = $mapping['ids'][0];
$id_field = $mapping['name'];
if (empty($value)) {
$this->_set($entity, $field, null);
$this->_set($entity, $id_field, null);
} else {
if ($this->_get($value, $entity_id) != $this->_get($entity, $field)) {
$this->_set($entity, $field, $value);
$this->_set($entity, $id_field, $this->_get($value, $entity_id));
}
}
break;
case "many":
$obj_class = $mapping['entity'];
if ($mapping['is_owning_side']) {
/** @var Collection $collection */
$collection = $this->_get($entity, $field);
$collection->clear();
if ($value) {
foreach ((array)$value as $field_id) {
if (($field_item = $this->_em->find($obj_class, (int)$field_id)) instanceof $obj_class) {
$collection->add($field_item);
}
}
}
} else {
$foreign_field = $mapping['mappedBy'];
if (count($this->_get($entity, $field)) > 0) {
foreach ($this->_get($entity, $field) as $record) {
/** @var Collection $collection */
$collection = $this->_get($record, $foreign_field);
$collection->removeElement($entity);
$this->_em->persist($record);
}
}
foreach ((array)$value as $field_id) {
$record = $this->_em->find($obj_class, (int)$field_id);
if ($record instanceof $obj_class) {
/** @var Collection $collection */
$collection = $this->_get($record, $foreign_field);
$collection->add($entity);
$this->_em->persist($record);
}
}
$this->_em->flush();
}
break;
}
} else {
if (!isset($meta->fieldMappings[$field])) {
$field_info = [];
} else {
$field_info = $meta->fieldMappings[$field];
}
switch ($field_info['type']) {
case "datetime":
case "date":
if (!($value instanceof \DateTime)) {
if ($value) {
if (!is_numeric($value)) {
$value = strtotime($value . ' UTC');
}
$value = \DateTime::createFromFormat(\DateTime::ISO8601,
gmdate(\DateTime::ISO8601, (int)$value));
} else {
$value = null;
}
}
break;
case "string":
if (is_string($value) && $field_info['length'] && strlen($value) > $field_info['length']) {
$value = substr($value, 0, $field_info['length']);
}
break;
case "decimal":
case "float":
if ($value !== null) {
if (is_numeric($value)) {
$value = (float)$value;
} elseif (empty($value)) {
$value = ($field_info['nullable']) ? NULL : 0.0;
}
}
break;
case "integer":
case "smallint":
case "bigint":
if ($value !== null) {
$value = (int)$value;
}
break;
case "boolean":
if ($value !== null) {
$value = (bool)$value;
}
break;
}
$this->_set($entity, $field, $value);
}
}
}
/**
* ToArray (A Doctrine 1 Classic)
*
* @param object $entity
* @param bool $deep Iterate through collections associated with this item.
* @param bool $form_mode Return values in a format suitable for ZendForm setDefault function.
* @return array
*/
public function toArray($entity, $deep = false, $form_mode = false)
{
$return_arr = [];
$class_meta = $this->_em->getClassMetadata(get_class($entity));
$reflect = new \ReflectionClass($entity);
$props = $reflect->getProperties(\ReflectionProperty::IS_PUBLIC | \ReflectionProperty::IS_PROTECTED);
if ($props) {
foreach ($props as $property) {
$property->setAccessible(true);
$prop_name = $property->getName();
$prop_val = $this->_get($entity, $prop_name) ?? $property->getValue($entity);
if (isset($class_meta->fieldMappings[$prop_name])) {
$prop_info = $class_meta->fieldMappings[$prop_name];
} else {
$prop_info = [];
}
if (is_array($prop_val)) {
$return_arr[$prop_name] = $prop_val;
} else {
if (!is_object($prop_val)) {
if ($prop_info['type'] == "array") {
$return_arr[$prop_name] = (array)$prop_val;
} else {
$return_arr[$prop_name] = (string)$prop_val;
}
} else {
if ($prop_val instanceof \DateTime) {
$return_arr[$prop_name] = $prop_val->getTimestamp();
} else {
if ($deep) {
if ($prop_val instanceof \Doctrine\Common\Collections\Collection) {
$return_val = [];
if (count($prop_val) > 0) {
foreach ($prop_val as $val_obj) {
if ($form_mode) {
$obj_meta = $this->_em->getClassMetadata(get_class($val_obj));
$id_field = $obj_meta->identifier;
if ($id_field && count($id_field) == 1) {
$return_val[] = $this->_get($val_obj, $id_field[0]);
}
} else {
$return_val[] = $this->toArray($val_obj, false);
}
}
}
$return_arr[$prop_name] = $return_val;
} else {
$return_arr[$prop_name] = $this->toArray($prop_val, false);
}
}
}
}
}
}
}
return $return_arr;
}
/**
* Internal function for pulling metadata, used in toArray and fromArray
*
* @param object $source_entity
* @return array
*/
protected function _getMetadata($source_entity)
{
$class = get_class($source_entity);
$meta_result = [];
$meta_result['em'] = $this->_em;
$meta_result['factory'] = $this->_em->getMetadataFactory();
$meta_result['meta'] = $meta_result['factory']->getMetadataFor($class);
$meta_result['mappings'] = [];
if ($meta_result['meta']->associationMappings) {
foreach ($meta_result['meta']->associationMappings as $mapping_name => $mapping_info) {
$entity = $mapping_info['targetEntity'];
if (isset($mapping_info['joinTable'])) {
$meta_result['mappings'][$mapping_info['fieldName']] = [
'type' => 'many',
'entity' => $entity,
'is_owning_side' => ($mapping_info['isOwningSide'] == 1),
'mappedBy' => $mapping_info['mappedBy'],
];
} else {
if (isset($mapping_info['joinColumns'])) {
foreach ($mapping_info['joinColumns'] as $col) {
$join_meta = $meta_result['factory']->getMetadataFor($entity);
$join_ids = $join_meta->getIdentifierFieldNames();
$col_name = $col['name'];
$col_name = (isset($meta_result['meta']->fieldNames[$col_name])) ? $meta_result['meta']->fieldNames[$col_name] : $col_name;
$meta_result['mappings'][$col_name] = [
'name' => $mapping_name,
'type' => 'one_id',
'entity' => $entity,
'ids' => $join_ids,
];
$meta_result['mappings'][$mapping_name] = [
'name' => $col_name,
'type' => 'one_entity',
'entity' => $entity,
'ids' => $join_ids,
];
}
}
}
}
}
return $meta_result;
}
/**
* Shortcut to persist an object and flush the entity manager.
*
* @param object $entity
*/
public function save($entity)
{
$this->_em->persist($entity);
$this->_em->flush();
}
protected function _get($entity, $key)
{
$method_name = $this->_getMethodName($key, '');
// Default to "getStatus", "getConfig", etc...but also allow "isEnabled" instead of "getIsEnabled"
if (method_exists($entity, 'get'.$method_name)) {
return $entity->{'get'.$method_name}();
}
if (method_exists($entity, $method_name)) {
return $entity->{$method_name}();
}
return null;
}
protected function _set($entity, $key, $value)
{
$method_name = $this->_getMethodName($key, 'set');
return (method_exists($entity, $method_name))
? $entity->$method_name($value)
: null;
}
// Converts "getvar_name_blah" to "getVarNameBlah".
protected function _getMethodName($var, $prefix = '')
{
return $prefix . str_replace(" ", "", ucwords(strtr($var, "_-", " ")));
}
}

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