september update 1

This commit is contained in:
Dennis Eichhorn 2021-09-19 19:50:15 +02:00
parent 420c34aec2
commit 99df0e971e
16 changed files with 331 additions and 218 deletions

View File

@ -206,6 +206,26 @@ class Account implements \JsonSerializable, ArrayableInterface
$this->groups[] = $group; $this->groups[] = $group;
} }
/**
* User has group.
*
* @param int $group Group id
*
* @return void
*
* @since 1.0.0
*/
public function hasGroup(int $id) : bool
{
foreach ($this->groups as $group) {
if ($group->getId() === $id) {
return true;
}
}
return false;
}
/** /**
* Get email. * Get email.
* *

View File

@ -222,4 +222,16 @@ final class ApplicationInfo
{ {
return $this->info['version'] ?? ''; return $this->info['version'] ?? '';
} }
/**
* Get info data.
*
* @return array<string, string>
*
* @since 1.0.0
*/
public function getProviding() : array
{
return $this->info['providing'] ?? [];
}
} }

View File

@ -16,7 +16,6 @@ declare(strict_types=1);
namespace phpOMS\Application; namespace phpOMS\Application;
use phpOMS\Module\ModuleManager;
use phpOMS\System\File\Local\Directory; use phpOMS\System\File\Local\Directory;
use phpOMS\System\File\PathException; use phpOMS\System\File\PathException;
@ -32,14 +31,6 @@ use phpOMS\System\File\PathException;
*/ */
final class ApplicationManager final class ApplicationManager
{ {
/**
* Module manager
*
* @var ModuleManager
* @since 1.0.0
*/
private ModuleManager $moduleManager;
/** /**
* Applications * Applications
* *
@ -51,13 +42,10 @@ final class ApplicationManager
/** /**
* Constructor. * Constructor.
* *
* @param ModuleManager $moduleManager Module manager
*
* @since. 1.0.0 * @since. 1.0.0
*/ */
public function __construct(ModuleManager $moduleManager) public function __construct()
{ {
$this->moduleManager = $moduleManager;
} }
/** /**
@ -106,7 +94,6 @@ final class ApplicationManager
$this->installFiles($source, $destination); $this->installFiles($source, $destination);
$this->installTheme($destination, $theme); $this->installTheme($destination, $theme);
$this->installFromModules($app);
$files = Directory::list($destination, '*', true); $files = Directory::list($destination, '*', true);
foreach ($files as $file) { foreach ($files as $file) {
@ -173,21 +160,4 @@ final class ApplicationManager
); );
} }
} }
/**
* Install routes and hooks from modules for application
*
* @param ApplicationInfo $info Application info
*
* @return void
*
* @since 1.0.0
*/
public function installFromModules(ApplicationInfo $info) : void
{
$installed = $this->moduleManager->getInstalledModules();
foreach ($installed as $module => $moduleInfo) {
$this->moduleManager->reInit($module, $info);
}
}
} }

View File

