diff --git a/changelog.md b/CHANGELOG.md similarity index 74% rename from changelog.md rename to CHANGELOG.md index 6530c21..8c6b22d 100644 --- a/changelog.md +++ b/CHANGELOG.md @@ -4,4 +4,12 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [3.0.0] + +Pimcore X Compatibility + +## [2.0.0] + +[BC] Switch to Elements Process-Manager + ## [1.0.0] diff --git a/readme.md b/README.md similarity index 95% rename from readme.md rename to README.md index 4f99ebf..60638d8 100644 --- a/readme.md +++ b/README.md @@ -44,8 +44,8 @@ Enable the Bundle: ## Requirements -* Pimcore 6.3 -* ProcessManager +* Pimcore 10 (X) +* [ProcessManager](https://github.com/elements-at/ProcessManager) ## Usage Prepare a gridconfig that you want to export, open up ProcessManager, create a new Scheduled Export Executable. @@ -86,7 +86,7 @@ Pimcore Scheduled Export source code is completely free and released under the This module respects all Pimcore code quality rules and our own PHPCS and PHPMD rulesets. ## About Authors -![Divante-logo](http://divante.co/logo-HG.png "Divante") +![Divante-logo](https://www.divante.com/hubfs/raw_assets/public/Divante_March_2021/images/logo-new.svg "Divante") We are a Software House from Europe, existing from 2008 and employing about 150 people. Our core competencies are built around Magento, Pimcore and bespoke software projects (we love Symfony3, Node.js, Angular, React, Vue.js). diff --git a/composer.json b/composer.json index b5ce8b1..c3d67fa 100644 --- a/composer.json +++ b/composer.json @@ -33,21 +33,36 @@ "name": "Bruno Ramalho", "email": "bramalho@divante.pl", "role": "Developer" + }, + { + "name": "Michael Frank", + "email": "mf@the-spirit-of-it.com", + "role": "Developer" } ], + "minimum-stability": "dev", + "prefer-stable": true, + "config": { + "sort-packages": true, + "vendor-dir": "vendor", + "bin-dir": "vendor/bin/", + "optimize-autoloader": true, + "preferred-install": "dist", + "disable-tls": false, + "secure-http": true, + "allow-plugins": { + "ocramius/package-versions": true + } + }, "require": { - "pimcore/pimcore": ">=6.3", - "dpfaffenbauer/process-manager": "^2.0" + "ext-json": "*", + "php": ">=8.0", + "pimcore/pimcore": "^10.0 || ^11.0", + "elements/process-manager-bundle": "^4.0 || ^5.0" }, "require-dev": { - "phpunit/phpunit": "^7.5", - "symfony/phpunit-bridge": "^4.2", - "divante-ltd/pimcore-coding-standards": "^0.1", - "phpmd/phpmd": "^2.6", - "sebastian/phpcpd": "^4.1", - "block8/php-docblock-checker": "^1.10", - "phploc/phploc": "^4.0.0", - "codeception/codeception": "^3.0" + "codeception/codeception": "^5.0", + "phpmd/phpmd": "^2.6" }, "autoload": { "psr-4": { diff --git a/src/ScheduledExportBundle/Command/ScheduledExportCommand.php b/src/ScheduledExportBundle/Command/ScheduledExportCommand.php index 7770148..9734def 100644 --- a/src/ScheduledExportBundle/Command/ScheduledExportCommand.php +++ b/src/ScheduledExportBundle/Command/ScheduledExportCommand.php @@ -5,31 +5,49 @@ * @copyright Copyright (c) 2017 Divante Ltd. (https://divante.co) */ +declare(strict_types=1); + namespace Divante\ScheduledExportBundle\Command; use Divante\ScheduledExportBundle\Export\Export; +use Carbon\Carbon; +use Elements\Bundle\ProcessManagerBundle\ExecutionTrait; +use Elements\Bundle\ProcessManagerBundle\MetaDataFile; use Pimcore\Console\AbstractCommand; use Pimcore\Model\DataObject\AbstractObject; -use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand; +use Pimcore\Serializer\Serializer; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Input\InputOption; -use Symfony\Component\DependencyInjection\ContainerAwareTrait; +use Symfony\Component\DependencyInjection\ContainerInterface; /** * Class ScheduledExportCommand * * @package Divante\ScheduledExportBundle\Command */ -class ScheduledExportCommand extends ContainerAwareCommand +class ScheduledExportCommand extends AbstractCommand { + use ExecutionTrait; + + protected static $defaultName = 'scheduled-export:start'; + + private ContainerInterface $container; + + public function __construct(ContainerInterface $container) + { + parent::__construct(); + + $this->container = $container; + } + /** * {@inheritdoc} */ - protected function configure() + protected function configure(): void { $this - ->setName('scheduled-export:start') + ->setName(self::$defaultName) ->setDescription('Run Scheduled Export.') ->setHelp(<<%command.name% runs export of object based on predefined grid config. @@ -96,44 +114,72 @@ protected function configure() 'Divide file into parts with n lines' ) ->addOption( - 'preserve_process', - '', - InputOption::VALUE_OPTIONAL, - 'Don\'t delete process' - )->addOption( 'types', '', InputOption::VALUE_OPTIONAL, 'Set what types should be exported; e. g. "object,variant"; defaults to default list settings' - )->addOption( + ) + ->addOption( 'object-ids', '', InputOption::VALUE_OPTIONAL, 'Export only specified object ids' - )->addOption( + ) + ->addOption( 'add_utf_bom', '', InputOption::VALUE_OPTIONAL, 'Add BOM (Byte Order Mark)' ) + ->addOption( + 'monitoring-item-id', + null, + InputOption::VALUE_REQUIRED, + 'Contains the monitoring item if executed via the Pimcore backend' + ) ; } - /** - * {@inheritdoc} - */ - protected function execute(InputInterface $input, OutputInterface $output) + protected function execute(InputInterface $input, OutputInterface $output): int { + self::initProcessManager($input->getOption('monitoring-item-id'), ['autoCreate' => true]); + + $monitoringItem = self::getMonitoringItem(); + + $metDataFileObject = MetaDataFile::getById('sample-id'); + + $start = Carbon::now(); + if (!empty($metDataFileObject->getData()['lastRun'])) { + $lastRun = Carbon::createFromTimestamp($metDataFileObject->getData()['lastRun']); + } else { + $lastRun = Carbon::now(); + } + AbstractObject::setHideUnpublished(false); - $container = $this->getContainer(); + $pimcoreSerializer = new Serializer(); + $result = 0; + try { + $export = new Export( + $monitoringItem, + $this->container, + $pimcoreSerializer + ); - $export = new Export( - $container, - $input, - $output - ); + $export->execute(); + } catch (\Exception $e) { + $result = 1; + $monitoringItem->getLogger()->error('Error on ScheduledExportCommand: ' . $e->getMessage()); + $monitoringItem->setMessage($e->getMessage(), \Monolog\Logger::ERROR)->save(); + $monitoringItem->getLogger()->debug($e->getTraceAsString()); + $monitoringItem->setMessage($e->getTraceAsString(), \Monolog\Logger::ERROR)->save(); + $monitoringItem->setHasCriticalError(true); + } finally { + $monitoringItem->getLogger()->debug('Last Run: ' . $lastRun->format(Carbon::DEFAULT_TO_STRING_FORMAT)); + $metDataFileObject->setData(['lastRun' => $start->getTimestamp()])->save(); + $monitoringItem->setMessage('Job finished')->setCompleted(); + } - $export->export(); + return $result; } } diff --git a/src/ScheduledExportBundle/Controller/GridConfigController.php b/src/ScheduledExportBundle/Controller/GridConfigController.php index cb15711..18b21af 100644 --- a/src/ScheduledExportBundle/Controller/GridConfigController.php +++ b/src/ScheduledExportBundle/Controller/GridConfigController.php @@ -1,57 +1,66 @@ getAdminUser(); + $user = $this->getPimcoreUser(); + $gridConfigs = new GridConfig\Listing(); - $gridConfigs->setCondition("ownerId = ? or shareGlobally = ?", [$user->getId(), self::SHARE_GLOBALLY_TRUE]); + $gridConfigs->setCondition('ownerId = ? or shareGlobally = ?', [$user?->getId(), self::SHARE_GLOBALLY_TRUE]); $gridConfigs->load(); - $result = []; + $result = []; - /** @var GridConfig $gridConfig */ foreach ($gridConfigs->getGridConfigs() as $gridConfig) { $classDefinition = ClassDefinition::getById($gridConfig->getClassId()); - $user = \Pimcore\Model\User::getById($gridConfig->getOwnerId()); + $user = User::getById($gridConfig->getOwnerId()); if ($user) { $userName = $user->getName(); } else { - $userName = "unknown"; + $userName = 'unknown'; } $result[] = [ - "id" => $gridConfig->getId(), - "name" => "[" . $gridConfig->getId() . "] " . $classDefinition->getName() . ": " . - $gridConfig->getName() . " (" . $userName . ")" + 'id' => $gridConfig->getId(), + 'name' => '[' . $gridConfig->getId() . '] ' . + ($classDefinition !== null ? $classDefinition->getName() : 'no class found for ClassID: ' . $gridConfig->getClassId()) . + ': ' . $gridConfig->getName() . ' (' . $userName . ')' ]; } - return $this->adminJson(["success" => true, "result" => $result]); + return $this->jsonResponse(['success' => true, 'result' => $result]); } } diff --git a/src/ScheduledExportBundle/DependencyInjection/Configuration.php b/src/ScheduledExportBundle/DependencyInjection/Configuration.php index 91f0587..92a3a89 100644 --- a/src/ScheduledExportBundle/DependencyInjection/Configuration.php +++ b/src/ScheduledExportBundle/DependencyInjection/Configuration.php @@ -5,6 +5,8 @@ * @copyright Copyright (c) 2017 Divante Ltd. (https://divante.co) */ +declare(strict_types=1); + namespace Divante\ScheduledExportBundle\DependencyInjection; use Symfony\Component\Config\Definition\Builder\TreeBuilder; @@ -20,10 +22,10 @@ class Configuration implements ConfigurationInterface /** * {@inheritdoc} */ - public function getConfigTreeBuilder() + public function getConfigTreeBuilder(): TreeBuilder { - $treeBuilder = new TreeBuilder(); - $rootNode = $treeBuilder->root('divante_scheduled_export'); + $treeBuilder = new TreeBuilder('divante_scheduled_export'); + $rootNode = $treeBuilder->getRootNode(); // Here you should define the parameters that are allowed to // configure your bundle. See the documentation linked above for diff --git a/src/ScheduledExportBundle/DependencyInjection/DivanteScheduledExportExtension.php b/src/ScheduledExportBundle/DependencyInjection/DivanteScheduledExportExtension.php index 878cd53..13909f6 100644 --- a/src/ScheduledExportBundle/DependencyInjection/DivanteScheduledExportExtension.php +++ b/src/ScheduledExportBundle/DependencyInjection/DivanteScheduledExportExtension.php @@ -5,11 +5,14 @@ * @copyright Copyright (c) 2017 Divante Ltd. (https://divante.co) */ +declare(strict_types=1); + namespace Divante\ScheduledExportBundle\DependencyInjection; use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Extension\PrependExtensionInterface; use Symfony\Component\Config\FileLocator; -use Symfony\Component\HttpKernel\DependencyInjection\Extension; +use Symfony\Component\HttpKernel\DependencyInjection\ConfigurableExtension; use Symfony\Component\DependencyInjection\Loader; /** @@ -17,17 +20,21 @@ * * @link http://symfony.com/doc/current/cookbook/bundles/extension.html */ -class DivanteScheduledExportExtension extends Extension +class DivanteScheduledExportExtension extends ConfigurableExtension implements PrependExtensionInterface { /** * {@inheritdoc} */ - public function load(array $configs, ContainerBuilder $container) + public function loadInternal(array $mergedConfig, ContainerBuilder $container): void { $configuration = new Configuration(); - $config = $this->processConfiguration($configuration, $configs); + $config = $this->processConfiguration($configuration, $mergedConfig); $loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__ . '/../Resources/config')); - $loader->load('services.yml'); + $loader->load('services.yaml'); + } + + public function prepend(ContainerBuilder $container): void + { } } diff --git a/src/ScheduledExportBundle/DivanteScheduledExportBundle.php b/src/ScheduledExportBundle/DivanteScheduledExportBundle.php index 70dc781..b0fd44a 100644 --- a/src/ScheduledExportBundle/DivanteScheduledExportBundle.php +++ b/src/ScheduledExportBundle/DivanteScheduledExportBundle.php @@ -4,22 +4,29 @@ * @author Anna Zavodian * @copyright Copyright (c) 2017 Divante Ltd. (https://divante.co) */ +declare(strict_types=1); namespace Divante\ScheduledExportBundle; -use Divante\ScheduledExportBundle\Migrations\Installer; +use Elements\Bundle\ProcessManagerBundle\ElementsProcessManagerBundle; use Pimcore\Extension\Bundle\AbstractPimcoreBundle; +use Pimcore\Extension\Bundle\Installer\InstallerInterface; use Pimcore\Extension\Bundle\Traits\PackageVersionTrait; +use Pimcore\HttpKernel\Bundle\DependentBundleInterface; +use Pimcore\HttpKernel\BundleCollection\BundleCollection; /** * Class ScheduledExportBundle * @package Divante\ScheduledExportBundle */ -class DivanteScheduledExportBundle extends AbstractPimcoreBundle +class DivanteScheduledExportBundle extends AbstractPimcoreBundle implements DependentBundleInterface { use PackageVersionTrait; - public function getInstaller(): Installer + private const BUNDLE_NAME = 'DivanteScheduledExportBundle'; + public const TABLE_NAME = 'bundle_scheduledexport_registry'; + + public function getInstaller(): ?InstallerInterface { return $this->container->get(Installer::class); } @@ -27,7 +34,7 @@ public function getInstaller(): Installer /** * {@inheritdoc} */ - protected function getComposerPackageName() + protected function getComposerPackageName(): string { return 'divante-ltd/pimcore-scheduled-export'; } @@ -35,7 +42,7 @@ protected function getComposerPackageName() /** * {@inheritdoc} */ - public function getJsPaths() + public function getJsPaths(): array { return [ '/bundles/divantescheduledexport/js/startup.js', @@ -46,10 +53,20 @@ public function getJsPaths() /** * {@inheritdoc} */ - public function getCssPaths() + public function getCssPaths(): array { return [ '/bundles/divantescheduledexport/css/importdefinition.css', ]; } + + public function getNiceName(): string + { + return self::BUNDLE_NAME; + } + + public static function registerDependentBundles(BundleCollection $collection): void + { + $collection->addBundle(ElementsProcessManagerBundle::class, 10); + } } diff --git a/src/ScheduledExportBundle/Enums/Permissions.php b/src/ScheduledExportBundle/Enums/Permissions.php new file mode 100644 index 0000000..2ac474f --- /dev/null +++ b/src/ScheduledExportBundle/Enums/Permissions.php @@ -0,0 +1,12 @@ +filename = $filename; } - /** - * @return string - */ public function getFilename(): string { return $this->filename; diff --git a/src/ScheduledExportBundle/Exceptions/ScheduledExportException.php b/src/ScheduledExportBundle/Exceptions/ScheduledExportException.php new file mode 100644 index 0000000..abc4703 --- /dev/null +++ b/src/ScheduledExportBundle/Exceptions/ScheduledExportException.php @@ -0,0 +1,11 @@ +setMonitoringItem($monitoringItem); + $this->setLogger($monitoringItem->getLogger()); + $this->setCallbackSettings($monitoringItem->getCallbackSettings()); $this->setContainer($container); - $this->setTimestamp((string) $input->getOption("timestamp")); - $this->setGridConfig($input->getOption("gridconfig")); - $this->setObjectsFolder($input->getOption("folder")); - $this->setOnlyChanges((string) $input->getOption("only-changes")); - $this->setAssetFolder($input->getOption("asset")); - $this->setCondition((string) $input->getOption("condition")); - $this->setTimestampFormat((string) $input->getOption("format")); - $this->setFilename(\Pimcore\File::getValidFilename((string) $input->getOption("filename"))); - $this->setDelimiter((string) $input->getOption("delimiter")); + + + $this->assignCallbackSettings(); + $this->controller = new DataObjectHelperController(); $this->controller->setContainer($this->container); - $this->input = $input; - $this->output = $output; - $this->process = new Process( - sprintf("Scheduled Export (%s) - %s", $this->objectsFolder, $this->gridConfig->getName()), - "Scheduled Export", - "Starting" - ); - $this->process->setStarted(time()); - $this->process->setStoppable(true); - $this->processRepository = $this->container->get('process_manager.repository.process'); - $this->types = str_replace(' ', '', (string) $input->getOption("types")); + $this->controller->setPimcoreSerializer($serializer); + + $this->logger->info(sprintf("Scheduled Export (%s) - %s", $this->objectsFolder, $this->gridConfig->getName())); + } + + public function setMonitoringItem(MonitoringItem $monitoringItem): Export + { + $this->monitoringItem = $monitoringItem; + + return $this; + } + + public function getLogger(): ApplicationLogger + { + return $this->logger; + } + + public function setLogger(ApplicationLogger $logger): Export + { + $this->logger = $logger; + + return $this; + } + + public function getCallbackSettings(): array + { + return $this->callbackSettings; + } + + public function setCallbackSettings(array $callbackSettings): Export + { + $this->callbackSettings = $callbackSettings; + + return $this; + } + + /** + * @throws Exception + */ + public function execute(): void + { + if (!empty($this->getCallbackSettings()['OBJECT_IDS'])) { + $objectIds = explode(',', $this->getCallbackSettings()['OBJECT_IDS']); + $objectIds = array_map('intval', $objectIds); + $objectIds = array_chunk($objectIds, self::INTERNAL_BATCH_SIZE); + } else { + $this->prepareListing(); + $objectIds = $this->getObjectIds(); + } + + $filenames = $this->exportToFilenames($objectIds); + + $status = $this->monitoringItem::STATUS_FINISHED; + $statusMsg = 'Finished Export: ' . implode(' / ', $filenames); + + if (!empty($filenames)) { + try { + $this->saveFileInAssets($filenames); + $this->monitoringItem->setWorkloadCompleted(); + $this->updateExportRegistry(); + } catch (Exception $exception) { + $status = $this->monitoringItem::STATUS_FAILED; + $statusMsg = sprintf("Error - %s", $exception->getMessage()); + } + } + + $this->monitoringItem->setStatus($status)->setMessage($statusMsg); + } + + /** + * @throws Exception + */ + private function assignCallbackSettings(): void + { + $this->logger->info(var_export($this->getCallbackSettings(), true)); + $this->setTimestamp(); + $this->setGridConfig(); + $this->setObjectsFolder(); + $this->setOnlyChanges(); + $this->setAssetFolder(); + $this->setCondition(); + $this->setTimestampFormat(); + $this->setFilename(); + $this->setDelimiter(); + $this->types = str_replace(' ', '', (string) $this->getCallbackSettings()['TYPES']); } /** @@ -135,39 +212,30 @@ public function getExportRegistry() : ScheduledExportRegistry return $exportRegistry; } - /** - * @param bool $timestamp - * @return void - */ - public function setTimestamp(bool $timestamp): void + public function setTimestamp(): void { - if ($timestamp == "1") { - $this->timestamp = true; - } else { - $this->timestamp = false; - } + $this->timestamp = !empty($this->getCallbackSettings()['ADD_TIMESTAMP']); } /** - * @param string $gridId - * @return void * @throws Exception */ - public function setGridConfig(string $gridId): void + public function setGridConfig(): void { - $this->gridConfig = GridConfig::getById($gridId); + $callbackSettings = $this->getCallbackSettings(); + $this->gridConfig = !empty($callbackSettings['GRID_CONFIG']) ? GridConfig::getById($callbackSettings['GRID_CONFIG']) : null; } /** - * @param string $onlyChanges - * @return void + * @throws Exception */ - public function setOnlyChanges(string $onlyChanges): void + public function setOnlyChanges(): void { $exportRegistry = $this->getExportRegistry(); - if ($onlyChanges === "1") { + + if (!empty($this->getCallbackSettings()['ONLY_CHANGES'])) { $this->onlyChanges = true; - $this->changesFromTimestamp = strtotime($exportRegistry->getData()); + $this->changesFromTimestamp = !empty($exportRegistry->getData()) ? strtotime($exportRegistry->getData()) : time(); } else { $this->onlyChanges = false; $this->changesFromTimestamp = 0; @@ -176,144 +244,68 @@ public function setOnlyChanges(string $onlyChanges): void $this->importStartTimestamp = time(); } - /** - * @param string $objectsFolder - * @return void - */ - public function setObjectsFolder($objectsFolder): void + public function setObjectsFolder(): void { - $this->objectsFolder = $objectsFolder; + $this->objectsFolder = $this->getCallbackSettings()['OBJECTS_FOLDER']; } - /** - * @param string $assetFolder - * @return void - */ - public function setAssetFolder($assetFolder): void + public function setAssetFolder(): void { - $this->assetFolder = $assetFolder; + $this->assetFolder = $this->getCallbackSettings()['ASSET_FOLDER']; } - /** - * @param string|null $delimiter - * @return void - */ - public function setDelimiter($delimiter): void + public function setDelimiter(): void { - if (!$delimiter) { - $delimiter = ";"; + $delimiter = $this->getCallbackSettings()['DELIMITER']; + + if (empty($delimiter)) { + $delimiter = ';'; } + $this->delimiter = $delimiter; } - /** - * @param string|null $condition - * @return void - */ - public function setCondition($condition): void + public function setCondition(): void { - $this->condition = $condition; + $this->condition = (string) $this->getCallbackSettings()['CONDITION']; } - /** - * @param string $fileName - * @return void - */ - public function setFileName(string $fileName): void + public function setFileName(): void { + $fileName = File::getValidFilename((string) $this->getCallbackSettings()['ASSET_FILENAME']); + if ($this->timestamp) { - if ($this->timestampFormat == "") { - $format = "-%s"; - } else { - $format = $this->timestampFormat; - } + $format = empty($this->timestampFormat) ? "-%s" : $this->timestampFormat; - $fileName = $fileName . strftime($format); + $fileName .= strftime($format); } $this->fileName = $fileName; } - /** - * @param ContainerInterface $container - * @return void - */ - public function setContainer($container): void + public function setContainer(ContainerInterface $container): void { $this->container = $container; } - /** - * @return string - */ public function getTimestampFormat(): string { return $this->timestampFormat; } - /** - * @param mixed $timestampFormat - */ - public function setTimestampFormat(string $timestampFormat): void - { - $this->timestampFormat = $timestampFormat; - } - - /** - * @return void - */ - public function export(): void + public function setTimestampFormat(): void { - $this->process->setStatus(ProcessManagerBundle::STATUS_RUNNING); - $this->process->save(); - - if ($this->input->getOption('object-ids')) { - $objectIds = explode(',', $this->input->getOption('object-ids')); - $objectIds = array_map('intval', $objectIds); - $objectIds = array_chunk($objectIds, self::INTERNAL_BATCH_SIZE); - } else { - $this->prepareListing(); - $objectIds = $this->getObjectIds(); - } - $filenames = []; - - $this->process->setMessage("Starting"); - $this->process->save(); - - $filenames = $this->exportToFilenames($objectIds, $filenames); - - if (!empty($filenames)) { - $this->process->setMessage("Saving results"); - $this->process->save(); - try { - $this->saveFileInAssets($filenames); - $this->process->setStatus(ProcessManagerBundle::STATUS_COMPLETED); - $this->process->setMessage(sprintf("Done (%d objects exported)", $this->process->getProgress())); - $this->updateExportRegistry(); - } catch (Exception $exception) { - $this->process->setStatus(ProcessManagerBundle::STATUS_FAILED); - $this->process->setMessage(sprintf("Error - %s", $exception->getMessage())); - } - } - - if (!$this->input->getOption('preserve_process')) { - $this->process->delete(); - } else { - $this->process->save(); - } + $this->timestampFormat = (string) $this->getCallbackSettings()['TIMESTAMP']; } - /** - * @return Request - */ protected function prepareRequest(array $objectIds, string $filename): Request { $request = Request::createFromGlobals(); $request->request->set('fileHandle', $filename); $request->request->set('ids', $objectIds); - $request->request->set('settings', str_replace("%delimiter%", $this->delimiter, self::SETTINGS)); - $request->request->set('classId', $this->gridConfig->classId); + $request->request->set('settings', str_replace('%delimiter%', $this->delimiter, self::SETTINGS)); + $request->request->set('classId', $this->gridConfig->getClassId()); $request->request->set('initial', '1'); $request->request->set('fields', $this->prepareFields()); $request->request->set('language', 'en_GB'); @@ -321,45 +313,38 @@ protected function prepareRequest(array $objectIds, string $filename): Request return $request; } - /** - * - */ protected function prepareListing(): void { $objectsFolder = Folder::getByPath($this->objectsFolder); $className = "\\Pimcore\\Model\\DataObject\\" - . ucfirst(ClassDefinition::getById($this->gridConfig->classId)->getName()) + . ucfirst(ClassDefinition::getById($this->gridConfig->getClassId())->getName()) . "\\Listing"; $this->listing = new $className(); $this->listing->setCondition($this->condition); $this->listing->addConditionParam( - "o_path LIKE ?", - rtrim($objectsFolder->getFullPath(), "/") . '/%', - "AND" + 'o_path LIKE ?', + rtrim($objectsFolder->getFullPath(), '/') . '/%' ); - $this->listing->addConditionParam("o_classId = ?", $this->gridConfig->classId, "AND"); + $this->listing->addConditionParam('o_classId = ?', $this->gridConfig->getClassId()); if ($this->changesFromTimestamp) { - $this->listing->addConditionParam("o_modificationDate >= ?", $this->changesFromTimestamp, "AND"); + $this->listing->addConditionParam('o_modificationDate >= ?', $this->changesFromTimestamp); } $this->listing->setUnpublished(true); if ($this->types) { $this->listing->setObjectTypes(explode(',', $this->types)); } - $this->process->setMessage("Counting objects to export"); - $this->process->save(); - $this->process->setTotal($this->listing->getCount()); - $this->process->save(); + + $this->totalToExport = $this->listing->getCount(); + + $this->logger->info('Objects to export: ' . $this->totalToExport); } - /** - * @return array - */ protected function getObjectIds(): array { $objectIds = []; - for ($i = 0; $i <= $this->process->getTotal(); $i = $i + self::INTERNAL_BATCH_SIZE) { + for ($i = 0; $i <= $this->totalToExport; $i += self::INTERNAL_BATCH_SIZE) { $this->listing->setOffset($i); $this->listing->setLimit(self::INTERNAL_BATCH_SIZE); $objectIds[] = $this->listing->loadIdList(); @@ -368,13 +353,10 @@ protected function getObjectIds(): array return $objectIds; } - /** - * @return array - */ protected function prepareFields(): array { /** @var GridConfig $fieldsRaw */ - $fieldsRaw = json_decode($this->gridConfig->getConfig())->columns; + $fieldsRaw = json_decode($this->gridConfig->getConfig(), false)->columns; $this->setHelperColumnsInSession($fieldsRaw); @@ -385,10 +367,6 @@ protected function prepareFields(): array return $fields; } - /** - * @param array $fieldsRaw - * @return void - */ protected function setHelperColumnsInSession($fieldsRaw): void { $helperColumns = []; @@ -398,25 +376,36 @@ protected function setHelperColumnsInSession($fieldsRaw): void } } - Tool\Session::useSession(function (AttributeBagInterface $session) use ($helperColumns) { - $existingColumns = $session->get('helpercolumns', []); - $helperColumns = array_merge($helperColumns, $existingColumns); - $session->set('helpercolumns', $helperColumns); - }, 'pimcore_gridconfig'); + Tool\Session::useSession( + function (AttributeBagInterface $session) use ($helperColumns) { + $existingColumns = $session->get('helpercolumns', []); + $helperColumns = array_merge($helperColumns, $existingColumns); + $session->set('helpercolumns', $helperColumns); + }, + 'pimcore_gridconfig' + ); } /** - * @return void + * @throws Exception */ protected function saveFileInAssets(array $filenames): void { $assetFolder = $this->assetFolder; - $content = ""; + $content = ''; $separator = "\r\n"; $firstFile = true; + + $storage = Tool\Storage::get('temp'); + foreach ($filenames as $filename) { - $tmpFile = PIMCORE_SYSTEM_TEMP_DIRECTORY . "/" . $filename . ".csv"; - $fileContent = file_get_contents($tmpFile); + $tmpFile = PIMCORE_SYSTEM_TEMP_DIRECTORY . '/' . $filename . '.csv'; + $fileContent = ''; + try { + $fileContent = $storage->read($filename . '.csv'); + } catch (\Exception $e) { + } + if (!$firstFile) { $content .= $separator . preg_replace('/^.+\n/', '', $fileContent); } else { @@ -426,23 +415,24 @@ protected function saveFileInAssets(array $filenames): void $firstFile = false; } - if ($this->input->getOption('divide_file')) { + if (!empty($this->getCallbackSettings()['DIVIDE_FILE'])) { + $divide = (int) $this->getCallbackSettings()['DIVIDE_FILE']; $rows = explode($separator, $content); $header = array_shift($rows); $counter = 0; $fileCounter = 0; - $subContent = ""; + $subContent = ''; foreach ($rows as $row) { $subContent .= $row . "\r\n"; - if (++$counter % $this->input->getOption('divide_file') == 0) { + if (++$counter % $divide === 0) { $this->saveAsset($assetFolder, $fileCounter, $header, $subContent); - $subContent = ""; + $subContent = ''; $fileCounter++; } } - if (strlen($subContent)) { + if ($subContent !== '') { $this->saveAsset($assetFolder, $fileCounter, $header, $subContent); } } else { @@ -451,18 +441,17 @@ protected function saveFileInAssets(array $filenames): void } /** - * @param string $assetFolder - * @return Asset + * @throws Exception */ - protected function prepareAssetFile($assetFolder, ?int $index = null): Asset + protected function prepareAssetFile(string $assetFolder, ?int $index = null): Asset { if ($index) { - $filename = $this->fileName . "-" . $index; + $filename = $this->fileName . '-' . $index; } else { $filename = $this->fileName; } - $filename .= ".csv"; + $filename .= '.csv'; if (!$this->timestamp) { $assetFile = Asset::getByPath(Asset\Service::createFolderByPath($assetFolder) . '/' . $filename); @@ -481,7 +470,7 @@ protected function prepareAssetFile($assetFolder, ?int $index = null): Asset } /** - * + * @throws Exception */ private function updateExportRegistry(): void { @@ -493,47 +482,57 @@ private function updateExportRegistry(): void } /** - * @param array $objectIds - * @param array $filenames - * @return array * @throws Exception */ protected function exportToFilenames(array $objectIds): array { $filenames = []; $localeService = new LocaleService(); + /** @var EventDispatcher $dispatcher */ $dispatcher = $this->container->get('event_dispatcher'); + + if ($dispatcher === null) { + $this->logger->error('Event Dispatch is null - could not proceed!'); + throw new ScheduledExportException('Event Dispatch is null - could not proceed!'); + } + + $this->monitoringItem->setTotalSteps(count($objectIds))->save(); + + $count = 0; + $storage = Tool\Storage::get('temp'); + foreach ($objectIds as $objectIdBatch) { - $filename = uniqid(); + $count++; + $filename = uniqid('', true); $request = $this->prepareRequest($objectIdBatch, $filename); if (!count($request->request->get('ids'))) { break; } - $this->controller->doExportAction($request, $localeService); + $storage->write($filename . '.csv', ''); + + $this->controller->doExportAction($request, $localeService, $dispatcher); + $event = new BatchExportedEvent($objectIdBatch ?? []); - $dispatcher->dispatch(BatchExportedEvent::NAME, $event); + $dispatcher->dispatch($event, BatchExportedEvent::NAME); $filenames[] = $filename; - $this->process = $this->processRepository->find($this->process->getId()); - $this->process->progress(count($objectIdBatch)); - $this->process->setMessage( - sprintf("Running (%d/%d)", $this->process->getProgress(), $this->process->getTotal()) - ); - $this->process->save(); - if ($this->process->getStatus() == ProcessManagerBundle::STATUS_STOPPING) { + + $batchCount = count($objectIdBatch); + + $this->monitoringItem->setCurrentStep($count)->setMessage('Processing ' . $batchCount . ' export objects')->save(); + + if ($this->monitoringItem->getStatus() === MonitoringItem::STATUS_FAILED) { foreach ($filenames as $filename) { - $tmpFile = PIMCORE_SYSTEM_TEMP_DIRECTORY . "/" . $filename . ".csv"; + $tmpFile = PIMCORE_SYSTEM_TEMP_DIRECTORY . '/' . $filename . '.csv'; unlink($tmpFile); } - $this->process->setStatus(ProcessManagerBundle::STATUS_STOPPED); - $this->process->setMessage("Stopped by user"); - $this->process->save(); break; } - \Pimcore::collectGarbage(); + Pimcore::collectGarbage(); } + return $filenames; } @@ -547,25 +546,39 @@ protected function exportToFilenames(array $objectIds): array protected function saveAsset($assetFolder, ?int $fileCounter, ?string $header, string $content): void { $assetFile = $this->prepareAssetFile($assetFolder, $fileCounter); + $data = ''; - if ($this->input->getOption('add_utf_bom')) { + if (!empty($this->getCallbackSettings()['ADD_UTF_BOM'])) { $data = chr(0xEF) . chr(0xBB) . chr(0xBF); } if ($header) { - $data = $data . $header . "\r\n" . $content; + $data .= $header . "\r\n" . $content; } else { - $data = $data . $content; + $data .= $content; } + $assetFile->setData($data); + $assetFilename = Asset\Service::getUniqueKey($assetFile); $assetFile->setFilename($assetFilename); - $assetFile->save(['versionNote' => sprintf( - "Scheduled Export on folder (%s), gridconfig - %s", - $this->objectsFolder, - $this->gridConfig->getName() - )]); + + try { + $assetFile->save([ + 'versionNote' => sprintf( + "Scheduled Export on folder (%s), gridconfig - %s", + $this->objectsFolder, + $this->gridConfig->getName() + ) + ]); + } catch (\Exception $e) { + \Pimcore\Log\ApplicationLogger::getInstance()->warning('Error on saving Asset "' . $assetFilename . '" : ' . $e->getMessage()); + } + $event = new ScheduledExportSavedEvent($assetFilename); - $this->container->get('event_dispatcher')->dispatch(ScheduledExportSavedEvent::NAME, $event); + + /** @var TraceableEventDispatcher $eventDispatcher */ + $eventDispatcher = $this->container->get('event_dispatcher'); + $eventDispatcher?->dispatch($event, ScheduledExportSavedEvent::NAME); } } diff --git a/src/ScheduledExportBundle/Form/Type/ProcessManager/ScheduledExportType.php b/src/ScheduledExportBundle/Form/Type/ProcessManager/ScheduledExportType.php deleted file mode 100644 index d568cd9..0000000 --- a/src/ScheduledExportBundle/Form/Type/ProcessManager/ScheduledExportType.php +++ /dev/null @@ -1,51 +0,0 @@ - - * @copyright Copyright (c) 2017 Divante Ltd. (https://divante.co) - */ - -namespace Divante\ScheduledExportBundle\Form\Type\ProcessManager; - -use Symfony\Component\Form\AbstractType; -use Symfony\Component\Form\Extension\Core\Type\CheckboxType; -use Symfony\Component\Form\Extension\Core\Type\TextType; -use Symfony\Component\Form\FormBuilderInterface; - -/** - * Class ScheduledExportType - * - * @package Divante\ScheduledExportBundle\Form\Type\ProcessManager - */ -class ScheduledExportType extends AbstractType -{ - /** - * {@inheritdoc} - */ - public function buildForm(FormBuilderInterface $builder, array $options) - { - $builder - ->add('grid_config', TextType::class) - ->add('objects_folder', TextType::class) - ->add('asset_folder', TextType::class) - ->add('asset_filename', TextType::class) - ->add('condition', TextType::class) - ->add('delimiter', TextType::class) - ->add('add_timestamp', CheckboxType::class) - ->add('timestamp', TextType::class) - ->add('only_changes', CheckboxType::class) - ->add('divide_file', TextType::class) - ->add('preserve_process', CheckboxType::class) - ->add('types', TextType::class) - ->add('add_utf_bom', CheckboxType::class) - ; - } - - /** - * {@inheritdoc} - */ - public function getBlockPrefix() - { - return 'process_manager_process_scheduled_export'; - } -} diff --git a/src/ScheduledExportBundle/Installer.php b/src/ScheduledExportBundle/Installer.php new file mode 100644 index 0000000..18abbde --- /dev/null +++ b/src/ScheduledExportBundle/Installer.php @@ -0,0 +1,95 @@ +createPermissions(); + $this->createTables(); + parent::install(); + } + + protected function createPermissions(): void + { + foreach ($this->permissions as $permissionKey) { + Definition::create($permissionKey); + } + } + + public function needsReloadAfterInstall(): bool + { + return true; + } + + /** + * @return Connection|ConnectionInterface + */ + protected function getDb(): ConnectionInterface|Connection + { + return \Pimcore\Db::get(); + } + + protected function createTables(): void + { + $db = $this->getDb(); + $db->query( + 'CREATE TABLE IF NOT EXISTS `' . DivanteScheduledExportBundle::TABLE_NAME . '` ( + `id` BIGINT(20) NOT NULL AUTO_INCREMENT, + `gridConfigId` VARCHAR(255) NOT NULL, + `data` VARCHAR(255), + PRIMARY KEY (`id`) + ) ENGINE=InnoDB DEFAULT CHARSET=utf8;' + ); + + $list = new Listing(); + $settings = $list->getSettings(); + + foreach ($settings as $item) { + $name = $item->getName(); + if (strpos($name, 'Last_Scheduled_Export_Date')) { + $name = explode('_', $name); + + $adaptedGridConfigId = sprintf('%s_%s', $name[0], $name[1]); + + $exportRegistry = new ScheduledExportRegistry($adaptedGridConfigId, (string) $item->getData()); + $exportRegistry->save(); + } + } + } + + public function uninstall(): void + { + $tables = [ + DivanteScheduledExportBundle::TABLE_NAME, + ]; + foreach ($tables as $table) { + $this->getDb()->query('DROP TABLE IF EXISTS ' . $table); + } + + foreach ($this->permissions as $permissionKey) { + $this->getDb()->query( + 'DELETE FROM users_permission_definitions WHERE ' . $this->getDb()->quoteIdentifier('key').' = :permission', + ['permission' => $permissionKey] + ); + } + + parent::uninstall(); + } +} diff --git a/src/ScheduledExportBundle/Migrations/.gitkeep b/src/ScheduledExportBundle/Migrations/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/src/ScheduledExportBundle/Migrations/Installer.php b/src/ScheduledExportBundle/Migrations/Installer.php deleted file mode 100644 index bef888c..0000000 --- a/src/ScheduledExportBundle/Migrations/Installer.php +++ /dev/null @@ -1,97 +0,0 @@ -installDatabase(); - - return $this->isInstalled(); - } - - public function migrateUninstall(Schema $schema, Version $version): bool - { - $this->uninstallDatabase(); - - return !$this->isInstalled(); - } - - private function installDatabase(): void - { - \Pimcore\Db::get()->query( - 'CREATE TABLE IF NOT EXISTS `' . Dao::TABLE_NAME . '` ( - `id` bigint(20) NOT NULL AUTO_INCREMENT, - `gridConfigId` varchar(255) NOT NULL, - `data` varchar(255), - PRIMARY KEY (`id`) - ) ENGINE=InnoDB DEFAULT CHARSET=utf8;' - ); - - $list = new Listing(); - $settings = $list->getSettings(); - - foreach ($settings as $item) { - $name = $item->getName(); - if (strpos($name, 'Last_Scheduled_Export_Date')) { - $name = explode('_', $name); - - $adaptedGridConfigId = sprintf('%s_%s', $name[0], $name[1]); - - $exportRegistry = new ScheduledExportRegistry($adaptedGridConfigId, (string) $item->getData()); - $exportRegistry->save(); - } - } - } - - private function persistDatabase(): void - { - } - - private function uninstallDatabase(): void - { - \Pimcore\Db::get()->query( - 'DROP TABLE IF EXISTS `' . Dao::TABLE_NAME . '`;' - ); - - //TODO Remove permission ??? - } - - /** - * @return bool - */ - public function isInstalled() - { - $result = null; - - try { - if (Config::getSystemConfig()) { - $result = \Pimcore\Db::get()->fetchAll("SHOW TABLES LIKE '" . Dao::TABLE_NAME . "';"); - } - } catch (\Exception $e) { - return false; - } - - return !empty($result); - } -} diff --git a/src/ScheduledExportBundle/Model/ScheduledExportRegistry.php b/src/ScheduledExportBundle/Model/ScheduledExportRegistry.php index 540e89f..c52c7ea 100644 --- a/src/ScheduledExportBundle/Model/ScheduledExportRegistry.php +++ b/src/ScheduledExportBundle/Model/ScheduledExportRegistry.php @@ -7,10 +7,7 @@ use Divante\ScheduledExportBundle\Model\ScheduledExportRegistry\Dao; use Exception; use Pimcore\Cache; -use Pimcore\Model\Element\ElementInterface; -use Pimcore\Model\Element\Service; use Pimcore\Model\AbstractModel; -use Pimcore\Cache\Runtime; /** * @method Dao getDao() @@ -20,25 +17,13 @@ class ScheduledExportRegistry extends AbstractModel { protected const WS_NAME = 'Scheduled_Export_Registry'; - /** - * @var int - */ - protected $id; + protected ?int $id = null; - /** - * @var string - */ - protected $gridConfigId; + protected string $gridConfigId; - /** - * @var mixed - */ - protected $data; + protected mixed $data = null; - /** - * @var array - */ - protected static $nameIdMappingCache = []; + protected static array $nameIdMappingCache = []; public function __construct(string $gridConfigId = null, $data = null) @@ -92,7 +77,7 @@ protected static function getCacheKey(string $gridConfigId): string return $gridConfigId . '~~~' . self::WS_NAME; } - public function clearDependentCache() + public function clearDependentCache(): void { Cache::clearTag('scheduled_export_exports'); } @@ -103,7 +88,7 @@ public static function getById(int $id): ?self $cacheKey = 'scheduled_export_exports_' . $id; try { - $export = Runtime::get($cacheKey); + $export = Cache\RuntimeCache::get($cacheKey); if (!$export) { throw new Exception(sprintf('Scheduled export with ID `%s` does not exist.', $id)); } @@ -111,7 +96,7 @@ public static function getById(int $id): ?self try { $export = new self(); $export->getDao()->getById($id); - Runtime::set($cacheKey, $export); + Cache\RuntimeCache::set($cacheKey, $export); } catch (Exception $e) { return null; } diff --git a/src/ScheduledExportBundle/Model/ScheduledExportRegistry/Dao.php b/src/ScheduledExportBundle/Model/ScheduledExportRegistry/Dao.php index f695ffd..4ed4c93 100644 --- a/src/ScheduledExportBundle/Model/ScheduledExportRegistry/Dao.php +++ b/src/ScheduledExportBundle/Model/ScheduledExportRegistry/Dao.php @@ -4,7 +4,9 @@ namespace Divante\ScheduledExportBundle\Model\ScheduledExportRegistry; +use Divante\ScheduledExportBundle\DivanteScheduledExportBundle; use Exception; +use Pimcore\Db\Helper; use Pimcore\Model; /** @@ -12,15 +14,13 @@ */ class Dao extends Model\Dao\AbstractDao { - const TABLE_NAME = 'bundle_scheduledexport_registry'; - /** * @throws Exception */ - public function getById(int $id) + public function getById(int $id): void { - $data = $this->db->fetchRow( - 'SELECT * FROM ' . $this->db->quoteIdentifier(self::TABLE_NAME) . ' WHERE id = ?', + $data = $this->db->fetchAssociative( + 'SELECT * FROM ' . $this->db->quoteIdentifier(DivanteScheduledExportBundle::TABLE_NAME) . ' WHERE id = ?', [ $id, ] @@ -36,10 +36,10 @@ public function getById(int $id) /** * @throws Exception */ - public function getByGridConfigId(string $gridConfigId) + public function getByGridConfigId(string $gridConfigId): void { - $data = $this->db->fetchRow( - 'SELECT * FROM ' . self::TABLE_NAME . ' WHERE gridConfigId = ?', + $data = $this->db->fetchAssociative( + 'SELECT * FROM ' . DivanteScheduledExportBundle::TABLE_NAME . ' WHERE gridConfigId = ?', [ $gridConfigId, ] @@ -55,7 +55,7 @@ public function getByGridConfigId(string $gridConfigId) $this->assignVariablesToModel($data); } - public function save() + public function save(): void { $dataRaw = $this->model->getObjectVars(); $data = []; @@ -64,7 +64,7 @@ public function save() $data[$key] = $value; } - $this->db->insertOrUpdate(self::TABLE_NAME, $data); + Helper::insertOrUpdate($this->db, DivanteScheduledExportBundle::TABLE_NAME, $data); $lastInsertId = (int) $this->db->lastInsertId(); if (empty($this->model->getId()) && $lastInsertId) { @@ -74,9 +74,9 @@ public function save() $this->model->clearDependentCache(); } - public function delete() + public function delete(): void { - $this->db->delete(self::TABLE_NAME, ['id' => $this->model->getId()]); + $this->db->delete(DivanteScheduledExportBundle::TABLE_NAME, ['id' => $this->model->getId()]); $this->model->clearDependentCache(); } diff --git a/src/ScheduledExportBundle/Model/ScheduledExportRegistry/Listing.php b/src/ScheduledExportBundle/Model/ScheduledExportRegistry/Listing.php index 1c900f1..ca61ef2 100644 --- a/src/ScheduledExportBundle/Model/ScheduledExportRegistry/Listing.php +++ b/src/ScheduledExportBundle/Model/ScheduledExportRegistry/Listing.php @@ -12,12 +12,11 @@ * @method ScheduledExportRegistry[] load() * @method int getTotalCount() */ -class Listing extends Model\Listing\JsonListing +class Listing { - /** - * @var array|null - */ - protected $exports; + use Model\Listing\Traits\FilterListingTrait; + use Model\Listing\Traits\OrderListingTrait; + protected ?array $exports; public function setExports(array $exports): void { diff --git a/src/ScheduledExportBundle/Model/ScheduledExportRegistry/Listing/Dao.php b/src/ScheduledExportBundle/Model/ScheduledExportRegistry/Listing/Dao.php index 54a6f89..e3668ce 100644 --- a/src/ScheduledExportBundle/Model/ScheduledExportRegistry/Listing/Dao.php +++ b/src/ScheduledExportBundle/Model/ScheduledExportRegistry/Listing/Dao.php @@ -30,11 +30,10 @@ public function load(): array /** * @return int */ - public function getTotalCount() + public function getTotalCount(): int { $data = $this->db->fetchAll($this->model->getFilter(), $this->model->getOrder()); - $amount = count($data); - return $amount; + return count($data); } } diff --git a/src/ScheduledExportBundle/ProcessManager/ScheduledExportProcess.php b/src/ScheduledExportBundle/ProcessManager/ScheduledExportProcess.php deleted file mode 100644 index c0b6880..0000000 --- a/src/ScheduledExportBundle/ProcessManager/ScheduledExportProcess.php +++ /dev/null @@ -1,65 +0,0 @@ - - * @copyright Copyright (c) 2017 Divante Ltd. (https://divante.co) - */ - -namespace Divante\ScheduledExportBundle\ProcessManager; - -use Pimcore\Bootstrap; -use Pimcore\Console\Application; -use Pimcore\Tool\Console; -use ProcessManagerBundle\Model\ExecutableInterface; -use ProcessManagerBundle\Process\ProcessInterface; -use Symfony\Component\DependencyInjection\ContainerAwareTrait; -use Symfony\Component\HttpKernel\Kernel; -use Symfony\Component\Process\Process; - -/** - * Class ScheduledExportProcess - * @package Divante\ScheduledExportBundle\ProcessManager - * @SuppressWarnings(PHPMD) - */ -final class ScheduledExportProcess implements ProcessInterface -{ - /** - * {@inheritdoc} - */ - public function run(ExecutableInterface $executable, ?array $params = null) - { - $settings = $executable->getSettings(); - - if (is_array($params)) { - $settings = array_merge($settings, $params); - } - - $command = sprintf( - 'scheduled-export:start -g %s -f %s -a %s --filename %s -t %s --format %s' - . ' -c %s --only-changes %s --delimiter %s --divide_file %s --preserve_process %s --types %s' - . ' --object-ids %s --add_utf_bom %s', - escapeshellarg($settings['grid_config']), - escapeshellarg($settings['objects_folder']), - escapeshellarg($settings['asset_folder']), - escapeshellarg($settings['asset_filename']), - escapeshellarg($settings['add_timestamp']), - escapeshellarg($settings['timestamp']), - escapeshellarg($settings['condition']), - escapeshellarg($settings['only_changes']), - escapeshellarg($settings['delimiter']), - escapeshellarg($settings['divide_file']), - escapeshellarg($settings['preserve_process']), - escapeshellarg($settings['types']), - escapeshellarg($settings['object_ids'] ?? ''), - escapeshellarg($settings['add_utf_bom'] ?? 0) - ); - - $command = Console::getExecutable('php') . ' ' . PIMCORE_PROJECT_ROOT . "/bin/console " . $command; - - if ($settings['foreground']) { - return Console::exec($command); - } else { - return Console::execInBackground($command); - }; - } -} diff --git a/src/ScheduledExportBundle/Resources/config/pimcore/routing.yml b/src/ScheduledExportBundle/Resources/config/pimcore/routing.yaml similarity index 100% rename from src/ScheduledExportBundle/Resources/config/pimcore/routing.yml rename to src/ScheduledExportBundle/Resources/config/pimcore/routing.yaml diff --git a/src/ScheduledExportBundle/Resources/config/services.yml b/src/ScheduledExportBundle/Resources/config/services.yaml similarity index 65% rename from src/ScheduledExportBundle/Resources/config/services.yml rename to src/ScheduledExportBundle/Resources/config/services.yaml index c3fd2fd..50bf9a7 100644 --- a/src/ScheduledExportBundle/Resources/config/services.yml +++ b/src/ScheduledExportBundle/Resources/config/services.yaml @@ -18,12 +18,16 @@ services: resource: '../../Command/*' tags: ['console.command'] - scheduledexport.process_manager.process: - class: Divante\ScheduledExportBundle\ProcessManager\ScheduledExportProcess - tags: - - { name: 'process_manager.process', type: 'scheduledexport', form-type: Divante\ScheduledExportBundle\Form\Type\ProcessManager\ScheduledExportType } - - Divante\ScheduledExportBundle\Migrations\Installer: + Divante\ScheduledExportBundle\Installer: public: true arguments: $bundle: "@=service('kernel').getBundle('DivanteScheduledExportBundle')" + + scheduledExport: + class : Elements\Bundle\ProcessManagerBundle\Executor\Callback\General + arguments : + $name: "scheduledExport" + $extJsClass: "pimcore.plugin.processmanager.executor.callback.scheduledExport" + $jsFile: "/bundles/divantescheduledexport/js/process-manager/scheduled-export.js" + tags: + - { name: "elements.processManager.executorCallbackClasses" } diff --git a/src/ScheduledExportBundle/Resources/public/js/process-manager/scheduled-export.js b/src/ScheduledExportBundle/Resources/public/js/process-manager/scheduled-export.js index 83f1e7f..124a0e5 100644 --- a/src/ScheduledExportBundle/Resources/public/js/process-manager/scheduled-export.js +++ b/src/ScheduledExportBundle/Resources/public/js/process-manager/scheduled-export.js @@ -1,204 +1,211 @@ -/** - * @date 05/01/18 13:57 - * @author Anna Zavodian - * @copyright Copyright (c) 2017 Divante Ltd. (https://divante.co) - */ -document.addEventListener('processmanager.ready', function() { - processmanager.executable.types.scheduledexport = Class.create(pimcore.plugin.processmanager.executable.abstractType, { - getItems: function () { - var storedThis = this; - return [ - { - xtype: 'textfield', - fieldLabel: t('scheduledexport_objects_folder'), - name: 'objects_folder', - value: this.data.settings.objects_folder, - cls: 'input_drop_target', - canDrop: function(data) - { - return storedThis.isObjectFolder(data.records[0].data); - }, - listeners: { - 'render': function (el) { - new Ext.dd.DropZone(el.getEl(), { - reference: this, - ddGroup: 'element', - getTargetFromEvent: function (e) { - return this.getEl(); - }.bind(el), +pimcore.registerNS("pimcore.plugin.processmanager.executor.callback.scheduledExport"); +pimcore.plugin.processmanager.executor.callback.scheduledExport = Class.create(pimcore.plugin.processmanager.executor.callback.abstractCallback, { + name : 'scheduledExport', - onNodeOver: function (target, dd, e, data) { - if (this.canDrop(data)) { - return Ext.dd.DropZone.prototype.dropAllowed; - } else { - return Ext.dd.DropZone.prototype.dropNotAllowed; - } - }.bind(el), + initialize : function(){ + this.settings.windowHeight = 600; + }, - onNodeDrop: function (target, dd, e, data) { - if (this.canDrop(data)) { - this.setValue(data.records[0].data.path); - return true; - } - return false; - }.bind(el) - }); - }, - 'change': function (el) { - console.log(el); - } - }, - allowBlank: false - }, { - xtype: 'combo', - fieldLabel: t('scheduledexport_grid_config'), - name: 'grid_config', - displayField: 'name', - valueField: 'id', - store: this.getGridConfig(), - emptyText: t('scheduledexport_select_grid_config'), - value: this.data.settings.grid_config, - allowBlank: false - }, { - xtype: 'textfield', - fieldLabel: t('scheduledexport_asset_folder'), - name: 'asset_folder', - value: this.data.settings.asset_folder, - cls: 'input_drop_target', - canDrop: function(data) - { - return storedThis.isAssetFolder(data.records[0].data); - }, - listeners: { - 'render': function (el) { - new Ext.dd.DropZone(el.getEl(), { - reference: this, - ddGroup: 'element', - getTargetFromEvent: function (e) { - return this.getEl(); - }.bind(el), + getFieldValue : function(fieldName){ + var value = ''; + if (this.rec) { + value = this.rec.get('extJsSettings').values[fieldName]; + } + return value; + }, - onNodeOver: function (target, dd, e, data) { - if (this.canDrop(data)) { - return Ext.dd.DropZone.prototype.dropAllowed; - } else { - return Ext.dd.DropZone.prototype.dropNotAllowed; - } - }.bind(el), + getFormItems : function () { + var storedThis = this; - onNodeDrop: function (target, dd, e, data) { - if (this.canDrop(data)) { - this.setValue(data.records[0].data.path); - return true; - } - return false; - }.bind(el) - }); - } + return [ + { + xtype: 'textfield', + fieldLabel: t('scheduledexport_objects_folder'), + name: 'OBJECTS_FOLDER', + value: this.getFieldValue('OBJECTS_FOLDER'), + cls: 'input_drop_target', + canDrop: function(data) + { + return storedThis.isObjectFolder(data.records[0].data); + }, + listeners: { + 'render': function (el) { + new Ext.dd.DropZone(el.getEl(), { + reference: this, + ddGroup: 'element', + getTargetFromEvent: function (e) { + return this.getEl(); + }.bind(el), + + onNodeOver: function (target, dd, e, data) { + if (this.canDrop(data)) { + return Ext.dd.DropZone.prototype.dropAllowed; + } else { + return Ext.dd.DropZone.prototype.dropNotAllowed; + } + }.bind(el), + + onNodeDrop: function (target, dd, e, data) { + if (this.canDrop(data)) { + this.setValue(data.records[0].data.path); + return true; + } + return false; + }.bind(el) + }); }, - allowBlank: false - }, { - xtype: 'textfield', - fieldLabel: t('scheduledexport_asset_filename'), - name: 'asset_filename', - value: this.data.settings.asset_filename, - emptyText: t('scheduledexport_asset_filename_example') - }, { - xtype: 'checkbox', - fieldLabel: t('scheduledexport_only_changes'), - name: 'only_changes', - value: this.data.settings.only_changes, + 'change': function (el) { + console.log(el); + } }, + allowBlank: false + }, + { + xtype: 'combo', + fieldLabel: t('scheduledexport_grid_config'), + name: 'GRID_CONFIG', + displayField: 'name', + valueField: 'id', + store: this.getGridConfig(), + emptyText: t('scheduledexport_select_grid_config'), + value: this.getFieldValue('GRID_CONFIG'), + allowBlank: false + }, + { + xtype: 'textfield', + fieldLabel: t('scheduledexport_asset_folder'), + name: 'ASSET_FOLDER', + value: this.getFieldValue('ASSET_FOLDER'), + cls: 'input_drop_target', + canDrop: function(data) { - xtype: 'checkbox', - fieldLabel: t('scheduledexport_add_timestamp'), - name: 'add_timestamp', - value: this.data.settings.add_timestamp, + return storedThis.isAssetFolder(data.records[0].data); }, - { - xtype: "form", - bodyStyle: "padding: 10px; border: 1px;", - style: "margin: 10px 0 10px 0", - collapsible: true, - collapsed: true, - title: t('scheduledexport_advanced_settings'), - items: [ - { - xtype: 'textfield', - fieldLabel: t('scheduledexport_delimiter'), - name: 'delimiter', - value: this.data.settings.delimiter, - emptyText: t('scheduledexport_delimiter_example') - }, { - xtype: 'textfield', - fieldLabel: t('scheduledexport_condition'), - name: 'condition', - value: this.data.settings.condition, - emptyText: t('scheduledexport_condition_example') - }, - { - xtype: 'textfield', - fieldLabel: t('scheduledexport_timestamp'), - name: 'timestamp', - value: this.data.settings.timestamp, - emptyText: t('scheduledexport_timestamp_example') - }, - { - xtype: 'textfield', - fieldLabel: t('scheduledexport_divide_file'), - name: 'divide_file', - value: this.data.settings.divide_file, - emptyText: '1000' - }, - { - xtype: 'checkbox', - fieldLabel: t('scheduledexport_preserve_process'), - name: 'preserve_process', - value: this.data.settings.preserve_process, - }, - { - xtype: 'textfield', - fieldLabel: t('scheduledexport_types'), - name: 'types', - value: this.data.settings.types, - emptyText: t('scheduledexport_types_example'), - }, - { - xtype: 'checkbox', - fieldLabel: t('scheduledexport_add_utf_bom'), - name: 'add_utf_bom', - value: this.data.settings.add_utf_bom, - }, - ] - } - , - ]; - }, + listeners: { + 'render': function (el) { + new Ext.dd.DropZone(el.getEl(), { + reference: this, + ddGroup: 'element', + getTargetFromEvent: function (e) { + return this.getEl(); + }.bind(el), - getGridConfig: function () { - var gridConfigStore = Ext.create("Ext.data.JsonStore", { - id: "gridConfigStore", - proxy: { - type: "ajax", - url: "/admin/scheduled-export/grid-config/get-list", - reader: { - type: 'json', - rootProperty: 'result' + onNodeOver: function (target, dd, e, data) { + if (this.canDrop(data)) { + return Ext.dd.DropZone.prototype.dropAllowed; + } else { + return Ext.dd.DropZone.prototype.dropNotAllowed; + } + }.bind(el), + + onNodeDrop: function (target, dd, e, data) { + if (this.canDrop(data)) { + this.setValue(data.records[0].data.path); + return true; + } + return false; + }.bind(el) + }); } }, - fields: ['id', 'name'] - }); + allowBlank: false + }, + { + xtype: 'textfield', + fieldLabel: t('scheduledexport_asset_filename'), + name: 'ASSET_FILENAME', + value: this.getFieldValue('ASSET_FILENAME'), + emptyText: t('scheduledexport_asset_filename_example') + }, + { + xtype: 'checkbox', + fieldLabel: t('scheduledexport_only_changes'), + name: 'ONLY_CHANGES', + value: this.getFieldValue('ONLY_CHANGES'), + }, + { + xtype: 'checkbox', + fieldLabel: t('scheduledexport_add_timestamp'), + name: 'ADD_TIMESTAMP', + value: this.getFieldValue('ADD_TIMESTAMP'), + }, + { + xtype: "form", + bodyStyle: "padding: 10px; border: 1px;", + style: "margin: 10px 0 10px 0", + collapsible: true, + collapsed: true, + title: t('scheduledexport_advanced_settings'), + items: [ + { + xtype: 'textfield', + fieldLabel: t('scheduledexport_delimiter'), + name: 'DELIMITER', + value: this.getFieldValue('DELIMITER'), + emptyText: t('scheduledexport_delimiter_example') + }, { + xtype: 'textfield', + fieldLabel: t('scheduledexport_condition'), + name: 'CONDITION', + value: this.getFieldValue('CONDITION'), + emptyText: t('scheduledexport_condition_example') + }, + { + xtype: 'textfield', + fieldLabel: t('scheduledexport_timestamp'), + name: 'TIMESTAMP', + value: this.getFieldValue('TIMESTAMP'), + emptyText: t('scheduledexport_timestamp_example') + }, + { + xtype: 'textfield', + fieldLabel: t('scheduledexport_divide_file'), + name: 'DIVIDE_FILE', + value: this.getFieldValue('DIVIDE_FILE'), + emptyText: '1000' + }, + { + xtype: 'textfield', + fieldLabel: t('scheduledexport_types'), + name: 'TYPES', + value: this.getFieldValue('TYPES'), + emptyText: t('scheduledexport_types_example'), + }, + { + xtype: 'checkbox', + fieldLabel: t('scheduledexport_add_utf_bom'), + name: 'ADD_UTF_BOM', + value: this.getFieldValue('ADD_UTF_BOM'), + }, + ] + } + , + ]; + }, - return gridConfigStore.load(); - }, + getGridConfig: function () { + var gridConfigStore = Ext.create("Ext.data.JsonStore", { + id: "gridConfigStore", + proxy: { + type: "ajax", + url: "/admin/scheduled-export/grid-config/get-list", + reader: { + type: 'json', + rootProperty: 'result' + } + }, + fields: ['id', 'name'] + }); - isAssetFolder: function(data) { - return data.elementType === 'asset' && data.type === 'folder'; - }, + return gridConfigStore.load(); + }, - isObjectFolder: function(data) { - return data.elementType === 'object' && data.type === 'folder'; - } - }); + isAssetFolder: function(data) { + return data.elementType === 'asset' && data.type === 'folder'; + }, + + isObjectFolder: function(data) { + return data.elementType === 'object' && data.type === 'folder'; + } }); diff --git a/src/ScheduledExportBundle/Resources/translations/admin.de.yml b/src/ScheduledExportBundle/Resources/translations/admin.de.yml new file mode 100644 index 0000000..ba3ff26 --- /dev/null +++ b/src/ScheduledExportBundle/Resources/translations/admin.de.yml @@ -0,0 +1,21 @@ +plugin_pm_scheduledExport: "Geplanter Export" +scheduledexport_grid_config: "Grid Konfiguration:" +scheduledexport_objects_folder: "Ordner mit Objekten:" +scheduledexport_asset_folder: "Asset Ordner für Datei:" +scheduledexport_send_email: "Sende E-Mail an:" +scheduledexport_select_grid_config: "Wählen Sie eine Grid Konfiguration" +scheduledexport_asset_filename_example: "export.csv" +scheduledexport_condition: "SQL Query" +scheduledexport_condition_example: "o_key like '%partOfKey%'" +scheduledexport_asset_filename: "Export Dateiname:" +scheduledexport_add_timestamp: "Zeitstempel an den Dateinamen anhängen" +scheduledexport_only_changes: "Nur Änderungen vom letzten Export exportieren" +scheduledexport_timestamp: "Zeitstempelformat (more info)" +scheduledexport_timestamp_example: "-%s" +scheduledexport_delimiter_example: ";" +scheduledexport_delimiter: "Trennzeichen" +scheduledexport_advanced_settings: "Erweiterte Einstellungen" +scheduledexport_divide_file: "Datei teilen, wenn sie größer als n Zeilen ist" +scheduledexport_types: "Typen (kommagetrennt)" +scheduledexport_types_example: "object,variant,folder" +scheduledexport_add_utf_bom: "BOM hinzufügen (Byte Order Mark)" diff --git a/src/ScheduledExportBundle/Resources/translations/admin.en.yml b/src/ScheduledExportBundle/Resources/translations/admin.en.yml index dcf2c98..bd03529 100644 --- a/src/ScheduledExportBundle/Resources/translations/admin.en.yml +++ b/src/ScheduledExportBundle/Resources/translations/admin.en.yml @@ -1,8 +1,8 @@ +plugin_pm_scheduledExport: "Scheduled export" scheduledexport_grid_config: "Grid config:" scheduledexport_objects_folder: "Folder with objects:" scheduledexport_asset_folder: "Asset folder for file:" scheduledexport_send_email: "Send e-mail:" -processmanager_type_scheduledexport: "Scheduled export" scheduledexport_select_grid_config: "Select a grid configuration" scheduledexport_asset_filename_example: "export.csv" scheduledexport_condition: "SQL Condition" @@ -16,7 +16,6 @@ scheduledexport_delimiter_example: ";" scheduledexport_delimiter: "Delimiter" scheduledexport_advanced_settings: "Advanced settings" scheduledexport_divide_file: "Divide file if larger than n lines" -scheduledexport_preserve_process: "Don't delete process after it's finished" scheduledexport_types: "Types (comma separated)" scheduledexport_types_example: "object,variant,folder" scheduledexport_add_utf_bom: "Add BOM (Byte Order Mark)"