Commit c7c55ec0 authored by Tino Goratsch's avatar Tino Goratsch

Merge branch 'release/v4.25.0'

parents 8acac86e 08d1cf20
Pipeline #19034624 passed with stages
in 32 minutes and 41 seconds
engines:
duplication:
enabled: true
config:
languages:
- php
- javascript
eslint:
enabled: true
phpcodesniffer:
enabled: true
phpmd:
enabled: true
ratings:
paths:
- "**.js"
- "**.php"
exclude_paths:
- "ACP3/Core/Test/**/*"
- "ACP3/Modules/ACP3/**/Test/**/*"
- "ACP3/Modules/ACP3/Filemanager/libraries/kcfinder/**/*"
- "ACP3/Modules/ACP3/System/Resources/Assets/js/bootbox.js"
- "ACP3/Modules/ACP3/System/Resources/Assets/js/bootstrap.min.js"
- "ACP3/Modules/ACP3/System/Resources/Assets/js/bootstrap-datetimepicker.min.js"
- "ACP3/Modules/ACP3/System/Resources/Assets/js/dataTables.bootstrap.js"
- "ACP3/Modules/ACP3/System/Resources/Assets/js/html5shiv.min.js"
- "ACP3/Modules/ACP3/System/Resources/Assets/js/jquery.dataTables.js"
- "ACP3/Modules/ACP3/System/Resources/Assets/js/jquery.fancybox.min.js"
- "ACP3/Modules/ACP3/System/Resources/Assets/js/jquery.min.js"
- "ACP3/Modules/ACP3/System/Resources/Assets/js/js.cookie.js"
- "ACP3/Modules/ACP3/System/Resources/Assets/js/moment.min.js"
- "ACP3/Modules/ACP3/Wysiwygckeditor/Resources/Assets/js/ckeditor/plugins/**/*"
- "vendor/**/*"
coverage_clover: build/logs/clover.xml
json_path: build/logs/coveralls-upload.json
service_name: travis-ci
......@@ -4,13 +4,13 @@
/ACP3/Core/Test export-ignore
/ACP3/Modules/*/Test export-ignore
/build export-ignore
.codeclimate.yml export-ignore
.coveralls.yml export-ignore
.editorconfig export-ignore
.eslintignore export-ignore
.eslintrc export-ignore
.gitattributes export-ignore
.gitignore export-ignore
.gitlab-ci.yml export-ignore
.gitsplit.yml export-ignore
.php_cs.dist export-ignore
.sami.php export-ignore
.travis.yml export-ignore
......@@ -50,6 +50,8 @@ splits:
target: "git@gitlab.com:ACP3/module-search.git"
- prefix: "ACP3/Modules/ACP3/Seo"
target: "git@gitlab.com:ACP3/module-seo.git"
- prefix: "ACP3/Modules/ACP3/Share"
target: "git@gitlab.com:ACP3/module-social-sharing.git"
- prefix: "ACP3/Modules/ACP3/System"
target: "git@gitlab.com:ACP3/module-system.git"
- prefix: "ACP3/Modules/ACP3/Users"
......
......@@ -16,7 +16,7 @@ interface BootstrapInterface extends HttpKernelInterface
/**
* Contains the current ACP3 version string.
*/
const VERSION = '4.24.0';
const VERSION = '4.25.0';
/**
* Performs some startup checks.
......
......@@ -62,6 +62,7 @@ class ControllerActionDispatcher
*
* @throws ControllerActionNotFoundException
* @throws \ACP3\Core\Controller\Exception\ResultNotExistsException
* @throws \ReflectionException
*/
public function dispatch($serviceId = '', array $arguments = [])
{
......@@ -112,6 +113,7 @@ class ControllerActionDispatcher
* @return mixed
*
* @throws \ACP3\Core\Controller\Exception\ResultNotExistsException
* @throws \ReflectionException
*/
private function executeControllerAction(ActionInterface $controller, array $arguments)
{
......@@ -128,6 +130,8 @@ class ControllerActionDispatcher
* @param ActionInterface $controller
*
* @return array
*
* @throws \ReflectionException
*/
private function getCallable(ActionInterface $controller)
{
......
......@@ -8,6 +8,7 @@
namespace ACP3\Core\Assets;
use ACP3\Core\Assets\Event\AddLibraryEvent;
use ACP3\Core\Http\RequestInterface;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
class Libraries
......@@ -22,12 +23,20 @@ class Libraries
],
'jquery' => [
'enabled' => true,
'enabled_for_ajax' => false,
'js' => 'jquery.min.js',
],
'js-cookie' => [
'enabled' => true,
'enabled' => false,
'enabled_for_ajax' => false,
'js' => 'js.cookie.js',
],
'ajax-form' => [
'enabled' => true,
'enabled_for_ajax' => false,
'dependencies' => ['jquery'],
'js' => 'ajax-form.js',
],
'fancybox' => [
'enabled' => false,
'dependencies' => ['jquery'],
......@@ -57,20 +66,32 @@ class Libraries
'css' => 'bootstrap-datetimepicker.css',
'js' => 'bootstrap-datetimepicker.min.js',
],
'font-awesome' => [
'enabled' => false,
'css' => 'font-awesome.css',
],
];
/**
* @var EventDispatcherInterface
*/
protected $eventDispatcher;
/**
* @var \ACP3\Core\Http\RequestInterface
*/
private $request;
/**
* Libraries constructor.
*
* @param EventDispatcherInterface $eventDispatcher
* @param \ACP3\Core\Http\RequestInterface $request
* @param EventDispatcherInterface $eventDispatcher
*/
public function __construct(EventDispatcherInterface $eventDispatcher)
public function __construct(
RequestInterface $request,
EventDispatcherInterface $eventDispatcher)
{
$this->eventDispatcher = $eventDispatcher;
$this->request = $request;
}
public function dispatchAddLibraryEvent()
......@@ -98,6 +119,10 @@ class Libraries
$this->libraries[$identifier] = $options;
}
if (isset($options['enabled']) && $options['enabled'] === true) {
$this->enableLibraries($options['dependencies'] ?? []);
}
return $this;
}
......@@ -132,11 +157,28 @@ class Libraries
{
$enabledLibraries = [];
foreach ($this->libraries as $library => $values) {
if ($values['enabled'] === true) {
$enabledLibraries[] = $library;
if ($this->includeInXmlHttpRequest($values)) {
continue;
}
if ($values['enabled'] === false) {
continue;
}
$enabledLibraries[] = $library;
}
return $enabledLibraries;
}
/**
* @param array $values
*
* @return bool
*/
private function includeInXmlHttpRequest(array $values): bool
{
return $this->request->isXmlHttpRequest()
&& isset($values['enabled_for_ajax'])
&& $values['enabled_for_ajax'] === false;
}
}
<?php
/**
* Copyright (c) by the ACP3 Developers.
* See the LICENSE file at the top-level module directory for licensing details.
*/
namespace ACP3\Core\Helpers\DataGrid\ColumnRenderer;
class RoundNumberColumnRenderer extends AbstractColumnRenderer
{
/**
* @var int
*/
protected $precision = 0;
/**
* {@inheritdoc}
*/
public function fetchDataAndRenderColumn(array $column, array $dbResultRow)
{
$this->precision = $column['custom']['precision'];
return $this->render($column, $this->getValue($column, $dbResultRow));
}
/**
* {@inheritdoc}
*/
protected function getDbValueIfExists(array $dbResultRow, $field)
{
return isset($dbResultRow[$field]) ? \round($dbResultRow[$field], $this->precision) : null;
}
}
<?php
/**
* Copyright (c) by the ACP3 Developers.
* See the LICENSE file at the top-level module directory for licensing details.
*/
namespace ACP3\Core\Model\DataProcessor\ColumnType;
class SerializableColumnType implements ColumnTypeStrategyInterface
{
/**
* @param mixed $value
*
* @return string
*/
public function doEscape($value)
{
return \serialize($value);
}
}
......@@ -17,4 +17,5 @@ interface ColumnTypes
const COLUMN_TYPE_TEXT = 'text';
const COLUMN_TYPE_TEXT_WYSIWYG = 'text_wysiwyg';
const COLUMN_TYPE_RAW = 'raw';
const COLUMN_TYPE_SERIALIZABLE = 'serializable';
}
......@@ -65,7 +65,7 @@ class Modules
*
* @return bool
*/
public function controllerActionExists($path)
public function controllerActionExists(string $path)
{
return $this->controllerActionExists->controllerActionExists($path);
}
......@@ -73,13 +73,13 @@ class Modules
/**
* Returns, whether a module is active or not.
*
* @param string $module
* @param string $moduleName
*
* @return bool
*/
public function isActive($module)
public function isActive(string $moduleName)
{
$info = $this->getModuleInfo($module);
$info = $this->getModuleInfo($moduleName);
return !empty($info) && $info['active'] === true;
}
......@@ -87,28 +87,28 @@ class Modules
/**
* Returns the available information about the given module.
*
* @param string $module
* @param string $moduleName
*
* @return array
*/
public function getModuleInfo($module)
public function getModuleInfo(string $moduleName)
{
$module = \strtolower($module);
$moduleName = \strtolower($moduleName);
if (empty($this->modulesInfo)) {
$this->modulesInfo = $this->moduleInfoCache->getModulesInfoCache();
}
return !empty($this->modulesInfo[$module]) ? $this->modulesInfo[$module] : [];
return !empty($this->modulesInfo[$moduleName]) ? $this->modulesInfo[$moduleName] : [];
}
/**
* @param string $module
* @param string $moduleName
*
* @return int
*/
public function getModuleId($module)
public function getModuleId(string $moduleName)
{
$info = $this->getModuleInfo($module);
$info = $this->getModuleInfo($moduleName);
return !empty($info) ? $info['id'] : 0;
}
......@@ -120,7 +120,7 @@ class Modules
*
* @return bool
*/
public function isInstalled($moduleName)
public function isInstalled(string $moduleName)
{
$info = $this->getModuleInfo($moduleName);
......
......@@ -101,6 +101,8 @@ class Action
* @param string|null $moduleIndexUrl
*
* @return array|JsonResponse|RedirectResponse
*
* @throws \ACP3\Core\Controller\Exception\ResultNotExistsException
*/
public function handleDeleteAction(
$action,
......
......@@ -31,7 +31,7 @@ class ControllerActionExists
*
* @return bool
*/
public function controllerActionExists($path)
public function controllerActionExists(string $path)
{
$pathArray = \explode('/', \strtolower($path));
......
......@@ -74,7 +74,14 @@ class Settings implements SettingsInterface
$bool = $bool2 = false;
$moduleId = $this->systemModuleRepository->getModuleId($module);
if (!empty($moduleId)) {
$this->eventDispatcher->dispatch('core.settings.save_before', new SettingsSaveEvent($module, $data));
$this->eventDispatcher->dispatch(
'core.settings.save_before',
new SettingsSaveEvent($module, $data)
);
$this->eventDispatcher->dispatch(
$module . '.settings.save_before',
new SettingsSaveEvent($module, $data)
);
foreach ($data as $key => $value) {
$updateValues = [
......
......@@ -8,6 +8,7 @@
namespace ACP3\Core\Test\Assets;
use ACP3\Core\Assets\Libraries;
use ACP3\Core\Http\RequestInterface;
use Symfony\Component\EventDispatcher\EventDispatcher;
class LibrariesTest extends \PHPUnit_Framework_TestCase
......@@ -20,14 +21,17 @@ class LibrariesTest extends \PHPUnit_Framework_TestCase
* @var \PHPUnit_Framework_MockObject_MockObject
*/
private $eventDispatcherMock;
/**
* @var \PHPUnit_Framework_MockObject_MockObject
*/
private $requestMock;
protected function setUp()
{
$this->eventDispatcherMock = $this->getMockBuilder(EventDispatcher::class)
->disableOriginalConstructor()
->getMock();
$this->requestMock = $this->getMockForAbstractClass(RequestInterface::class);
$this->eventDispatcherMock = $this->createMock(EventDispatcher::class);
$this->libraries = new Libraries($this->eventDispatcherMock);
$this->libraries = new Libraries($this->requestMock, $this->eventDispatcherMock);
}
public function testAddLibrary()
......@@ -49,6 +53,6 @@ class LibrariesTest extends \PHPUnit_Framework_TestCase
$this->libraries->enableLibraries(['foobar']);
$this->assertEquals(['jquery', 'js-cookie', 'foobar'], $this->libraries->getEnabledLibraries());
$this->assertEquals(['jquery', 'ajax-form', 'foobar'], $this->libraries->getEnabledLibraries());
}
}
......@@ -10,6 +10,7 @@ namespace ACP3\Core\Test;
use ACP3\Core\Assets;
use ACP3\Core\Environment\ApplicationMode;
use ACP3\Core\Environment\ApplicationPath;
use ACP3\Core\Http\RequestInterface;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
class AssetsTest extends \PHPUnit_Framework_TestCase
......@@ -22,6 +23,10 @@ class AssetsTest extends \PHPUnit_Framework_TestCase
* @var \PHPUnit_Framework_MockObject_MockObject
*/
private $eventDispatcherMock;
/**
* @var \PHPUnit_Framework_MockObject_MockObject
*/
private $requestMock;
protected function setUp()
{
......@@ -31,13 +36,14 @@ class AssetsTest extends \PHPUnit_Framework_TestCase
$appPath
->setDesignRootPathInternal(ACP3_ROOT_DIR . 'tests/designs/')
->setDesignPathInternal('acp3/');
$libraries = new Assets\Libraries($this->eventDispatcherMock);
$libraries = new Assets\Libraries($this->requestMock, $this->eventDispatcherMock);
$this->assets = new Assets($appPath, $libraries);
}
private function setUpMockObjects()
{
$this->requestMock = $this->getMockForAbstractClass(RequestInterface::class);
$this->eventDispatcherMock = $this->getMockBuilder(EventDispatcherInterface::class)
->setMethods([
'dispatch',
......@@ -56,7 +62,7 @@ class AssetsTest extends \PHPUnit_Framework_TestCase
public function testDefaultLibrariesEnabled()
{
$libraries = $this->assets->getEnabledLibrariesAsString();
$this->assertEquals('jquery,js-cookie,bootstrap', $libraries);
$this->assertEquals('jquery,ajax-form,bootstrap', $libraries);
}
public function testEnableDatepicker()
......@@ -64,7 +70,7 @@ class AssetsTest extends \PHPUnit_Framework_TestCase
$this->assets->enableLibraries(['datetimepicker']);
$libraries = $this->assets->getEnabledLibrariesAsString();
$this->assertEquals('moment,jquery,js-cookie,bootstrap,datetimepicker', $libraries);
$this->assertEquals('moment,jquery,ajax-form,bootstrap,datetimepicker', $libraries);
}
public function testFetchAdditionalThemeCssFiles()
......
......@@ -14,7 +14,7 @@ class InArrayValidationRule extends AbstractValidationRule
*/
public function isValid($data, $field = '', array $extra = [])
{
if (\is_array($data) && \array_key_exists($field, $data)) {
if (\is_array($data) && \array_key_exists($field, $data) && !\is_array($data[$field])) {
return $this->isValid($data[$field], $field, $extra);
}
......@@ -22,17 +22,28 @@ class InArrayValidationRule extends AbstractValidationRule
return false;
}
return $this->checkInArray($data, $extra['haystack']);
return $this->checkInArray($data, $field, $extra['haystack']);
}
/**
* @param string $needle
* @param array $haystack
* @param string|array $data
* @param string $field
* @param array $haystack
*
* @return bool
*/
protected function checkInArray($needle, array $haystack)
protected function checkInArray($data, string $field, array $haystack)
{
return \in_array($needle, $haystack);
if (isset($data[$field]) && \is_array($data[$field])) {
foreach ($data[$field] as $row) {
if (\in_array($row, $haystack) === false) {
return false;
}
}
return true;
}
return \in_array($data, $haystack);
}
}
......@@ -28,15 +28,13 @@ interface ValidationRuleInterface
* @param mixed $data
* @param string $field
* @param array $extra
*
* @return
*/
public function validate(Validator $validator, $data, $field = '', array $extra = []);
/**
* @param mixed $data
* @param string $field
* @param array $extra
* @param bool|int|float|string|array $data
* @param string $field
* @param array $extra
*
* @return bool
*/
......
......@@ -30,12 +30,10 @@ class View
* View constructor.
*
* @param \ACP3\Core\View\Renderer\RendererInterface $renderer
* @param array $params
*/
public function __construct(RendererInterface $renderer, array $params = [])
public function __construct(RendererInterface $renderer)
{
$this->renderer = $renderer;
$this->renderer->configure($params);
}
/**
......
......@@ -9,11 +9,6 @@ namespace ACP3\Core\View\Renderer;
interface RendererInterface
{
/**
* @param array $params
*/
public function configure(array $params = []);
/**
* @param string|array $name
* @param null $value
......
......@@ -7,9 +7,6 @@
namespace ACP3\Core\View\Renderer;
use ACP3\Core\Environment\ApplicationMode;
use ACP3\Core\Environment\ApplicationPath;
/**
* Renderer for the Smarty template engine.
*/
......@@ -19,54 +16,15 @@ class Smarty implements RendererInterface
* @var \Smarty
*/
protected $smarty;
/**
* @var \ACP3\Core\Environment\ApplicationPath
*/
protected $appPath;
/**
* @var string
*/
protected $environment;
/**
* Smarty constructor.
*
* @param \Smarty $smarty
* @param \ACP3\Core\Environment\ApplicationPath $appPath
* @param string $environment
*/
public function __construct(
\Smarty $smarty,
ApplicationPath $appPath,
$environment
) {
$this->smarty = $smarty;
$this->appPath = $appPath;
$this->environment = $environment;
}
/**
* @param array $params
*
* @throws \SmartyException
* @param \Smarty $smarty
*/
public function configure(array $params = [])
public function __construct(\Smarty $smarty)
{
$this->smarty->setErrorReporting($this->isDevOrInstall() ? E_ALL : 0);
$this->smarty->setCompileId(!empty($params['compile_id']) ? $params['compile_id'] : $this->environment);
$this->smarty->setCompileCheck($this->isDevOrInstall());
$this->smarty->setCompileDir($this->appPath->getCacheDir() . 'tpl_compiled/');
$this->smarty->setCacheDir($this->appPath->getCacheDir() . 'tpl_cached/');
}
/**
* @return bool
*/
protected function isDevOrInstall()
{
$environments = [ApplicationMode::DEVELOPMENT, ApplicationMode::INSTALLER, ApplicationMode::UPDATER];
return \in_array($this->environment, $environments);
$this->smarty = $smarty;
}
/**
......
......@@ -8,6 +8,7 @@
namespace ACP3\Core\View\Renderer\Smarty\Functions;
use ACP3\Core\ACL;
use ACP3\Core\Application\BootstrapCache\Esi;
use ACP3\Core\Environment\ApplicationMode;
use ACP3\Core\Router\RouterInterface;
......@@ -36,7 +37,7 @@ class LoadModule extends AbstractFunction
public function __construct(
ACL $acl,
RouterInterface $router,
$applicationMode
string $applicationMode
) {
$this->acl = $acl;
$this->router = $router;
......@@ -72,7 +73,7 @@ class LoadModule extends AbstractFunction
*