@ -33,6 +33,7 @@ interface SettingsInterface extends OptionsInterface
* *
* @param null|int|int[]|string|string[] $ids Ids * @param null|int|int[]|string|string[] $ids Ids
* @param null|string|string[] $names Setting name * @param null|string|string[] $names Setting name
* @param null|int $app Application
* @param null|string $module Module name * @param null|string $module Module name
* @param null|int $group Group id * @param null|int $group Group id
* @param null|int $account Account id * @param null|int $account Account id
@ -44,6 +45,7 @@ interface SettingsInterface extends OptionsInterface
public function get( public function get(
mixed $ids = null, mixed $ids = null,
string | array $names = null, string | array $names = null,
int $app = null,
string $module = null, string $module = null,
int $group = null, int $group = null,
int $account = null int $account = null

View File

@ -121,14 +121,6 @@ class DataMapperAbstract implements DataMapperInterface
*/ */
protected static string $createdAt = ''; protected static string $createdAt = '';
/**
* Language
*
* @var string
* @since 1.0.0
*/
protected static string $languageField = '';
/** /**
* Columns. * Columns.
* *
@ -246,6 +238,14 @@ class DataMapperAbstract implements DataMapperInterface
*/ */
protected static array $lastQueryData = []; protected static array $lastQueryData = [];
/**
* Fields to sort by.
*
* @var array[]
* @since 1.0.0
*/
protected static array $sortFields = [];
/** /**
* Constructor. * Constructor.
* *
@ -342,6 +342,34 @@ class DataMapperAbstract implements DataMapperInterface
'ignore' => $models === null, // don't load this model 'ignore' => $models === null, // don't load this model
]; ];
// @todo: ignore seems to be a bug, models === null is true VERY often because i usually omit the models definition. Why is it still working, or is it?
/** @var string */
return static::class;
}
/**
* Create a conditional value
*
* @param string $by Name of the variable to sort by
* @param string $order ASC or DESC
* @param string[] $models Models to apply the sort on
*
* @return string
*
* @since 1.0.0
*/
public static function sortBy(
string $by,
string $order = 'DESC',
?array $models = [],
) : string
{
self::$sortFields[$by] = [
'order' => $order,
'models' => $models === [] ? null : $models,
];
/** @var string */ /** @var string */
return static::class; return static::class;
} }
@ -364,6 +392,7 @@ class DataMapperAbstract implements DataMapperInterface
self::$parentMapper = null; self::$parentMapper = null;
self::$withFields = []; self::$withFields = [];
self::$sortFields = [];
self::$relations = RelationType::ALL; self::$relations = RelationType::ALL;
} }
@ -750,6 +779,31 @@ class DataMapperAbstract implements DataMapperInterface
return true; return true;
} }
/**
* Delete relation
*
* This is only possible for hasMany objects which are stored in a relation table
*
* @param string $member Member name of the relation
* @param mixed $id1 Id of the primary object
* @param mixed $id2 Id of the secondary object
*
* @return bool
*
* @since 1.0.0
*/
public static function delteRelation(string $member, mixed $id1, mixed $id2) : bool
{
if (!isset(static::$hasMany[$member]) || !isset(static::$hasMany[$member]['external'])) {
return false;
}
self::removeInitialized(static::class, $id1);
self::deleteRelationTable($member, \is_array($id2) ? $id2 : [$id2], $id1);
return true;
}
/** /**
* Create has many * Create has many
* *
@ -1329,7 +1383,7 @@ class DataMapperAbstract implements DataMapperInterface
* Delete relation table entry * Delete relation table entry
* *
* @param string $propertyName Property name to initialize * @param string $propertyName Property name to initialize
* @param array $objsIds Object ids to insert * @param array $objsIds Object ids to delete
* @param mixed $objId Model to reference * @param mixed $objId Model to reference
* *
* @return void * @return void
@ -2585,7 +2639,6 @@ class DataMapperAbstract implements DataMapperInterface
* @param mixed $pivot Pivot * @param mixed $pivot Pivot
* @param string $column Sort column/pivot column * @param string $column Sort column/pivot column
* @param int $limit Result limit * @param int $limit Result limit
* @param string $order Order of the elements
* @param int $relations Load relations * @param int $relations Load relations
* @param int $depth Relation depth * @param int $depth Relation depth
* @param Builder $query Query * @param Builder $query Query
@ -2598,16 +2651,13 @@ class DataMapperAbstract implements DataMapperInterface
mixed $pivot, mixed $pivot,
string $column = null, string $column = null,
int $limit = 50, int $limit = 50,
string $order = 'ASC',
int $relations = RelationType::ALL, int $relations = RelationType::ALL,
int $depth = 3, int $depth = 3,
Builder $query = null Builder $query = null
) : array ) : array
{ {
$query ??= self::getQuery(depth: $depth); $query ??= self::getQuery(depth: $depth);
$query->where(static::$table . '_d' . $depth . '.' . ($column !== null ? self::getColumnByMember($column) : static::$primaryField), '>', $pivot) $query->where(static::$table . '_d' . $depth . '.' . ($column !== null ? self::getColumnByMember($column) : static::$primaryField), '>', $pivot);
->orderBy(static::$table . '_d' . $depth . '.' . ($column !== null ? self::getColumnByMember($column) : static::$primaryField), $order)
->limit($limit);
return self::getAllByQuery($query, $relations, $depth); return self::getAllByQuery($query, $relations, $depth);
} }
@ -2618,7 +2668,6 @@ class DataMapperAbstract implements DataMapperInterface
* @param mixed $pivot Pivot * @param mixed $pivot Pivot
* @param string $column Sort column/pivot column * @param string $column Sort column/pivot column
* @param int $limit Result limit * @param int $limit Result limit
* @param string $order Order of the elements
* @param int $relations Load relations * @param int $relations Load relations
* @param int $depth Relation depth * @param int $depth Relation depth
* @param Builder $query Query * @param Builder $query Query
@ -2635,7 +2684,6 @@ class DataMapperAbstract implements DataMapperInterface
mixed $pivot, mixed $pivot,
string $column = null, string $column = null,
int $limit = 50, int $limit = 50,
string $order = 'ASC',
int $relations = RelationType::ALL, int $relations = RelationType::ALL,
int $depth = 3, int $depth = 3,
Builder $query = null Builder $query = null
@ -2643,7 +2691,6 @@ class DataMapperAbstract implements DataMapperInterface
{ {
$query ??= self::getQuery(depth: $depth); $query ??= self::getQuery(depth: $depth);
$query->where(static::$table . '_d' . $depth . '.' . ($column !== null ? self::getColumnByMember($column) : static::$primaryField), '<', $pivot) $query->where(static::$table . '_d' . $depth . '.' . ($column !== null ? self::getColumnByMember($column) : static::$primaryField), '<', $pivot)
->orderBy(static::$table . '_d' . $depth . '.' . ($column !== null ? self::getColumnByMember($column) : static::$primaryField), $order)
->limit($limit); ->limit($limit);
return self::getAllByQuery($query, $relations, $depth); return self::getAllByQuery($query, $relations, $depth);
@ -3205,69 +3252,73 @@ class DataMapperAbstract implements DataMapperInterface
foreach (static::$hasMany as $member => $value) { foreach (static::$hasMany as $member => $value) {
if ($value['writeonly'] ?? false === true if ($value['writeonly'] ?? false === true
|| self::$relations !== RelationType::ALL
|| (isset(self::$withFields[$member]['ignore']) && self::$withFields[$member]['ignore']) // should not be loaded || (isset(self::$withFields[$member]['ignore']) && self::$withFields[$member]['ignore']) // should not be loaded
) { ) {
continue; continue;
} }
if (!isset($cachedTables[$value['table']])) { if (isset($cachedTables[$value['table']])) {
$query = new Builder(self::$db); $result[$member] = $cachedTables[$value['table']];
if (self::$relations === RelationType::ALL) { continue;
$src = $value['external'] ?? $value['mapper']::$primaryField; }
// @todo: what if a specific column name is defined instead of primaryField for the join? Fix, it should be stored in 'column' $query = new Builder(self::$db);
$query->select($value['table'] . '.' . $src) $src = $value['external'] ?? $value['mapper']::$primaryField;
->from($value['table'])
->where($value['table'] . '.' . $value['self'], '=', $primaryKey);
if ($value['mapper']::getTable() !== $value['table']) { // @todo: what if a specific column name is defined instead of primaryField for the join? Fix, it should be stored in 'column'
$query->leftJoin($value['mapper']::getTable()) $query->select($value['table'] . '.' . $src)
->on($value['table'] . '.' . $src, '=', $value['mapper']::getTable() . '.' . $value['mapper']::getPrimaryField()); ->from($value['table'])
} ->where($value['table'] . '.' . $value['self'], '=', $primaryKey);
// @todo: here the relation table should probably join the the model table for better ::with() handling if ($value['mapper']::getTable() !== $value['table']) {
$query->leftJoin($value['mapper']::getTable())
->on($value['table'] . '.' . $src, '=', $value['mapper']::getTable() . '.' . $value['mapper']::getPrimaryField());
}
if (isset(self::$withFields[$member]) && self::$withFields[$member]['orderBy'] !== null) { $modelName = $value['mapper']::getModelName();
$query->orderBy($value['mapper']::getTable() . '.' . $value['mapper']::getColumnByMember(self::$withFields[$member]['orderBy']), self::$withFields[$member]['sortOrder']);
}
if (isset(self::$withFields[$member]) && self::$withFields[$member]['limit'] !== null) { // @todo: here the relation table should probably join the the model table for better ::with() handling
$query->limit(self::$withFields[$member]['limit']);
}
$modelName = $value['mapper']::getModelName(); if (isset(self::$sortFields[$member])
foreach (self::$withFields as $condKey => $condValue) { && ($column = $value['mapper']::getColumnByMember($member)) !== null
if (($column = $value['mapper']::getColumnByMember($condKey)) === null && (self::$sortFields[$member]['models'] === null || \in_array($modelName, self::$sortFields[$member]['models']))
|| ($condValue['models'] !== null && !\in_array($modelName, $condValue['models'])) ) {
|| ($value['conditional'] ?? false) === false $query->orderBy($value['mapper']::getTable() . '.' . $column, self::$sortFields[$member]['order']);
|| $condValue['ignore'] } elseif (isset($value['sort'])) {
) { $query->orderBy($value['mapper']::getTable() . '.' . $value['mapper']::getColumnByMember($value['sort']['orderBy']), $value['sort']['sortOrder']);
continue; }
}
if ($condValue['value'] !== null) { if (isset(self::$withFields[$member]) && self::$withFields[$member]['limit'] !== null) {
$query->andWhere($value['mapper']::getTable() . '.' . $column, $condValue['comparison'], $condValue['value']); $query->limit(self::$withFields[$member]['limit']);
} }
if ($condValue['orderBy'] !== null) { // @todo: like the foreach loop below, I probably also need to loop all sortFields to check if ther is a sortField defined which is part of the hasMany definition?!
$query->orderBy($value['mapper']::getTable() . '.' . $column . '.' . $value['mapper']::getColumnByMember($condValue['orderBy']), $condValue['sortOrder']);
}
if ($condValue['limit'] !== null) { foreach (self::$withFields as $condKey => $condValue) {
$query->limit($condValue['limit']); if (($column = $value['mapper']::getColumnByMember($condKey)) === null
} || ($condValue['models'] !== null && !\in_array($modelName, $condValue['models']))
} || ($value['conditional'] ?? false) === false
|| $condValue['ignore']
) {
continue;
} }
$sth = self::$db->con->prepare($query->toSql()); if ($condValue['value'] !== null) {
if ($sth !== false) { $query->andWhere($value['mapper']::getTable() . '.' . $column, $condValue['comparison'], $condValue['value']);
$sth->execute(); }
$cachedTables[$value['table']] = $sth->fetchAll(\PDO::FETCH_COLUMN);
if ($condValue['limit'] !== null) {
$query->limit($condValue['limit']);
} }
} }
$result[$member] = $cachedTables[$value['table']]; $sth = self::$db->con->prepare($query->toSql());
if ($sth !== false) {
$sth->execute();
$result[$member] = $cachedTables[$value['table']] = $sth->fetchAll(\PDO::FETCH_COLUMN);
}
} }
// @todo: this returns IDs it should return the database data here in order to reduce the requests. // @todo: this returns IDs it should return the database data here in order to reduce the requests.
@ -3306,8 +3357,19 @@ class DataMapperAbstract implements DataMapperInterface
$query->fromAs(static::$table, static::$table . '_d' . $depth); $query->fromAs(static::$table, static::$table . '_d' . $depth);
} }
// handle conditional // handle sort, the column name order is very important. Therefore it cannot be done in the foreach loop above!
$modelName = self::getModelName(); $modelName = self::getModelName();
foreach (self::$sortFields as $member => $sort) {
if (($column = self::getColumnByMember($member)) === null
|| ($sort['models'] !== null && !\in_array($modelName, $sort['models']))
) {
continue;
}
$query->orderBy(static::$table . '_d' . $depth . '.' . $column, $sort['order']);
}
// handle conditional
foreach (self::$withFields as $condKey => $condValue) { foreach (self::$withFields as $condKey => $condValue) {
if (($column = self::getColumnByMember($condKey)) === null if (($column = self::getColumnByMember($condKey)) === null
|| ($condValue['models'] !== null && !\in_array($modelName, $condValue['models'])) || ($condValue['models'] !== null && !\in_array($modelName, $condValue['models']))
@ -3382,7 +3444,7 @@ class DataMapperAbstract implements DataMapperInterface
// get HasManyQuery (but only for elements which have a 'column' defined) // get HasManyQuery (but only for elements which have a 'column' defined)
if ($depth > 1 && self::$relations === RelationType::ALL) { if ($depth > 1 && self::$relations === RelationType::ALL) {
foreach (static::$hasMany as $key => $rel) { foreach (static::$hasMany as $key => $rel) {
// @todo: impl. conditiona/with handling, sort, limit, filter or is this not required here? // @todo: impl. conditional/with handling, sort, limit, filter or is this not required here?
if (isset($rel['external']) || !isset($rel['column']) // @todo: conflict with getHasMany()???!?!?!?! if (isset($rel['external']) || !isset($rel['column']) // @todo: conflict with getHasMany()???!?!?!?!
|| (isset(self::$withFields[$key]) && self::$withFields[$key]['ignore']) || (isset(self::$withFields[$key]) && self::$withFields[$key]['ignore'])
) { ) {

View File

@ -724,7 +724,7 @@ class Builder extends BuilderAbstract
} }
/** /**
* Order by oldest. * Order by.
* *
* @param string|array $columns Columns * @param string|array $columns Columns
* @param string|string[] $order Orders * @param string|string[] $order Orders
@ -736,19 +736,13 @@ class Builder extends BuilderAbstract
public function orderBy(string | array $columns, string | array $order = 'DESC') : self public function orderBy(string | array $columns, string | array $order = 'DESC') : self
{ {
if (\is_string($columns)) { if (\is_string($columns)) {
if (!\is_string($order)) { $columns = [$columns];
throw new \InvalidArgumentException(); }
}
if (!isset($this->orders[$order])) { foreach ($columns as $key => $column) {
$this->orders[$order] = []; $tOrder = \is_string($order) ? $order : $order[$key];
}
$this->orders[$order][] = $columns; $this->orders[$column] = $tOrder;
} else {
foreach ($columns as $key => $column) {
$this->orders[\is_string($order) ? $order : $order[$key]][] = $column;
}
} }
return $this; return $this;

View File

@ -535,23 +535,19 @@ class Grammar extends GrammarAbstract
*/ */
protected function compileOrders(Builder $query, array $orders) : string protected function compileOrders(Builder $query, array $orders) : string
{ {
$expression = ''; $expression = '';
$lastOrderType = '';
foreach ($orders as $key => $order) { foreach ($orders as $column => $order) {
foreach ($order as $column) { $expression .= $this->compileSystem($column) . ' ' . $order . ', ';
$expression .= $this->compileSystem($column) . ', ';
}
$expression = \rtrim($expression, ', ');
$expression .= ' ' . $key . ', ';
} }
$expression = \rtrim($expression, ', ');
if ($expression === '') { if ($expression === '') {
return ''; return '';
} }
$expression = \rtrim($expression, ', ');
return 'ORDER BY ' . $expression; return 'ORDER BY ' . $expression;
} }

