├── .github └── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── .gitignore ├── .gitlab-ci.yml ├── Api ├── ConfigParserInterface.php ├── ConfigurationInterface.php ├── ContentResolverInterface.php ├── Data │ ├── BlockEntryInterface.php │ ├── EntryInterface.php │ └── PageEntryInterface.php ├── MediaFilesParserInterface.php ├── StoreCodeResolverInterface.php └── TargetMediaDirectoryPathProviderInterface.php ├── Block ├── Adminhtml │ ├── Block │ │ └── Edit │ │ │ └── SaveButton.php │ └── Page │ │ └── Edit │ │ └── SaveButton.php └── MaintainedContentWarning.php ├── CHANGELOG.md ├── Controller └── Adminhtml │ ├── Block │ └── Save.php │ └── Page │ └── Save.php ├── Exception └── CommandInputException.php ├── LICENCE.txt ├── Model ├── BlockEntry.php ├── BlockInstaller.php ├── Command │ ├── ApplyBlockEntry.php │ ├── ApplyMediaFiles.php │ ├── ApplyPageEntry.php │ └── NormalizeData.php ├── Config │ ├── Converter.php │ ├── Data.php │ ├── NodeConverter.php │ ├── Parser │ │ ├── ContentHeadingParser.php │ │ ├── ContentParser.php │ │ ├── CustomDesignParser.php │ │ ├── DesignParser.php │ │ ├── MediaDirectoryParser.php │ │ ├── MetaDataParser.php │ │ ├── ParserChain.php │ │ ├── Query │ │ │ ├── FetchAttributeValue.php │ │ │ ├── FetchBooleanAttributeValue.php │ │ │ ├── FetchChildNodeValue.php │ │ │ ├── FetchMediaFilesChain.php │ │ │ └── Media │ │ │ │ └── MediaDirectiveFileParser.php │ │ ├── SeoParser.php │ │ └── StoresParser.php │ └── SchemaLocator.php ├── Configuration │ └── TargetMediaDirectoryPathProvider.php ├── Console │ ├── AddBlockCommand.php │ ├── AddPageCommand.php │ ├── BlockListCommand.php │ ├── BlockResetCommand.php │ └── PageListCommand.php ├── PageEntry.php ├── PageInstaller.php ├── Query │ ├── GetBlockEntryByBlock.php │ ├── GetBlockEntryByKey.php │ ├── GetBlockEntryList.php │ ├── GetBlocksByBlockEntry.php │ ├── GetFirstBlockByBlockEntry.php │ ├── GetFirstPageByPageEntry.php │ ├── GetPageEntryByKey.php │ ├── GetPageEntryByPage.php │ ├── GetPageEntryList.php │ ├── GetPagesByPageEntry.php │ ├── HasDefaultBlockConfiguration.php │ ├── HasDefaultPageConfiguration.php │ ├── HasStoreMatches.php │ ├── IsBlockMaintained.php │ ├── IsMaintainedContentRequested.php │ └── IsPageMaintained.php ├── Resolver │ ├── ContentResolverProvider.php │ ├── FileContentResolver.php │ ├── PathResolver.php │ ├── PlainContentResolver.php │ ├── StoreCodeResolver.php │ └── StoreIdsByStoreCodeResolver.php └── Validator │ ├── CanApplyBlockEntry.php │ └── CanApplyPageEntry.php ├── README.md ├── Setup └── RecurringData.php ├── Test └── Integration │ ├── Model │ ├── BlockInstaller │ │ ├── InstallTest.php │ │ ├── TestCase.php │ │ └── UpdateMaintainedTest.php │ ├── Config │ │ ├── ConverterTest.php │ │ ├── Parser │ │ │ └── Query │ │ │ │ └── FetchMediaFilesChainTest.php │ │ └── _files │ │ │ ├── content_provisioning.xml │ │ │ ├── result.php │ │ │ └── test-files │ │ │ ├── content-with-images-1.html │ │ │ ├── file-1.html │ │ │ └── media │ │ │ ├── foobar │ │ │ └── test.png │ │ │ ├── image-1.png │ │ │ └── some-test-image.png │ ├── PageInstaller │ │ ├── InstallMediaFilesTest.php │ │ ├── InstallTest.php │ │ ├── TestCase.php │ │ └── UpdateMaintainedTest.php │ └── _files │ │ └── dummy-content.html │ ├── _files │ ├── content │ │ ├── content-with-images-1.html │ │ ├── dummy-content.html │ │ └── file-1.html │ ├── content_provisioning.xml │ └── result.php │ └── phpunit.gitlab.xml ├── composer.json ├── etc ├── adminhtml │ └── routes.xml ├── content_provisioning.xsd ├── di.xml └── module.xml ├── registration.php └── view └── adminhtml ├── templates └── maintained_content_warning.phtml └── ui_component ├── cms_block_form.xml └── cms_page_form.xml /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report about: Create a report to help us improve title: '' 3 | labels: '' 4 | assignees: '' 5 | 6 | --- 7 | 8 | **Describe the bug** 9 | A clear and concise description of what the bug is. 10 | 11 | **To Reproduce** 12 | Steps to reproduce the behavior: 13 | 14 | 1. Go to '...' 15 | 2. Click on '....' 16 | 3. Scroll down to '....' 17 | 4. See error 18 | 19 | **Expected behavior** 20 | A clear and concise description of what you expected to happen. 21 | 22 | **Screenshots** 23 | If applicable, add screenshots to help explain your problem. 24 | 25 | **Desktop (please complete the following information):** 26 | 27 | - OS: [e.g. iOS] 28 | - Browser [e.g. chrome, safari] 29 | - Version [e.g. 22] 30 | 31 | **Smartphone (please complete the following information):** 32 | 33 | - Device: [e.g. iPhone6] 34 | - OS: [e.g. iOS8.1] 35 | - Browser [e.g. stock browser, safari] 36 | - Version [e.g. 22] 37 | 38 | **Additional context** 39 | Add any other context about the problem here. 40 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request about: Suggest an idea for this project title: '' 3 | labels: '' 4 | assignees: '' 5 | 6 | --- 7 | 8 | **Is your feature request related to a problem? Please describe.** 9 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 10 | 11 | **Describe the solution you'd like** 12 | A clear and concise description of what you want to happen. 13 | 14 | **Describe alternatives you've considered** 15 | A clear and concise description of any alternative solutions or features you've considered. 16 | 17 | **Additional context** 18 | Add any other context or screenshots about the feature request here. 19 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.idea 2 | /Test/*/phpunit.local.* 3 | /Test/*/phpunit.dev.* -------------------------------------------------------------------------------- /.gitlab-ci.yml: -------------------------------------------------------------------------------- 1 | include: 2 | - remote: 'https://gitlab.met.tdintern.de/met-public/gitlab-ci-templates/-/raw/master/module/stages.yml' 3 | - remote: 'https://gitlab.met.tdintern.de/met-public/gitlab-ci-templates/-/raw/master/module/merge-request/2.4.4/static-tests.yml' 4 | #- remote: 'https://gitlab.met.tdintern.de/met-public/gitlab-ci-templates/-/raw/master/module/merge-request/2.4.4/unit-tests.yml' 5 | - remote: 'https://gitlab.met.tdintern.de/met-public/gitlab-ci-templates/-/raw/master/module/merge-request/2.4.4/integration-tests.yml' 6 | - remote: 'https://gitlab.met.tdintern.de/met-public/gitlab-ci-templates/-/raw/master/module/merge-request/2.4.4/semver-tests.yml' 7 | #- remote: 'https://gitlab.met.tdintern.de/met-public/gitlab-ci-templates/-/raw/master/module/nightly-build/2.4/unit-tests.yml' 8 | - remote: 'https://gitlab.met.tdintern.de/met-public/gitlab-ci-templates/-/raw/master/module/nightly-build/2.4/static-tests.yml' 9 | - remote: 'https://gitlab.met.tdintern.de/met-public/gitlab-ci-templates/-/raw/master/module/nightly-build/2.4/integration-tests.yml' 10 | 11 | #variables: 12 | # SEMVER_ALLOWED_CHANGE_LEVEL: 3 13 | -------------------------------------------------------------------------------- /Api/ConfigParserInterface.php: -------------------------------------------------------------------------------- 1 | hasDefaultConfiguration = $hasDefaultConfiguration; 38 | } 39 | 40 | /** 41 | * @return array 42 | */ 43 | public function getButtonData() 44 | { 45 | return [ 46 | 'label' => __('Save'), 47 | 'class' => 'save primary', 48 | 'data_attribute' => [ 49 | 'mage-init' => [ 50 | 'buttonAdapter' => [ 51 | 'actions' => [ 52 | [ 53 | 'targetName' => 'cms_block_form.cms_block_form', 54 | 'actionName' => 'save', 55 | 'params' => [ 56 | true, 57 | [ 58 | 'back' => 'continue', 59 | ], 60 | ], 61 | ], 62 | ], 63 | ], 64 | ], 65 | ], 66 | 'class_name' => Container::SPLIT_BUTTON, 67 | 'options' => $this->getOptions(), 68 | ]; 69 | } 70 | 71 | /** 72 | * Retrieve options 73 | * 74 | * @return array 75 | */ 76 | private function getOptions() 77 | { 78 | $options = [ 79 | [ 80 | 'label' => __('Save & Duplicate'), 81 | 'id_hard' => 'save_and_duplicate', 82 | 'data_attribute' => [ 83 | 'mage-init' => [ 84 | 'buttonAdapter' => [ 85 | 'actions' => [ 86 | [ 87 | 'targetName' => 'cms_block_form.cms_block_form', 88 | 'actionName' => 'save', 89 | 'params' => [ 90 | true, 91 | [ 92 | 'back' => 'duplicate', 93 | ], 94 | ], 95 | ], 96 | ], 97 | ], 98 | ], 99 | ], 100 | ], 101 | [ 102 | 'id_hard' => 'save_and_close', 103 | 'label' => __('Save & Close'), 104 | 'data_attribute' => [ 105 | 'mage-init' => [ 106 | 'buttonAdapter' => [ 107 | 'actions' => [ 108 | [ 109 | 'targetName' => 'cms_block_form.cms_block_form', 110 | 'actionName' => 'save', 111 | 'params' => [ 112 | true, 113 | [ 114 | 'back' => 'close', 115 | ], 116 | ], 117 | ], 118 | ], 119 | ], 120 | ], 121 | ], 122 | ], 123 | ]; 124 | 125 | if ($this->hasDefaultConfiguration->execute((int)$this->getBlockId())) { 126 | $options = array_merge( 127 | $options, 128 | [ 129 | [ 130 | 'is_hard' => 'apply_default', 131 | 'label' => __('Reset to Default & Save'), 132 | 'data_attribute' => [ 133 | 'mage-init' => [ 134 | 'buttonAdapter' => [ 135 | 'actions' => [ 136 | [ 137 | 'targetName' => 'cms_block_form.cms_block_form', 138 | 'actionName' => 'save', 139 | 'params' => [ 140 | true, 141 | [ 142 | 'back' => 'applyDefault', 143 | ], 144 | ], 145 | ], 146 | ], 147 | ], 148 | ], 149 | ] 150 | ] 151 | ] 152 | ); 153 | } 154 | 155 | return $options; 156 | } 157 | } 158 | -------------------------------------------------------------------------------- /Block/Adminhtml/Page/Edit/SaveButton.php: -------------------------------------------------------------------------------- 1 | hasDefaultConfiguration = $hasDefaultConfiguration; 38 | } 39 | 40 | /** 41 | * @return array 42 | */ 43 | public function getButtonData() 44 | { 45 | return [ 46 | 'label' => __('Save'), 47 | 'class' => 'save primary', 48 | 'data_attribute' => [ 49 | 'mage-init' => [ 50 | 'buttonAdapter' => [ 51 | 'actions' => [ 52 | [ 53 | 'targetName' => 'cms_page_form.cms_page_form', 54 | 'actionName' => 'save', 55 | 'params' => [ 56 | true, 57 | [ 58 | 'back' => 'continue', 59 | ], 60 | ], 61 | ], 62 | ], 63 | ], 64 | ], 65 | ], 66 | 'class_name' => Container::SPLIT_BUTTON, 67 | 'options' => $this->getOptions(), 68 | ]; 69 | } 70 | 71 | /** 72 | * Retrieve options 73 | * 74 | * @return array 75 | */ 76 | private function getOptions() 77 | { 78 | $options = [ 79 | [ 80 | 'label' => __('Save & Duplicate'), 81 | 'id_hard' => 'save_and_duplicate', 82 | 'data_attribute' => [ 83 | 'mage-init' => [ 84 | 'buttonAdapter' => [ 85 | 'actions' => [ 86 | [ 87 | 'targetName' => 'cms_page_form.cms_page_form', 88 | 'actionName' => 'save', 89 | 'params' => [ 90 | true, 91 | [ 92 | 'back' => 'duplicate', 93 | ], 94 | ], 95 | ], 96 | ], 97 | ], 98 | ], 99 | ], 100 | ], 101 | [ 102 | 'id_hard' => 'save_and_close', 103 | 'label' => __('Save & Close'), 104 | 'data_attribute' => [ 105 | 'mage-init' => [ 106 | 'buttonAdapter' => [ 107 | 'actions' => [ 108 | [ 109 | 'targetName' => 'cms_page_form.cms_page_form', 110 | 'actionName' => 'save', 111 | 'params' => [ 112 | true, 113 | [ 114 | 'back' => 'close', 115 | ], 116 | ], 117 | ], 118 | ], 119 | ], 120 | ], 121 | ], 122 | ], 123 | ]; 124 | 125 | if ($this->hasDefaultConfiguration->execute((int)$this->getPageId())) { 126 | $options = array_merge( 127 | $options, 128 | [ 129 | [ 130 | 'is_hard' => 'apply_default', 131 | 'label' => __('Reset to Default & Save'), 132 | 'data_attribute' => [ 133 | 'mage-init' => [ 134 | 'buttonAdapter' => [ 135 | 'actions' => [ 136 | [ 137 | 'targetName' => 'cms_page_form.cms_page_form', 138 | 'actionName' => 'save', 139 | 'params' => [ 140 | true, 141 | [ 142 | 'back' => 'applyDefault', 143 | ], 144 | ], 145 | ], 146 | ], 147 | ], 148 | ], 149 | ] 150 | ] 151 | ] 152 | ); 153 | } 154 | 155 | return $options; 156 | } 157 | } 158 | -------------------------------------------------------------------------------- /Block/MaintainedContentWarning.php: -------------------------------------------------------------------------------- 1 | isMaintainedContentRequested = $isMaintainedContentRequested; 30 | } 31 | 32 | /** 33 | * @return bool 34 | */ 35 | public function isMaintained(): bool 36 | { 37 | return $this->isMaintainedContentRequested->execute($this->getRequest()); 38 | } 39 | 40 | /** 41 | * @return Phrase 42 | */ 43 | public function getMessage(): Phrase 44 | { 45 | return __( 46 | 'This content is managed by the release. ' . 47 | 'All changes will be overwritten by the next update!' 48 | ); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # CHANGELOG 2 | 3 | ## 1.4.0 4 | 5 | * *Feature:* Introduce reset CLI command to force content import for already given contents manually 6 | 7 | ## 1.3.7 8 | 9 | * PHP 8.1 support 10 | 11 | ## 1.3.6 12 | 13 | * Change to MIT licence 14 | * No other changes between 1.3.5 to 1.3.6 15 | 16 | ## 1.3.5 17 | 18 | * *Improvement:* Add support for full-width cms pages 19 | 20 | ## 1.3.4 21 | 22 | * *Bugfix:* Set correct XSD URN 23 | * *Bugfix:* Pin composer to Version 1 24 | 25 | ## 1.3.3 26 | 27 | * *Bugfix:* Fix return type of convert on error 28 | 29 | ## 1.3.2 30 | 31 | * *Bugfix:* Fix xml schema reference to pass magento static tests 32 | 33 | ## 1.3.0 / 1.3.1 34 | 35 | * *Feature:* Add Magento 2.4 support 36 | * *Feature:* Add phpUnit 9 support 37 | 38 | ## 1.2.4 39 | 40 | * *Feature:* Enable PHP 7.2 support 41 | * *Feature:* Enable PHP 7.3 support 42 | * *Feature:* Add command to add blocks and pages 43 | 44 | ## 1.2.2 / 1.2.3 45 | 46 | * *Bugfix:* `composer.json` blocked installation of the module in Magento 2.2 47 | ** Change `composer.json` dependency definition 48 | ** Remove some type and return type hints in order to support Magento's code generation in older versions 49 | 50 | ## 1.2.1 51 | 52 | * *Improvement:* Better extensibility for fetching media files from content (PR from @roma-glushko) 53 | 54 | ## 1.2.0 55 | 56 | * *Feature:* Enables module to provide media files 57 | ** Introduce optional XML node for pages and blocks: `media_directory` 58 | ** Media files, which are used in the content will be copied to Magento's `pub/media` directory if they are present in 59 | the defined source media directory 60 | 61 | ## 1.1.1 62 | 63 | * *Bugfix:* In some cases `bin/magento` execution was not able at all during the initial Magento install 64 | 65 | ## 1.1.0 66 | 67 | * *Feature:* Introduce further interfaces and refactor config reader structure, in order to improve extensibility 68 | 69 | ## 1.0.4 70 | 71 | * *Bugfix:* Backport bugfix from version 1.1.1 72 | 73 | ## 1.0.3 74 | 75 | * *Bugfix:* Fix issue with missing `store_id` values while persisting block entries. 76 | 77 | ## 1.0.2 78 | 79 | * *Bugfix:* Extend schema in order to allow `.` for `key` like it is shown in the examples 80 | 81 | ## 1.0.1 82 | 83 | * *Bugfix:* Add PHP 7.1 compatibility 84 | 85 | ## 1.0.0 86 | 87 | * *Feature:* Notification in Magento backend (admin), for editors - if the content entry is maintained by code 88 | * *Improvement:* Introduce `key` attribute for configured entries, in order to improve merging of all configurations 89 | * *Refactoring:* Improve query for fetching existing cms entities by configured entries 90 | 91 | ## 0.1.0 92 | 93 | * *Feature:* Implement initial functionality 94 | ** Configuration for pages 95 | ** Configuration for blocks 96 | ** Recurring setup installer for pages 97 | ** Recurring setup installer for blocks 98 | -------------------------------------------------------------------------------- /Controller/Adminhtml/Block/Save.php: -------------------------------------------------------------------------------- 1 | dataPersistor = $dataPersistor; 74 | $this->blockFactory = $blockFactory 75 | ?: ObjectManager::getInstance()->get(BlockFactory::class); 76 | $this->blockRepository = $blockRepository 77 | ?: ObjectManager::getInstance()->get(BlockRepositoryInterface::class); 78 | parent::__construct($context, $coreRegistry); 79 | $this->getBlockEntryByBlock = $getBlockEntryByBlock; 80 | $this->applyBlockEntry = $applyBlockEntry; 81 | } 82 | 83 | /** 84 | * Save action 85 | * 86 | * @SuppressWarnings(PHPMD.CyclomaticComplexity) 87 | * @return ResultInterface 88 | */ 89 | public function execute() 90 | { 91 | /** @var Redirect $resultRedirect */ 92 | $resultRedirect = $this->resultRedirectFactory->create(); 93 | $data = $this->getRequest()->getPostValue(); 94 | if ($data) { 95 | if (isset($data['is_active']) && $data['is_active'] === 'true') { 96 | $data['is_active'] = Block::STATUS_ENABLED; 97 | } 98 | if (empty($data['block_id'])) { 99 | $data['block_id'] = null; 100 | } 101 | 102 | /** @var Block $model */ 103 | $model = $this->blockFactory->create(); 104 | 105 | $id = $this->getRequest()->getParam('block_id'); 106 | if ($id) { 107 | try { 108 | $model = $this->blockRepository->getById($id); 109 | } catch (LocalizedException $e) { 110 | $this->messageManager->addErrorMessage(__('This block no longer exists.')); 111 | return $resultRedirect->setPath('*/*/'); 112 | } 113 | } 114 | 115 | $model->setData($data); 116 | 117 | try { 118 | $this->blockRepository->save($model); 119 | $this->messageManager->addSuccessMessage(__('You saved the block.')); 120 | $this->dataPersistor->clear('cms_block'); 121 | return $this->processBlockReturn($model, $data, $resultRedirect); 122 | } catch (LocalizedException $e) { 123 | $this->messageManager->addErrorMessage($e->getMessage()); 124 | } catch (Exception $e) { 125 | $this->messageManager->addExceptionMessage($e, __('Something went wrong while saving the block.')); 126 | } 127 | 128 | $this->dataPersistor->set('cms_block', $data); 129 | return $resultRedirect->setPath('*/*/edit', ['block_id' => $id]); 130 | } 131 | return $resultRedirect->setPath('*/*/'); 132 | } 133 | 134 | /** 135 | * Process and set the block return 136 | * 137 | * @param Block $model 138 | * @param array $data 139 | * @param ResultInterface $resultRedirect 140 | * 141 | * @return ResultInterface 142 | * @throws LocalizedException 143 | */ 144 | private function processBlockReturn($model, $data, $resultRedirect) 145 | { 146 | $redirect = $data['back'] ?? 'close'; 147 | 148 | if ($redirect === 'continue') { 149 | $resultRedirect->setPath('*/*/edit', ['block_id' => $model->getId()]); 150 | } elseif ($redirect === 'close') { 151 | $resultRedirect->setPath('*/*/'); 152 | } elseif ($redirect === 'duplicate') { 153 | $duplicateModel = $this->blockFactory->create(['data' => $data]); 154 | $duplicateModel->setId(null); 155 | $duplicateModel->setIdentifier($data['identifier'] . '-' . uniqid()); 156 | $duplicateModel->setIsActive(Block::STATUS_DISABLED); 157 | $this->blockRepository->save($duplicateModel); 158 | $id = $duplicateModel->getId(); 159 | $this->messageManager->addSuccessMessage(__('You duplicated the block.')); 160 | $this->dataPersistor->set('cms_block', $data); 161 | $resultRedirect->setPath('*/*/edit', ['block_id' => $id]); 162 | } elseif ($redirect === 'applyDefault') { 163 | $block = $this->getBlockEntryByBlock->execute($model); 164 | $this->applyBlockEntry->execute($block); 165 | $resultRedirect->setPath('*/*/edit', ['block_id' => $model->getId()]); 166 | } 167 | return $resultRedirect; 168 | } 169 | } 170 | -------------------------------------------------------------------------------- /Controller/Adminhtml/Page/Save.php: -------------------------------------------------------------------------------- 1 | dataProcessor = $dataProcessor; 88 | $this->dataPersistor = $dataPersistor; 89 | $this->pageFactory = $pageFactory 90 | ?: ObjectManager::getInstance()->get(PageFactory::class); 91 | $this->pageRepository = $pageRepository 92 | ?: ObjectManager::getInstance() 93 | ->get(PageRepositoryInterface::class); 94 | parent::__construct($context); 95 | $this->getPageEntryByPage = $getPageEntryByPage; 96 | $this->applyPageEntry = $applyPageEntry; 97 | } 98 | 99 | /** 100 | * Save action 101 | * 102 | * @SuppressWarnings(PHPMD.CyclomaticComplexity) 103 | * @return ResultInterface 104 | */ 105 | public function execute() 106 | { 107 | $data = $this->getRequest()->getPostValue(); 108 | /** @var Redirect $resultRedirect */ 109 | $resultRedirect = $this->resultRedirectFactory->create(); 110 | if ($data) { 111 | $data = $this->dataProcessor->filter($data); 112 | if (isset($data['is_active']) && $data['is_active'] === 'true') { 113 | $data['is_active'] = Page::STATUS_ENABLED; 114 | } 115 | if (empty($data['page_id'])) { 116 | $data['page_id'] = null; 117 | } 118 | 119 | /** @var Page $model */ 120 | $model = $this->pageFactory->create(); 121 | 122 | $id = $this->getRequest()->getParam('page_id'); 123 | if ($id) { 124 | try { 125 | $model = $this->pageRepository->getById($id); 126 | } catch (LocalizedException $e) { 127 | $this->messageManager->addErrorMessage(__('This page no longer exists.')); 128 | return $resultRedirect->setPath('*/*/'); 129 | } 130 | } 131 | 132 | $model->setData($data); 133 | 134 | $this->_eventManager->dispatch( 135 | 'cms_page_prepare_save', 136 | ['page' => $model, 'request' => $this->getRequest()] 137 | ); 138 | 139 | if (!$this->dataProcessor->validate($data)) { 140 | return $resultRedirect->setPath('*/*/edit', ['page_id' => $model->getId(), '_current' => true]); 141 | } 142 | 143 | try { 144 | $this->pageRepository->save($model); 145 | $this->messageManager->addSuccessMessage(__('You saved the page.')); 146 | return $this->processResultRedirect($model, $resultRedirect, $data); 147 | } catch (LocalizedException $e) { 148 | $this->messageManager->addExceptionMessage($e->getPrevious() ?: $e); 149 | } catch (Exception $e) { 150 | $this->messageManager->addExceptionMessage($e, __('Something went wrong while saving the page.')); 151 | } 152 | 153 | $this->dataPersistor->set('cms_page', $data); 154 | return $resultRedirect->setPath('*/*/edit', ['page_id' => $this->getRequest()->getParam('page_id')]); 155 | } 156 | return $resultRedirect->setPath('*/*/'); 157 | } 158 | 159 | /** 160 | * Process result redirect 161 | * 162 | * @param PageInterface $model 163 | * @param Redirect $resultRedirect 164 | * @param array $data 165 | * @return Redirect 166 | * @throws LocalizedException 167 | */ 168 | private function processResultRedirect($model, $resultRedirect, $data) 169 | { 170 | if ($this->getRequest()->getParam('back', false) === 'duplicate') { 171 | $newPage = $this->pageFactory->create(['data' => $data]); 172 | $newPage->setId(null); 173 | $identifier = $model->getIdentifier() . '-' . uniqid(); 174 | $newPage->setIdentifier($identifier); 175 | $newPage->setIsActive(false); 176 | $this->pageRepository->save($newPage); 177 | $this->messageManager->addSuccessMessage(__('You duplicated the page.')); 178 | return $resultRedirect->setPath( 179 | '*/*/edit', 180 | [ 181 | 'page_id' => $newPage->getId(), 182 | '_current' => true, 183 | ] 184 | ); 185 | } elseif ($this->getRequest()->getParam('back', false) === 'applyDefault') { 186 | $block = $this->getPageEntryByPage->execute($model); 187 | $this->applyPageEntry->execute($block); 188 | return $resultRedirect->setPath('*/*/edit', ['page_id' => $model->getId()]); 189 | } 190 | $this->dataPersistor->clear('cms_page'); 191 | if ($this->getRequest()->getParam('back')) { 192 | return $resultRedirect->setPath('*/*/edit', ['page_id' => $model->getId(), '_current' => true]); 193 | } 194 | return $resultRedirect->setPath('*/*/'); 195 | } 196 | } 197 | -------------------------------------------------------------------------------- /Exception/CommandInputException.php: -------------------------------------------------------------------------------- 1 | getData(BlockEntryInterface::KEY); 17 | } 18 | 19 | /** 20 | * @param string $key 21 | */ 22 | public function setKey($key) 23 | { 24 | $this->setData(BlockEntryInterface::KEY, $key); 25 | } 26 | 27 | /** 28 | * @return bool 29 | */ 30 | public function isMaintained() 31 | { 32 | return (bool)$this->getData(BlockEntryInterface::IS_MAINTAINED); 33 | } 34 | 35 | /** 36 | * @param bool $isMaintained 37 | */ 38 | public function setIsMaintained($isMaintained) 39 | { 40 | $this->setData(BlockEntryInterface::IS_MAINTAINED, $isMaintained); 41 | } 42 | 43 | /** 44 | * @return array 45 | */ 46 | public function getStores() 47 | { 48 | return (array)$this->getData(BlockEntryInterface::STORES); 49 | } 50 | 51 | /** 52 | * @param array $stores 53 | */ 54 | public function setStores(array $stores) 55 | { 56 | $this->setData(BlockEntryInterface::STORES, $stores); 57 | } 58 | 59 | /** 60 | * @return string 61 | */ 62 | public function getMediaDirectory() 63 | { 64 | return (string)$this->getData(BlockEntryInterface::MEDIA_DIRECTORY); 65 | } 66 | 67 | /** 68 | * @param string $path 69 | */ 70 | public function setMediaDirectory($path) 71 | { 72 | $this->setData(BlockEntryInterface::MEDIA_DIRECTORY, $path); 73 | } 74 | 75 | /** 76 | * @return array 77 | */ 78 | public function getMediaFiles() 79 | { 80 | return (array)$this->getData(BlockEntryInterface::MEDIA_FILES); 81 | } 82 | 83 | /** 84 | * @param array $files 85 | */ 86 | public function setMediaFiles(array $files) 87 | { 88 | $this->setData(BlockEntryInterface::MEDIA_FILES, $files); 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /Model/BlockInstaller.php: -------------------------------------------------------------------------------- 1 | logger = $logger; 55 | $this->getAllBlockEntries = $getAllBlockEntries; 56 | $this->canApplyBlockEntry = $canApplyBlockEntry; 57 | $this->applyBlockEntry = $applyBlockEntry; 58 | $this->applyMediaFiles = $applyMediaFiles; 59 | } 60 | 61 | /** 62 | * Apply all configured CMS page changes 63 | * 64 | * @return void 65 | */ 66 | public function install(): void 67 | { 68 | foreach ($this->getAllBlockEntries->get() as $blockEntry) { 69 | try { 70 | if ($this->canApplyBlockEntry->execute($blockEntry)) { 71 | $this->applyBlockEntry->execute($blockEntry); 72 | $this->applyMediaFiles->execute($blockEntry); 73 | } 74 | } catch (Exception $exception) { 75 | $this->logger->error( 76 | sprintf( 77 | 'An error appeared while applying cms block content: %s', 78 | $exception->getMessage() 79 | ), 80 | [ 81 | 'block-data' => $blockEntry->getData(), 82 | 'trace' => $exception->getTrace(), 83 | ] 84 | ); 85 | } 86 | } 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /Model/Command/ApplyBlockEntry.php: -------------------------------------------------------------------------------- 1 | blockFactory = $blockFactory; 48 | $this->getFirstBlockByBlockEntry = $getFirstBlockByBlockEntry; 49 | $this->blockRepository = $blockRepository; 50 | $this->normalizeData = $normalizeData; 51 | } 52 | 53 | /** 54 | * @param BlockEntryInterface $blockEntry 55 | * @throws LocalizedException 56 | */ 57 | public function execute(BlockEntryInterface $blockEntry): void 58 | { 59 | $block = $this->getFirstBlockByBlockEntry->execute($blockEntry); 60 | if ($block === null) { 61 | /** @var BlockInterface $page */ 62 | $block = $this->blockFactory->create([]); 63 | } 64 | $block->addData($this->normalizeData->execute($blockEntry->getData())); 65 | $this->blockRepository->save($block); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /Model/Command/ApplyMediaFiles.php: -------------------------------------------------------------------------------- 1 | targetMediaDirectoryPathProvider = $targetMediaDirectoryPathProvider; 32 | $this->fileSystemDriver = $fileSystemDriver; 33 | } 34 | 35 | /** 36 | * @param EntryInterface $entry 37 | * @throws FileSystemException 38 | */ 39 | public function execute(EntryInterface $entry): void 40 | { 41 | if ($entry->getMediaDirectory() === null) { 42 | return; 43 | } 44 | 45 | $sourceDirPath = $entry->getMediaDirectory(); 46 | foreach ($entry->getMediaFiles() as $fileName) { 47 | $this->copyFile($sourceDirPath, $fileName); 48 | } 49 | } 50 | 51 | /** 52 | * @param string $sourceDirPath 53 | * @param string $fileName 54 | * @throws FileSystemException 55 | */ 56 | private function copyFile(string $sourceDirPath, string $fileName): void 57 | { 58 | $targetDirPath = $this->targetMediaDirectoryPathProvider->get(); 59 | $sourcePathname = $sourceDirPath . DIRECTORY_SEPARATOR . $fileName; 60 | $targetPathname = $targetDirPath . DIRECTORY_SEPARATOR . $fileName; 61 | 62 | if ($this->fileSystemDriver->isFile($sourcePathname) 63 | && $this->fileSystemDriver->isReadable($sourcePathname) 64 | ) { 65 | $this->createDirectory($targetDirPath, $sourceDirPath, $sourcePathname); 66 | $this->fileSystemDriver->copy($sourcePathname, $targetPathname); 67 | } 68 | } 69 | 70 | /** 71 | * @param string $targetDirPath 72 | * @param string $sourceDirPath 73 | * @param string $sourcePathname 74 | * @throws FileSystemException 75 | */ 76 | private function createDirectory(string $targetDirPath, string $sourceDirPath, string $sourcePathname): void 77 | { 78 | $subPath = str_replace( 79 | $sourceDirPath, 80 | '', 81 | $this->fileSystemDriver->getParentDirectory($sourcePathname) 82 | ); 83 | $pathname = $targetDirPath . DIRECTORY_SEPARATOR . $subPath; 84 | if (!$this->fileSystemDriver->isDirectory($pathname)) { 85 | $this->fileSystemDriver->createDirectory($pathname); 86 | } 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /Model/Command/ApplyPageEntry.php: -------------------------------------------------------------------------------- 1 | pageFactory = $pageFactory; 48 | $this->getFirstPageByPageEntry = $getFirstPageByPageEntry; 49 | $this->pageRepository = $pageRepository; 50 | $this->normalizeData = $normalizeData; 51 | } 52 | 53 | /** 54 | * @param PageEntryInterface $pageEntry 55 | * @throws LocalizedException 56 | */ 57 | public function execute(PageEntryInterface $pageEntry): void 58 | { 59 | $page = $this->getFirstPageByPageEntry->execute($pageEntry); 60 | if ($page === null) { 61 | /** @var PageInterface $page */ 62 | $page = $this->pageFactory->create([]); 63 | } 64 | $page->addData($this->normalizeData->execute($pageEntry->getData())); 65 | $this->pageRepository->save($page); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /Model/Command/NormalizeData.php: -------------------------------------------------------------------------------- 1 | storeManager = $storeManager; 24 | } 25 | 26 | /** 27 | * Normalize entry data in order to pass them to 28 | * CMS entity model like Block or Page 29 | * 30 | * @param array $data 31 | * @return array 32 | * @throws NoSuchEntityException 33 | */ 34 | public function execute(array $data): array 35 | { 36 | $storeIds = []; 37 | $storeCodes = $data[EntryInterface::STORES] ?? []; 38 | foreach ($storeCodes as $code) { 39 | $storeIds[] = $this->storeManager->getStore($code)->getId(); 40 | } 41 | $data[EntryInterface::STORES] = $storeIds; 42 | $data['store_id'] = $storeIds; 43 | 44 | unset($data[EntryInterface::IS_MAINTAINED]); 45 | unset($data[EntryInterface::KEY]); 46 | 47 | return $data; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /Model/Config/Converter.php: -------------------------------------------------------------------------------- 1 | logger = $logger; 39 | $this->pageNodeConverter = $pageNodeConverter; 40 | $this->blockNodeConverter = $blockNodeConverter; 41 | } 42 | 43 | /** 44 | * @param DOMDocument $source 45 | * @return array 46 | */ 47 | public function convert($source): array 48 | { 49 | try { 50 | return [ 51 | 'pages' => $this->pageNodeConverter->convert($source), 52 | 'blocks' => $this->blockNodeConverter->convert($source), 53 | ]; 54 | } catch (Exception $exception) { 55 | $this->logger->error($exception->getMessage(), $exception->getTrace()); 56 | } 57 | return []; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /Model/Config/Data.php: -------------------------------------------------------------------------------- 1 | _data; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Model/Config/NodeConverter.php: -------------------------------------------------------------------------------- 1 | configParser = $configParser; 39 | $this->fetchAttributeValue = $fetchAttributeValue; 40 | $this->nodeName = $nodeName; 41 | } 42 | 43 | /** 44 | * Convert config 45 | * 46 | * @param DOMDocument $source 47 | * @return array 48 | */ 49 | public function convert($source) 50 | { 51 | $output = []; 52 | foreach ($source->getElementsByTagName($this->nodeName) as $node) { 53 | $key = $this->fetchAttributeValue->execute($node, 'key'); 54 | $output[$key] = $this->configParser->execute($node); 55 | } 56 | return $output; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /Model/Config/Parser/ContentHeadingParser.php: -------------------------------------------------------------------------------- 1 | fetchAttributeValue = $fetchAttributeValue; 25 | } 26 | 27 | /** 28 | * @param DOMElement $element 29 | * @return array 30 | */ 31 | public function execute(DOMElement $element): array 32 | { 33 | $contentNodes = $element->getElementsByTagName('content'); 34 | $contentNode = $contentNodes[0]; 35 | return [ 36 | PageEntryInterface::CONTENT_HEADING => 37 | $this->fetchAttributeValue->execute($contentNode, 'heading', ''), 38 | ]; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /Model/Config/Parser/ContentParser.php: -------------------------------------------------------------------------------- 1 | contentResolverProvider = $contentResolverProvider; 50 | $this->fetchAttributeValue = $fetchAttributeValue; 51 | $this->arrayKey = $arrayKey; 52 | $this->fetchMediaFilesChain = $fetchMediaFilesChain; 53 | } 54 | 55 | /** 56 | * @param DOMElement $element 57 | * @return array 58 | * @throws LocalizedException 59 | */ 60 | public function execute(DOMElement $element): array 61 | { 62 | $contentNode = $element->getElementsByTagName('content')->item(0); 63 | $type = $this->fetchAttributeValue->execute($contentNode, 'type', 'plain'); 64 | $contentResolver = $this->contentResolverProvider->get($type); 65 | $content = $contentResolver->execute($contentNode); 66 | 67 | return [ 68 | $this->arrayKey => $content, 69 | EntryInterface::MEDIA_FILES => $this->fetchMediaFilesChain->execute($content), 70 | ]; 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /Model/Config/Parser/CustomDesignParser.php: -------------------------------------------------------------------------------- 1 | fetchChildNodeValue = $fetchChildNodeValue; 25 | } 26 | 27 | /** 28 | * @param DOMElement $element 29 | * @return array 30 | */ 31 | public function execute(DOMElement $element): array 32 | { 33 | $nodes = $element->getElementsByTagName('custom_design'); 34 | if ($nodes->length > 0) { 35 | $node = $nodes->item(0); 36 | return [ 37 | PageEntryInterface::CUSTOM_THEME_FROM => $this->fetchChildNodeValue->execute($node, 'from'), 38 | PageEntryInterface::CUSTOM_THEME_TO => $this->fetchChildNodeValue->execute($node, 'to'), 39 | PageEntryInterface::CUSTOM_THEME => $this->fetchChildNodeValue->execute($node, 'theme_id'), 40 | PageEntryInterface::CUSTOM_ROOT_TEMPLATE => $this->fetchChildNodeValue->execute($node, 'layout'), 41 | ]; 42 | } 43 | 44 | return []; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /Model/Config/Parser/DesignParser.php: -------------------------------------------------------------------------------- 1 | fetchChildNodeValue = $fetchChildNodeValue; 25 | } 26 | 27 | /** 28 | * @param DOMElement $element 29 | * @return array 30 | */ 31 | public function execute(DOMElement $element): array 32 | { 33 | $nodes = $element->getElementsByTagName('design'); 34 | if ($nodes->length > 0) { 35 | $node = $nodes->item(0); 36 | return [ 37 | PageEntryInterface::PAGE_LAYOUT => $this->fetchChildNodeValue->execute($node, 'layout'), 38 | PageEntryInterface::LAYOUT_UPDATE_XML => $this->fetchChildNodeValue->execute($node, 'layout_xml'), 39 | ]; 40 | } 41 | 42 | return []; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /Model/Config/Parser/MediaDirectoryParser.php: -------------------------------------------------------------------------------- 1 | fetchChildNodeValue = $fetchChildNodeValue; 32 | $this->pathResolver = $pathResolver; 33 | } 34 | 35 | /** 36 | * @param DOMElement $element 37 | * @return array 38 | */ 39 | public function execute(DOMElement $element): array 40 | { 41 | $nodeValue = $this->fetchChildNodeValue->execute($element, 'media_directory'); 42 | 43 | $mediaDirectory = null; 44 | if (!empty($nodeValue)) { 45 | $mediaDirectory = $this->pathResolver->execute($nodeValue); 46 | } 47 | 48 | return [ 49 | EntryInterface::MEDIA_DIRECTORY => $mediaDirectory, 50 | ]; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /Model/Config/Parser/MetaDataParser.php: -------------------------------------------------------------------------------- 1 | fetchAttributeValue = $fetchAttributeValue; 42 | $this->fetchBooleanAttributeValue = $fetchBooleanAttributeValue; 43 | $this->fetchChildNodeValue = $fetchChildNodeValue; 44 | } 45 | 46 | /** 47 | * @param DOMElement $element 48 | * @return array 49 | */ 50 | public function execute(DOMElement $element): array 51 | { 52 | return [ 53 | EntryInterface::KEY => $this->fetchAttributeValue->execute($element, 'key'), 54 | PageEntryInterface::IDENTIFIER => $this->fetchAttributeValue->execute($element, 'identifier'), 55 | PageEntryInterface::TITLE => $this->fetchChildNodeValue->execute($element, 'title'), 56 | PageEntryInterface::IS_ACTIVE => 57 | $this->fetchBooleanAttributeValue->execute($element, 'active', 'false'), 58 | EntryInterface::IS_MAINTAINED => 59 | $this->fetchBooleanAttributeValue->execute($element, 'maintained', 'false'), 60 | ]; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /Model/Config/Parser/ParserChain.php: -------------------------------------------------------------------------------- 1 | ConfigParserInterface::class] 32 | ) 33 | ); 34 | } 35 | } 36 | 37 | $this->parser = $parser; 38 | } 39 | 40 | /** 41 | * @param DOMElement $element 42 | * @return array 43 | */ 44 | public function execute(DOMElement $element): array 45 | { 46 | $data = []; 47 | 48 | foreach ($this->parser as $parser) { 49 | $data[] = $parser->execute($element); 50 | } 51 | 52 | return array_merge(...$data); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /Model/Config/Parser/Query/FetchAttributeValue.php: -------------------------------------------------------------------------------- 1 | attributes->getNamedItem($attributeName); 19 | return $item ? $item->nodeValue : $defaultValue; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Model/Config/Parser/Query/FetchBooleanAttributeValue.php: -------------------------------------------------------------------------------- 1 | fetchAttributeValue = $fetchAttributeValue; 22 | } 23 | 24 | /** 25 | * @param DOMNode $node 26 | * @param string $attributeName 27 | * @param null|string $defaultValue 28 | * @return bool 29 | */ 30 | public function execute(DOMNode $node, string $attributeName, ?string $defaultValue = null): bool 31 | { 32 | return $this->castBoolean( 33 | (string)$this->fetchAttributeValue->execute($node, $attributeName, (string)$defaultValue) 34 | ); 35 | } 36 | 37 | /** 38 | * @param string $value 39 | * @return bool 40 | */ 41 | private function castBoolean(string $value): bool 42 | { 43 | if (in_array($value, ['false', 'no', '0'])) { 44 | return false; 45 | } 46 | return (bool)$value; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /Model/Config/Parser/Query/FetchChildNodeValue.php: -------------------------------------------------------------------------------- 1 | getElementsByTagName($childNodeName); 18 | if ($children->length > 0) { 19 | return (string)$children->item(0)->textContent; 20 | } 21 | return ''; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Model/Config/Parser/Query/FetchMediaFilesChain.php: -------------------------------------------------------------------------------- 1 | MediaFilesParserInterface::class] 31 | ) 32 | ); 33 | } 34 | } 35 | 36 | $this->parsers = $parsers; 37 | } 38 | 39 | /** 40 | * Parse media files from CMS content delegating the parsing strategy to child components 41 | * 42 | * @param string $content 43 | * @return array 44 | */ 45 | public function execute(string $content): array 46 | { 47 | $mediaFiles = []; 48 | 49 | foreach ($this->parsers as $parser) { 50 | $mediaFiles[] = $parser->execute($content); 51 | } 52 | 53 | return array_merge(...$mediaFiles); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /Model/Config/Parser/Query/Media/MediaDirectiveFileParser.php: -------------------------------------------------------------------------------- 1 | .*?)\}\}/', $content, $matches)) { 19 | return $matches['path']; 20 | } 21 | 22 | return []; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Model/Config/Parser/SeoParser.php: -------------------------------------------------------------------------------- 1 | fetchChildNodeValue = $fetchChildNodeValue; 25 | } 26 | 27 | /** 28 | * @param DOMElement $element 29 | * @return array 30 | */ 31 | public function execute(DOMElement $element): array 32 | { 33 | $seoNodes = $element->getElementsByTagName('seo'); 34 | if ($seoNodes->length > 0) { 35 | $node = $seoNodes->item(0); 36 | return [ 37 | PageEntryInterface::META_TITLE => $this->fetchChildNodeValue->execute($node, 'title'), 38 | PageEntryInterface::META_KEYWORDS => $this->fetchChildNodeValue->execute($node, 'keywords'), 39 | PageEntryInterface::META_DESCRIPTION => $this->fetchChildNodeValue->execute($node, 'description'), 40 | ]; 41 | } 42 | 43 | return []; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /Model/Config/Parser/StoresParser.php: -------------------------------------------------------------------------------- 1 | storeCodeResolver = $storeCodeResolver; 32 | $this->fetchAttributeValue = $fetchAttributeValue; 33 | } 34 | 35 | /** 36 | * @param DOMElement $element 37 | * @return array 38 | */ 39 | public function execute(DOMElement $element): array 40 | { 41 | $storeCodes = []; 42 | foreach ($element->getElementsByTagName('store') as $store) { 43 | $storeCodes[] = $this->storeCodeResolver->execute( 44 | (string)$this->fetchAttributeValue->execute($store, 'code', '*') 45 | ); 46 | } 47 | 48 | if (!empty($storeCodes)) { 49 | $storeCodes = array_merge(...$storeCodes); 50 | } 51 | 52 | if (empty($storeCodes)) { 53 | $storeCodes = $this->storeCodeResolver->execute('*'); 54 | } 55 | 56 | return ['stores' => $storeCodes]; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /Model/Config/SchemaLocator.php: -------------------------------------------------------------------------------- 1 | getModuleDir( 32 | Dir::MODULE_ETC_DIR, 33 | 'Firegento_ContentProvisioning' 34 | ); 35 | $this->_schema = $etcDir . '/content_provisioning.xsd'; 36 | $this->_perFileSchema = $etcDir . '/content_provisioning.xsd'; 37 | } 38 | 39 | /** 40 | * Get path to merged config schema 41 | * 42 | * @return string|null 43 | */ 44 | public function getSchema() 45 | { 46 | return $this->_schema; 47 | } 48 | 49 | /** 50 | * Get path to per file validation schema 51 | * 52 | * @return string|null 53 | */ 54 | public function getPerFileSchema() 55 | { 56 | return $this->_perFileSchema; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /Model/Configuration/TargetMediaDirectoryPathProvider.php: -------------------------------------------------------------------------------- 1 | directoryList = $directoryList; 24 | } 25 | 26 | /** 27 | * {@inheritdoc} 28 | * @throws FileSystemException 29 | */ 30 | public function get(): string 31 | { 32 | return (string)$this->directoryList->getPath(DirectoryList::MEDIA); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /Model/Console/AddBlockCommand.php: -------------------------------------------------------------------------------- 1 | getBlockEntryByKeyFactory = $getBlockEntryByKeyFactory; 41 | $this->applyBlockEntry = $applyBlockEntry; 42 | } 43 | 44 | /** 45 | * @param InputInterface $input 46 | * @param OutputInterface $output 47 | * @return void 48 | * @throws LocalizedException 49 | * 50 | * @SuppressWarnings(PHPMD.UnusedFormalParameter) 51 | */ 52 | protected function execute(InputInterface $input, OutputInterface $output) 53 | { 54 | $key = $input->getArgument(self::ARG_BLOCK_KEY); 55 | $block = $this->getBlockEntryByKeyFactory->create()->get($key); 56 | $this->applyBlockEntry->execute($block); 57 | } 58 | 59 | /** 60 | * @inheritdoc 61 | */ 62 | protected function configure() 63 | { 64 | $this->setName('content-provisioning:block:apply'); 65 | $this->setDescription('Add a block by key'); 66 | $this->addArgument( 67 | self::ARG_BLOCK_KEY, 68 | InputArgument::REQUIRED, 69 | 'The key of the block to apply.' 70 | ); 71 | parent::configure(); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /Model/Console/AddPageCommand.php: -------------------------------------------------------------------------------- 1 | getPageEntryByKeyFactory = $getPageEntryByKeyFactory; 41 | $this->applyPageEntry = $applyPageEntry; 42 | } 43 | 44 | /** 45 | * @param InputInterface $input 46 | * @param OutputInterface $output 47 | * @return void 48 | * @throws LocalizedException 49 | * 50 | * @SuppressWarnings(PHPMD.UnusedFormalParameter) 51 | */ 52 | protected function execute(InputInterface $input, OutputInterface $output) 53 | { 54 | $key = $input->getArgument(self::ARG_PAGE_KEY); 55 | $page = $this->getPageEntryByKeyFactory->create()->get($key); 56 | $this->applyPageEntry->execute($page); 57 | } 58 | 59 | /** 60 | * @inheritdoc 61 | */ 62 | protected function configure() 63 | { 64 | $this->setName('content-provisioning:page:apply'); 65 | $this->setDescription('Add a page by key'); 66 | $this->addArgument( 67 | self::ARG_PAGE_KEY, 68 | InputArgument::REQUIRED, 69 | 'The key of the page to apply.' 70 | ); 71 | parent::configure(); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /Model/Console/BlockListCommand.php: -------------------------------------------------------------------------------- 1 | getAllBlockEntries = $getAllBlockEntries; 39 | $this->getBlocksByBlockEntry = $getBlocksByBlockEntry; 40 | } 41 | 42 | /** 43 | * @param InputInterface $input 44 | * @param OutputInterface $output 45 | * @return void 46 | * 47 | * @SuppressWarnings(PHPMD.UnusedFormalParameter) 48 | */ 49 | protected function execute(InputInterface $input, OutputInterface $output) 50 | { 51 | $table = new Table($output); 52 | $table->setHeaders(['Key', 'Identifier', 'Stores', 'Maintained', 'Active', 'Title', 'in DB (IDs)']); 53 | 54 | foreach ($this->getAllBlockEntries->get() as $entry) { 55 | $table->addRow( 56 | [ 57 | $entry->getKey(), 58 | $entry->getIdentifier(), 59 | implode(', ', $entry->getStores()), 60 | $entry->isMaintained() ? 'yes' : 'no', 61 | $entry->isActive() ? 'yes' : 'no', 62 | $entry->getTitle(), 63 | $this->getExistsInDbValue($entry), 64 | ] 65 | ); 66 | } 67 | 68 | $table->render($output); 69 | } 70 | 71 | /** 72 | * @inheritdoc 73 | */ 74 | protected function configure() 75 | { 76 | $this->setName('content-provisioning:block:list'); 77 | $this->setDescription('List all configured CMS block entries'); 78 | parent::configure(); 79 | } 80 | 81 | /** 82 | * @param BlockEntryInterface $entry 83 | * @return string 84 | */ 85 | private function getExistsInDbValue(BlockEntryInterface $entry): string 86 | { 87 | try { 88 | $ids = []; 89 | foreach ($this->getBlocksByBlockEntry->execute($entry) as $page) { 90 | $ids[] = $page->getId(); 91 | } 92 | 93 | if (empty($ids)) { 94 | return 'no'; 95 | } 96 | 97 | return 'yes (' . implode(', ', $ids) . ')'; 98 | } catch (LocalizedException $noSuchEntityException) { 99 | return 'ERROR: ' . $noSuchEntityException->getMessage(); 100 | } 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /Model/Console/BlockResetCommand.php: -------------------------------------------------------------------------------- 1 | - TechDivision GmbH 4 | * All rights reserved 5 | * 6 | * This product includes proprietary software developed at TechDivision GmbH, Germany 7 | * For more information see http://www.techdivision.com/ 8 | * 9 | * To obtain a valid license for using this software please contact us at 10 | * license@techdivision.com 11 | */ 12 | declare(strict_types=1); 13 | 14 | namespace Firegento\ContentProvisioning\Model\Console; 15 | 16 | use Exception; 17 | use Firegento\ContentProvisioning\Api\Data\BlockEntryInterface; 18 | use Firegento\ContentProvisioning\Exception\CommandInputException; 19 | use Firegento\ContentProvisioning\Model\Command\ApplyBlockEntry; 20 | use Firegento\ContentProvisioning\Model\Command\ApplyMediaFiles; 21 | use Firegento\ContentProvisioning\Model\Query\GetBlockEntryList; 22 | use Magento\Framework\Console\Cli; 23 | use Symfony\Component\Console\Command\Command; 24 | use Symfony\Component\Console\Input\InputInterface; 25 | use Symfony\Component\Console\Input\InputOption; 26 | use Symfony\Component\Console\Output\OutputInterface; 27 | 28 | /** 29 | * @copyright Copyright (c) 2021 TechDivision GmbH - TechDivision GmbH 30 | * 31 | * @link https://www.techdivision.com/ 32 | * @author Team Zero 33 | */ 34 | class BlockResetCommand extends Command 35 | { 36 | public const COMMAND = 'content-provisioning:block:reset'; 37 | public const PARAM_KEY = 'key'; 38 | public const PARAM_IDENTIFIER = 'identifier'; 39 | 40 | /** 41 | * @var GetBlockEntryList 42 | */ 43 | private $getBlockEntryList; 44 | 45 | /** 46 | * @var ApplyBlockEntry 47 | */ 48 | private $applyBlockEntry; 49 | 50 | /** 51 | * @var ApplyMediaFiles 52 | */ 53 | private $applyMediaFiles; 54 | 55 | /** 56 | * @param GetBlockEntryList $getBlockEntryList 57 | * @param ApplyBlockEntry $applyBlockEntry 58 | * @param ApplyMediaFiles $applyMediaFiles 59 | */ 60 | public function __construct( 61 | GetBlockEntryList $getBlockEntryList, 62 | ApplyBlockEntry $applyBlockEntry, 63 | ApplyMediaFiles $applyMediaFiles 64 | ) { 65 | parent::__construct(); 66 | 67 | $this->getBlockEntryList = $getBlockEntryList; 68 | $this->applyBlockEntry = $applyBlockEntry; 69 | $this->applyMediaFiles = $applyMediaFiles; 70 | } 71 | 72 | /** 73 | * Configures the current command. 74 | */ 75 | protected function configure(): void 76 | { 77 | $this->setName(self::COMMAND); 78 | $this->setDescription('Reset CMS content'); 79 | $this->addOption(self::PARAM_KEY, 'k', InputOption::VALUE_OPTIONAL, 'Key'); 80 | $this->addOption(self::PARAM_IDENTIFIER, 'i', InputOption::VALUE_OPTIONAL, 'Identifier'); 81 | 82 | parent::configure(); 83 | } 84 | 85 | /** 86 | * @param InputInterface $input 87 | * @param OutputInterface $output 88 | * @return int 89 | */ 90 | protected function execute(InputInterface $input, OutputInterface $output): int 91 | { 92 | try { 93 | [$search, $type] = $this->parseParams($input); 94 | } catch (CommandInputException $e) { 95 | $output->writeln('' . $e->getMessage() . '' . "\n"); 96 | return Cli::RETURN_FAILURE; 97 | } 98 | 99 | $blockEntries = $this->getBlockEntries($search, $type); 100 | $updateCount = 0; 101 | 102 | if (empty($blockEntries)) { 103 | $output->writeln('block entry not found for ' . $type . ' "' . $search . '"' . "\n"); 104 | return Cli::RETURN_FAILURE; 105 | } 106 | 107 | try { 108 | foreach ($blockEntries as $blockEntry) { 109 | $this->applyBlockEntry->execute($blockEntry); 110 | $this->applyMediaFiles->execute($blockEntry); 111 | $updateCount++; 112 | } 113 | } catch (Exception $exception) { 114 | $output->writeln('' . $exception->getMessage() . '' . "\n"); 115 | return Cli::RETURN_FAILURE; 116 | } 117 | 118 | $pluralS = ($updateCount > 1) ? 's' : ''; 119 | $output->writeln('' . $updateCount . ' block' . $pluralS . ' successfully updated'); 120 | 121 | return Cli::RETURN_SUCCESS; 122 | } 123 | 124 | /** 125 | * @param InputInterface $input 126 | * @return string[] 127 | * @throws CommandInputException 128 | */ 129 | private function parseParams(InputInterface $input): array 130 | { 131 | $key = $input->getOption(self::PARAM_KEY); 132 | $identifier = $input->getOption(self::PARAM_IDENTIFIER); 133 | 134 | if ((!empty($key)) && (!empty($identifier))) { 135 | throw new CommandInputException( 136 | 'Provide either "' . self::PARAM_KEY . '" or "' . self::PARAM_IDENTIFIER . '", not both!' 137 | ); 138 | } 139 | 140 | if (!empty($key)) { 141 | $search = $key; 142 | $type = self::PARAM_KEY; 143 | } elseif (!empty($identifier)) { 144 | $search = $identifier; 145 | $type = self::PARAM_IDENTIFIER; 146 | } else { 147 | throw new CommandInputException( 148 | 'Provide either "' . self::PARAM_KEY . '" or "' . self::PARAM_IDENTIFIER . '"!' 149 | ); 150 | } 151 | 152 | return [$search, $type]; 153 | } 154 | 155 | /** 156 | * @param string $search 157 | * @param string $type 158 | * @return BlockEntryInterface[] 159 | */ 160 | private function getBlockEntries(string $search, string $type = self::PARAM_KEY): array 161 | { 162 | if (!in_array($type, [self::PARAM_KEY, self::PARAM_IDENTIFIER])) { 163 | return []; 164 | } 165 | 166 | $method = 'get' . ucfirst($type); 167 | $entries = []; 168 | 169 | foreach ($this->getBlockEntryList->get() as $blockEntry) { 170 | if ($blockEntry->$method() === $search) { 171 | $entries[] = $blockEntry; 172 | } 173 | } 174 | 175 | return $entries; 176 | } 177 | } 178 | -------------------------------------------------------------------------------- /Model/Console/PageListCommand.php: -------------------------------------------------------------------------------- 1 | getAllContentEntries = $getAllContentEntries; 40 | $this->getPagesByPageEntry = $getPagesByPageEntry; 41 | } 42 | 43 | /** 44 | * @param InputInterface $input 45 | * @param OutputInterface $output 46 | * @return void 47 | * 48 | * @SuppressWarnings(PHPMD.UnusedFormalParameter) 49 | */ 50 | protected function execute(InputInterface $input, OutputInterface $output) 51 | { 52 | $table = new Table($output); 53 | $table->setHeaders(['Key', 'Identifier', 'Stores', 'Maintained', 'Active', 'Title', 'in DB (IDs)']); 54 | 55 | /** @var EntryInterface $entry */ 56 | foreach ($this->getAllContentEntries->get() as $entry) { 57 | $table->addRow( 58 | [ 59 | $entry->getKey(), 60 | $entry->getIdentifier(), 61 | implode(', ', $entry->getStores()), 62 | $entry->isMaintained() ? 'yes' : 'no', 63 | $entry->isActive() ? 'yes' : 'no', 64 | $entry->getTitle(), 65 | $this->getExistsInDbValue($entry), 66 | ] 67 | ); 68 | } 69 | 70 | $table->render($output); 71 | } 72 | 73 | /** 74 | * @inheritdoc 75 | */ 76 | protected function configure() 77 | { 78 | $this->setName('content-provisioning:page:list'); 79 | $this->setDescription('List all configured CMS page entries'); 80 | parent::configure(); 81 | } 82 | 83 | /** 84 | * @param PageEntryInterface $entry 85 | * @return string 86 | */ 87 | private function getExistsInDbValue(PageEntryInterface $entry): string 88 | { 89 | try { 90 | $ids = []; 91 | foreach ($this->getPagesByPageEntry->execute($entry) as $page) { 92 | $ids[] = $page->getId(); 93 | } 94 | 95 | if (empty($ids)) { 96 | return 'no'; 97 | } 98 | 99 | return 'yes (' . implode(', ', $ids) . ')'; 100 | } catch (LocalizedException $noSuchEntityException) { 101 | return 'ERROR: ' . $noSuchEntityException->getMessage(); 102 | } 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /Model/PageEntry.php: -------------------------------------------------------------------------------- 1 | getData(PageEntryInterface::KEY); 17 | } 18 | 19 | /** 20 | * @param string $key 21 | */ 22 | public function setKey($key) 23 | { 24 | $this->setData(PageEntryInterface::KEY, $key); 25 | } 26 | 27 | /** 28 | * @return bool 29 | */ 30 | public function isMaintained() 31 | { 32 | return (bool)$this->getData(PageEntryInterface::IS_MAINTAINED); 33 | } 34 | 35 | /** 36 | * @param bool $isMaintained 37 | */ 38 | public function setIsMaintained($isMaintained) 39 | { 40 | $this->setData(PageEntryInterface::IS_MAINTAINED, $isMaintained); 41 | } 42 | 43 | /** 44 | * @return array 45 | */ 46 | public function getStores() 47 | { 48 | return (array)$this->getData(PageEntryInterface::STORES); 49 | } 50 | 51 | /** 52 | * @param array $stores 53 | */ 54 | public function setStores(array $stores) 55 | { 56 | $this->setData(PageEntryInterface::STORES, $stores); 57 | } 58 | 59 | /** 60 | * @return string 61 | */ 62 | public function getMediaDirectory() 63 | { 64 | return (string)$this->getData(PageEntryInterface::MEDIA_DIRECTORY); 65 | } 66 | 67 | /** 68 | * @param string $path 69 | */ 70 | public function setMediaDirectory($path) 71 | { 72 | $this->setData(PageEntryInterface::MEDIA_DIRECTORY, $path); 73 | } 74 | 75 | /** 76 | * @return array 77 | */ 78 | public function getMediaFiles() 79 | { 80 | return (array)$this->getData(PageEntryInterface::MEDIA_FILES); 81 | } 82 | 83 | /** 84 | * @param array $files 85 | */ 86 | public function setMediaFiles(array $files) 87 | { 88 | $this->setData(PageEntryInterface::MEDIA_FILES, $files); 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /Model/PageInstaller.php: -------------------------------------------------------------------------------- 1 | logger = $logger; 55 | $this->getAllPageEntries = $getAllPageEntries; 56 | $this->applyPageEntry = $applyPageEntry; 57 | $this->canApplyPageEntry = $canApplyPageEntry; 58 | $this->applyMediaFiles = $applyMediaFiles; 59 | } 60 | 61 | /** 62 | * Apply all configured CMS page changes 63 | * 64 | * @return void 65 | */ 66 | public function install(): void 67 | { 68 | foreach ($this->getAllPageEntries->get() as $pageEntry) { 69 | try { 70 | if ($this->canApplyPageEntry->execute($pageEntry)) { 71 | $this->applyPageEntry->execute($pageEntry); 72 | $this->applyMediaFiles->execute($pageEntry); 73 | } 74 | } catch (Exception $exception) { 75 | $this->logger->error( 76 | sprintf( 77 | 'An error appeared while applying cms page content: %s', 78 | $exception->getMessage() 79 | ), 80 | [ 81 | 'page-data' => $pageEntry->getData(), 82 | 'trace' => $exception->getTrace(), 83 | ] 84 | ); 85 | } 86 | } 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /Model/Query/GetBlockEntryByBlock.php: -------------------------------------------------------------------------------- 1 | getBlockEntryList = $getBlockEntryList; 40 | $this->storeIdsByStoreCodeResolver = $storeIdsByStoreCodeResolver; 41 | $this->hasStoreMatches = $hasStoreMatches; 42 | } 43 | 44 | /** 45 | * @param BlockInterface $block 46 | * @return BlockEntryInterface|null 47 | */ 48 | public function execute(BlockInterface $block): ?BlockEntryInterface 49 | { 50 | foreach ($this->getBlockEntryList->get() as $entry) { 51 | if ($block->getIdentifier() !== $entry->getIdentifier()) { 52 | continue; 53 | } 54 | 55 | try { 56 | $entryStoreIds = $this->storeIdsByStoreCodeResolver->execute($entry->getStores()); 57 | $entityStoreIds = $this->retrieveStoreIds($block); 58 | if ($this->hasStoreMatches->execute($entryStoreIds, $entityStoreIds)) { 59 | return $entry; 60 | } 61 | } catch (NoSuchEntityException $e) { 62 | continue; 63 | } 64 | } 65 | 66 | return null; 67 | } 68 | 69 | /** 70 | * @param BlockInterface $block 71 | * @return array 72 | */ 73 | public function retrieveStoreIds(BlockInterface $block): array 74 | { 75 | if ($block instanceof Block) { 76 | return $block->getStores(); 77 | } 78 | 79 | return []; 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /Model/Query/GetBlockEntryByKey.php: -------------------------------------------------------------------------------- 1 | configuration = $configuration; 32 | $this->blockEntryFactory = $blockEntryFactory; 33 | } 34 | 35 | /** 36 | * @param string $key 37 | * @return BlockEntryInterface 38 | * @throws NotFoundException 39 | */ 40 | public function get(string $key): BlockEntryInterface 41 | { 42 | $blocks = $this->configuration->getList()['blocks'] ?? []; 43 | 44 | if (!array_key_exists($key, $blocks)) { 45 | throw new NotFoundException(__('Block with key %1 not found.', $key)); 46 | } 47 | 48 | $item = $this->blockEntryFactory->create(['data' => $blocks[$key]]); 49 | 50 | return $item; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /Model/Query/GetBlockEntryList.php: -------------------------------------------------------------------------------- 1 | configuration = $configuration; 36 | $this->blockEntryFactory = $blockEntryFactory; 37 | } 38 | 39 | /** 40 | * Prepare items array by transforming all configured entries into content entry data models 41 | */ 42 | private function prepare() 43 | { 44 | if (empty($this->items)) { 45 | $blocks = $this->configuration->getList()['blocks'] ?? []; 46 | foreach ($blocks as $block) { 47 | $item = $this->blockEntryFactory->create(['data' => $block]); 48 | $this->items[] = $item; 49 | } 50 | } 51 | } 52 | 53 | /** 54 | * @return BlockEntryInterface[] 55 | */ 56 | public function get() 57 | { 58 | $this->prepare(); 59 | return $this->items; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /Model/Query/GetBlocksByBlockEntry.php: -------------------------------------------------------------------------------- 1 | blockRepository = $blockRepository; 42 | $this->searchCriteriaBuilder = $searchCriteriaBuilder; 43 | $this->storeIdsByStoreCodeResolver = $storeIdsByStoreCodeResolver; 44 | } 45 | 46 | /** 47 | * @param BlockEntryInterface $pageEntry 48 | * @return BlockInterface[] 49 | * @throws NoSuchEntityException 50 | * @throws LocalizedException 51 | */ 52 | public function execute(BlockEntryInterface $pageEntry): array 53 | { 54 | $searchCriteria = $this->searchCriteriaBuilder 55 | ->addFilter('identifier', $pageEntry->getIdentifier()) 56 | ->addFilter('store_id', $this->storeIdsByStoreCodeResolver->execute($pageEntry->getStores())) 57 | ->create(); 58 | $searchResult = $this->blockRepository->getList($searchCriteria); 59 | return $searchResult->getItems(); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /Model/Query/GetFirstBlockByBlockEntry.php: -------------------------------------------------------------------------------- 1 | getBlocksByBlockEntry = $getBlocksByBlockEntry; 25 | } 26 | 27 | /** 28 | * @param BlockEntryInterface $blockEntry 29 | * @return BlockInterface|null 30 | * @throws NoSuchEntityException 31 | * @throws LocalizedException 32 | */ 33 | public function execute(BlockEntryInterface $blockEntry): ?BlockInterface 34 | { 35 | $pages = $this->getBlocksByBlockEntry->execute($blockEntry); 36 | return array_shift($pages); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Model/Query/GetFirstPageByPageEntry.php: -------------------------------------------------------------------------------- 1 | getPagesByPageEntry = $getPagesByPageEntry; 25 | } 26 | 27 | /** 28 | * @param PageEntryInterface $pageEntry 29 | * @return PageInterface|null 30 | * @throws NoSuchEntityException 31 | * @throws LocalizedException 32 | */ 33 | public function execute(PageEntryInterface $pageEntry): ?PageInterface 34 | { 35 | $pages = $this->getPagesByPageEntry->execute($pageEntry); 36 | return array_shift($pages); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Model/Query/GetPageEntryByKey.php: -------------------------------------------------------------------------------- 1 | configuration = $configuration; 32 | $this->pageEntryFactory = $pageEntryFactory; 33 | } 34 | 35 | /** 36 | * @param string $key 37 | * 38 | * @return PageEntryInterface 39 | * @throws NotFoundException 40 | */ 41 | public function get(string $key) 42 | { 43 | $pages = $this->configuration->getList()['pages'] ?? []; 44 | 45 | if (!array_key_exists($key, $pages)) { 46 | throw new NotFoundException(__('Page with key %1 not found.', $key)); 47 | } 48 | 49 | $item = $this->pageEntryFactory->create(['data' => $pages[$key]]); 50 | 51 | return $item; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /Model/Query/GetPageEntryByPage.php: -------------------------------------------------------------------------------- 1 | getPageEntryList = $getPageEntryList; 40 | $this->storeIdsByStoreCodeResolver = $storeIdsByStoreCodeResolver; 41 | $this->hasStoreMatches = $hasStoreMatches; 42 | } 43 | 44 | /** 45 | * @param PageInterface $page 46 | * @return PageEntryInterface|null 47 | */ 48 | public function execute(PageInterface $page): ?PageEntryInterface 49 | { 50 | foreach ($this->getPageEntryList->get() as $entry) { 51 | if ($page->getIdentifier() !== $entry->getIdentifier()) { 52 | continue; 53 | } 54 | 55 | try { 56 | $entryStoreIds = $this->storeIdsByStoreCodeResolver->execute($entry->getStores()); 57 | $pageStoreIds = $this->retrieveStoreIds($page); 58 | if ($this->hasStoreMatches->execute($entryStoreIds, $pageStoreIds)) { 59 | return $entry; 60 | } 61 | } catch (NoSuchEntityException $e) { 62 | continue; 63 | } 64 | } 65 | 66 | return null; 67 | } 68 | 69 | /** 70 | * @param PageInterface $page 71 | * @return int[] 72 | */ 73 | public function retrieveStoreIds(PageInterface $page) 74 | { 75 | if ($page instanceof Page) { 76 | return $page->getStores(); 77 | } 78 | 79 | return []; 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /Model/Query/GetPageEntryList.php: -------------------------------------------------------------------------------- 1 | configuration = $configuration; 36 | $this->pageEntryFactory = $pageEntryFactory; 37 | } 38 | 39 | /** 40 | * Prepare items array by transforming all configured entries into content entry data models 41 | */ 42 | private function prepare() 43 | { 44 | if (empty($this->items)) { 45 | $pages = $this->configuration->getList()['pages'] ?? []; 46 | foreach ($pages as $data) { 47 | $item = $this->pageEntryFactory->create(['data' => $data]); 48 | $this->items[] = $item; 49 | } 50 | } 51 | } 52 | 53 | /** 54 | * @return PageEntryInterface[] 55 | */ 56 | public function get() 57 | { 58 | $this->prepare(); 59 | return $this->items; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /Model/Query/GetPagesByPageEntry.php: -------------------------------------------------------------------------------- 1 | searchCriteriaBuilder = $searchCriteriaBuilder; 42 | $this->pageRepository = $pageRepository; 43 | $this->storeIdsByStoreCodeResolver = $storeIdsByStoreCodeResolver; 44 | } 45 | 46 | /** 47 | * @param PageEntryInterface $pageEntry 48 | * @return PageInterface[] 49 | * @throws NoSuchEntityException 50 | * @throws LocalizedException 51 | */ 52 | public function execute(PageEntryInterface $pageEntry): array 53 | { 54 | $searchCriteria = $this->searchCriteriaBuilder 55 | ->addFilter('identifier', $pageEntry->getIdentifier()) 56 | ->addFilter('store_id', $this->storeIdsByStoreCodeResolver->execute($pageEntry->getStores())) 57 | ->create(); 58 | $searchResult = $this->pageRepository->getList($searchCriteria); 59 | return $searchResult->getItems(); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /Model/Query/HasDefaultBlockConfiguration.php: -------------------------------------------------------------------------------- 1 | blockRepository = $pageRepository; 30 | 31 | $this->getBlockEntryByBlock = $getPageEntryByPage; 32 | } 33 | 34 | /** 35 | * @param int $entityId 36 | * @return bool 37 | */ 38 | public function execute(int $entityId): bool 39 | { 40 | try { 41 | $block = $this->blockRepository->getById($entityId); 42 | $entry = $this->getBlockEntryByBlock->execute($block); 43 | return $entry ? true : false; 44 | } catch (LocalizedException $noSuchEntityException) { 45 | return false; 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /Model/Query/HasDefaultPageConfiguration.php: -------------------------------------------------------------------------------- 1 | pageRepository = $pageRepository; 30 | 31 | $this->getPageEntryByPage = $getPageEntryByPage; 32 | } 33 | 34 | /** 35 | * @param int $entityId 36 | * @return bool 37 | */ 38 | public function execute(int $entityId): bool 39 | { 40 | try { 41 | $page = $this->pageRepository->getById($entityId); 42 | $entry = $this->getPageEntryByPage->execute($page); 43 | return $entry ? true : false; 44 | } catch (LocalizedException $noSuchEntityException) { 45 | return false; 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /Model/Query/HasStoreMatches.php: -------------------------------------------------------------------------------- 1 | blockRepository = $blockRepository; 31 | $this->getBlockEntryByBlock = $getBlockEntryByBlock; 32 | } 33 | 34 | /** 35 | * @param int $entityId 36 | * @return bool 37 | */ 38 | public function execute(int $entityId): bool 39 | { 40 | try { 41 | $page = $this->blockRepository->getById($entityId); 42 | $entry = $this->getBlockEntryByBlock->execute($page); 43 | return $entry && $entry->isMaintained(); 44 | } catch (LocalizedException $noSuchEntityException) { 45 | return false; 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /Model/Query/IsMaintainedContentRequested.php: -------------------------------------------------------------------------------- 1 | isPageMaintained = $isPageMaintained; 29 | $this->isBlockMaintained = $isBlockMaintained; 30 | } 31 | 32 | /** 33 | * @param RequestInterface $request 34 | * @return bool 35 | */ 36 | public function execute(RequestInterface $request): bool 37 | { 38 | if ($this->isCmsPageEditFormRequested($request)) { 39 | return $this->isPageMaintained->execute($this->getPageIdFromRequest($request)); 40 | } 41 | 42 | if ($this->isCmsBlockEditFormRequested($request)) { 43 | return $this->isBlockMaintained->execute($this->getBlockIdFromRequest($request)); 44 | } 45 | 46 | return false; 47 | } 48 | 49 | /** 50 | * @param RequestInterface $request 51 | * @return bool 52 | */ 53 | private function isCmsPageEditFormRequested(RequestInterface $request): bool 54 | { 55 | return $request->getModuleName() === 'cms' 56 | && $request->getActionName() === 'edit' 57 | && $request->getParam('page_id', false); 58 | } 59 | 60 | /** 61 | * @param RequestInterface $request 62 | * @return bool 63 | */ 64 | private function isCmsBlockEditFormRequested(RequestInterface $request): bool 65 | { 66 | return $request->getModuleName() === 'cms' 67 | && $request->getActionName() === 'edit' 68 | && $request->getParam('block_id', false); 69 | } 70 | 71 | /** 72 | * @param RequestInterface $request 73 | * @return int 74 | */ 75 | private function getPageIdFromRequest(RequestInterface $request): int 76 | { 77 | return (int)$request->getParam('page_id', null); 78 | } 79 | 80 | /** 81 | * @param RequestInterface $request 82 | * @return int 83 | */ 84 | private function getBlockIdFromRequest(RequestInterface $request): int 85 | { 86 | return (int)$request->getParam('block_id', null); 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /Model/Query/IsPageMaintained.php: -------------------------------------------------------------------------------- 1 | pageRepository = $pageRepository; 30 | 31 | $this->getPageEntryByPage = $getPageEntryByPage; 32 | } 33 | 34 | /** 35 | * @param int $entityId 36 | * @return bool 37 | */ 38 | public function execute(int $entityId): bool 39 | { 40 | try { 41 | $page = $this->pageRepository->getById($entityId); 42 | $entry = $this->getPageEntryByPage->execute($page); 43 | return $entry && $entry->isMaintained(); 44 | } catch (LocalizedException $noSuchEntityException) { 45 | return false; 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /Model/Resolver/ContentResolverProvider.php: -------------------------------------------------------------------------------- 1 | ContentResolverInterface::class] 29 | ) 30 | ); 31 | } 32 | } 33 | $this->contentResolvers = $contentResolvers; 34 | } 35 | 36 | /** 37 | * @param string $typeCode 38 | * @return ContentResolverInterface 39 | * @throws LocalizedException 40 | */ 41 | public function get(string $typeCode): ContentResolverInterface 42 | { 43 | if (!isset($this->contentResolvers[$typeCode])) { 44 | throw new LocalizedException( 45 | __( 46 | 'There is no content resolver defined for given type code %code', 47 | ['code' => $typeCode] 48 | ) 49 | ); 50 | } 51 | 52 | return $this->contentResolvers[$typeCode]; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /Model/Resolver/FileContentResolver.php: -------------------------------------------------------------------------------- 1 | pathResolver = $pathResolver; 32 | $this->fileSystemDriver = $fileSystemDriver; 33 | } 34 | 35 | /** 36 | * @param DOMElement $node 37 | * @return string 38 | * @throws LocalizedException 39 | */ 40 | public function execute(DOMElement $node): string 41 | { 42 | $path = $this->pathResolver->execute((string)$node->textContent); 43 | if (!$this->fileSystemDriver->isFile($path)) { 44 | throw new LocalizedException(__('Given content file %file does not exists.', ['file' => $path])); 45 | } 46 | 47 | return $this->fileSystemDriver->fileGetContents($path); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /Model/Resolver/PathResolver.php: -------------------------------------------------------------------------------- 1 | moduleReader = $moduleReader; 30 | $this->directoryList = $directoryList; 31 | } 32 | 33 | /** 34 | * @param string $path 35 | * @return string 36 | */ 37 | public function execute(string $path): string 38 | { 39 | if (strpos($path, '::') !== false) { 40 | $pathParts = explode('::', $path, 2); 41 | $moduleName = $pathParts[0]; 42 | $filePath = $pathParts[1]; 43 | $moduleDirectory = $this->moduleReader->getModuleDir('', $moduleName); 44 | return implode( 45 | DIRECTORY_SEPARATOR, 46 | [ 47 | $moduleDirectory, 48 | $filePath, 49 | ] 50 | ); 51 | } else { 52 | return implode( 53 | DIRECTORY_SEPARATOR, 54 | [ 55 | $this->directoryList->getRoot(), 56 | $path, 57 | ] 58 | ); 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /Model/Resolver/PlainContentResolver.php: -------------------------------------------------------------------------------- 1 | textContent; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Model/Resolver/StoreCodeResolver.php: -------------------------------------------------------------------------------- 1 | storeManager = $storeManager; 24 | } 25 | 26 | /** 27 | * @param string $code 28 | * @return array 29 | * @throws NoSuchEntityException 30 | */ 31 | public function execute(string $code): array 32 | { 33 | $output = []; 34 | 35 | if ($code === '*') { 36 | $output[] = $this->storeManager->getStore('admin')->getCode(); 37 | } else { 38 | $output[] = $this->storeManager->getStore($code)->getCode(); 39 | } 40 | 41 | return $output; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /Model/Resolver/StoreIdsByStoreCodeResolver.php: -------------------------------------------------------------------------------- 1 | storeManager = $storeManager; 24 | } 25 | 26 | /** 27 | * @param array $storeCodes 28 | * @return int[] 29 | * @throws NoSuchEntityException 30 | */ 31 | public function execute(array $storeCodes): array 32 | { 33 | $storeIds = [Store::DEFAULT_STORE_ID]; 34 | foreach ($storeCodes as $storeCode) { 35 | $storeIds[] = $this->storeManager->getStore($storeCode)->getId(); 36 | } 37 | return $storeIds; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /Model/Validator/CanApplyBlockEntry.php: -------------------------------------------------------------------------------- 1 | getFirstBlockByBlockEntry = $getFirstBlockByBlockEntry; 24 | } 25 | 26 | /** 27 | * @param BlockEntryInterface $blockEntry 28 | * @return bool 29 | * @throws NoSuchEntityException 30 | */ 31 | public function execute(BlockEntryInterface $blockEntry): bool 32 | { 33 | $page = $this->getFirstBlockByBlockEntry->execute($blockEntry); 34 | return $page === null || $blockEntry->isMaintained(); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /Model/Validator/CanApplyPageEntry.php: -------------------------------------------------------------------------------- 1 | getFirstPageByPageEntry = $getFirstPageByPageEntry; 24 | } 25 | 26 | /** 27 | * @param PageEntryInterface $pageEntry 28 | * @return bool 29 | * @throws NoSuchEntityException 30 | */ 31 | public function execute(PageEntryInterface $pageEntry): bool 32 | { 33 | $page = $this->getFirstPageByPageEntry->execute($pageEntry); 34 | return $page === null || $pageEntry->isMaintained(); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # FireGento Magento 2 Content Provisioning 2 | 3 | This module was developed during a Magento Hackathon organized by FireGento e.V. (https://firegento.com/). 4 | 5 | > **ℹ️ Maintenance** 6 | > 7 | > This module is maintained by [TechDivision](https://www.techdivision.com/). Therefore, there we created a mirror repository, which allow us 8 | > to run automated quality checks on our internal infrastructure for this module. Please see https://gitlab.met.tdintern.de/techdivision-public/m2-content-provisioning 9 | > for details. Please feel free to contact us, if you have questions regarding the repository structure or mirroring. 10 | 11 | #### Build-Status (`develop` branch) 12 | [![pipeline status](https://gitlab.met.tdintern.de/techdivision-public/m2-content-provisioning/badges/develop/pipeline.svg)](https://gitlab.met.tdintern.de/techdivision-public/m2-content-provisioning/-/commits/develop) 13 | 14 | ## The idea behind this module 15 | 16 | It is a common requirement, that some parts of content (like CMS pages or blocks) need be deployed within a release. 17 | There are content entries, which should be maintained by code all the time and some content just needs delivered one 18 | time to each system. 19 | 20 | In most cases such requirements will be solved by setup scripts (or setup patches), which is possible way there is no 21 | chance to declare the responsibility for each content entity. 22 | 23 | This module allows you to declare such content entries via XML file and ensures, that this declaration will be applied 24 | to database on each `setup:upgrade` run. 25 | 26 | ## Install with composer 27 | 28 | ```bash 29 | composer require firegento/magento2-content-provisioning 30 | ``` 31 | 32 | ## How it works 33 | 34 | After installing this module you can create your own `content_provisioning.xml` in each of your modules. 35 | 36 | ## Example configurations 37 | 38 | ### Minimal configuration for a page 39 | 40 | ```xml 41 | 42 | 43 | 44 | Page Title 45 | Your_Module::path/to/content.html 46 | 47 | ... 48 | 49 | ``` 50 | 51 | ### Full configuration for a page 52 | 53 | ```xml 54 | 55 | 56 | 57 | Page Title 58 | Your_Module::path/to/content.html 59 | Your_Module::path/to/media 60 | 61 | 62 | 63 | 64 | 65 | 66 | SEO Page Title 67 | Some, SEO, keywords 68 | SEO description 69 | 70 | 71 | 3columns 72 | bar]]> 73 | 74 | 75 | 2019-03-03 76 | 2019-03-29 77 | 3columns 78 | 3 79 | 80 | 81 | ... 82 | 83 | ``` 84 | 85 | ### Minimal configuration for a block 86 | 87 | ```xml 88 | 89 | 90 | 91 | Test Block 1 92 | test foobar Aenean commodo ligula eget dolor aenean massa]]> 93 | 94 | ... 95 | 96 | ``` 97 | 98 | ### Full configuration for a block 99 | 100 | ```xml 101 | 102 | 103 | 104 | Test Block 2 105 | Your_Module::path/to/content.html 106 | Your_Module::path/to/media 107 | 108 | 109 | 110 | 111 | 112 | 113 | ... 114 | 115 | ``` 116 | 117 | ## Some explanation 118 | 119 | ### `key`-Attribute 120 | 121 | The `key` attribute is required in order to merge all content provisioning configurations across all modules. It is like 122 | the `name` attribute for layout blocks... 123 | 124 | #### You could use `identifier` - or? 125 | 126 | No, identifier is not unique since the same identifier can be used for multiple store views. 127 | 128 | ### `maintained`-Attribute 129 | 130 | With this attribute you define whether this content should be applied every time or even only once. Is the value 131 | `false` the content will only be persisted, if there is no `identifier` for the defined stores present in database. 132 | 133 | ### `content`-Node 134 | 135 | This node provide THE content for your page or block. It can be added as node value in a CDATA block or as a file path, 136 | which is relative to your Magento instance or prefixed by a module namespace. In order to use files you need to add 137 | the `type="file"` attribute to the content node. 138 | 139 | ### `stores`-Node 140 | 141 | This node is optional. If it is not defined, the block or page will be applied to all stores. A "maintained" entry will 142 | also be applied to stores, which will be created in the future after re-running `setup:upgrade` command. You can also 143 | use the 'wildcard' `*` in order to define that the content should be applied to all stores. 144 | 145 | ### `media_directory`-Node (since version 1.2.0) 146 | 147 | Specifies the directory for media files. Each used media file in the content and present in media source directory will 148 | be copied to Magento's `pub/media` directory. Sub-directory structure should be same like inspected it to be 149 | in `pub/media`. Only existing and used media files will be copied. 150 | 151 | ## Executing integration tests on local environment 152 | 153 | ```bash 154 | # Create a new Magento instance 155 | composer create-project --repository=https://repo.magento.com/ magento/project-community-edition magento 156 | 157 | # Install content provisioning extension 158 | cd magento 159 | composer require firegento/magento2-content-provisioning 160 | 161 | # Update database configuration for integration tests 162 | mv dev/tests/integration/etc/install-config-mysql.php.dist dev/tests/integration/etc/install-config-mysql.php 163 | vi dev/tests/integration/etc/install-config-mysql.php 164 | 165 | # Execute tests 166 | php vendor/bin/phpunit -c $(pwd)/vendor/firegento/magento2-content-provisioning/Test/Integration/phpunit.xml 167 | ``` 168 | 169 | ## Console Commands 170 | ```shell 171 | # reset a CMS block (all localizations) by its key 172 | bin/magento content-provisioning:block:reset --key "myKey" 173 | bin/magento content-provisioning:block:reset -k "myKey" 174 | 175 | # reset a CMS blocks by its identifier 176 | bin/magento content-provisioning:block:reset --identifier "myIdentifier" 177 | bin/magento content-provisioning:block:reset -i "myIdentifier" 178 | 179 | # add a CMS block by key 180 | bin/magento content-provisioning:block:apply "myKey" 181 | 182 | # add a CMS page by key 183 | bin/magento content-provisioning:page:apply "myKey" 184 | 185 | # list all configured CMS block entries 186 | bin/magento content-provisioning:block:list 187 | 188 | # list all configured CMS page entries 189 | bin/magento content-provisioning:page:list 190 | ``` 191 | 192 | ## Issues and planned features 193 | 194 | See issues to see what's planed next: https://github.com/magento-hackathon/m2-content-provisioning/issues 195 | Feel free to add your ideas there. 196 | 197 | ## Changelog 198 | 199 | See [changelog file](CHANGELOG.md) 200 | 201 | ## Extensions for this extension ;) 202 | 203 | For [`magenerds/pagedesigner`](https://github.com/Magenerds/PageDesigner) there is an module, which extends this content 204 | provisioning 205 | module: [`techdivision/pagedesigner-content-provisioning`](https://github.com/techdivision/pagedesigner-content-provisioning) 206 | . 207 | 208 | -------------------------------------------------------------------------------- /Setup/RecurringData.php: -------------------------------------------------------------------------------- 1 | pageInstaller = $pageInstaller; 36 | $this->blockInstaller = $blockInstaller; 37 | } 38 | 39 | /** 40 | * @param ModuleDataSetupInterface $setup 41 | * @param ModuleContextInterface $context 42 | * 43 | * @SuppressWarnings(PHPMD.UnusedFormalParameter) 44 | */ 45 | public function install(ModuleDataSetupInterface $setup, ModuleContextInterface $context) 46 | { 47 | $this->pageInstaller->install(); 48 | $this->blockInstaller->install(); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /Test/Integration/Model/BlockInstaller/InstallTest.php: -------------------------------------------------------------------------------- 1 | initBlockEntries(); 11 | 12 | $this->installer->install(); 13 | 14 | // Verify, that block are in database like defined 15 | $this->compareBlockWithEntryForStore(1); 16 | $this->compareBlockWithEntryForStore(2); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Test/Integration/Model/BlockInstaller/TestCase.php: -------------------------------------------------------------------------------- 1 | getBlockEntryListMock = self::getMockBuilder(GetBlockEntryList::class) 56 | ->disableOriginalConstructor() 57 | ->getMock(); 58 | 59 | $this->installer = Bootstrap::getObjectManager() 60 | ->create(BlockInstaller::class, ['getAllBlockEntries' => $this->getBlockEntryListMock]); 61 | 62 | $this->blockEntryInterfaceFactory = Bootstrap::getObjectManager() 63 | ->create(BlockEntryInterfaceFactory::class); 64 | 65 | $this->storeManager = Bootstrap::getObjectManager() 66 | ->create(StoreManagerInterface::class); 67 | 68 | $this->getFirstBlockByBlockEntry = Bootstrap::getObjectManager() 69 | ->create(GetFirstBlockByBlockEntry::class); 70 | } 71 | 72 | protected function tearDown(): void 73 | { 74 | parent::tearDown(); 75 | 76 | $connection = Bootstrap::getObjectManager()->get(ResourceConnection::class); 77 | $connection->getConnection()->delete( 78 | $connection->getTableName('cms_block'), 79 | [ 80 | 'identifier LIKE ?' => 'firegento-content-provisioning-test-%', 81 | ] 82 | ); 83 | } 84 | 85 | protected function initBlockEntries() 86 | { 87 | $this->blockEntries[1] = $this->blockEntryInterfaceFactory->create( 88 | [ 89 | 'data' => [ 90 | BlockEntryInterface::TITLE => 'Test Block 1', 91 | BlockEntryInterface::CONTENT => '

