Commit f52df03e authored by Tino Goratsch's avatar Tino Goratsch

Merge branch 'release/v4.22.0'

parents f7d846c4 89f2c1ea
Pipeline #17925916 failed with stages
in 12 minutes and 36 seconds
......@@ -119,12 +119,12 @@ post-deploy:notify-packagist: &post-deploy-base
- tags
- schedules
#post-deploy:update-version-check:
# <<: *post-deploy-base
# script:
# - bash ./build/gitlab/update_version_check.sh
# only:
# - tags
post-deploy:update-version-check:
<<: *post-deploy-base
script:
- bash ./build/gitlab/update_version_check.sh
only:
- tags
#post-deploy:apidocs:
# <<: *post-deploy-base
......
......@@ -10,6 +10,8 @@ splits:
target: "git@gitlab.com:ACP3/core.git"
- prefix: "ACP3/Modules/ACP3/Acp"
target: "git@gitlab.com:ACP3/module-acp.git"
- prefix: "ACP3/Modules/ACP3/Auditlog"
target: "git@gitlab.com:ACP3/module-audit-log.git"
- prefix: "ACP3/Modules/ACP3/Articles"
target: "git@gitlab.com:ACP3/module-articles.git"
- prefix: "ACP3/Modules/ACP3/Captcha"
......
......@@ -4,6 +4,7 @@ dist: trusty
group: edge
php:
- 7.1
- 7.2
- nightly
matrix:
......@@ -11,10 +12,6 @@ matrix:
allow_failures:
- php: nightly
addons:
code_climate:
repo_token: ${CC_REPO_TOKEN}
before_install:
- echo -e "machine github.com\n\tlogin ${ACP3_CI_USER_GH_TOKEN}" >> ~/.netrc
......@@ -24,16 +21,9 @@ before_script:
script:
- phpdbg -qrr ./vendor/bin/phpunit -c ./tests/phpunit.dist.xml --coverage-clover ./build/logs/clover.xml
after_success:
- travis_retry vendor/bin/coveralls -v
before_deploy:
- ./build/travis/generate_artifact.sh ${TRAVIS_PHP_VERSION}
deploy:
- provider: releases
api_key: ${ACP3_CI_USER_GH_TOKEN}
file: 'release.zip'
- provider: script
script: ./build/travis/update_version_check.sh ${TRAVIS_TAG}
skip_cleanup: true
on:
tags: true
......
......@@ -16,7 +16,7 @@ interface BootstrapInterface extends HttpKernelInterface
/**
* Contains the current ACP3 version string.
*/
const VERSION = '4.21.2';
const VERSION = '4.22.0';
/**
* Performs some startup checks.
......
......@@ -51,17 +51,19 @@ class Connection
* @param LoggerInterface $logger
* @param ApplicationPath $appPath
* @param CacheDriverFactory $cacheDriverFactory
* @param $appMode
* @param array $connectionParams
* @param $tablePrefix
* @param $appMode
* @param array $connectionParams
* @param $tablePrefix
*
* @throws \Doctrine\DBAL\DBALException
*/
public function __construct(
LoggerInterface $logger,
ApplicationPath $appPath,
CacheDriverFactory $cacheDriverFactory,
$appMode,
string $appMode,
array $connectionParams,
$tablePrefix
string $tablePrefix
) {
$this->logger = $logger;
$this->appPath = $appPath;
......@@ -116,6 +118,8 @@ class Connection
* @param null $cacheKey
*
* @return array
*
* @throws \Doctrine\DBAL\DBALException
*/
public function fetchAll(
$statement,
......@@ -138,6 +142,8 @@ class Connection
* @param array $types
*
* @return mixed
*
* @throws \Doctrine\DBAL\DBALException
*/
public function fetchArray($statement, array $params = [], array $types = [])
{
......@@ -150,6 +156,8 @@ class Connection
* @param array $types
*
* @return mixed
*
* @throws \Doctrine\DBAL\DBALException
*/
public function fetchAssoc($statement, array $params = [], array $types = [])
{
......@@ -163,6 +171,8 @@ class Connection
* @param array $types
*
* @return bool|string
*
* @throws \Doctrine\DBAL\DBALException
*/
public function fetchColumn($statement, array $params = [], $column = 0, array $types = [])
{
......@@ -179,7 +189,6 @@ class Connection
*
* @return \Doctrine\DBAL\Driver\ResultStatement|\Doctrine\DBAL\Driver\Statement
*
* @throws \Doctrine\DBAL\Cache\CacheException
* @throws \Doctrine\DBAL\DBALException
*/
public function executeQuery(
......@@ -213,7 +222,7 @@ class Connection
$result = $callback();
$this->connection->commit();
} catch (\Exception $e) {
} catch (DBAL\DBALException $e) {
$this->connection->rollBack();
$this->logger->error($e);
$result = false;
......
......@@ -96,19 +96,16 @@ class ServiceContainerBuilder extends ContainerBuilder
// Try to get all available services
/** @var Modules $modules */
$modules = $this->get('core.modules');
$vendors = $this->get('core.modules.vendors')->getVendors();
foreach ($modules->getAllModules() as $module) {
foreach ($vendors as $vendor) {
$modulePath = $this->applicationPath->getModulesDir() . $vendor . '/' . $module['dir'];
$path = $modulePath . '/Resources/config/services.yml';
foreach ($modules->getAllModulesTopSorted() as $module) {
$modulePath = $this->applicationPath->getModulesDir() . $module['vendor'] . '/' . $module['dir'];
$path = $modulePath . '/Resources/config/services.yml';
if (\is_file($path)) {
$loader->load($path);
}
$this->registerCompilerPass($vendor, $module['dir']);
if (\is_file($path)) {
$loader->load($path);
}
$this->registerCompilerPass($module['vendor'], $module['dir']);
}
$this->compile();
......
......@@ -47,9 +47,7 @@ abstract class AbstractRequest implements RequestInterface
}
/**
* Returns the used protocol of the current request.
*
* @return string
* {@inheritdoc}
*/
public function getScheme()
{
......@@ -57,9 +55,7 @@ abstract class AbstractRequest implements RequestInterface
}
/**
* Returns the hostname of the current request.
*
* @return string
* {@inheritdoc}
*/
public function getHost()
{
......@@ -67,9 +63,7 @@ abstract class AbstractRequest implements RequestInterface
}
/**
* Returns the protocol with the hostname.
*
* @return string
* {@inheritdoc}
*/
public function getHttpHost()
{
......@@ -77,7 +71,7 @@ abstract class AbstractRequest implements RequestInterface
}
/**
* @return bool
* {@inheritdoc}
*/
public function isXmlHttpRequest()
{
......@@ -85,9 +79,7 @@ abstract class AbstractRequest implements RequestInterface
}
/**
* Returns the parameter bag of the $_COOKIE superglobal.
*
* @return \Symfony\Component\HttpFoundation\ParameterBag
* {@inheritdoc}
*/
public function getCookies()
{
......@@ -95,9 +87,7 @@ abstract class AbstractRequest implements RequestInterface
}
/**
* Returns the parameter bag of the uploaded files ($_FILES superglobal).
*
* @return \Symfony\Component\HttpFoundation\FileBag
* {@inheritdoc}
*/
public function getFiles()
{
......@@ -105,9 +95,7 @@ abstract class AbstractRequest implements RequestInterface
}
/**
* Returns the parameter bag of the $_POST superglobal.
*
* @return \Symfony\Component\HttpFoundation\ParameterBag
* {@inheritdoc}
*/
public function getPost()
{
......@@ -115,9 +103,7 @@ abstract class AbstractRequest implements RequestInterface
}
/**
* Returns the parameter bag of the $_SERVER superglobal.
*
* @return \Symfony\Component\HttpFoundation\ServerBag
* {@inheritdoc}
*/
public function getServer()
{
......@@ -125,7 +111,7 @@ abstract class AbstractRequest implements RequestInterface
}
/**
* @return \ACP3\Core\Http\Request\UserAgent
* {@inheritdoc}
*/
public function getUserAgent()
{
......@@ -133,9 +119,7 @@ abstract class AbstractRequest implements RequestInterface
}
/**
* @param string $homepage
*
* @return $this
* {@inheritdoc}
*/
public function setHomepage($homepage)
{
......
......@@ -31,7 +31,9 @@ interface RequestInterface
public function getHost();
/**
* Returns the protocol with the hostname.
* Returns the HTTP host being requested.
*
* The port name will be appended to the host if it's non-standard.
*
* @return string
*/
......
......@@ -50,13 +50,18 @@ abstract class AbstractModel
* @param null|int $entryId
*
* @return bool|int
*
* @throws \Doctrine\DBAL\DBALException
*/
public function save(array $rawData, $entryId = null)
{
$filteredData = $this->prepareData($rawData);
$isNewEntry = $entryId === null;
$this->dispatchBeforeSaveEvent($this->repository, $entryId, $filteredData, $rawData, $isNewEntry);
$hasDataChanges = $this->hasDataChanges($filteredData, $entryId);
$event = $this->createModelSaveEvent($entryId, $isNewEntry, $hasDataChanges, $filteredData, $rawData);
$this->dispatchBeforeSaveEvent($this->repository, $event);
if ($entryId === null) {
$result = $this->repository->insert($filteredData);
......@@ -65,50 +70,85 @@ abstract class AbstractModel
$entryId = $result;
}
} else {
$result = $this->repository->update($filteredData, $entryId);
$result = $hasDataChanges ? $this->repository->update($filteredData, $entryId) : 1;
}
$this->dispatchAfterSaveEvent($this->repository, $entryId, $filteredData, $rawData, $isNewEntry);
$event = $this->createModelSaveEvent($entryId, $isNewEntry, $hasDataChanges, $filteredData, $rawData);
$this->dispatchAfterSaveEvent(
$this->repository,
$event
);
return $result;
}
protected function hasDataChanges(array $filteredData, ?int $entryId): bool
{
if ($entryId === null) {
return true;
}
$result = $this->getOneById($entryId);
if ($this instanceof UpdatedAtAwareModelInterface) {
unset($result['updated_at']);
}
foreach ($result as $column => $value) {
if (isset($filteredData[$column]) && $filteredData[$column] != $value) {
return true;
}
}
return false;
}
/**
* @param AbstractRepository $repository
* @param int|null|array $entryId
* @param array $filteredData
* @param array $rawData
* @param bool $isNewEntry
* @param \ACP3\Core\Model\Repository\AbstractRepository $repository
* @param \ACP3\Core\Model\Event\ModelSaveEvent $event
*/
protected function dispatchBeforeSaveEvent(
AbstractRepository $repository,
$entryId,
array $filteredData,
array $rawData,
$isNewEntry
ModelSaveEvent $event
) {
$this->dispatchEvent('core.model.before_save', $entryId, $isNewEntry, $filteredData, $rawData);
$this->dispatchEvent(
'core.model.before_save',
$event
);
$this->dispatchEvent(
static::EVENT_PREFIX . '.model.' . $repository::TABLE_NAME . '.before_save',
$entryId,
$isNewEntry,
$filteredData,
$rawData
$event
);
}
/**
* @param string $eventName
* @param int|null|array $entryId
* @param bool $isNewEntry
* @param array $filteredData
* @param array $rawData
* @param string $eventName
* @param \ACP3\Core\Model\Event\ModelSaveEvent $event
*/
protected function dispatchEvent($eventName, $entryId, $isNewEntry, array $filteredData = [], array $rawData = [])
protected function dispatchEvent(
string $eventName,
ModelSaveEvent $event)
{
$this->eventDispatcher->dispatch(
$eventName,
new ModelSaveEvent(static::EVENT_PREFIX, $filteredData, $rawData, $entryId, $isNewEntry)
$event
);
}
protected function createModelSaveEvent(
$entryId,
bool $isNewEntry,
bool $hasDataChanges,
array $filteredData = [],
array $rawData = []
): ModelSaveEvent {
return new ModelSaveEvent(
static::EVENT_PREFIX,
$filteredData,
$rawData,
$entryId,
$isNewEntry,
$hasDataChanges
);
}
......@@ -128,26 +168,20 @@ abstract class AbstractModel
abstract protected function getAllowedColumns();
/**
* @param AbstractRepository $repository
* @param int|null|array $entryId
* @param array $filteredData
* @param array $rawData
* @param bool $isNewEntry
* @param \ACP3\Core\Model\Repository\AbstractRepository $repository
* @param \ACP3\Core\Model\Event\ModelSaveEvent $event
*/
protected function dispatchAfterSaveEvent(
AbstractRepository $repository,
$entryId,
array $filteredData,
array $rawData,
$isNewEntry
ModelSaveEvent $event
) {
$this->dispatchEvent('core.model.after_save', $entryId, $isNewEntry, $filteredData, $rawData);
$this->dispatchEvent(
'core.model.after_save',
$event
);
$this->dispatchEvent(
static::EVENT_PREFIX . '.model.' . $repository::TABLE_NAME . '.after_save',
$entryId,
$isNewEntry,
$filteredData,
$rawData
$event
);
}
......@@ -155,6 +189,8 @@ abstract class AbstractModel
* @param int|array $entryId
*
* @return int
*
* @throws \Doctrine\DBAL\DBALException
*/
public function delete($entryId)
{
......@@ -164,11 +200,12 @@ abstract class AbstractModel
$entryId = [$entryId];
}
$this->dispatchEvent('core.model.before_delete', $entryId, false);
$event = $this->createModelSaveEvent($entryId, false, true);
$this->dispatchEvent('core.model.before_delete', $event);
$this->dispatchEvent(
static::EVENT_PREFIX . '.model.' . $repository::TABLE_NAME . '.before_delete',
$entryId,
false
$event
);
$affectedRows = 0;
......@@ -176,11 +213,10 @@ abstract class AbstractModel
$affectedRows += (int) $this->repository->delete($item);
}
$this->dispatchEvent('core.model.before_delete', $entryId, false);
$this->dispatchEvent('core.model.after_delete', $event);
$this->dispatchEvent(
static::EVENT_PREFIX . '.model.' . $repository::TABLE_NAME . '.after_delete',
$entryId,
false
$event
);
return $affectedRows;
......@@ -191,7 +227,7 @@ abstract class AbstractModel
*
* @return array
*/
public function getOneById($entryId)
public function getOneById(int $entryId)
{
return $this->repository->getOneById($entryId);
}
......
......@@ -58,14 +58,18 @@ abstract class AbstractNestedSetModel extends AbstractModel
* @param int|null $entryId
*
* @return bool|int
*
* @throws \Doctrine\DBAL\ConnectionException
*/
public function save(array $rawData, $entryId = null)
{
$filteredData = $this->prepareData($rawData);
$isNewEntry = $entryId === null;
$hasDataChanges = $this->hasDataChanges($filteredData, $entryId);
$event = $this->createModelSaveEvent($entryId, $isNewEntry, $hasDataChanges, $rawData, $isNewEntry);
$this->dispatchBeforeSaveEvent($this->repository, $entryId, $filteredData, $rawData, $isNewEntry);
$this->dispatchBeforeSaveEvent($this->repository, $event);
if ($entryId === null) {
$result = $this->insertOperation->execute($filteredData, $rawData['parent_id']);
......@@ -82,7 +86,8 @@ abstract class AbstractNestedSetModel extends AbstractModel
);
}
$this->dispatchAfterSaveEvent($this->repository, $entryId, $filteredData, $rawData, $isNewEntry);
$event = $this->createModelSaveEvent($entryId, $isNewEntry, $hasDataChanges, $rawData, $isNewEntry);
$this->dispatchAfterSaveEvent($this->repository, $event);
return $result;
}
......@@ -91,6 +96,8 @@ abstract class AbstractNestedSetModel extends AbstractModel
* @param int|array $entryId
*
* @return int
*
* @throws \Doctrine\DBAL\ConnectionException
*/
public function delete($entryId)
{
......@@ -100,11 +107,11 @@ abstract class AbstractNestedSetModel extends AbstractModel
$entryId = [$entryId];
}
$this->dispatchEvent('core.model.before_delete', $entryId, false);
$event = $this->createModelSaveEvent($entryId, false, true);
$this->dispatchEvent('core.model.before_delete', $event);
$this->dispatchEvent(
static::EVENT_PREFIX . '.model.' . $repository::TABLE_NAME . '.before_delete',
$entryId,
false
static::EVENT_PREFIX . '.model.' . $repository::TABLE_NAME . '.before_delete', $event
);
$affectedRows = 0;
......@@ -112,11 +119,9 @@ abstract class AbstractNestedSetModel extends AbstractModel
$affectedRows += (int) $this->deleteOperation->execute($item);
}
$this->dispatchEvent('core.model.before_delete', $entryId, false);
$this->dispatchEvent('core.model.before_delete', $event);
$this->dispatchEvent(
static::EVENT_PREFIX . '.model.' . $repository::TABLE_NAME . '.after_delete',
$entryId,
false
static::EVENT_PREFIX . '.model.' . $repository::TABLE_NAME . '.after_delete', $event
);
return $affectedRows;
......
......@@ -31,6 +31,10 @@ class ModelSaveEvent extends Event
* @var bool
*/
private $isNewEntry;
/**
* @var bool
*/
private $hasDataChanges;