View File

@ -120,6 +120,18 @@ final class EventManager implements \Countable
return true; return true;
} }
/**
* Clear all events
*
* @return void
* @since 1.0.0
*/
public function clear() : bool
{
$this->groups = [];
$this->callbacks = [];
}
/** /**
* Attach new event * Attach new event
* *

View File

@ -263,15 +263,17 @@ abstract class ModuleAbstract
$this->app->eventManager->triggerSimilar('PRE:Module:' . $trigger, '', $obj); $this->app->eventManager->triggerSimilar('PRE:Module:' . $trigger, '', $obj);
$id = $mapper::create($obj); $id = $mapper::create($obj);
$this->app->eventManager->triggerSimilar('POST:Module:' . $trigger, '', [ $this->app->eventManager->triggerSimilar('POST:Module:' . $trigger, '',
$account, [
null, $obj, $account,
StringUtils::intHash($mapper), $trigger, null, $obj,
static::MODULE_NAME, StringUtils::intHash($mapper), $trigger,
(string) $id, static::MODULE_NAME,
'', (string) $id,
$ip, '',
]); $ip,
]
);
} }
/** /**
@ -294,15 +296,17 @@ abstract class ModuleAbstract
foreach ($objs as $obj) { foreach ($objs as $obj) {
$this->app->eventManager->triggerSimilar('PRE:Module:' . $trigger, '', $obj); $this->app->eventManager->triggerSimilar('PRE:Module:' . $trigger, '', $obj);
$id = $mapper::create($obj); $id = $mapper::create($obj);
$this->app->eventManager->triggerSimilar('POST:Module:' . $trigger, '', [ $this->app->eventManager->triggerSimilar('POST:Module:' . $trigger, '',
$account, [
null, $obj, $account,
StringUtils::intHash($mapper), $trigger, null, $obj,
static::MODULE_NAME, StringUtils::intHash($mapper), $trigger,
(string) $id, static::MODULE_NAME,
'', (string) $id,
$ip, '',
]); $ip,
]
);
} }
} }
@ -331,15 +335,17 @@ abstract class ModuleAbstract
$mapper(); $mapper();
} }
$this->app->eventManager->triggerSimilar('POST:Module:' . static::MODULE_NAME . '-' . $trigger . '-update', '', [ $this->app->eventManager->triggerSimilar('POST:Module:' . static::MODULE_NAME . '-' . $trigger . '-update', '',
$account, [
$old, $new, $account,
StringUtils::intHash(\is_string($mapper) ? $mapper : \get_class($mapper)), $trigger, $old, $new,
static::MODULE_NAME, StringUtils::intHash(\is_string($mapper) ? $mapper : \get_class($mapper)), $trigger,
(string) $id, static::MODULE_NAME,
'', (string) $id,
$ip, '',
]); $ip,
]
);
} }
/** /**
@ -361,15 +367,17 @@ abstract class ModuleAbstract
$this->app->eventManager->triggerSimilar('PRE:Module:' . $trigger, '', $obj); $this->app->eventManager->triggerSimilar('PRE:Module:' . $trigger, '', $obj);
$id = $mapper::delete($obj); $id = $mapper::delete($obj);
$this->app->eventManager->triggerSimilar('POST:Module:' . $trigger, '', [ $this->app->eventManager->triggerSimilar('POST:Module:' . $trigger, '',
$account, [
$obj, null, $account,
StringUtils::intHash($mapper), $trigger, $obj, null,
static::MODULE_NAME, StringUtils::intHash($mapper), $trigger,
(string) $id, static::MODULE_NAME,
'', (string) $id,
$ip, '',
]); $ip,
]
);
} }
/** /**
@ -389,18 +397,54 @@ abstract class ModuleAbstract
*/ */
protected function createModelRelation(int $account, mixed $rel1, mixed $rel2, string $mapper, string $field, string $trigger, string $ip) : void protected function createModelRelation(int $account, mixed $rel1, mixed $rel2, string $mapper, string $field, string $trigger, string $ip) : void
{ {
$trigger = static::MODULE_NAME . '-' . $trigger . '-relation'; $trigger = static::MODULE_NAME . '-' . $trigger . '-relation-create';
$this->app->eventManager->triggerSimilar('PRE:Module:' . $trigger, '', $rel1); $this->app->eventManager->triggerSimilar('PRE:Module:' . $trigger, '', $rel1);
$mapper::createRelation($field, $rel1, $rel2); $mapper::createRelation($field, $rel1, $rel2);
$this->app->eventManager->triggerSimilar('POST:Module:' . $trigger, '', [ $this->app->eventManager->triggerSimilar('POST:Module:' . $trigger, '',
$account, [
$rel1, $rel2, $account,
StringUtils::intHash($mapper), $trigger, $rel1, $rel2,
static::MODULE_NAME, StringUtils::intHash($mapper), $trigger,
'0', static::MODULE_NAME,
'', '0',
$ip, '',
]); $ip,
]
);
}
/**
* Create a model relation
*
* @param int $account Account id
* @param mixed $rel1 Object relation1
* @param mixed $rel2 Object relation2
* @param string $mapper Object mapper
* @param string $field Relation field
* @param string $trigger Trigger for the event manager
* @param string $ip Ip
*
* @return void
*
* @since 1.0.0
*/
protected function deleteModelRelation(int $account, mixed $rel1, mixed $rel2, string $mapper, string $field, string $trigger, string $ip) : void
{
$trigger = static::MODULE_NAME . '-' . $trigger . '-relation-delete';
$this->app->eventManager->triggerSimilar('PRE:Module:' . $trigger, '', $rel1);
$mapper::deleteRelation($field, $rel1, $rel2);
$this->app->eventManager->triggerSimilar('POST:Module:' . $trigger, '',
[
$account,
$rel1, $rel2,
StringUtils::intHash($mapper), $trigger,
static::MODULE_NAME,
'0',
'',
$ip,
]
);
} }
} }