test foobar Aenean commodo ligula eget ' 92 | . 'dolor aenean massa

', 93 | BlockEntryInterface::KEY => 'test.block.1', 94 | BlockEntryInterface::IDENTIFIER => 'firegento-content-provisioning-test-1', 95 | BlockEntryInterface::IS_ACTIVE => false, 96 | BlockEntryInterface::IS_MAINTAINED => true, 97 | BlockEntryInterface::STORES => ['admin'], 98 | ] 99 | ] 100 | ); 101 | 102 | $this->blockEntries[2] = $this->blockEntryInterfaceFactory->create( 103 | [ 104 | 'data' => [ 105 | BlockEntryInterface::TITLE => 'Test Block 2', 106 | BlockEntryInterface::CONTENT => file_get_contents( 107 | __DIR__ . '/../../_files/content/dummy-content.html' 108 | ), 109 | BlockEntryInterface::KEY => 'test.block.2', 110 | BlockEntryInterface::IDENTIFIER => 'firegento-content-provisioning-test-2', 111 | BlockEntryInterface::IS_ACTIVE => true, 112 | BlockEntryInterface::IS_MAINTAINED => false, 113 | BlockEntryInterface::STORES => ['default', 'admin'], 114 | ] 115 | ] 116 | ); 117 | 118 | $this->getBlockEntryListMock->method('get')->willReturn($this->blockEntries); 119 | } 120 | 121 | protected function compareBlockWithEntryForStore($entryIndex) 122 | { 123 | $entry = $this->blockEntries[$entryIndex]; 124 | $block = $this->getBlockByBlockEntry($entry); 125 | 126 | $this->assertSame($block->getTitle(), $entry->getTitle()); 127 | $this->assertSame($block->getContent(), $entry->getContent()); 128 | $this->assertSame($block->isActive(), $entry->isActive()); 129 | } 130 | 131 | /** 132 | * @param BlockEntryInterface $entry 133 | * @return BlockInterface 134 | * @throws NoSuchEntityException 135 | * @throws LocalizedException 136 | */ 137 | protected function getBlockByBlockEntry(BlockEntryInterface $entry): BlockInterface 138 | { 139 | return $this->getFirstBlockByBlockEntry->execute($entry); 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /Test/Integration/Model/BlockInstaller/UpdateMaintainedTest.php: -------------------------------------------------------------------------------- 1 | initBlockEntries(); 11 | 12 | $this->installer->install(); 13 | 14 | // Change block entry values 15 | $this->blockEntries[1]->setTitle('Changed Block 1'); 16 | $this->blockEntries[1]->setIsActive(true); 17 | $this->blockEntries[1]->setContent('New Content'); 18 | 19 | $this->blockEntries[2]->setTitle('Changed Block 2'); 20 | $this->blockEntries[2]->setIsActive(true); 21 | $this->blockEntries[2]->setContent('New Content'); 22 | 23 | // Execute installer a second time 24 | $this->installer->install(); 25 | 26 | // Verify that first block was updated 27 | $this->compareBlockWithEntryForStore(1); 28 | 29 | // Verify that second block did not change 30 | $entry = $this->blockEntries[2]; 31 | $block = $this->getBlockByBlockEntry($entry); 32 | 33 | $this->assertNotSame($block->getTitle(), $entry->getTitle()); 34 | $this->assertNotSame($block->getContent(), $entry->getContent()); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /Test/Integration/Model/Config/ConverterTest.php: -------------------------------------------------------------------------------- 1 | model = Bootstrap::getObjectManager() 22 | ->create(Converter::class); 23 | } 24 | 25 | public function testConverter() 26 | { 27 | $pathFiles = __DIR__ . '/../../_files'; 28 | $expectedResult = require $pathFiles . '/result.php'; 29 | $path = $pathFiles . '/content_provisioning.xml'; 30 | $domDocument = new DOMDocument(); 31 | $domDocument->load($path); 32 | $result = $this->model->convert($domDocument); 33 | $this->assertEquals($expectedResult, $result); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /Test/Integration/Model/Config/Parser/Query/FetchMediaFilesChainTest.php: -------------------------------------------------------------------------------- 1 | parser1 = self::getMockBuilder(MediaFilesParserInterface::class) 32 | ->disableOriginalConstructor() 33 | ->getMock(); 34 | 35 | $this->parser2 = self::getMockBuilder(MediaFilesParserInterface::class) 36 | ->disableOriginalConstructor() 37 | ->getMock(); 38 | 39 | $this->chain = Bootstrap::getObjectManager() 40 | ->create( 41 | FetchMediaFilesChain::class, 42 | [ 43 | 'parsers' => [ 44 | $this->parser1, 45 | $this->parser2, 46 | ] 47 | ] 48 | ); 49 | } 50 | 51 | public function testMergingData() 52 | { 53 | $this->parser1->method('execute')->willReturn( 54 | [ 55 | 'path/to/file1.png', 56 | 'path/to/file2.png', 57 | 'file3.png', 58 | ] 59 | ); 60 | $this->parser2->method('execute')->willReturn( 61 | [ 62 | 'file3.png', 63 | 'some/other/path.jpg', 64 | ] 65 | ); 66 | 67 | $result = $this->chain->execute(''); 68 | 69 | $this->assertSame( 70 | [ 71 | 'path/to/file1.png', 72 | 'path/to/file2.png', 73 | 'file3.png', 74 | 'file3.png', 75 | 'some/other/path.jpg', 76 | ], 77 | $result 78 | ); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /Test/Integration/Model/Config/_files/content_provisioning.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Test Page 1 5 | Firegento_ContentProvisioning::Test/Integration/Model/Config/_files/test-files/file-1.html 6 | 7 | 8 | Title 2 9 | Firegento_ContentProvisioning::Test/Integration/Model/Config/_files/test-files/file-1.html 10 | 11 | 12 | 13 | 14 | 15 | SEO Page Title 16 | Some, SEO, keywords 17 | SEO description 18 | 19 | 20 | 3columns 21 | bar]]> 22 | 23 | 24 | 2019-03-03 25 | 2019-03-29 26 | 3columns 27 | 3 28 | 29 | 30 | 31 | Page With Images 32 | Firegento_ContentProvisioning::Test/Integration/Model/Config/_files/test-files/content-with-images-1.html 33 | Firegento_ContentProvisioning::Test/Integration/Model/Config/_files/test-files/media 34 | 35 | 36 | Test Block 1 37 | test foobar Aenean commodo ligula eget dolor aenean massa]]> 38 | 39 | 40 | Test Block 2 41 | Firegento_ContentProvisioning::Test/Integration/Model/Config/_files/test-files/file-1.html 42 | 43 | 44 | 45 | 46 | 47 | 48 | Block With Images 49 | Firegento_ContentProvisioning::Test/Integration/Model/Config/_files/test-files/content-with-images-1.html 50 | Firegento_ContentProvisioning::Test/Integration/Model/Config/_files/test-files/media 51 | 52 | 53 | -------------------------------------------------------------------------------- /Test/Integration/Model/Config/_files/result.php: -------------------------------------------------------------------------------- 1 | [ 8 | 'test.page.1' => [ 9 | PageEntryInterface::TITLE => 'Test Page 1', 10 | PageEntryInterface::CONTENT => file_get_contents(__DIR__ . '/test-files/file-1.html'), 11 | PageEntryInterface::KEY => 'test.page.1', 12 | PageEntryInterface::IDENTIFIER => 'test-page-1', 13 | PageEntryInterface::IS_ACTIVE => true, 14 | PageEntryInterface::IS_MAINTAINED => true, 15 | PageEntryInterface::STORES => ['admin'], 16 | PageEntryInterface::CONTENT_HEADING => '', 17 | PageEntryInterface::MEDIA_DIRECTORY => null, 18 | PageEntryInterface::MEDIA_FILES => [], 19 | ], 20 | 'test.page.2' => [ 21 | PageEntryInterface::TITLE => 'Title 2', 22 | PageEntryInterface::CONTENT => file_get_contents(__DIR__ . '/test-files/file-1.html'), 23 | PageEntryInterface::KEY => 'test.page.2', 24 | PageEntryInterface::IDENTIFIER => 'test-page-2', 25 | PageEntryInterface::IS_ACTIVE => false, 26 | PageEntryInterface::IS_MAINTAINED => false, 27 | PageEntryInterface::STORES => ['default', 'admin'], 28 | PageEntryInterface::CONTENT_HEADING => 'New Page Heading 2', 29 | PageEntryInterface::META_TITLE => 'SEO Page Title', 30 | PageEntryInterface::META_KEYWORDS => 'Some, SEO, keywords', 31 | PageEntryInterface::META_DESCRIPTION => 'SEO description', 32 | PageEntryInterface::PAGE_LAYOUT => '3columns', 33 | PageEntryInterface::LAYOUT_UPDATE_XML => 'bar', 34 | PageEntryInterface::CUSTOM_THEME_FROM => '2019-03-03', 35 | PageEntryInterface::CUSTOM_THEME_TO => '2019-03-29', 36 | PageEntryInterface::CUSTOM_THEME => '3', 37 | PageEntryInterface::CUSTOM_ROOT_TEMPLATE => '3columns', 38 | PageEntryInterface::MEDIA_DIRECTORY => null, 39 | PageEntryInterface::MEDIA_FILES => [], 40 | ], 41 | 'test.page.3' => [ 42 | PageEntryInterface::TITLE => 'Page With Images', 43 | PageEntryInterface::CONTENT => file_get_contents(__DIR__ . '/test-files/content-with-images-1.html'), 44 | PageEntryInterface::KEY => 'test.page.3', 45 | PageEntryInterface::IDENTIFIER => 'test-page-3', 46 | PageEntryInterface::IS_ACTIVE => true, 47 | PageEntryInterface::IS_MAINTAINED => true, 48 | PageEntryInterface::STORES => ['admin'], 49 | PageEntryInterface::CONTENT_HEADING => '', 50 | PageEntryInterface::MEDIA_DIRECTORY => __DIR__ . '/test-files/media', 51 | PageEntryInterface::MEDIA_FILES => [ 52 | 'image-1.png', 53 | 'some-test-image.png', 54 | 'foobar/test.png', 55 | ], 56 | ], 57 | ], 58 | 'blocks' => [ 59 | 'test.block.1' => [ 60 | BlockEntryInterface::TITLE => 'Test Block 1', 61 | BlockEntryInterface::CONTENT => '

