<?php
/**
* Copyright Blackbit digital Commerce GmbH <info@blackbit.de>
*
* This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
namespace Blackbit\DataDirectorBundle\EventListener;
use Blackbit\DataDirectorBundle\lib\Pim\Helper;
use Blackbit\DataDirectorBundle\lib\Pim\Item\ItemMoldBuilder;
use Blackbit\DataDirectorBundle\lib\Pim\LocationAwareConfigRepository;
use Blackbit\DataDirectorBundle\lib\Pim\Logger\TagLogger;
use Blackbit\DataDirectorBundle\model\Dataport;
use Blackbit\DataDirectorBundle\model\PimcoreDbRepository;
use Blackbit\DataDirectorBundle\Tools\Installer;
use Pimcore\Event\Model\DataObject\ClassDefinitionEvent;
use Pimcore\Event\Model\DataObject\ObjectbrickDefinitionEvent;
use Pimcore\File;
use Pimcore\Model\DataObject\ClassDefinition;
use Pimcore\Model\DataObject\ClassDefinition\Data\Hotspotimage;
use Pimcore\Model\DataObject\ClassDefinition\Data\Image;
use Pimcore\Model\DataObject\ClassDefinition\Data\Localizedfields;
use Pimcore\Model\DataObject\ClassDefinition\Data\Objectbricks;
use Pimcore\Model\DataObject\ClassDefinition\Data\Relations\AbstractRelations;
use Pimcore\Model\DataObject\ClassDefinition\Listing;
use Pimcore\Model\DataObject\ClassDefinition\PathFormatterAwareInterface;
use Pimcore\Model\DataObject\Data\ImageGallery;
use Pimcore\Model\DataObject\Objectbrick\Data\AbstractData;
use Pimcore\Model\DataObject\Objectbrick\Definition;
use Pimcore\Perspective\Config;
use Pimcore\Tool;
class ClassChangedListener
{
/** @var ItemMoldBuilder */
private $itemMoldBuilder;
public function __construct(ItemMoldBuilder $itemMoldBuilder)
{
$this->itemMoldBuilder = $itemMoldBuilder;
}
public function removeCompiledDataQuerySelectors(ClassDefinitionEvent $e) {
try {
$dummyObject = $this->itemMoldBuilder->getItemMoldByClassname($e->getClassDefinition()->getName());
$classFqn = \get_class($dummyObject);
} catch (\Exception $classNotFoundException) {
$classFqn = 'Pimcore\\Model\\DataObject\\'.$e->getClassDefinition()->getName();
}
$directoryIterator = new \DirectoryIterator(Installer::getCachePath());
$fileNamePrefix = preg_replace('/\W+/', '_', $classFqn.' ');
$filterIterator = new \CallbackFilterIterator($directoryIterator, static function (\SplFileInfo $fileInfo) use ($fileNamePrefix) {
return strpos($fileInfo->getFilename(), $fileNamePrefix) === 0 || strpos($fileInfo->getFilename(), 'Dao_'.$fileNamePrefix) === 0 || strpos($fileInfo->getFilename(), 'Listing_'.$fileNamePrefix) === 0;
});
/** @var \SplFileInfo $compiledFileInfo */
foreach ($filterIterator as $compiledFile) {
unlink($compiledFile->getPathname());
}
}
public function removeAllCompiledDataQuerySelectors() {
/** @var \SplFileInfo $compiledFileInfo */
foreach(new \DirectoryIterator(Installer::getCachePath()) as $compiledFileInfo) {
if (!$compiledFileInfo->isDot()) {
@unlink($compiledFileInfo->getPathname());
}
}
}
public function createStoreView(ClassDefinitionEvent $e)
{
if ($e->getClassDefinition()->getAllowInherit()) {
foreach (Tool::getValidLanguages() as $language) {
$hasLocalizedFields = false;
foreach($e->getClassDefinition()->getFieldDefinitions() as $fieldDefinition) {
if($fieldDefinition instanceof Localizedfields) {
$hasLocalizedFields = true;
break;
}
}
$query = 'CREATE OR REPLACE VIEW `object_localized_store_'.$e->getClassDefinition()->getId().'_'.$language.'` AS SELECT * FROM `object_store_'.$e->getClassDefinition()->getId().'` JOIN `objects` ON `objects`.`'.Helper::prefixObjectSystemColumn('id').'` = `object_store_'.$e->getClassDefinition()->getId().'`.`oo_id`';
$parameters = [];
if($hasLocalizedFields) {
$query .= ' JOIN `object_localized_data_'.$e->getClassDefinition()->getId().'` ON `object_store_'.$e->getClassDefinition()->getId().'`.oo_id=`object_localized_data_'.$e->getClassDefinition()->getId().'`.ooo_id AND language=?';
$parameters[] = $language;
}
PimcoreDbRepository::getInstance()->execute($query, $parameters);
}
} else {
$this->removeStoreView($e);
}
}
public function removeStoreView(ClassDefinitionEvent $e)
{
foreach (Tool::getValidLanguages() as $language) {
PimcoreDbRepository::getInstance()->execute('DROP VIEW IF EXISTS `object_localized_store_'.$e->getClassDefinition()->getId().'_'.$language.'`');
}
}
public function addPreviewService(ClassDefinitionEvent $e)
{
$classDefinition = $e->getClassDefinition();
if (method_exists($classDefinition, 'setPreviewGeneratorReference')) {
if (!$classDefinition->getPreviewGeneratorReference()) {
$classDefinition->setPreviewGeneratorReference('@DataDirectorPreview');
}
} elseif (method_exists($classDefinition, 'setPreviewUrl')) {
if (!$classDefinition->getPreviewUrl()) {
$classDefinition->setPreviewUrl('/admin/BlackbitDataDirector/import/object-preview?id=%o_id');
}
}
}
/**
* @param ClassDefinitionEvent|ObjectbrickDefinitionEvent $e
* @return void
*/
public function addPathFormatterService($e)
{
if($e instanceof ObjectbrickDefinitionEvent) {
$classDefinition = $e->getObjectbrickDefinition();
} else {
$classDefinition = $e->getClassDefinition();
}
foreach ($classDefinition->getFieldDefinitions() as $fieldDefinition) {
if ($fieldDefinition instanceof PathFormatterAwareInterface && !$fieldDefinition->getPathFormatterClass() && method_exists($fieldDefinition, 'setPathFormatterClass')) {
$fieldDefinition->setPathFormatterClass('@DataDirectorSearchViewPathFormatter');
} elseif ($fieldDefinition instanceof Localizedfields) {
foreach ($fieldDefinition->getFieldDefinitions() as $localizedFieldDefinition) {
if ($localizedFieldDefinition instanceof PathFormatterAwareInterface && !$localizedFieldDefinition->getPathFormatterClass() && method_exists($localizedFieldDefinition, 'setPathFormatterClass')) {
$localizedFieldDefinition->setPathFormatterClass('@DataDirectorSearchViewPathFormatter');
}
}
}
}
}
public function updatePerspective(ClassDefinitionEvent $e)
{
self::createPerspective();
}
public static function createPerspective()
{
$existingPerspectives = Config::get();
if(!$existingPerspectives) {
return;
}
if($existingPerspectives instanceof \Pimcore\Config\Config) {
$existingPerspectives = $existingPerspectives->toArray();
}
foreach($existingPerspectives as $perspectiveName => $existingPerspective) {
if(strpos($perspectiveName, 'pim.perspective') !== 0 && $perspectiveName !== 'default') {
return;
}
}
$dependencies = [];
$customViews = [];
foreach ((new Listing())->load() as $classDefinition) {
$folders = PimcoreDbRepository::getInstance()->findColumnInSql('SELECT DISTINCT '.Helper::prefixObjectSystemColumn('path').' FROM objects WHERE '.Helper::prefixObjectSystemColumn('classId').'=? ORDER BY '.Helper::prefixObjectSystemColumn('path'), [$classDefinition->getId()]);
if (count($folders) === 0) {
$folders = ['/'];
}
$firstFolder = $folders[0];
$lastFolder = $folders[count($folders) - 1];
$len = min(mb_strlen($firstFolder), mb_strlen($lastFolder));
for ($i = 0; $i < $len; $i++) {
if (mb_substr($firstFolder, $i, 1) !== mb_substr($lastFolder, $i, 1)) {
break;
}
}
$commonPrefix = substr($firstFolder, 0, $i);
$customViewId = 'dd_'.File::getValidFilename($classDefinition->getGroup() ?? '');
if (!isset($dependencies[$customViewId])) {
$dependencies[$customViewId] = [];
}
if (!isset($customViews[$customViewId])) {
$customViews[$customViewId] = [
'id' => $customViewId,
'name' => $classDefinition->getGroup() ?: 'data_objects',
'treetype' => 'object',
'position' => 'left',
'rootfolder' => $commonPrefix ?: '/',
'classes' => $classDefinition->getId(),
'showroot' => true,
'sort' => 0,
'treeContextMenu' => [
'object' => [
'items' => [
'add' => true,
'addFolder' => true,
'importCsv' => true,
'cut' => true,
'copy' => true,
'paste' => true,
'delete' => true,
'rename' => true,
'reload' => true,
'publish' => true,
'unpublish' => true,
'searchAndMove' => true,
'lock' => true,
'unlock' => true,
'lockAndPropagate' => true,
'unlockAndPropagate' => true,
'changeChildrenSortBy' => true
]
]
],
'icon' => $classDefinition->getIcon() ?: '/bundles/pimcoreadmin/img/flat-white-icons/pimcore-main-icon-object.svg',
'where' => Helper::prefixObjectSystemColumn('path').' LIKE \''.$commonPrefix.'%\''
];
} else {
$customViews[$customViewId]['classes'] .= ','.$classDefinition->getId();
$firstFolder = (mb_strlen($customViews[$customViewId]['rootfolder']) <= mb_strlen($commonPrefix) ? $customViews[$customViewId]['rootfolder'] : $commonPrefix);
$lastFolder = (mb_strlen($customViews[$customViewId]['rootfolder']) > mb_strlen($commonPrefix) ? $customViews[$customViewId]['rootfolder'] : $commonPrefix);
$len = min(mb_strlen($firstFolder), mb_strlen($lastFolder));
for ($i = 0; $i < $len; $i++) {
if (mb_substr($firstFolder, $i, 1) !== mb_substr($lastFolder, $i, 1)) {
break;
}
}
$customViews[$customViewId]['where'] .= ' OR '.Helper::prefixObjectSystemColumn('path').' LIKE \''.$commonPrefix.'%\'';
$commonPrefix = substr($firstFolder, 0, $i);
$customViews[$customViewId]['rootfolder'] = $commonPrefix;
}
foreach ($classDefinition->getFieldDefinitions() as $fieldDefinition) {
if ($fieldDefinition instanceof AbstractRelations && method_exists($fieldDefinition, 'getDocumentsAllowed') && $fieldDefinition->getDocumentsAllowed()) {
$dependencies[$customViewId][] = 'document';
} elseif($fieldDefinition instanceof AbstractRelations && !$fieldDefinition instanceof ClassDefinition\Data\ReverseManyToManyObjectRelation && !$fieldDefinition instanceof ClassDefinition\Data\ReverseObjectRelation && method_exists($fieldDefinition, 'getObjectsAllowed') && $fieldDefinition->getObjectsAllowed()) {
foreach ((array)$fieldDefinition->getClasses() as $allowedClass) {
$allowedClass = ClassDefinition::getByName($allowedClass['classes']);
if($allowedClass instanceof ClassDefinition) {
$dependencies[$customViewId][] = 'dd_'.File::getValidFilename($allowedClass->getGroup() ?? '');
}
}
} elseif ($fieldDefinition instanceof Localizedfields) {
foreach ($fieldDefinition->getFieldDefinitions() as $localizedFieldDefinition) {
if ($localizedFieldDefinition instanceof AbstractRelations && method_exists($localizedFieldDefinition, 'getDocumentsAllowed') && $localizedFieldDefinition->getDocumentsAllowed()) {
$dependencies[$customViewId][] = 'document';
} elseif ($localizedFieldDefinition instanceof AbstractRelations && !$fieldDefinition instanceof ClassDefinition\Data\ReverseManyToManyObjectRelation && !$fieldDefinition instanceof ClassDefinition\Data\ReverseObjectRelation && method_exists($localizedFieldDefinition, 'getObjectsAllowed') && $localizedFieldDefinition->getObjectsAllowed()) {
foreach ((array)$localizedFieldDefinition->getClasses() as $allowedClass) {
$allowedClass = ClassDefinition::getByName($allowedClass['classes']);
if ($allowedClass instanceof ClassDefinition) {
$dependencies[$customViewId][] = 'dd_'.File::getValidFilename($allowedClass->getGroup());
}
}
}
}
} elseif ($fieldDefinition instanceof Objectbricks) {
foreach ($fieldDefinition->getAllowedTypes() as $brickName) {
$brickDefinition = Definition::getByKey($brickName);
if(!$brickDefinition instanceof Definition) {
continue;
}
foreach ($brickDefinition->getFieldDefinitions() as $brickFieldDefinition) {
if ($brickFieldDefinition instanceof Localizedfields) {
foreach ($brickFieldDefinition->getFieldDefinitions() as $localizedFieldDefinition) {
if ($localizedFieldDefinition instanceof AbstractRelations && method_exists($localizedFieldDefinition, 'getDocumentsAllowed') && $localizedFieldDefinition->getDocumentsAllowed()) {
$dependencies[$customViewId][] = 'document';
} elseif ($localizedFieldDefinition instanceof AbstractRelations && !$fieldDefinition instanceof ClassDefinition\Data\ReverseManyToManyObjectRelation && !$fieldDefinition instanceof ClassDefinition\Data\ReverseObjectRelation && method_exists($localizedFieldDefinition, 'getObjectsAllowed') && $localizedFieldDefinition->getObjectsAllowed()) {
foreach ((array)$localizedFieldDefinition->getClasses() as $allowedClass) {
$allowedClass = ClassDefinition::getByName($allowedClass['classes']);
if ($allowedClass instanceof ClassDefinition) {
$dependencies[$customViewId][] = 'dd_'.File::getValidFilename($allowedClass->getGroup());
}
}
}
}
} elseif ($brickFieldDefinition instanceof AbstractRelations && method_exists($brickFieldDefinition, 'getDocumentsAllowed') && $brickFieldDefinition->getDocumentsAllowed()) {
$dependencies[$customViewId][] = 'document';
} elseif ($brickFieldDefinition instanceof AbstractRelations && !$fieldDefinition instanceof ClassDefinition\Data\ReverseManyToManyObjectRelation && !$fieldDefinition instanceof ClassDefinition\Data\ReverseObjectRelation && method_exists($brickFieldDefinition, 'getObjectsAllowed') && $brickFieldDefinition->getObjectsAllowed()) {
foreach ((array)$brickFieldDefinition->getClasses() as $allowedClass) {
$allowedClass = ClassDefinition::getByName($allowedClass['classes']);
if ($allowedClass instanceof ClassDefinition) {
$dependencies[$customViewId][] = 'dd_'.File::getValidFilename($allowedClass->getGroup());
}
}
}
}
}
} elseif ($fieldDefinition instanceof ClassDefinition\Data\Fieldcollections) {
foreach ($fieldDefinition->getAllowedTypes() as $brickName) {
$brickDefinition = \Pimcore\Model\DataObject\Fieldcollection\Definition::getByKey($brickName);
foreach ($brickDefinition->getFieldDefinitions() as $brickFieldDefinition) {
if ($brickFieldDefinition instanceof Localizedfields) {
foreach ($brickFieldDefinition->getFieldDefinitions() as $localizedFieldDefinition) {
if ($localizedFieldDefinition instanceof AbstractRelations && method_exists($localizedFieldDefinition, 'getDocumentsAllowed') && $localizedFieldDefinition->getDocumentsAllowed()) {
$dependencies[$customViewId][] = 'document';
} elseif ($localizedFieldDefinition instanceof AbstractRelations && method_exists($localizedFieldDefinition, 'getObjectsAllowed') && $localizedFieldDefinition->getObjectsAllowed()) {
foreach ((array)$localizedFieldDefinition->getClasses() as $allowedClass) {
$allowedClass = ClassDefinition::getByName($allowedClass['classes']);
if ($allowedClass instanceof ClassDefinition) {
$dependencies[$customViewId][] = 'dd_'.File::getValidFilename($allowedClass->getGroup());
}
}
}
}
} elseif ($brickFieldDefinition instanceof AbstractRelations && method_exists($brickFieldDefinition, 'getDocumentsAllowed') && $brickFieldDefinition->getDocumentsAllowed()) {
$dependencies[$customViewId][] = 'document';
} elseif ($brickFieldDefinition instanceof AbstractRelations && !$fieldDefinition instanceof ClassDefinition\Data\ReverseManyToManyObjectRelation && !$fieldDefinition instanceof ClassDefinition\Data\ReverseObjectRelation && method_exists($brickFieldDefinition, 'getObjectsAllowed') && $brickFieldDefinition->getObjectsAllowed()) {
foreach ((array)$brickFieldDefinition->getClasses() as $allowedClass) {
$allowedClass = ClassDefinition::getByName($allowedClass['classes']);
if ($allowedClass instanceof ClassDefinition) {
$dependencies[$customViewId][] = 'dd_'.File::getValidFilename($allowedClass->getGroup());
}
}
}
}
}
}
}
}
$perspectives = [
'pim.perspective.default' => [
'elementTree' => [
[
'type' => 'documents',
'position' => 'left',
'expanded' => false,
'hidden' => false,
'sort' => 2
],
[
'type' => 'assets',
'position' => 'left',
'expanded' => false,
'hidden' => false,
'sort' => 1
],
[
'type' => 'objects',
'position' => 'left',
'expanded' => false,
'hidden' => false,
'sort' => 0
]
],
'iconCls' => 'pimcore_nav_icon_perspective',
'icon' => null,
'dashboards' => [
'predefined' => [
'welcome' => [
'positions' => [
[
[
'id' => 1,
'type' => 'pimcore.layout.portlets.modificationStatistic',
'config' => null
],
[
'id' => 2,
'type' => 'pimcore.layout.portlets.modifiedAssets',
'config' => null
]
],
[
[
'id' => 3,
'type' => 'pimcore.layout.portlets.modifiedObjects',
'config' => null
],
[
'id' => 4,
'type' => 'pimcore.layout.portlets.modifiedDocuments',
'config' => null
]
]
]
]
]
]
]
];
if (count($customViews) > 0) {
self::saveCustomView($customViews);
uksort($customViews, static function ($customViewId1, $customViewId2) use ($customViews, $dependencies) {
$order = count($dependencies[$customViewId2]) <=> count($dependencies[$customViewId1]);
if ($order !== 0) {
return $order;
}
$order = substr_count($customViews[$customViewId2]['classes'], ',') <=> substr_count($customViews[$customViewId1]['classes'], ',');
if ($order !== 0) {
return $order;
}
if ($customViews[$customViewId1]['name'] === 'data_objects' && $customViews[$customViewId2]['name'] !== 'data_objects') {
return 1;
}
if ($customViews[$customViewId1]['name'] !== 'data_objects' && $customViews[$customViewId2]['name'] === 'data_objects') {
return -1;
}
return strcasecmp($customViews[$customViewId1]['name'], $customViews[$customViewId2]['name']);
});
$elementTree = [[
'type' => 'assets',
'position' => 'right',
'expanded' => false,
'hidden' => false,
'sort' => 9000
]];
foreach ($customViews as $customViewId => $customView) {
if (in_array('document', $dependencies[$customViewId], true)) {
$elementTree[] = [
'type' => 'documents',
'position' => 'left',
'expanded' => false,
'hidden' => false,
'sort' => 9001
];
}
}
$index = 0;
foreach ($customViews as $customViewId => $customView) {
$position = 'left';
foreach ($elementTree as $perspectiveView) {
if ($perspectiveView['type'] === 'customview' && in_array($customViewId, $dependencies[$perspectiveView['id']] ?? [])) {
$position = 'right';
break;
}
}
$elementTree[] = [
'type' => 'customview',
'position' => $position,
'expanded' => false,
'hidden' => false,
'sort' => $customViewId === 'dd_' ? 8000 : ($index++),
'id' => $customViewId
];
}
$perspectives['pim.perspective.PIM'] = [
'iconCls' => 'pimcore_icon_object',
'elementTree' => $elementTree,
'dashboards' => [
'predefined' => [
'welcome' => [
'positions' => [
[
[
'id' => 1,
'type' => 'pimcore.layout.portlets.DataDirector_ErrorMonitor',
'config' => null
],
[
'id' => 2,
'type' => 'pimcore.layout.portlets.DataDirector_TaggedElements',
'config' => json_encode([
'title' => 'Objects with import errors',
'tags' => [TagLogger::getOrCreateTag('Data Director')->getId()]
])
]
],
[
[
'id' => 3,
'type' => 'pimcore.layout.portlets.DataDirector_QueueMonitor',
'config' => null
]
]
]
]
]
]
];
}
self::savePerspective($perspectives);
}
private static function savePerspective(array $data) {
$containerConfig = \Pimcore::getContainer()->getParameter('pimcore.config');
$config = $containerConfig['perspectives']['definitions'];
if (method_exists(\Pimcore\Config\LocationAwareConfigRepository::class, 'getStorageConfigurationCompatibilityLayer')) {
$storageConfig = \Pimcore\Config\LocationAwareConfigRepository::getStorageConfigurationCompatibilityLayer(
$containerConfig,
'perspectives',
'PIMCORE_CONFIG_STORAGE_DIR_PERSPECTIVES',
'PIMCORE_WRITE_TARGET_PERSPECTIVES'
);
} else {
$storageConfig = $containerConfig['config_location']['perspectives'] ?? null;
}
if (empty($storageConfig['write_target']['options']['directory'])) {
$storageConfig['write_target']['options']['directory'] = PIMCORE_CONFIGURATION_DIRECTORY.'/perspectives';
}
try {
$repository = new LocationAwareConfigRepository(
$config,
'pimcore_perspectives',
$storageConfig
);
} catch (\Throwable $e) {
$repository = new LocationAwareConfigRepository(
$config,
'pimcore_perspectives',
$storageConfig['write_target']['options']['directory'],
null
);
}
foreach ($data as $key => $value) {
list($configKey, $dataSource) = $repository->loadConfigByKey($key);
if ($repository->isWriteable($key, $dataSource) === true) {
unset($value['writeable']);
$repository->saveConfig($key, $value, function ($key, $data) {
return [
'pimcore' => [
'perspectives' => [
'definitions' => [
$key => $data,
],
],
],
];
});
}
}
}
private static function saveCustomView(array $data)
{
$containerConfig = \Pimcore::getContainer()->getParameter('pimcore.config');
$config = $containerConfig['custom_views']['definitions'];
if (method_exists(\Pimcore\Config\LocationAwareConfigRepository::class, 'getStorageConfigurationCompatibilityLayer')) {
$storageConfig = \Pimcore\Config\LocationAwareConfigRepository::getStorageConfigurationCompatibilityLayer(
$containerConfig,
'custom_views',
'PIMCORE_CONFIG_STORAGE_DIR_CUSTOM_VIEWS',
'PIMCORE_WRITE_TARGET_CUSTOM_VIEWS'
);
} else {
$storageConfig = $containerConfig['config_location']['custom_views'] ?? null;
}
if(empty($storageConfig['write_target']['options']['directory'])) {
$storageConfig['write_target']['options']['directory'] = PIMCORE_CONFIGURATION_DIRECTORY.'/custom-views';
}
try {
$repository = new LocationAwareConfigRepository(
$config,
'pimcore_custom_views',
$storageConfig
);
} catch(\Throwable $e) {
$repository = new LocationAwareConfigRepository(
$config,
'pimcore_custom_views',
$storageConfig['write_target']['options']['directory'],
null
);
}
foreach ($data as $key => $value) {
$key = (string)$key;
list($configKey, $dataSource) = $repository->loadConfigByKey($key);
if ($repository->isWriteable($key, $dataSource)) {
unset($value['writeable']);
$repository->saveConfig($key, $value, function ($key, $data) {
return [
'pimcore' => [
'custom_views' => [
'definitions' => [
$key => $data,
],
],
],
];
});
}
}
}
}