View File

@ -61,14 +61,6 @@ final class ModuleManager
*/ */
private ApplicationAbstract $app; private ApplicationAbstract $app;
/**
* Application manager.
*
* @var ApplicationManager
* @since 1.0.0
*/
private ApplicationManager $appManager;
/** /**
* Installed modules. * Installed modules.
* *
@ -511,18 +503,15 @@ final class ModuleManager
$providing = $info->getProviding(); $providing = $info->getProviding();
foreach ($providing as $key => $version) { foreach ($providing as $key => $version) {
if (isset($installed[$key])) { if (isset($installed[$key])) {
$this->installProviding($module, $key); $this->installProviding('/Modules/' . $module, $key);
} }
} }
/* Install receiving and applications */ /* Install receiving and applications */
foreach ($this->installed as $key => $value) { foreach ($this->installed as $key => $value) {
$this->installProviding($key, $module); $this->installProviding('/Modules/' . $key, $module);
} }
$this->appManager = new ApplicationManager($this);
$this->installApplications($module);
return true; return true;
} catch (\Throwable $t) { } catch (\Throwable $t) {
return false; // @codeCoverageIgnore return false; // @codeCoverageIgnore
@ -630,7 +619,7 @@ final class ModuleManager
* *
* Installing additional functionality for another module * Installing additional functionality for another module
* *
* @param string $from From module * @param string $from From path
* @param string $for For module * @param string $for For module
* *
* @return void * @return void
@ -639,45 +628,16 @@ final class ModuleManager
*/ */
public function installProviding(string $from, string $for) : void public function installProviding(string $from, string $for) : void
{ {
if (!\is_file($this->modulePath . $from . '/Admin/Install/' . $for . '.php')) { if (!\is_file(__DIR__ . '/../..' . $from . '/Admin/Install/' . $for . '.php')) {
return; return;
} }
$class = '\\Modules\\' . $from . '\\Admin\\Install\\' . $for; $from = \str_replace('/', '\\', $from);
$class = $from . '\\Admin\\Install\\' . $for;
$class::install($this->modulePath, $this->app); $class::install($this->modulePath, $this->app);
} }
/**
* Install applications.
*
* Installing additional functionality for another module
*
* @param string $from From module
*
* @return void
*
* @since 1.0.0
*/
public function installApplications(string $from) : void
{
if (!\is_dir($this->modulePath . $from . '/Admin/Install/Application')) {
return;
}
$dirs = \scandir($this->modulePath . $from . '/Admin/Install/Application');
if ($dirs === false) {
return;
}
foreach ($dirs as $dir) {
if ($dir === '.' || $dir === '..') {
continue;
}
$this->appManager->install($dir, __DIR__ . '/../../Web/' . \basename($dir));
}
}
/** /**
* Get module instance. * Get module instance.
* *

View File

@ -228,7 +228,7 @@ abstract class StatusAbstract
{ {
$query = new Builder($dbPool->get('update')); $query = new Builder($dbPool->get('update'));
$query->update('module') $query->update('module')
->sets('module.module_active', 1) ->sets('module.module_active', ModuleStatus::ACTIVE)
->where('module.module_id', '=', $info->getInternalName()) ->where('module.module_id', '=', $info->getInternalName())
->execute(); ->execute();
} }
@ -416,7 +416,7 @@ abstract class StatusAbstract
{ {
$query = new Builder($dbPool->get('update')); $query = new Builder($dbPool->get('update'));
$query->update('module') $query->update('module')
->sets('module.module_active', 0) ->sets('module.module_active', ModuleStatus::INACTIVE)
->where('module.module_id', '=', $info->getInternalName()) ->where('module.module_id', '=', $info->getInternalName())
->execute(); ->execute();
} }

View File

@ -34,4 +34,12 @@ interface RouterInterface
* @since 1.0.0 * @since 1.0.0
*/ */
public function importFromFile(string $path) : bool; public function importFromFile(string $path) : bool;
/**
* Clear routes
*
* @return void
* @since 1.0.0
*/
public function clear() : void;
} }

