Newer
Older
<?php
/**
* This file contains \QUI\Bricks\Manager
*/
namespace QUI\Bricks;
use DOMNode;
use QUI\Utils\Text\XML;
use function array_filter;
use function array_flip;
use function array_map;
use function array_merge;
use function array_reverse;
use function array_unique;
use function array_values;
use function class_exists;
use function count;
use function implode;
use function in_array;
use function is_array;
use function is_callable;
use function json_decode;
use function json_encode;
use function md5;
use function realpath;
use function str_replace;
use function trim;
use function usort;
/**
* Brick Manager
*
* @package quiqqer/bricks
*/
class Manager
{
/**
* Bricks table name
*/
const TABLE = 'bricks';
/**
* Bricks uid table name
*/
const TABLE_UID = 'bricksUID';
/**
* Brick Cache table name
*/
const TABLE_CACHE = 'bricksCache';
/**
* Brick temp collector
*
* @var array
*/
/**
* Brick UID temp collector
*
* @var array
*/
* @var null|Manager
/**
* Return the global QUI\Bricks\Manager
*
public static function init(): ?Manager
self::$BrickManager = new Manager(true);
}
/**
* Constructor
* Please use \QUI\Bricks\Manager::init()
*
* @param boolean $init - please use \QUI\Bricks\Manager::init()
*/
{
if ($init === false) {
QUI\System\Log::addWarning('Please use \QUI\Bricks\Manager::init()');
}
}
* Returns the bricks table name
public static function getTable(): string
{
return QUI::getDBTableName(self::TABLE);
}
/**
* @return string
*/
public static function getUIDTable(): string
{
return QUI::getDBTableName(self::TABLE_UID);
}
/**
* Return the long time cache namespace
*
* @return string
*/
public static function getBrickCacheNamespace(): string
{
return 'quiqqer/package/quiqqer/bricks/';
}
/**
* Creates a new brick for the project
*
* @param Project $Project
* @param Brick $Brick
*
* @return integer - Brick-ID
public function createBrickForProject(Project $Project, Brick $Brick): int
QUI\Permissions\Permission::checkPermission('quiqqer.bricks.create');
QUI::getDataBase()->insert(
$this->getTable(),
'project' => $Project->getName(),
'lang' => $Project->getLang(),
'title' => $Brick->getAttribute('title'),
'description' => $Brick->getAttribute('description'),
$brickId = QUI::getPDO()->lastInsertId();
try {
QUI::getEvents()->fireEvent('quiqqerBricksCreate', [$brickId]);
QUI\System\Log::writeException($Exception);
}
return (int)$brickId;

Henning Leutz
committed
/**
* Create and update a unique site brick
*
* @param QUI\Interfaces\Projects\Site $Site

Henning Leutz
committed
* @param array $brickData
* @return string - Unique ID

Henning Leutz
committed
*/
public function createUniqueSiteBrick(QUI\Interfaces\Projects\Site $Site, array $brickData = []): string

Henning Leutz
committed
{

Henning Leutz
committed
$uid = $brickData['uid'];
if ($this->existsUniqueBrickId($uid) === false) {
$uid = $this->createUniqueBrickId((int)$brickData['brickId'], $Site);
}
} else {
$uid = $this->createUniqueBrickId((int)$brickData['brickId'], $Site);
}
$customFields = [];

Henning Leutz
committed
if (isset($brickData['customfields'])) {
$customFields = $brickData['customfields'];
}
if (is_array($customFields)) {
$customFields = json_encode($customFields);

Henning Leutz
committed
}
QUI::getDataBase()->update($this->getUIDTable(), [

Henning Leutz
committed
'customfields' => $customFields
], [

Henning Leutz
committed
'uid' => $uid
]);

Henning Leutz
committed
return $uid;
}
/**
* Create a new unique Brick ID
*
* @param integer $brickId - Brick ID
* @param QUI\Interfaces\Projects\Site $Site - Current Site

Henning Leutz
committed
*/
protected function createUniqueBrickId(int $brickId, QUI\Interfaces\Projects\Site $Site): string