test foobar Aenean commodo ligula eget dolor aenean massa

', 62 | BlockEntryInterface::KEY => 'test.block.1', 63 | BlockEntryInterface::IDENTIFIER => 'test-block-1', 64 | BlockEntryInterface::IS_ACTIVE => true, 65 | BlockEntryInterface::IS_MAINTAINED => true, 66 | BlockEntryInterface::STORES => ['admin'], 67 | BlockEntryInterface::MEDIA_DIRECTORY => null, 68 | BlockEntryInterface::MEDIA_FILES => [], 69 | ], 70 | 'test.block.2' => [ 71 | BlockEntryInterface::TITLE => 'Test Block 2', 72 | BlockEntryInterface::CONTENT => file_get_contents(__DIR__ . '/test-files/file-1.html'), 73 | BlockEntryInterface::KEY => 'test.block.2', 74 | BlockEntryInterface::IDENTIFIER => 'test-block-2', 75 | BlockEntryInterface::IS_ACTIVE => true, 76 | BlockEntryInterface::IS_MAINTAINED => false, 77 | BlockEntryInterface::STORES => ['default', 'admin'], 78 | BlockEntryInterface::MEDIA_DIRECTORY => null, 79 | BlockEntryInterface::MEDIA_FILES => [], 80 | ], 81 | 'test.block.3' => [ 82 | BlockEntryInterface::TITLE => 'Block With Images', 83 | BlockEntryInterface::CONTENT => file_get_contents(__DIR__ . '/test-files/content-with-images-1.html'), 84 | BlockEntryInterface::KEY => 'test.block.3', 85 | BlockEntryInterface::IDENTIFIER => 'test-block-3', 86 | BlockEntryInterface::IS_ACTIVE => true, 87 | BlockEntryInterface::IS_MAINTAINED => true, 88 | BlockEntryInterface::STORES => ['admin'], 89 | BlockEntryInterface::MEDIA_DIRECTORY => __DIR__ . '/test-files/media', 90 | BlockEntryInterface::MEDIA_FILES => [ 91 | 'image-1.png', 92 | 'some-test-image.png', 93 | 'foobar/test.png', 94 | ], 95 | ], 96 | ], 97 | ]; 98 | -------------------------------------------------------------------------------- /Test/Integration/Model/Config/_files/test-files/content-with-images-1.html: -------------------------------------------------------------------------------- 1 |
Some foobar
2 |