View File

@ -69,6 +69,17 @@ final class SocketRouter implements RouterInterface
return true; return true;
} }
/**
* Clear routes
*
* @return void
* @since 1.0.0
*/
public function clear() : void
{
$this->routes = [];
}
/** /**
* Add route. * Add route.
* *

View File

@ -71,6 +71,17 @@ final class WebRouter implements RouterInterface
return true; return true;
} }
/**
* Clear routes
*
* @return void
* @since 1.0.0
*/
public function clear() : void
{
$this->routes = [];
}
/** /**
* Add route. * Add route.
* *
@ -144,25 +155,35 @@ final class WebRouter implements RouterInterface
) { ) {
// if csrf is required but not set // if csrf is required but not set
if (isset($d['csrf']) && $d['csrf'] && $csrf === null) { if (isset($d['csrf']) && $d['csrf'] && $csrf === null) {
return $app !== null ? $this->route('/' . \strtolower($app) . '/e403', $csrf, $verb) : $this->route('/e403', $csrf, $verb); return $app !== null
? $this->route('/' . \strtolower($app) . '/e403', $csrf, $verb)
: $this->route('/e403', $csrf, $verb);
} }
// if permission check is invalid // if permission check is invalid
if ((isset($d['permission']) && $account === null) if ((isset($d['permission']) && $account === null)
|| (isset($d['permission']) || (isset($d['permission'])
&& !$account?->hasPermission( && !$account?->hasPermission(
$d['permission']['type'] ?? 0, $d['permission']['unit'] ?? $orgId, $app, $d['permission']['module'] ?? null, $d['permission']['state'] ?? null $d['permission']['type'] ?? 0,
$d['permission']['unit'] ?? $orgId,
$app,
$d['permission']['module'] ?? null,
$d['permission']['state'] ?? null
) )
) )
) { ) {
return $app !== null ? $this->route('/' . \strtolower($app) . '/e403', $csrf, $verb) : $this->route('/e403', $csrf, $verb); return $app !== null
? $this->route('/' . \strtolower($app) . '/e403', $csrf, $verb)
: $this->route('/e403', $csrf, $verb);
} }
// if validation check is invalid // if validation check is invalid
if (isset($d['validation'])) { if (isset($d['validation'])) {
foreach ($d['validation'] as $name => $pattern) { foreach ($d['validation'] as $name => $validation) {
if (!isset($data[$name]) || \preg_match($pattern, $data[$name]) !== 1) { if (!isset($data[$name]) || \preg_match($validation, $data[$name]) !== 1) {
return $app !== null ? $this->route('/' . \strtolower($app) . '/e403', $csrf, $verb) : $this->route('/e403', $csrf, $verb); return $app !== null
? $this->route('/' . \strtolower($app) . '/e403', $csrf, $verb)
: $this->route('/e403', $csrf, $verb);
} }
} }
} }

View File

@ -69,7 +69,7 @@ class ApplicationManagerTest extends \PHPUnit\Framework\TestCase
$app->moduleManager = new ModuleManager($app, __DIR__ . '/../../../Modules/'); $app->moduleManager = new ModuleManager($app, __DIR__ . '/../../../Modules/');
$this->appManager = new ApplicationManager($app->moduleManager); $this->appManager = new ApplicationManager();
} }
/** /**

View File

@ -349,7 +349,7 @@ class WebRouterTest extends \PHPUnit\Framework\TestCase
RouteVerb::GET | RouteVerb::SET, RouteVerb::GET | RouteVerb::SET,
false, false,
[], [],
'/^.*?(something)=(\d*).*?$/' '/^.*?(something)=(?<name>\d*).*?$/'
); );
self::assertEquals( self::assertEquals(
@ -358,6 +358,7 @@ class WebRouterTest extends \PHPUnit\Framework\TestCase
'data' => [ 'data' => [
'/backends/admin?something=123&sd=asdf', '/backends/admin?something=123&sd=asdf',
'something', 'something',
'name' => '123',
'123', '123',
], ],
]], ]],