Henning Leutz
committed
{
$Project = $Site->getProject();
$uuid = QUI\Utils\Uuid::get();
$Brick = $this->getBrickById($brickId);

Henning Leutz
committed
QUI::getDataBase()->insert($this->getUIDTable(), [
'uid' => $uuid,
'brickId' => $brickId,
'project' => $Project->getName(),
'lang' => $Project->getLang(),
'siteId' => $Site->getId(),
'attributes' => json_encode($Brick->getAttributes())
]);

Henning Leutz
committed

Henning Leutz
committed
}
/**
* Check if an unique brick ID exists
*
* @param string $uid - Brick Unique ID
* @return bool
*/
public function existsUniqueBrickId(string $uid): bool

Henning Leutz
committed
{
try {
$result = QUI::getDataBase()->fetch([
'where' => [
'uid' => $uid
],
'limit' => 1
]);
} catch (QUI\Database\Exception $Exception) {
QUI\System\Log::addError($Exception->getMessage());
return false;
}

Henning Leutz
committed
return isset($result[0]);
}
{
QUI\Cache\Manager::clear('quiqqer/bricks');
}
/**
* Delete the brick
*
public function deleteBrick(int $brickId): void
QUI\Permissions\Permission::checkPermission('quiqqer.bricks.delete');

Henning Leutz
committed
$Brick = $this->getBrickById($brickId);
QUI::getEvents()->fireEvent('quiqqerBricksBrickDeleteBefore', [$Brick]);
QUI::getDataBase()->delete($this->getTable(), [
]);

Henning Leutz
committed
if (isset($this->bricks[$brickId])) {
unset($this->bricks[$brickId]);
}
$uniqueBrickIds = QUI::getDataBase()->fetch([

Henning Leutz
committed
'select' => 'siteId, project, lang',
'from' => QUI\Bricks\Manager::getUIDTable(),
'where' => [

Henning Leutz
committed
'brickId' => $brickId,
'project' => $Brick->getAttribute('project'),
]);

Henning Leutz
committed
// delete bricks in sites
foreach ($uniqueBrickIds as $uniqueBrickId) {
$project = $uniqueBrickId['project'];

Henning Leutz
committed
$Project = QUI::getProject($project, $lang);
$Site = $Project->get($uniqueBrickId['siteId']);
$Edit = $Site->getEdit();

Henning Leutz
committed
$Edit->load();
$Edit->save(QUI::getUsers()->getSystemUser());
}
// delete unique ids
QUI::getDataBase()->delete(QUI\Bricks\Manager::getUIDTable(), [

Henning Leutz
committed
'brickId' => $brickId,
'project' => $Brick->getAttribute('project'),
]);
QUI::getEvents()->fireEvent('quiqqerBricksBrickDeleteAfter', [$brickId]);
}
/**
* Return the areas which are available in the project
*
* @param Project $Project
* @param boolean|string $layoutType - optional, returns only the areas
* for the specific layout type
* (default = false)
* @param boolean|string $siteType - optional, returns only the areas
* for the specific site type
* (default = false)
public function getAreasByProject(
Project $Project,
bool | string $layoutType = false,
bool | string $siteType = false
$templates = [];
$projectName = $Project->getName();
if ($Project->getAttribute('template')) {
$templates[] = $Project->getAttribute('template');
}
// inheritance
try {
$Package = QUI::getPackage($Project->getAttribute('template'));
if ($Parent) {
$templates[] = $Parent->getName();
}
// get all vhosts, and the used templates of the project
$vhosts = QUI::getRewrite()->getVHosts();
foreach ($vhosts as $vhost) {
if (!isset($vhost['template'])) {
continue;
}
if ($vhost['project'] != $projectName) {
continue;
}
$templates[] = $vhost['template'];
}
// get bricks
foreach ($templates as $template) {
$brickXML = realpath(OPT_DIR . $template . '/bricks.xml');
Utils::getTemplateAreasFromXML($brickXML, $layoutType, $siteType)
// unique values
$cleaned = [];
foreach ($bricks as $val) {
if (!isset($cleaned[$val['name']])) {
$cleaned[$val['name']] = $val;
}
}
if (isset($a['priority']) && isset($b['priority'])) {
if ($a['priority'] == $b['priority']) {
return 0;
}
return ($a['priority'] < $b['priority']) ? -1 : 1;
}
$transA = QUI::getLocale()->get(
$a['title']['group'],
$a['title']['var']
);
$transB = QUI::getLocale()->get(
$b['title']['group'],
$b['title']['var']
);
return $transA > $transB ? 1 : -1;
});
try {
QUI::getEvents()->fireEvent(
'onBricksGetAreaByProject',
[$this, $Project, &$bricks]
);
} catch (QUI\Exception $Exception) {
QUI\System\Log::writeException($Exception);
}
return $bricks;
}
/**
* Returns the available bricks
*
* @return array
*/
public function getAvailableBricks(): array
{
$cache = 'quiqqer/bricks/availableBricks';
try {
return QUI\Cache\Manager::get($cache);
$result[] = [
'title' => ['quiqqer/bricks', 'brick.content.title'],
'description' => [
$result = array_merge($result, Utils::getBricksFromXML($bricksXML));
$result = array_filter($result, function ($brick) {
return !empty($brick['title']);
});
// js workaround
$list = [];
foreach ($result as $entry) {
$list[] = $entry;
}
try {
QUI\Cache\Manager::set($cache, $list);
QUI\System\Log::writeException($Exception);
}
}
/**
* Get a Brick by its Brick-ID
*
*
* @return Brick
* @throws QUI\Exception
*/
$data = QUI::getDataBase()->fetch([
'where' => [
]);
if (!isset($data[0])) {
throw new QUI\Exception('Brick not found');
}
$Brick = new Brick($data[0]);
$Brick->setAttribute('id', $id);
$this->bricks[$id] = $Brick;
/**
* Get a Brick by its unique ID
*
* @param string $uid - unique id
* @param QUI\Interfaces\Projects\Site|null $Site - unique id
*
* @return Brick
* @throws QUI\Exception
*/
public function getBrickByUID(string $uid, null | QUI\Interfaces\Projects\Site $Site = null): Brick
{
if (isset($this->brickUIDs[$uid])) {
return $this->brickUIDs[$uid];
}
$data = QUI::getDataBase()->fetch([
'where' => [

Henning Leutz
committed
'uid' => $uid
]);
if (!isset($data[0])) {
throw new QUI\Exception('Brick not found');
}

Henning Leutz
committed
$brickId = $data['brickId'];

Henning Leutz
committed
$attributes = $data['attributes'];

Henning Leutz
committed
$real = QUI::getDataBase()->fetch([
'where' => [
]);

Henning Leutz
committed
$Original->setAttribute('id', $brickId);
$Clone = clone $Original;
if (!empty($custom)) {

Henning Leutz
committed
if ($custom) {
$Clone->setSettings($custom);
}
// workaround
if (isset($custom['brickTitle'])) {
$Clone->setAttribute('frontendTitle', $custom['brickTitle']);
}
}

Henning Leutz
committed
$this->brickUIDs[$uid] = $Clone;

Henning Leutz
committed
return $Clone;
/**
* Return the available brick settings by the brick type
*
* @param $brickType
*
* @return array
*/
public function getAvailableBrickSettingsByBrickType($brickType): array
$cache = 'quiqqer/bricks/brickType/' . md5($brickType);
try {
return QUI\Cache\Manager::get($cache);
$settings = [];
$settings[] = [
'name' => 'width',
'text' => ['quiqqer/bricks', 'site.area.window.settings.setting.width'],
'type' => '',
'class' => '',
$settings[] = [
'name' => 'height',
'text' => ['quiqqer/bricks', 'site.area.window.settings.setting.height'],
'type' => '',
'class' => '',
$settings[] = [
'name' => 'classes',
'text' => ['quiqqer/bricks', 'site.area.window.settings.setting.classes'],
'type' => '',
'class' => '',
$xmlFiles = $this->getBricksXMLFiles();
foreach ($xmlFiles as $brickXML) {
"//quiqqer/bricks/brick[@control='$brickType']/settings/setting"
$Globals = $Path->query(
"//quiqqer/bricks/brick[@control='*']/settings/setting"
);
foreach ($Globals as $Setting) {
$settings[] = $this->parseSettingToBrickArray($Setting);
$settings[] = $this->parseSettingToBrickArray($Setting);
}
}
// cleanup duplicated
// quiqqer/package-bricks#90
$exists = [];
$settings = array_filter($settings, function ($entry) use (&$exists) {
$name = $entry['name'];
if (isset($exists[$name])) {
return false;
}
$exists[$name] = true;
return true;
});
try {
QUI\Cache\Manager::set($cache, $settings);
QUI\System\Log::writeException($Exception);
}
/**
* Parse a xml setting element to a brick array
*
* @param DOMNode|DOMElement $Setting
protected function parseSettingToBrickArray(DOMNode | DOMElement $Setting): array
$options = null;
if (
method_exists($Setting, 'getAttribute')
&& method_exists($Setting, 'getElementsByTagName')
&& $Setting->getAttribute('type') == 'select'
) {
$optionElements = $Setting->getElementsByTagName('option');
foreach ($optionElements as $Option) {
$options[] = [
'value' => $Option->getAttribute('value'),
'text' => QUI\Utils\DOM::getTextFromNode($Option, false)

Michael Danielczok
committed
$dataAttributes = [];

Michael Danielczok
committed
foreach ($Setting->attributes as $attribute) {
if ($attribute->nodeName === 'data-qui') {
continue;
}
if (str_contains($attribute->nodeName, 'data-')) {
$dataAttributes[$attribute->nodeName] = trim($attribute->nodeValue);

Michael Danielczok
committed
}
}
if (method_exists($Setting, 'getElementsByTagName')) {
$Description = $Setting->getElementsByTagName('description');
if ($Description->length) {
$description = QUI\Utils\DOM::getTextFromNode($Description->item(0), false);
}
return [
'name' => method_exists($Setting, 'getAttribute') ? $Setting->getAttribute('name') : '',
'text' => QUI\Utils\DOM::getTextFromNode($Setting, false),
'description' => $description,
'type' => method_exists($Setting, 'getAttribute') ? $Setting->getAttribute('type') : '',
'class' => method_exists($Setting, 'getAttribute') ? $Setting->getAttribute('class') : '',
'data-qui' => method_exists($Setting, 'getAttribute') ? $Setting->getAttribute('data-qui') : '',

Michael Danielczok
committed
'data-attributes' => $dataAttributes
}
/**
* Return the bricks from the area
*
* @param string $brickArea - Name of the area
* @param QUI\Interfaces\Projects\Site $Site
public function getBricksByArea(
string $brickArea,
QUI\Interfaces\Projects\Site $Site
): array {
return [];
}
$brickAreas = $Site->getAttribute('quiqqer.bricks.areas');
if (!is_array($brickAreas)) {
$brickAreas = json_decode($brickAreas, true);
$bricks = $this->getInheritedBricks($brickArea, $Site);
} else {
$brickData = $brickAreas[$brickArea];
foreach ($brickData as $brick) {
if (isset($brick['deactivate'])) {
break;
}
$bricks[] = $brick;
}
}
$result = [];
QUI::getEvents()->fireEvent(
'onQuiqqerBricksGetBricksByAreaBegin',
[$brickArea, $Site, &$result]
);
$brickId = (int)$brickData['brickId'];
try {
if (!empty($brickData['uid'])) {
$Brick = $this->getBrickByUID($brickData['uid'], $Site);

Henning Leutz
committed
$result[] = $Brick->check();
continue;
}

Henning Leutz
committed
try {
if (!$brickId) {
continue;
}

Henning Leutz
committed
// fallback
$Clone = clone $Brick;
$custom = json_decode($brickData['customfields'], true);
$Clone->setSettings($custom);
$result[] = $Clone->check();

Henning Leutz
committed
QUI\System\Log::writeRecursive($brickData);
QUI\System\Log::writeException($Exception);
QUI::getEvents()->fireEvent(
'onQuiqqerBricksGetBricksByAreaEnd',
[$brickArea, $Site, &$result]
);
return $result;
}
/**
* Return a list with \QUI\Bricks\Brick which are assigned to a project
*
* @param Project $Project
* @return array
public function getBricksFromProject(Project $Project): array
$result = [];
$list = QUI::getDataBase()->fetch([
'where' => [
]
]);
foreach ($list as $entry) {
$result[] = $this->getBrickById($entry['id']);
}
return $result;
}
/**
* Return a list with \QUI\Bricks\Brick which are assigned to a project
*
* @param Brick $Brick
* @return array
*/
public function getSitesByBrick(Brick $Brick): array
try {
$list = QUI::getDataBase()->fetch([
'select' => ['brickId', 'project', 'lang', 'siteId'],
'from' => $this->getUIDTable(),
'where' => [
'project' => $Brick->getAttribute('project'),
'brickId' => $Brick->getAttribute('id')
]
]);
$Project = QUI::getProject(
$Brick->getAttribute('project'),
$Brick->getAttribute('lang')
);
} catch (QUI\Exception $Exception) {
QUI\System\Log::addError($Exception->getMessage());
return [];
}
$result = [];
foreach ($list as $entry) {
try {
$result[] = $Project->get($entry['siteId']);
} catch (QUI\Exception $Exception) {
QUI\System\Log::writeDebugException($Exception);
continue;
}
}
return $result;
}
* @param integer|string $brickId - Brick-ID
public function saveBrick(int | string $brickId, array $brickData): void
QUI\Permissions\Permission::checkPermission('quiqqer.bricks.edit');
QUI::getEvents()->fireEvent('quiqqerBricksSaveBefore', [$brickId]);
$Brick = $this->getBrickById($brickId);
$areas = [];
$areaString = '';
if (isset($brickData['id'])) {
unset($brickData['id']);
}
// check areas
$Project = QUI::getProjectManager()->getProject(
$Brick->getAttribute('project')
);
if (isset($data['name'])) {
return $data['name'];
}
return '';
}, $this->getAreasByProject($Project));
if (isset($brickData['attributes']['areas'])) {
$brickData['areas'] = $brickData['attributes']['areas'];
}
if (isset($brickData['areas'])) {

Henning Leutz
committed
if (defined('QUIQQER_BRICKS_IGNORE_AREA_CHECK')) {
$areas[] = $area;
continue;
}
$areas[] = $area;
}
}
}
if (!empty($areas)) {
$areaString = ',' . implode(',', $areas) . ',';
}
$Brick->setAttributes($brickData);
// fields
if (isset($brickData['attributes']) && is_array($brickData['attributes'])) {
foreach ($brickData['attributes'] as $key => $value) {
if ($key == 'areas') {
continue;
}
$Brick->setAttribute($key, $value);
}
}
// brick settings
if (isset($brickData['settings'])) {
$Brick->setSettings($brickData['settings']);
}
$brickAttributes = Utils::getAttributesForBrick($Brick);
foreach ($brickAttributes as $attribute) {
if (isset($brickData['attributes'][$attribute])) {
$Brick->setSetting($attribute, $brickData['attributes'][$attribute]);
}
}