Foobar: Link zu einem Bilder

3 |

Dummy content...

4 |

Image: fooo

5 |

Image 2: 

-------------------------------------------------------------------------------- /Test/Integration/Model/Config/_files/test-files/file-1.html: -------------------------------------------------------------------------------- 1 |

2 | Some test page content 3 |

-------------------------------------------------------------------------------- /Test/Integration/Model/Config/_files/test-files/media/foobar/test.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/magento-hackathon/m2-content-provisioning/1d74b18c02670c1241e1f2d7c4a75d2fd6169452/Test/Integration/Model/Config/_files/test-files/media/foobar/test.png -------------------------------------------------------------------------------- /Test/Integration/Model/Config/_files/test-files/media/image-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/magento-hackathon/m2-content-provisioning/1d74b18c02670c1241e1f2d7c4a75d2fd6169452/Test/Integration/Model/Config/_files/test-files/media/image-1.png -------------------------------------------------------------------------------- /Test/Integration/Model/Config/_files/test-files/media/some-test-image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/magento-hackathon/m2-content-provisioning/1d74b18c02670c1241e1f2d7c4a75d2fd6169452/Test/Integration/Model/Config/_files/test-files/media/some-test-image.png -------------------------------------------------------------------------------- /Test/Integration/Model/PageInstaller/InstallMediaFilesTest.php: -------------------------------------------------------------------------------- 1 | targetMediaDirectoryPathProvider = self::getMockBuilder(TargetMediaDirectoryPathProviderInterface::class) 32 | ->disableOriginalConstructor() 33 | ->getMock(); 34 | 35 | $structure = [ 36 | 'source' => [ 37 | 'media' => [ 38 | 'file-1.png' => 'some value', 39 | 'file-2.txt' => 'some value', 40 | 'not-used.png' => 'some value', 41 | 'sub-directory' => [ 42 | 'file-3.jpg' => 'some value', 43 | ], 44 | 'existing' => [ 45 | 'file-4.gif' => 'some value', 46 | ], 47 | ], 48 | ], 49 | 'pub' => [ 50 | 'media' => [ 51 | 'existing' => [], 52 | ], 53 | ], 54 | ]; 55 | 56 | $this->fileSystem = vfsStream::setup('root', null, $structure); 57 | 58 | $this->targetMediaDirectoryPathProvider->method('get')->willReturn( 59 | $this->getChildDirectoryUrl('pub/media') 60 | ); 61 | 62 | $applyMediaFiles = Bootstrap::getObjectManager() 63 | ->create( 64 | ApplyMediaFiles::class, 65 | ['targetMediaDirectoryPathProvider' => $this->targetMediaDirectoryPathProvider] 66 | ); 67 | 68 | $this->installer = Bootstrap::getObjectManager() 69 | ->create( 70 | PageInstaller::class, 71 | [ 72 | 'getAllPageEntries' => $this->getPageEntryListMock, 73 | 'applyMediaFiles' => $applyMediaFiles, 74 | ] 75 | ); 76 | } 77 | 78 | /** 79 | * @param string $name 80 | * @return string 81 | */ 82 | private function getChildDirectoryUrl(string $name): string 83 | { 84 | // workaround because getChild-Method is not allowed via static tests 85 | return 'vfs://root/' . $name; 86 | } 87 | 88 | protected function initEntries() 89 | { 90 | $this->pageEntries[1] = $this->pageEntryInterfaceFactory->create( 91 | [ 92 | 'data' => [ 93 | PageEntryInterface::TITLE => 'Page with images 1', 94 | PageEntryInterface::CONTENT => '', 95 | PageEntryInterface::CONTENT_HEADING => 'Some Content Heading', 96 | PageEntryInterface::KEY => 'page.with.images.1', 97 | PageEntryInterface::IDENTIFIER => 'firegento-content-provisioning-test-images-1', 98 | PageEntryInterface::IS_ACTIVE => true, 99 | PageEntryInterface::IS_MAINTAINED => true, 100 | PageEntryInterface::STORES => ['admin'], 101 | PageEntryInterface::MEDIA_DIRECTORY => $this->getChildDirectoryUrl('source/media'), 102 | PageEntryInterface::MEDIA_FILES => [ 103 | 'sub-directory/file-3.jpg', 104 | 'file-1.png', 105 | ], 106 | ] 107 | ] 108 | ); 109 | 110 | $this->pageEntries[2] = $this->pageEntryInterfaceFactory->create( 111 | [ 112 | 'data' => [ 113 | PageEntryInterface::TITLE => 'Page with images 2', 114 | PageEntryInterface::CONTENT => '', 115 | PageEntryInterface::CONTENT_HEADING => '', 116 | PageEntryInterface::KEY => 'page.with.images.2', 117 | PageEntryInterface::IDENTIFIER => 'firegento-content-provisioning-test-images-2', 118 | PageEntryInterface::IS_ACTIVE => true, 119 | PageEntryInterface::IS_MAINTAINED => true, 120 | PageEntryInterface::STORES => ['admin'], 121 | PageEntryInterface::MEDIA_DIRECTORY => $this->getChildDirectoryUrl('source/media'), 122 | PageEntryInterface::MEDIA_FILES => [ 123 | 'file-2.txt', 124 | 'existing/file-4.gif', 125 | ], 126 | ] 127 | ] 128 | ); 129 | 130 | $this->pageEntries[3] = $this->pageEntryInterfaceFactory->create( 131 | [ 132 | 'data' => [ 133 | PageEntryInterface::TITLE => 'Page with images 3', 134 | PageEntryInterface::CONTENT => '', 135 | PageEntryInterface::CONTENT_HEADING => '', 136 | PageEntryInterface::KEY => 'page.with.images.3', 137 | PageEntryInterface::IDENTIFIER => 'firegento-content-provisioning-test-images-3', 138 | PageEntryInterface::IS_ACTIVE => true, 139 | PageEntryInterface::IS_MAINTAINED => true, 140 | PageEntryInterface::STORES => ['admin'], 141 | PageEntryInterface::MEDIA_DIRECTORY => $this->getChildDirectoryUrl('source/media'), 142 | PageEntryInterface::MEDIA_FILES => [ 143 | 'sub-directory/file-3.jpg', 144 | 'file-1.png', 145 | 'not-existing/image.png', 146 | ], 147 | ] 148 | ] 149 | ); 150 | 151 | $this->getPageEntryListMock->method('get')->willReturn($this->pageEntries); 152 | } 153 | 154 | public function testInstall() 155 | { 156 | $this->initEntries(); 157 | 158 | $this->installer->install(); 159 | 160 | $this->assertTrue($this->fileSystem->hasChild('pub/media/file-1.png')); 161 | $this->assertTrue($this->fileSystem->hasChild('pub/media/file-2.txt')); 162 | $this->assertTrue($this->fileSystem->hasChild('pub/media/sub-directory/file-3.jpg')); 163 | $this->assertTrue($this->fileSystem->hasChild('pub/media/existing/file-4.gif')); 164 | 165 | $this->assertFalse($this->fileSystem->hasChild('pub/media/not-used.png')); 166 | 167 | $this->assertFalse($this->fileSystem->hasChild('pub/media/not-existing')); 168 | $this->assertFalse($this->fileSystem->hasChild('pub/media/not-existing/image.png')); 169 | } 170 | } 171 | -------------------------------------------------------------------------------- /Test/Integration/Model/PageInstaller/InstallTest.php: -------------------------------------------------------------------------------- 1 | initEntries(); 11 | 12 | $this->installer->install(); 13 | 14 | // Verify, that pages are in database like defined 15 | $this->comparePageWithEntryForStore(1); 16 | $this->comparePageWithEntryForStore(2); 17 | $this->comparePageWithEntryForStore(3); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Test/Integration/Model/PageInstaller/TestCase.php: -------------------------------------------------------------------------------- 1 | getPageEntryListMock = self::getMockBuilder(GetPageEntryList::class) 56 | ->disableOriginalConstructor() 57 | ->getMock(); 58 | 59 | $this->installer = Bootstrap::getObjectManager() 60 | ->create(PageInstaller::class, ['getAllPageEntries' => $this->getPageEntryListMock]); 61 | 62 | $this->pageEntryInterfaceFactory = Bootstrap::getObjectManager() 63 | ->create(PageEntryInterfaceFactory::class); 64 | 65 | $this->storeManager = Bootstrap::getObjectManager() 66 | ->create(StoreManagerInterface::class); 67 | 68 | $this->getFirstPageByPageEntry = Bootstrap::getObjectManager() 69 | ->create(GetFirstPageByPageEntry::class); 70 | } 71 | 72 | protected function tearDown(): void 73 | { 74 | parent::tearDown(); 75 | 76 | $connection = Bootstrap::getObjectManager()->get(ResourceConnection::class); 77 | $connection->getConnection()->delete( 78 | $connection->getTableName('cms_page'), 79 | [ 80 | 'identifier LIKE ?' => 'firegento-content-provisioning-test-%', 81 | ] 82 | ); 83 | $connection->getConnection()->delete( 84 | $connection->getTableName('url_rewrite'), 85 | [ 86 | 'request_path LIKE ?' => 'firegento-content-provisioning-test-%', 87 | 'entity_type = ?' => 'cms-page', 88 | ] 89 | ); 90 | } 91 | 92 | protected function initEntries() 93 | { 94 | $this->pageEntries[1] = $this->pageEntryInterfaceFactory->create( 95 | [ 96 | 'data' => [ 97 | PageEntryInterface::TITLE => 'Test Page 1', 98 | PageEntryInterface::CONTENT => '

test foobar Aenean commodo ligula eget dolor aenean massa

', 99 | PageEntryInterface::CONTENT_HEADING => 'Some Content Heading', 100 | PageEntryInterface::KEY => 'test.page.1', 101 | PageEntryInterface::IDENTIFIER => 'firegento-content-provisioning-test-1', 102 | PageEntryInterface::IS_ACTIVE => false, 103 | PageEntryInterface::IS_MAINTAINED => true, 104 | PageEntryInterface::STORES => ['admin'], 105 | PageEntryInterface::META_DESCRIPTION => 'Some seo description', 106 | PageEntryInterface::META_KEYWORDS => 'Some, seo, keywords', 107 | PageEntryInterface::META_TITLE => 'Seo title', 108 | PageEntryInterface::PAGE_LAYOUT => '3columns', 109 | PageEntryInterface::LAYOUT_UPDATE_XML => '', 110 | PageEntryInterface::CUSTOM_THEME => 3, 111 | PageEntryInterface::CUSTOM_THEME_FROM => '2019-03-29', 112 | PageEntryInterface::CUSTOM_THEME_TO => '2019-05-29', 113 | PageEntryInterface::CUSTOM_ROOT_TEMPLATE => '3columns', 114 | ] 115 | ] 116 | ); 117 | 118 | $this->pageEntries[2] = $this->pageEntryInterfaceFactory->create( 119 | [ 120 | 'data' => [ 121 | PageEntryInterface::TITLE => 'Test Page 2', 122 | PageEntryInterface::CONTENT => file_get_contents( 123 | __DIR__ . '/../../_files/content/dummy-content.html' 124 | ), 125 | PageEntryInterface::CONTENT_HEADING => 'Some Content Heading', 126 | PageEntryInterface::KEY => 'test.page.2', 127 | PageEntryInterface::IDENTIFIER => 'firegento-content-provisioning-test-2', 128 | PageEntryInterface::IS_ACTIVE => true, 129 | PageEntryInterface::IS_MAINTAINED => false, 130 | PageEntryInterface::STORES => ['default', 'admin'], 131 | ] 132 | ] 133 | ); 134 | 135 | $this->pageEntries[3] = $this->pageEntryInterfaceFactory->create( 136 | [ 137 | 'data' => [ 138 | PageEntryInterface::TITLE => 'Test Page 3', 139 | PageEntryInterface::CONTENT => '

test foobar Aenean commodo ligula eget dolor aenean massa

', 140 | PageEntryInterface::CONTENT_HEADING => 'Some Content Heading', 141 | PageEntryInterface::KEY => 'test.page.3', 142 | PageEntryInterface::IDENTIFIER => 'firegento-content-provisioning-test-3', 143 | PageEntryInterface::IS_ACTIVE => true, 144 | PageEntryInterface::IS_MAINTAINED => true, 145 | PageEntryInterface::STORES => ['default'], 146 | PageEntryInterface::META_DESCRIPTION => 'Some seo description', 147 | PageEntryInterface::META_KEYWORDS => 'Some, seo, keywords', 148 | PageEntryInterface::META_TITLE => 'Seo title', 149 | PageEntryInterface::PAGE_LAYOUT => '3columns', 150 | PageEntryInterface::LAYOUT_UPDATE_XML => '', 151 | ] 152 | ] 153 | ); 154 | 155 | $this->getPageEntryListMock->method('get')->willReturn($this->pageEntries); 156 | } 157 | 158 | protected function comparePageWithEntryForStore($entryIndex) 159 | { 160 | $entry = $this->pageEntries[$entryIndex]; 161 | $block = $this->getPageByPageEntry($entry); 162 | 163 | $this->assertSame($block->getTitle(), $entry->getTitle()); 164 | $this->assertSame($block->getContent(), $entry->getContent()); 165 | $this->assertSame($block->isActive(), $entry->isActive()); 166 | $this->assertSame($block->getMetaDescription(), $entry->getMetaDescription()); 167 | $this->assertSame($block->getMetaKeywords(), $entry->getMetaKeywords()); 168 | $this->assertSame($block->getMetaTitle(), $entry->getMetaTitle()); 169 | $this->assertSame($block->getCustomLayoutUpdateXml(), $entry->getCustomLayoutUpdateXml()); 170 | $this->assertSame($block->getPageLayout(), $entry->getPageLayout()); 171 | $this->assertSame($block->getContentHeading(), $entry->getContentHeading()); 172 | } 173 | 174 | /** 175 | * @param PageEntryInterface $entry 176 | * @return PageInterface 177 | * @throws NoSuchEntityException 178 | * @throws LocalizedException 179 | */ 180 | protected function getPageByPageEntry(PageEntryInterface $entry): PageInterface 181 | { 182 | return $this->getFirstPageByPageEntry->execute($entry); 183 | } 184 | } 185 | -------------------------------------------------------------------------------- /Test/Integration/Model/PageInstaller/UpdateMaintainedTest.php: -------------------------------------------------------------------------------- 1 | initEntries(); 11 | 12 | $this->installer->install(); 13 | 14 | // Change page entry values 15 | $this->pageEntries[1]->setTitle('Changed Page 1'); 16 | $this->pageEntries[1]->setIsActive(true); 17 | $this->pageEntries[1]->setContent('New Content'); 18 | 19 | $this->pageEntries[2]->setTitle('Changed Page 2'); 20 | $this->pageEntries[2]->setIsActive(true); 21 | $this->pageEntries[2]->setContent('New Content'); 22 | 23 | $this->pageEntries[3]->setTitle('Changed Page 3'); 24 | $this->pageEntries[3]->setContent('New Content 333'); 25 | $this->pageEntries[3]->setMetaDescription('New Meta description'); 26 | $this->pageEntries[3]->setPageLayout('1column'); 27 | $this->pageEntries[3]->setContentHeading('Another Content Heading'); 28 | 29 | // Execute installer a second time 30 | $this->installer->install(); 31 | 32 | // Verify that first and third page was updated 33 | $this->comparePageWithEntryForStore(1); 34 | $this->comparePageWithEntryForStore(3); 35 | 36 | // Verify that second page did not change 37 | $entry = $this->pageEntries[2]; 38 | $block = $this->getPageByPageEntry($entry); 39 | 40 | $this->assertNotSame($block->getTitle(), $entry->getTitle()); 41 | $this->assertNotSame($block->getContent(), $entry->getContent()); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /Test/Integration/Model/_files/dummy-content.html: -------------------------------------------------------------------------------- 1 |

Dummy Content

2 |

3 | Some test block content 4 |

-------------------------------------------------------------------------------- /Test/Integration/_files/content/content-with-images-1.html: -------------------------------------------------------------------------------- 1 |
Some foobar
2 |

Foobar: Link zu einem Bilder

3 |

Dummy content...

4 |

Image: fooo

5 |

Image 2: 

-------------------------------------------------------------------------------- /Test/Integration/_files/content/dummy-content.html: -------------------------------------------------------------------------------- 1 |

Dummy Content

2 |

3 | Some test block content 4 |

-------------------------------------------------------------------------------- /Test/Integration/_files/content/file-1.html: -------------------------------------------------------------------------------- 1 |

2 | Some test page content 3 |

-------------------------------------------------------------------------------- /Test/Integration/_files/content_provisioning.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Test Page 1 5 | Firegento_ContentProvisioning::Test/Integration/_files/content/file-1.html 6 | 7 | 8 | Title 2 9 | Firegento_ContentProvisioning::Test/Integration/_files/content/file-1.html 10 | 11 | 12 | 13 | 14 | 15 | SEO Page Title 16 | Some, SEO, keywords 17 | SEO description 18 | 19 | 20 | 3columns 21 | 22 | 23 | 24 | 2019-03-03 25 | 2019-03-29 26 | 3columns 27 | 3 28 | 29 | 30 | 31 | Page With Images 32 | Firegento_ContentProvisioning::Test/Integration/_files/content/content-with-images-1.html 33 | Firegento_ContentProvisioning::Test/Integration/_files/content/media 34 | 35 | 36 | Test Block 1 37 | test foobar Aenean commodo ligula eget dolor aenean massa]]> 38 | 39 | 40 | Test Block 2 41 | Firegento_ContentProvisioning::Test/Integration/_files/content/file-1.html 42 | 43 | 44 | 45 | 46 | 47 | 48 | Block With Images 49 | Firegento_ContentProvisioning::Test/Integration/_files/content/content-with-images-1.html 50 | Firegento_ContentProvisioning::Test/Integration/_files/content/media 51 | 52 | 53 | -------------------------------------------------------------------------------- /Test/Integration/_files/result.php: -------------------------------------------------------------------------------- 1 | [ 8 | 'test.page.1' => [ 9 | PageEntryInterface::TITLE => 'Test Page 1', 10 | PageEntryInterface::CONTENT => file_get_contents(__DIR__ . '/content/file-1.html'), 11 | PageEntryInterface::KEY => 'test.page.1', 12 | PageEntryInterface::IDENTIFIER => 'test-page-1', 13 | PageEntryInterface::IS_ACTIVE => true, 14 | PageEntryInterface::IS_MAINTAINED => true, 15 | PageEntryInterface::STORES => ['admin'], 16 | PageEntryInterface::CONTENT_HEADING => '', 17 | PageEntryInterface::MEDIA_DIRECTORY => null, 18 | PageEntryInterface::MEDIA_FILES => [], 19 | ], 20 | 'test.page.2' => [ 21 | PageEntryInterface::TITLE => 'Title 2', 22 | PageEntryInterface::CONTENT => file_get_contents(__DIR__ . '/content/file-1.html'), 23 | PageEntryInterface::KEY => 'test.page.2', 24 | PageEntryInterface::IDENTIFIER => 'test-page-2', 25 | PageEntryInterface::IS_ACTIVE => false, 26 | PageEntryInterface::IS_MAINTAINED => false, 27 | PageEntryInterface::STORES => ['default', 'admin'], 28 | PageEntryInterface::CONTENT_HEADING => 'New Page Heading 2', 29 | PageEntryInterface::META_TITLE => 'SEO Page Title', 30 | PageEntryInterface::META_KEYWORDS => 'Some, SEO, keywords', 31 | PageEntryInterface::META_DESCRIPTION => 'SEO description', 32 | PageEntryInterface::PAGE_LAYOUT => '3columns', 33 | PageEntryInterface::LAYOUT_UPDATE_XML => '', 34 | PageEntryInterface::CUSTOM_THEME_FROM => '2019-03-03', 35 | PageEntryInterface::CUSTOM_THEME_TO => '2019-03-29', 36 | PageEntryInterface::CUSTOM_THEME => '3', 37 | PageEntryInterface::CUSTOM_ROOT_TEMPLATE => '3columns', 38 | PageEntryInterface::MEDIA_DIRECTORY => null, 39 | PageEntryInterface::MEDIA_FILES => [], 40 | ], 41 | 'test.page.3' => [ 42 | PageEntryInterface::TITLE => 'Page With Images', 43 | PageEntryInterface::CONTENT => file_get_contents(__DIR__ . '/content/content-with-images-1.html'), 44 | PageEntryInterface::KEY => 'test.page.3', 45 | PageEntryInterface::IDENTIFIER => 'test-page-3', 46 | PageEntryInterface::IS_ACTIVE => true, 47 | PageEntryInterface::IS_MAINTAINED => true, 48 | PageEntryInterface::STORES => ['admin'], 49 | PageEntryInterface::CONTENT_HEADING => '', 50 | PageEntryInterface::MEDIA_DIRECTORY => __DIR__ . '/content/media', 51 | PageEntryInterface::MEDIA_FILES => [ 52 | 'image-1.png', 53 | 'some-test-image.png', 54 | 'foobar/test.png', 55 | ], 56 | ], 57 | ], 58 | 'blocks' => [ 59 | 'test.block.1' => [ 60 | BlockEntryInterface::TITLE => 'Test Block 1', 61 | BlockEntryInterface::CONTENT => '

test foobar Aenean commodo ligula eget dolor aenean massa

', 62 | BlockEntryInterface::KEY => 'test.block.1', 63 | BlockEntryInterface::IDENTIFIER => 'test-block-1', 64 | BlockEntryInterface::IS_ACTIVE => true, 65 | BlockEntryInterface::IS_MAINTAINED => true, 66 | BlockEntryInterface::STORES => ['admin'], 67 | BlockEntryInterface::MEDIA_DIRECTORY => null, 68 | BlockEntryInterface::MEDIA_FILES => [], 69 | ], 70 | 'test.block.2' => [ 71 | BlockEntryInterface::TITLE => 'Test Block 2', 72 | BlockEntryInterface::CONTENT => file_get_contents(__DIR__ . '/content/file-1.html'), 73 | BlockEntryInterface::KEY => 'test.block.2', 74 | BlockEntryInterface::IDENTIFIER => 'test-block-2', 75 | BlockEntryInterface::IS_ACTIVE => true, 76 | BlockEntryInterface::IS_MAINTAINED => false, 77 | BlockEntryInterface::STORES => ['default', 'admin'], 78 | BlockEntryInterface::MEDIA_DIRECTORY => null, 79 | BlockEntryInterface::MEDIA_FILES => [], 80 | ], 81 | 'test.block.3' => [ 82 | BlockEntryInterface::TITLE => 'Block With Images', 83 | BlockEntryInterface::CONTENT => file_get_contents(__DIR__ . '/content/content-with-images-1.html'), 84 | BlockEntryInterface::KEY => 'test.block.3', 85 | BlockEntryInterface::IDENTIFIER => 'test-block-3', 86 | BlockEntryInterface::IS_ACTIVE => true, 87 | BlockEntryInterface::IS_MAINTAINED => true, 88 | BlockEntryInterface::STORES => ['admin'], 89 | BlockEntryInterface::MEDIA_DIRECTORY => __DIR__ . '/content/media', 90 | BlockEntryInterface::MEDIA_FILES => [ 91 | 'image-1.png', 92 | 'some-test-image.png', 93 | 'foobar/test.png', 94 | ], 95 | ], 96 | ], 97 | ]; 98 | -------------------------------------------------------------------------------- /Test/Integration/phpunit.gitlab.xml: -------------------------------------------------------------------------------- 1 | 2 | 18 | 26 | 27 | 28 | 29 | . 30 | 31 | 32 | 33 | 34 | . 35 | ../../../../../../dev/tests/integration/ 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "firegento/magento2-content-provisioning", 3 | "description": "N/A", 4 | "require": { 5 | "ext-dom": "*", 6 | "magento/framework": ">=100.1", 7 | "magento/module-cms": ">=101.0", 8 | "magento/module-widget": ">=100.1", 9 | "magento/module-backend": ">=100", 10 | "magento/module-ui": ">=100", 11 | "magento/module-store": ">=100" 12 | }, 13 | "require-dev": { 14 | "mikey179/vfsstream": "^1.6" 15 | }, 16 | "license": [ 17 | "OSL-3.0", 18 | "AFL-3.0" 19 | ], 20 | "autoload": { 21 | "files": [ 22 | "registration.php" 23 | ], 24 | "psr-4": { 25 | "Firegento\\ContentProvisioning\\": "" 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /etc/adminhtml/routes.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /etc/content_provisioning.xsd: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | Entries must have unique code 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | Unique code for cms entities 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | -------------------------------------------------------------------------------- /etc/module.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /registration.php: -------------------------------------------------------------------------------- 1 | 9 | isMaintained()): ?> 10 |
11 |
12 |
13 | escapeHtml($block->getMessage()) ?> 14 |
15 |
16 |
17 | 18 | -------------------------------------------------------------------------------- /view/adminhtml/ui_component/cms_block_form.xml: -------------------------------------------------------------------------------- 1 | 2 |
3 | 4 | 5 |