├── Api
├── CmsEntityConverterManagerInterface.php
├── CmsEntityGeneratorManagerInterface.php
├── ContentExportInterface.php
├── ContentImportInterface.php
├── ContentVersionManagementInterface.php
├── ContentVersionRepositoryInterface.php
├── Data
│ └── ContentVersionInterface.php
└── StoreManagementInterface.php
├── Block
└── Adminhtml
│ ├── Cms
│ ├── Block
│ │ └── Edit
│ │ │ └── History.php
│ ├── CmsAbstract.php
│ └── Page
│ │ └── Edit
│ │ └── History.php
│ └── Import
│ └── Button
│ └── Import.php
├── CHANGELOG.md
├── Console
└── Command
│ ├── CMSUpgradeCommand.php
│ ├── ClearCMSHistoryCommand.php
│ └── ImportContent.php
├── Controller
└── Adminhtml
│ ├── AbstractMassExport.php
│ ├── Block
│ └── MassExport.php
│ ├── History
│ ├── Apply.php
│ └── View.php
│ ├── Import
│ ├── Import.php
│ ├── Index.php
│ └── Upload.php
│ └── Page
│ └── MassExport.php
├── Cron
└── DeleteBackups.php
├── Exception
└── InvalidXmlImportFilesException.php
├── File
├── FileManager.php
└── FileManagerInterface.php
├── LICENSE.txt
├── Model
├── BackupManager.php
├── Config.php
├── Config
│ ├── App
│ │ └── FileResolver.php
│ ├── Block
│ │ ├── Converter.php
│ │ ├── Reader.php
│ │ └── SchemaLocator.php
│ ├── ConverterAbstract.php
│ ├── Cron
│ │ └── SaveValue.php
│ ├── Page
│ │ ├── Converter.php
│ │ ├── Reader.php
│ │ └── SchemaLocator.php
│ ├── ReaderAbstract.php
│ └── SchemaLocatorAbstract.php
├── Content
│ ├── Converter
│ │ ├── AbstractConverter.php
│ │ ├── Block
│ │ │ └── Converter.php
│ │ ├── CmsEntityConverterInterface.php
│ │ ├── CmsEntityConverterManager.php
│ │ └── Page
│ │ │ └── Converter.php
│ ├── Export.php
│ ├── Generator
│ │ ├── CmsEntityGeneratorInterface.php
│ │ ├── CmsEntityGeneratorManager.php
│ │ ├── Json
│ │ │ └── Generator.php
│ │ └── Xml
│ │ │ └── Generator.php
│ └── Import.php
├── ContentVersion.php
├── ContentVersionManagement.php
├── ContentVersionRepository.php
├── Dir
│ └── Reader.php
├── EntityManagement.php
├── OptionSource
│ ├── CmsMode.php
│ ├── MediaMode.php
│ ├── Methods.php
│ └── Periods.php
├── ResourceModel
│ ├── ContentVersion.php
│ └── ContentVersion
│ │ └── Collection.php
├── Service
│ ├── ClearCMSHistory.php
│ ├── GetCmsEntityItems.php
│ └── GetContentVersions.php
└── StoreManagement.php
├── Observer
├── CmsSaveBefore.php
└── DeleteContentVersion.php
├── README.md
├── Setup
└── Recurring.php
├── Test
└── Unit
│ ├── Block
│ └── Adminhtml
│ │ └── Cms
│ │ └── CmsAbstractTest.php
│ ├── Controller
│ └── Adminhtml
│ │ ├── Block
│ │ └── MassExportTest.php
│ │ ├── History
│ │ └── ViewTest.php
│ │ ├── Import
│ │ ├── ImportTest.php
│ │ ├── IndexTest.php
│ │ └── UploadTest.php
│ │ └── Page
│ │ └── MassExportTest.php
│ ├── Model
│ ├── BackupManagerTest.php
│ ├── Config
│ │ ├── App
│ │ │ └── FileResolverTest.php
│ │ └── ConverterAbstractTest.php
│ ├── ConfigTest.php
│ ├── ContentVersionManagementTest.php
│ ├── ContentVersionRepositoryTest.php
│ ├── Service
│ │ └── ClearCMSHistoryTest.php
│ └── StoreManagementTest.php
│ └── Observer
│ └── CmsSaveBeforeTest.php
├── Ui
└── DataProvider
│ ├── HistoryView.php
│ └── Import.php
├── composer.json
├── etc
├── acl.xml
├── adminhtml
│ ├── events.xml
│ ├── menu.xml
│ ├── routes.xml
│ └── system.xml
├── cms_block_data.xsd
├── cms_page_data.xsd
├── config.xml
├── crontab.xml
├── db_schema.xml
├── di.xml
├── module.xml
└── od_cms
│ ├── cms_block_data.xml.sample
│ ├── cms_page_data.xml.sample
│ └── cms_page_data_no-route.xml.sample
├── registration.php
└── view
└── adminhtml
├── layout
├── cmscontent_history_view.xml
└── cmscontent_import_index.xml
├── templates
├── cms
│ └── backup
│ │ └── history.phtml
└── history
│ └── view.phtml
└── ui_component
├── cms_block_form.xml
├── cms_block_listing.xml
├── cms_page_form.xml
├── cms_page_listing.xml
├── cmscontent_import_form.xml
└── overdose_cmscontent_historyview_form.xml
/Api/CmsEntityConverterManagerInterface.php:
--------------------------------------------------------------------------------
1 | pageRepository = $pageRepository;
61 | $this->backupManager = $backupManager;
62 | $this->blockRepository = $blockRepository;
63 | $this->backendUrl = $backendUrl;
64 | }
65 |
66 | /**
67 | * Prepare backups list
68 | *
69 | * @return array
70 | * @throws LocalizedException
71 | */
72 | public function getBackups(): array
73 | {
74 | $id = $this->getRequest()->getParam($this->urlParamId);
75 | $cmsObject = $this->getCmsObject($id);
76 |
77 | return $this->backupManager->getBackupsByCmsEntity($this->bcType, $cmsObject);
78 | }
79 |
80 | /**
81 | * Retrieve url for viewing of backup content
82 | *
83 | * @param $backup
84 | * @return string
85 | */
86 | public function getBackupUrl($backup)
87 | {
88 | return $this->backendUrl->getUrl(
89 | 'cmscontent/history/view',
90 | [
91 | 'bc_type' => $this->bcType,
92 | 'bc_identifier' => $backup['identifier'],
93 | 'item' => $backup['name'],
94 | 'store_id' => $backup['store_id']
95 | ]
96 | );
97 | }
98 |
99 | /**
100 | * Retrieve url for viewing of backup content
101 | *
102 | * @param $backup
103 | * @return string
104 | */
105 | public function getBackupApplyUrl($backup)
106 | {
107 | return $this->backendUrl->getUrl(
108 | 'cmscontent/history/apply',
109 | [
110 | 'item_id' => $this->getRequest()->getParam($this->urlParamId),
111 | 'bc_type' => $this->bcType,
112 | 'bc_identifier' => $backup['identifier'],
113 | 'item' => $backup['name'],
114 | 'store_id' => $backup['store_id']
115 | ]
116 | );
117 | }
118 |
119 | /**
120 | * Retrieve cms block or cms page by identifier
121 | *
122 | * @param $id
123 | *
124 | * @return BlockInterface|PageInterface|null
125 | */
126 | public function getCmsObject($id)
127 | {
128 | try {
129 | if ($this->bcType == BackupManager::TYPE_CMS_BLOCK) {
130 | return $this->blockRepository->getById($id);
131 | } else if ($this->bcType == BackupManager::TYPE_CMS_PAGE) {
132 | return $this->pageRepository->getById($id);
133 | }
134 |
135 | return null;
136 | } catch (LocalizedException $e) {
137 | $this->_logger->critical(__('Something went wrong while getting cms block or page %1', $id));
138 | } catch (\Exception $e) {
139 | $this->_logger->critical(__('Something went wrong with block or page %1', $id));
140 | }
141 | }
142 | }
143 |
--------------------------------------------------------------------------------
/Block/Adminhtml/Cms/Page/Edit/History.php:
--------------------------------------------------------------------------------
1 | __('Import'),
18 | 'class' => 'action-secondary',
19 | 'sort_order' => 20,
20 | 'on_click' => '',
21 | 'data_attribute' => [
22 | 'mage-init' => [
23 | 'buttonAdapter' => [
24 | 'actions' => [
25 | [
26 | 'targetName' => 'cmscontent_import_form.cmscontent_import_form',
27 | 'actionName' => 'save',
28 | 'params' => [
29 | false
30 | ]
31 | ]
32 | ]
33 | ]
34 | ]
35 | ],
36 | ];
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 | All notable changes to this project will be documented in this file.
3 |
4 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
5 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6 |
7 | ## [2.0.5] - 25-04-2024
8 | ### Fix
9 | - Fix return type for console commands.
10 | - Fix rivate variable from parent class (`Model\Dir\Reader`).
11 |
12 | ## [2.0.4] - 17-07-2023
13 | ### Fix
14 | - Fix the issue with the argument type in `Console\Command\ImportContent`.
15 |
16 | ## [2.0.3] - 09-01-2023
17 | ### Changed
18 | - Use the Core module for configuration tab.
19 |
20 | ## [2.0.2] - 31-10-2022
21 | ### Added
22 | - Unit tests for Block, Model and Controller classes.
23 |
24 | ### Changed
25 | - Made minor change in code to fix php8.1 compatibility issue.
26 | - Updated return type of function.
27 |
28 | ### Removed
29 | - Removed leftover docblock params for a function.q
30 |
31 | ## [2.0.1] - 30-10-2022
32 | ### Added
33 | - Backup preview now show title and identifier.
34 | - Added Apply button with confirm popup to recover page builder content and save block/page content.
35 | ### Changed
36 | - Backup preview now in json format.
37 | - Updated notes for "Clear history" feature. (Make it more clear).
38 | - Enable module by default.
39 | - Some BE refactoring.
40 | ### Removed
41 | - Removed menu item "View history" and config for this.
42 |
43 | ## [2.0.0] - 26-06-2022
44 | ### Added
45 | - Feature "clear history"
46 | -- Automatic deleting an old history files by crone
47 | -- CLI command for clearing history files
48 | - Feature "separate files support" (you can import/export to/from separate xml/json files).
49 | - Simple Unit Test for some thin places.
50 |
51 | ### Changed
52 | - Refactored code.
53 | - Position of system configs.
54 | - Import form moved to UI instead of direct blocks.
55 | - Support store ID.
56 |
57 | ### Fixed
58 | - Using system configs.
59 |
60 | ## [1.1.0] - 10-06-2022
61 | ### Added
62 | - Possibility to import/export CMS pages or blocks.
63 | - Automatic deleting an old history backups.
64 | - Refactored code.
65 |
66 |
--------------------------------------------------------------------------------
/Console/Command/CMSUpgradeCommand.php:
--------------------------------------------------------------------------------
1 | contentVersionManagement = $contentVersionManagement;
42 | }
43 |
44 | /**
45 | * @inheritdoc
46 | */
47 | protected function configure()
48 | {
49 | $this->setName('od:cms:upgrade')
50 | ->setDescription('CMS page/blocks upgrade configuration')
51 | ->setDefinition([
52 | new InputOption(
53 | 'type',
54 | '-t',
55 | InputOption::VALUE_REQUIRED,
56 | 'CMS-type to upgrade: [block|blocks|page|pages]'
57 | ),
58 | new InputOption(
59 | 'identifier',
60 | '-i',
61 | InputOption::VALUE_REQUIRED,
62 | 'Comma-separated Identifiers of Block/Page to upgrade'
63 | ),
64 |
65 | ]);
66 |
67 | parent::configure();
68 | }
69 |
70 | /**
71 | * @inheritdoc
72 | */
73 | protected function execute(InputInterface $input, OutputInterface $output)
74 | {
75 | $identifiers = [];
76 | $cmsIdentifier = $input->getOption(self::OPTION_CMS_IDENTIFIER);
77 | if (!empty($cmsIdentifier)) {
78 | $identifiers = explode(',', $cmsIdentifier);
79 | }
80 |
81 | $cmsType = $input->getOption(self::OPTION_CMS_TYPE);
82 | if (empty($cmsType) && !empty($cmsIdentifier)) {
83 | throw new \InvalidArgumentException('CMS Entity type OPTION [--type] missed');
84 | }
85 |
86 | if (!empty($cmsType)) {
87 | switch ($cmsType) {
88 | case 'block':
89 | case 'blocks':
90 | $this->contentVersionManagement->processBlocks($identifiers);
91 | break;
92 | case 'page':
93 | case 'pages':
94 | $this->contentVersionManagement->processPages($identifiers);
95 | break;
96 | default:
97 | throw new \InvalidArgumentException($cmsType . ' is incorrect CMS entity type');
98 | }
99 | } else {
100 | $this->contentVersionManagement->processAll();
101 | }
102 | $output->writeln('Upgrade Completed!');
103 |
104 | return 0;
105 | }
106 | }
107 |
--------------------------------------------------------------------------------
/Console/Command/ClearCMSHistoryCommand.php:
--------------------------------------------------------------------------------
1 | clearCMSHistory = $clearCMSHistory;
31 | }
32 |
33 | /**
34 | * @inheritdoc
35 | */
36 | protected function configure(): void
37 | {
38 | $this->setName('od:cms:history-clear')
39 | ->setDescription('Delete old CMS history files.')
40 | ->setDefinition([
41 | new InputOption(
42 | 'type',
43 | '-t',
44 | InputOption::VALUE_REQUIRED,
45 | 'CMS-type to upgrade: [block|blocks|page|pages]'
46 | )
47 | ]);
48 |
49 | parent::configure();
50 | }
51 |
52 | /**
53 | * @inheritdoc
54 | */
55 | protected function execute(InputInterface $input, OutputInterface $output)
56 | {
57 | $cmsType = $input->getOption('type');
58 | if (empty($cmsType)) {
59 | $output->writeln('CMS Entity type OPTION [--type] missed!');
60 | return 0;
61 | }
62 |
63 | try {
64 | switch ($cmsType) {
65 | case 'block':
66 | case 'blocks':
67 | $count = $this->clearCMSHistory->execute(Config::TYPE_BLOCK);
68 | break;
69 | case 'page':
70 | case 'pages':
71 | $count = $this->clearCMSHistory->execute(Config::TYPE_PAGE);
72 | break;
73 | default:
74 | throw new \InvalidArgumentException($cmsType . ' is incorrect CMS entity type');
75 | }
76 | } catch (\Exception $e) {
77 | $output->writeln($e->getMessage());
78 | return 0;
79 | }
80 |
81 | if ($count > 0) {
82 | $output->writeln("" . __('%1 history files was deleted!', $count)->render() . "");
83 | } else {
84 | $output->writeln('All backups are actual!');
85 | }
86 | return 0;
87 | }
88 | }
89 |
--------------------------------------------------------------------------------
/Console/Command/ImportContent.php:
--------------------------------------------------------------------------------
1 | contentImport = $contentImport;
29 | parent::__construct();
30 | }
31 |
32 | /**
33 | * @inheritdoc
34 | */
35 | protected function configure()
36 | {
37 | $this->setName('od:cms:import');
38 | $this->setDescription('Import CMS zip file');
39 | $this->addArgument('zipfile', InputArgument::REQUIRED, __('Zip file containing CMS information')->render());
40 |
41 | parent::configure();
42 | }
43 |
44 | /**
45 | * @inheritdoc
46 | */
47 | protected function execute(InputInterface $input, OutputInterface $output)
48 | {
49 | $zipFile = $input->getArgument('zipfile');
50 | if ($this->contentImport->importContentFromZipFile($zipFile, false) == 0) {
51 | $output->writeln('Archive is empty.');
52 |
53 | return;
54 | }
55 | $output->writeln('Done.');
56 | return 0;
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/Controller/Adminhtml/AbstractMassExport.php:
--------------------------------------------------------------------------------
1 | fileFactory->create(
58 | $fileName,
59 | [
60 | 'type' => 'filename',
61 | 'value' => $this->returnZipFile($convertedBlocks, $fileName, $type),
62 | 'rm' => true,
63 | ],
64 | DirectoryList::VAR_DIR,
65 | 'application/zip'
66 | );
67 | }
68 |
69 | /**
70 | * Return zip file
71 | *
72 | * @param array $convertedBlocks
73 | * @param string $fileName
74 | * @param string $type
75 | *
76 | * @return string
77 | * @throws FileSystemException|LocalizedException
78 | */
79 | protected function returnZipFile(array $convertedBlocks, string $fileName, string $type): string
80 | {
81 | $fileType = (string)$this->getRequest()->getParam('type', 'json');
82 | $isSplit = (bool)$this->getRequest()->getParam('split', false);
83 |
84 | return $this->contentExport->createZipFile(
85 | $convertedBlocks,
86 | $type,
87 | $fileType,
88 | $fileName,
89 | $isSplit
90 | );
91 | }
92 | }
93 |
--------------------------------------------------------------------------------
/Controller/Adminhtml/Block/MassExport.php:
--------------------------------------------------------------------------------
1 | filter = $filter;
52 | $this->collectionFactory = $collectionFactory;
53 | $this->contentExport = $contentExport;
54 | $this->fileFactory = $fileFactory;
55 | $this->dateTime = $dateTime;
56 | $this->cmsEntityConverterManager = $cmsEntityConverterManager;
57 |
58 | parent::__construct($context);
59 | }
60 |
61 | /**
62 | * Make mass export
63 | *
64 | * @return ResponseInterface
65 | * @throws LocalizedException
66 | */
67 | public function execute(): ResponseInterface
68 | {
69 | $blocks = $this->filter->getCollection($this->collectionFactory->create())->getItems();
70 |
71 | $fileName = sprintf('cms_block_%s.zip', $this->dateTime->date('Ymd_His'));
72 |
73 | $convertedBlocks = $this->cmsEntityConverterManager
74 | ->getConverter(CmsEntityConverterManagerInterface::BLOCK_ENTITY_CODE)
75 | ->convertToArray($blocks);
76 |
77 | return $this->formFile(
78 | $fileName,
79 | $convertedBlocks,
80 | CmsEntityConverterManagerInterface::BLOCK_ENTITY_CODE
81 | );
82 | }
83 | }
84 |
--------------------------------------------------------------------------------
/Controller/Adminhtml/History/Apply.php:
--------------------------------------------------------------------------------
1 | importExportInterface = $importExportInterface;
75 | $this->redirectFactory = $redirectFactory;
76 | $this->pageRepository = $pageRepository;
77 | $this->blockRepository = $blockRepository;
78 | $this->backupManager = $backupManager;
79 | $this->file = $file;
80 | $this->jsonFormatter = $jsonFormatter;
81 |
82 | parent::__construct($context);
83 | }
84 |
85 | /**
86 | * @return Redirect
87 | */
88 | public function execute()
89 | {
90 | $identifier = $this->getRequest()->getParam('bc_identifier');
91 | $itemId = $this->getRequest()->getParam('item_id');
92 | $file = $this->getRequest()->getParam('item');
93 | $cmsType = $this->getRequest()->getParam('bc_type');
94 | $storeId = $this->getRequest()->getParam('store_id');
95 | try {
96 | if (!is_null($storeId)) {
97 | $path = $this->backupManager->getBackupPathByStoreId($cmsType, $identifier, (int)$storeId) . DIRECTORY_SEPARATOR . $file;
98 | } else {
99 | $path = $this->backupManager->getBackupPath($cmsType, $identifier) . DIRECTORY_SEPARATOR . $file;
100 | }
101 | $backupFile = $this->file->readData($path);
102 | $jsonBackup = $this->jsonFormatter->decode($backupFile, true);
103 | if ($cmsType == BackupManager::TYPE_CMS_BLOCK) {
104 | $cmsObject = $this->blockRepository->getById($itemId);
105 | $cmsObject->setContent($jsonBackup['content']);
106 | $this->blockRepository->save($cmsObject);
107 | } else if ($cmsType == BackupManager::TYPE_CMS_PAGE) {
108 | $cmsObject = $this->pageRepository->getById($itemId);
109 | $cmsObject->setContent($jsonBackup['content']);
110 | $this->pageRepository->save($cmsObject);
111 | }
112 | $this->messageManager->addSuccessMessage(__('Successfully changed content with backup file %1', $file));
113 | } catch (\Exception $e) {
114 | $this->messageManager->addErrorMessage(__('Something went wrong with saving block or page %1', $file));
115 | }
116 | $resultRedirect = $this->redirectFactory->create();
117 | $resultRedirect->setUrl($this->_redirect->getRefererUrl());
118 | return $resultRedirect;
119 | }
120 | }
121 |
--------------------------------------------------------------------------------
/Controller/Adminhtml/History/View.php:
--------------------------------------------------------------------------------
1 | resultFactory->create(ResultFactory::TYPE_PAGE);
23 |
24 | $resultPage->getConfig()->getTitle()->prepend(__('Backup Preview'));
25 |
26 | return $resultPage;
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/Controller/Adminhtml/Import/Import.php:
--------------------------------------------------------------------------------
1 | importExportInterface = $importExportInterface;
43 | $this->redirectFactory = $redirectFactory;
44 |
45 | parent::__construct($context);
46 | }
47 |
48 | /**
49 | * @return Redirect
50 | */
51 | public function execute(): Redirect
52 | {
53 | try {
54 | $cmsMode = $this->getRequest()->getParam('cms_import_mode');
55 | $mediaMode = $this->getRequest()->getParam('media_import_mode');
56 | $upload = $this->getRequest()->getParam('upload');
57 |
58 | $count = $this->importExportInterface
59 | ->setCmsModeOption($cmsMode)
60 | ->setMediaModeOption($mediaMode)
61 | ->importContentFromZipFile(
62 | $upload[0]['path'] . DIRECTORY_SEPARATOR . $upload[0]['file'],
63 | true
64 | );
65 |
66 | $this->messageManager->addSuccessMessage(
67 | __('A total of %1 item(s) have been imported/updated.', $count)
68 | );
69 | } catch (\Exception $e) {
70 | $this->messageManager->addExceptionMessage($e);
71 | }
72 |
73 | $resultRedirect = $this->redirectFactory->create();
74 | return $resultRedirect->setPath('*/*/index');
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/Controller/Adminhtml/Import/Index.php:
--------------------------------------------------------------------------------
1 | resultFactory->create(ResultFactory::TYPE_PAGE);
27 |
28 | $resultPage->setActiveMenu('Overdose_CMSContent::import')
29 | ->addBreadcrumb(__('CMS'), __('CMS'));
30 |
31 | $resultPage->addBreadcrumb(__('Import CMS'), __('Import CMS'));
32 |
33 | $resultPage->getConfig()->getTitle()->prepend(__('CMS'));
34 | $resultPage->getConfig()->getTitle()->prepend(__('CMS Import'));
35 |
36 | return $resultPage;
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/Controller/Adminhtml/Import/Upload.php:
--------------------------------------------------------------------------------
1 | config = $config;
50 | $this->resultJsonFactory = $resultJsonFactory;
51 | $this->fileUploaderFactory = $fileUploaderFactory;
52 | $this->messageManager = $messageManager;
53 | }
54 |
55 | /**
56 | * @return ResultInterface
57 | */
58 | public function execute(): ResultInterface
59 | {
60 | $result = $this->upload();
61 | $response = $this->resultJsonFactory->create();
62 | $response->setData($result);
63 |
64 | return $response;
65 | }
66 |
67 | /**
68 | * Upload file
69 | *
70 | * @return array
71 | */
72 | private function upload(): array
73 | {
74 | $result = [];
75 | $uploader = $this->fileUploaderFactory->create(['fileId' => 'upload']);
76 | $uploader->setAllowedExtensions(['zip']);
77 | $uploader->setAllowRenameFiles(true);
78 |
79 | try {
80 | $result = $uploader->save($this->config->getBackupsDir() . DIRECTORY_SEPARATOR . 'import');
81 | } catch (\Exception $e) {
82 | $this->messageManager->addErrorMessage($e->getMessage());
83 | }
84 | return $result;
85 | }
86 | }
87 |
--------------------------------------------------------------------------------
/Controller/Adminhtml/Page/MassExport.php:
--------------------------------------------------------------------------------
1 | filter = $filter;
52 | $this->collectionFactory = $collectionFactory;
53 | $this->contentExport = $contentExport;
54 | $this->fileFactory = $fileFactory;
55 | $this->dateTime = $dateTime;
56 | $this->cmsEntityConverterManager = $cmsEntityConverterManager;
57 |
58 | parent::__construct($context);
59 | }
60 |
61 | /**
62 | * Make mass export
63 | *
64 | * @return ResponseInterface
65 | * @throws LocalizedException
66 | */
67 | public function execute(): ResponseInterface
68 | {
69 | $pages = $this->filter->getCollection($this->collectionFactory->create())->getItems();
70 |
71 | $fileName = sprintf('cms_page_%s.zip', $this->dateTime->date('Ymd_His'));
72 |
73 | $convertedPages = $this->cmsEntityConverterManager
74 | ->getConverter(CmsEntityConverterManagerInterface::PAGE_ENTITY_CODE)
75 | ->convertToArray($pages);
76 |
77 | return $this->formFile(
78 | $fileName,
79 | $convertedPages,
80 | CmsEntityConverterManagerInterface::PAGE_ENTITY_CODE
81 | );
82 | }
83 | }
84 |
--------------------------------------------------------------------------------
/Cron/DeleteBackups.php:
--------------------------------------------------------------------------------
1 | clearCMSHistory = $clearCMSHistory;
32 | $this->config = $config;
33 | }
34 |
35 | /**
36 | * Process getting and deleting old CMS files
37 | *
38 | * @return void
39 | * @throws FileSystemException
40 | */
41 | public function execute(): void
42 | {
43 | if ($this->config->isCronEnabled()) {
44 | return;
45 | }
46 | $this->clearCMSHistory->execute(Config::TYPE_BLOCK);
47 | $this->clearCMSHistory->execute(Config::TYPE_PAGE);
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/Exception/InvalidXmlImportFilesException.php:
--------------------------------------------------------------------------------
1 | fileIo = $fileIo;
43 | $this->filesystem = $filesystem;
44 | $this->logger = $logger;
45 | }
46 |
47 | /**
48 | * @inheritDoc
49 | */
50 | public function writeData(
51 | string $path,
52 | string $fileName,
53 | string $content,
54 | string $fileExtension = self::FILE_EXTENSION
55 | ): FileManagerInterface {
56 | if (!$path || !$fileName) {
57 | return $this;
58 | }
59 |
60 | try {
61 | $path = $this->getFolder($path);
62 | $this->fileIo->open(['path' => $path]);
63 | $this->fileIo->write($fileName . $fileExtension, $content, 0666);
64 | } catch (\Exception $e) {
65 | $this->logger->critical(__('Something went wrong while saving file'));
66 | }
67 | return $this;
68 | }
69 |
70 | /**
71 | * @inheritDoc
72 | */
73 | public function readData($filename)
74 | {
75 | return $this->fileIo->read($filename);
76 | }
77 |
78 | /**
79 | * Get folder
80 | *
81 | * @param string $path
82 | *
83 | * @return string;
84 | */
85 | public function getFolder(string $path): string
86 | {
87 | try {
88 | $this->fileIo->checkAndCreateFolder($path, 0775);
89 |
90 | return $path;
91 | } catch (\Exception $e) {
92 | $this->logger->critical(__('Something went wrong while saving file'));
93 |
94 | return '';
95 | }
96 | }
97 |
98 | /**
99 | * Get media file path
100 | *
101 | * @param string $mediaFile
102 | * @param bool $write = false
103 | *
104 | * @return string
105 | * @throws FileSystemException
106 | */
107 | public function getMediaPath(string $mediaFile, bool $write = false): ?string
108 | {
109 | if ($write) {
110 | $mediaDir = $this->filesystem->getDirectoryWrite(DirectoryList::MEDIA);
111 | } else {
112 | $mediaDir = $this->filesystem->getDirectoryRead(DirectoryList::MEDIA);
113 | }
114 | return $mediaDir->getAbsolutePath($mediaFile);
115 | }
116 | }
117 |
--------------------------------------------------------------------------------
/File/FileManagerInterface.php:
--------------------------------------------------------------------------------
1 | scopeConfig = $scopeConfig;
75 | $this->directoryList = $directoryList;
76 | }
77 |
78 | /**
79 | * Check is functionality enabled
80 | *
81 | * @return bool
82 | */
83 | public function isEnabled(): bool
84 | {
85 | return (bool)$this->scopeConfig->getValue(self::XML_PATH_IS_ENABLED, ScopeInterface::SCOPE_WEBSITE);
86 | }
87 |
88 | /**
89 | * Check is delete by cron enabled
90 | *
91 | * @return bool
92 | */
93 | public function isCronEnabled(): bool
94 | {
95 | return (bool)$this->scopeConfig->getValue(self::XML_PATH_IS_CRON_ENABLED, ScopeInterface::SCOPE_STORE);
96 | }
97 |
98 | /**
99 | * Get method type
100 | *
101 | * @return string
102 | */
103 | public function getMethodType(): string
104 | {
105 | if (!$result = $this->scopeConfig->getValue(self::XML_PATH_DELETE_METHOD_TYPE, ScopeInterface::SCOPE_STORE)) {
106 | return self::PERIOD;
107 | }
108 | return $result;
109 | }
110 |
111 | /**
112 | * Get period type
113 | *
114 | * @return int
115 | */
116 | public function getPeriodType(): int
117 | {
118 | if (!$result = $this->scopeConfig->getValue(
119 | self::XML_PATH_OLDER_THAN_PERIOD_TYPE,
120 | ScopeInterface::SCOPE_STORE
121 | )) {
122 | return self::MONTH;
123 | }
124 | return (int)$result;
125 | }
126 |
127 | /**
128 | * Get period number
129 | *
130 | * @return int
131 | */
132 | public function getPeriodNumber(): int
133 | {
134 | if (!$result = $this->scopeConfig->getValue(
135 | self::XML_PATH_OLDER_THAN_PERIOD_NUMBER, ScopeInterface::SCOPE_STORE
136 | )) {
137 | return 5;
138 | }
139 | return (int)$result;
140 | }
141 |
142 | /**
143 | * @return bool
144 | */
145 | public function isLogsEnabled(): bool
146 | {
147 | return (bool)$this->scopeConfig->getValue(self::XML_PATH_LOGS, ScopeInterface::SCOPE_STORE);
148 | }
149 |
150 | /**
151 | * Get full path to directory with backups
152 | *
153 | * @return string
154 | */
155 | public function getBackupsDir(): string
156 | {
157 | try {
158 | return $this->directoryList->getPath(DirectoryList::VAR_DIR) . DIRECTORY_SEPARATOR . self::CMS_DIR;
159 | } catch (\Exception $e) {
160 | return '';
161 | }
162 | }
163 |
164 | /**
165 | * Form export path
166 | *
167 | * @return string
168 | */
169 | public function getExportPath(): string
170 | {
171 | try {
172 | return $this->directoryList->getPath(DirectoryList::VAR_DIR)
173 | . DIRECTORY_SEPARATOR
174 | . self::CMS_DIR
175 | . DIRECTORY_SEPARATOR
176 | . self::EXPORT_PATH;
177 | } catch (\Exception $e) {
178 | return '';
179 | }
180 | }
181 |
182 | /**
183 | * Get Extract Path
184 | *
185 | * @return string
186 | */
187 | public function getExtractPath(): string
188 | {
189 | try {
190 | return $this->directoryList->getPath(DirectoryList::VAR_DIR)
191 | . DIRECTORY_SEPARATOR
192 | . self::CMS_DIR
193 | . DIRECTORY_SEPARATOR
194 | . self::EXTRACT_PATH;
195 | } catch (\Exception $e) {
196 | return '';
197 | }
198 | }
199 | }
200 |
--------------------------------------------------------------------------------
/Model/Config/App/FileResolver.php:
--------------------------------------------------------------------------------
1 | iteratorFactory = $iteratorFactory;
45 | $this->filesystem = $filesystem;
46 | $this->moduleReader = $moduleReader;
47 | }
48 |
49 | /**
50 | * {@inheritdoc}
51 | */
52 | public function get($filename, $scope)
53 | {
54 | switch ($scope) {
55 | case 'primary':
56 | $directory = $this->filesystem->getDirectoryRead(DirectoryList::CONFIG);
57 | $absolutePaths = [];
58 | foreach ($directory->search('{' . $filename . ',*/' . $filename . '}') as $path) {
59 | $absolutePaths[] = $directory->getAbsolutePath($path);
60 | }
61 | $iterator = $this->iteratorFactory->create($absolutePaths);
62 | break;
63 | case 'global':
64 | $iterator = $this->moduleReader->getConfigurationFiles($filename);
65 | break;
66 | default:
67 | $iterator = $this->moduleReader->getConfigurationFiles($scope . '/' . $filename);
68 | break;
69 | }
70 | return $iterator;
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/Model/Config/Block/Converter.php:
--------------------------------------------------------------------------------
1 | '',
18 | '/config/cms_blocks/cms_block' => 'identifier',
19 | '/config/cms_blocks/cms_block/attribute' => 'code',
20 | ];
21 | }
22 |
--------------------------------------------------------------------------------
/Model/Config/Block/SchemaLocator.php:
--------------------------------------------------------------------------------
1 | getElementsByTagName($this->itemsNode);
30 |
31 | /** @var \DOMElement $item */
32 | foreach ($items as $item) {
33 | $resultArray = [];
34 |
35 | /** @var \DOMNodeList $children */
36 | $children = $item->getElementsByTagName($this->childNode);
37 | /** @var \DOMElement $child */
38 | foreach ($children as $child) {
39 | $childData = [];
40 | if (!$identifier = $child->getAttribute('identifier')) {
41 | throw new \InvalidArgumentException(
42 | __('Attribute "identifier" of "%1" does not exist', $this->childNode)
43 | );
44 | }
45 |
46 | $childData['identifier'] = $identifier;
47 |
48 | /** @var \DOMNodeList $cmsAttributes */
49 | $cmsAttributes = $child->getElementsByTagName('attribute');
50 | /** \DOMElement $cmsAttribute */
51 | foreach ($cmsAttributes as $cmsAttribute) {
52 | $childData[$cmsAttribute->getAttribute('code')] = $cmsAttribute->textContent;
53 | }
54 |
55 | $storeIdsString = empty($childData['store_ids'])
56 | ? '0' : str_replace(',', '_', $childData['store_ids']);
57 | $childIndex = $identifier . '_' . $storeIdsString;
58 | $resultArray[$childIndex] = $childData;
59 | }
60 |
61 | //IF need array wrapper cms_blocks/cms_pages use $output[$this->itemsNode] = $resultArray;
62 | $output = array_merge($output, $resultArray);
63 | }
64 |
65 | return $output;
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/Model/Config/Cron/SaveValue.php:
--------------------------------------------------------------------------------
1 | configInterface = $configInterface;
47 | }
48 |
49 | /**
50 | * @return SaveValue
51 | * @throws \Exception
52 | */
53 | public function afterSave(): SaveValue
54 | {
55 | $time = $this->getData(Config::CRON_ARRAY_PATH_TIME_VALUE);
56 | $frequency = $this->getData(Config::CRON_ARRAY_PATH_FREQUENCY_VALUE);
57 |
58 | $cronExprArray = [
59 | (int)$time[1], //Minute
60 | (int)$time[0], //Hour
61 | $frequency == Frequency::CRON_MONTHLY ? '1' : '*', //Day of the Month
62 | '*', //Month of the Year
63 | $frequency == Frequency::CRON_WEEKLY ? '1' : '*', //Day of the Week
64 | ];
65 |
66 | $cronExprString = implode(' ', $cronExprArray);
67 |
68 | try {
69 | $this->configInterface->saveConfig(
70 | Config::CRON_STRING_PATH,
71 | $cronExprString,
72 | $this->getScope(),
73 | $this->getScopeId()
74 | );
75 | } catch (\Exception $e) {
76 | throw new \Exception(__('We can\'t save the cron expression.'));
77 | }
78 |
79 | return parent::afterSave();
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/Model/Config/Page/Converter.php:
--------------------------------------------------------------------------------
1 | '',
18 | '/config/cms_pages/cms_page' => 'identifier',
19 | '/config/cms_pages/cms_page/attribute' => 'code',
20 | ];
21 | }
22 |
--------------------------------------------------------------------------------
/Model/Config/Page/SchemaLocator.php:
--------------------------------------------------------------------------------
1 | logger = $logger;
58 |
59 | parent::__construct(
60 | $fileResolver,
61 | $converter,
62 | $schemaLocator,
63 | $validationState,
64 | static::FILE_NAME,
65 | $idAttributes,
66 | $domDocumentClass,
67 | $defaultScope
68 | );
69 | }
70 |
71 | /**
72 | * Load configuration for primary and global scopes
73 | *
74 | * @param null $scope
75 | * @return array
76 | * @throws LocalizedException
77 | */
78 | public function read($scope = null)
79 | {
80 | $fileListGlobal = $this->_fileResolver->get($this->_fileName, self::SCOPE_GLOBAL);
81 | $fileListGlobal = count($fileListGlobal) ? $fileListGlobal->toArray() : [];
82 | $fileListPrimary = $this->_fileResolver->get($this->_fileName, self::SCOPE_PRIMARY);
83 | $fileListPrimary = count($fileListPrimary) ? $fileListPrimary->toArray() : [];
84 |
85 | $configGlobal = $this->_readFiles($fileListGlobal);
86 | $configPrimary = $this->_readFiles($fileListPrimary);
87 |
88 | if ($duplicateItems = array_intersect_key($configGlobal, $configPrimary)) {
89 | $listOfImportFiles = array_merge(array_keys($fileListGlobal), array_keys($fileListPrimary));
90 | $this->handleDuplicationError($duplicateItems, $listOfImportFiles);
91 | }
92 |
93 | return array_merge($configGlobal, $configPrimary);
94 | }
95 |
96 | /**
97 | * @param string $file
98 | * @return array
99 | * @throws LocalizedException
100 | */
101 | public function readFromFile(string $file)
102 | {
103 | return $this->_readFiles([$file => file_get_contents($file)]);
104 | }
105 |
106 | /**
107 | * @param array $duplicateItems
108 | * @param array $listOfImportFiles
109 | * @return mixed
110 | * @throws InvalidXmlImportFilesException
111 | */
112 | private function handleDuplicationError(array $duplicateItems, array $listOfImportFiles)
113 | {
114 | $entityName = $this->_fileName === BlockReader::FILE_NAME ? 'block' : 'page';
115 | $duplicates = '';
116 | foreach ($duplicateItems as $item) {
117 | $duplicates .= sprintf(
118 | "%s identifier \"%s\" for the store id - %s, ",
119 | $entityName,
120 | $item[ContentVersionInterface::IDENTIFIER],
121 | $item[ContentVersionInterface::STORE_IDS] ?: '0'
122 | );
123 | }
124 | $errorMessage = __(
125 | "CMS import failed. You have duplicate %1s. "
126 | . 'Please check your import files. List of duplicates - %2.',
127 | $entityName,
128 | $duplicates
129 | );
130 | $this->logger->critical(__(
131 | $errorMessage . "\n List of import files for the duplication check - %1",
132 | implode(', ', $listOfImportFiles)
133 | ));
134 |
135 | throw new InvalidXmlImportFilesException($errorMessage);
136 | }
137 | }
138 |
--------------------------------------------------------------------------------
/Model/Config/SchemaLocatorAbstract.php:
--------------------------------------------------------------------------------
1 | schema = $moduleReader->getModuleDir(Dir::MODULE_ETC_DIR, 'Overdose_CMSContent')
36 | . DIRECTORY_SEPARATOR . $this->schemaFile;
37 | $this->perFileSchema = $moduleReader->getModuleDir(Dir::MODULE_ETC_DIR, 'Overdose_CMSContent')
38 | . DIRECTORY_SEPARATOR . $this->schemaFile;
39 | }
40 |
41 | /**
42 | * @inheritDoc
43 | */
44 | public function getSchema(): ?string
45 | {
46 | return $this->schema;
47 | }
48 |
49 | /**
50 | * @inheritDoc
51 | */
52 | public function getPerFileSchema(): ?string
53 | {
54 | return $this->perFileSchema;
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/Model/Content/Converter/AbstractConverter.php:
--------------------------------------------------------------------------------
1 | storeRepositoryInterface = $storeRepositoryInterface;
33 | $this->blockRepositoryInterface = $blockRepositoryInterface;
34 | }
35 |
36 | /**
37 | * Get media attachments from content
38 | *
39 | * @param $content
40 | *
41 | * @return array
42 | */
43 | protected function getMediaAttachments($content): array
44 | {
45 | $result = [];
46 | if (preg_match_all('/\{\{media.+?url\s*=\s*("|")(.+?)("|").*?\}\}/', $content, $matches)) {
47 | $result += $matches[2];
48 | }
49 |
50 | if (preg_match_all('/{{media.+?url\s*=\s*(?!"|")(.+?)}}/', $content, $matches)) {
51 | $result += $matches[1];
52 | }
53 |
54 | return $result;
55 | }
56 |
57 | /**
58 | * @param string $content
59 | *
60 | * @return array
61 | * @throws LocalizedException
62 | */
63 | protected function saveBlockByIdent(string $content): array
64 | {
65 | $references = [];
66 |
67 | $pattern = '/{{widget.+?block_id\s*=\s*("|")(\d+?)("|").*?}}/';
68 |
69 | if (preg_match_all($pattern, $content, $matches)) {
70 | foreach ($matches[2] as $blockId) {
71 | $block = $this->blockRepositoryInterface->getById($blockId);
72 | $references[$blockId] = $block->getIdentifier();
73 | }
74 | }
75 |
76 | return $references;
77 | }
78 |
79 | /**
80 | * Get store codes
81 | *
82 | * @param array $storeIds
83 | *
84 | * @return array
85 | * @throws NoSuchEntityException
86 | */
87 | public function getStoreCodes(array $storeIds): array
88 | {
89 | $return = [];
90 | foreach ($storeIds as $storeId) {
91 | $return[] = $this->storeRepositoryInterface->getById($storeId)->getCode();
92 | }
93 | return $return;
94 | }
95 | }
96 |
--------------------------------------------------------------------------------
/Model/Content/Converter/Block/Converter.php:
--------------------------------------------------------------------------------
1 | convertBlockToArray($cmsEntity);
25 | $blocks[$this->getBlockKey($cmsEntity)] = $blockInfo;
26 | $media = array_merge($media, $blockInfo['media']);
27 | }
28 |
29 | return [
30 | self::BLOCK_ENTITY_CODE => $blocks,
31 | 'media' => $media,
32 | ];
33 | }
34 |
35 | /**
36 | * Convert CMS block to array
37 | *
38 | * @param BlockInterface $blockInterface
39 | *
40 | * @return array
41 | * @throws LocalizedException
42 | */
43 | private function convertBlockToArray(BlockInterface $blockInterface): array
44 | {
45 | // Extract attachments
46 | $media = $this->getMediaAttachments($blockInterface->getContent());
47 |
48 | return [
49 | 'cms' => [
50 | BlockInterface::IDENTIFIER => $blockInterface->getIdentifier(),
51 | BlockInterface::TITLE => $blockInterface->getTitle(),
52 | BlockInterface::CONTENT => $blockInterface->getContent(),
53 | BlockInterface::IS_ACTIVE => (string)$blockInterface->isActive(),
54 | ],
55 | 'stores' => $this->getStoreCodes($blockInterface->getStores()),
56 | 'media' => $media,
57 | 'block_references' => $this->saveBlockByIdent($blockInterface->getContent())
58 | ];
59 | }
60 |
61 | /**
62 | * Get block unique key
63 | *
64 | * @param BlockInterface $blockInterface
65 | *
66 | * @return string
67 | * @throws NoSuchEntityException
68 | */
69 | private function getBlockKey(BlockInterface $blockInterface): string
70 | {
71 | $keys = $this->getStoreCodes($blockInterface->getStores());
72 | $keys[] = $blockInterface->getIdentifier();
73 |
74 | return implode(':', $keys);
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/Model/Content/Converter/CmsEntityConverterInterface.php:
--------------------------------------------------------------------------------
1 | converters = $converters;
24 | }
25 |
26 | /**
27 | * @inheritdoc
28 | */
29 | public function getConverter(string $type): CmsEntityConverterInterface
30 | {
31 | if (isset($this->converters[$type])) {
32 | return $this->converters[$type];
33 | }
34 | throw new LocalizedException(__("Can't find converter"));
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/Model/Content/Converter/Page/Converter.php:
--------------------------------------------------------------------------------
1 | convertPageToArray($pageInterface);
25 | $pages[$this->getPageKey($pageInterface)] = $pageInfo;
26 | $media = array_merge($media, $pageInfo['media']);
27 | }
28 |
29 | return [
30 | self::PAGE_ENTITY_CODE => $pages,
31 | 'media' => $media,
32 | ];
33 | }
34 |
35 | /**
36 | * Return CMS page to array
37 | *
38 | * @param PageInterface $pageInterface
39 | *
40 | * @return array
41 | * @throws LocalizedException
42 | */
43 | public function convertPageToArray(PageInterface $pageInterface): array
44 | {
45 | // Extract attachments
46 | $media = $this->getMediaAttachments($pageInterface->getContent());
47 |
48 | return [
49 | 'cms' => [
50 | PageInterface::IDENTIFIER => $pageInterface->getIdentifier(),
51 | PageInterface::TITLE => $pageInterface->getTitle(),
52 | PageInterface::PAGE_LAYOUT => $pageInterface->getPageLayout(),
53 | PageInterface::META_KEYWORDS => $pageInterface->getMetaKeywords(),
54 | PageInterface::META_DESCRIPTION => $pageInterface->getMetaDescription(),
55 | PageInterface::CONTENT_HEADING => $pageInterface->getContentHeading(),
56 | PageInterface::CONTENT => $pageInterface->getContent(),
57 | PageInterface::SORT_ORDER => $pageInterface->getSortOrder(),
58 | PageInterface::LAYOUT_UPDATE_XML => $pageInterface->getLayoutUpdateXml(),
59 | PageInterface::CUSTOM_THEME => $pageInterface->getCustomTheme(),
60 | PageInterface::CUSTOM_ROOT_TEMPLATE => $pageInterface->getCustomRootTemplate(),
61 | PageInterface::CUSTOM_LAYOUT_UPDATE_XML => $pageInterface->getCustomLayoutUpdateXml(),
62 | PageInterface::CUSTOM_THEME_FROM => $pageInterface->getCustomThemeFrom(),
63 | PageInterface::CUSTOM_THEME_TO => $pageInterface->getCustomThemeTo(),
64 | PageInterface::IS_ACTIVE => (string)$pageInterface->isActive(),
65 | ],
66 | 'stores' => $this->getStoreCodes($pageInterface->getStores()),
67 | 'media' => $media,
68 | 'block_references' => $this->saveBlockByIdent($pageInterface->getContent()),
69 | ];
70 | }
71 |
72 | /**
73 | * Get page unique key
74 | *
75 | * @param PageInterface $pageInterface
76 | *
77 | * @return string
78 | * @throws NoSuchEntityException
79 | */
80 | private function getPageKey(PageInterface $pageInterface): string
81 | {
82 | $keys = $this->getStoreCodes($pageInterface->getStores());
83 | $keys[] = $pageInterface->getIdentifier();
84 |
85 | return implode(':', $keys);
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/Model/Content/Export.php:
--------------------------------------------------------------------------------
1 | cmsEntityGeneratorManager = $cmsEntityGeneratorManager;
53 | $this->config = $config;
54 | $this->file = $file;
55 | $this->fileManager = $fileManager;
56 | }
57 |
58 | /**
59 | * @inheridoc
60 | */
61 | public function createZipFile(
62 | array $convertedEntities,
63 | string $entityType,
64 | string $type,
65 | string $fileName,
66 | bool $split
67 | ): string {
68 | $exportPath = $this->fileManager->getFolder($this->config->getExportPath());
69 |
70 | $relativeZipFile = Config::CMS_DIR . DIRECTORY_SEPARATOR
71 | . Config::EXPORT_PATH . DIRECTORY_SEPARATOR
72 | . $fileName;
73 |
74 | $zipArchive = $this->putContentToZip(
75 | $convertedEntities,
76 | $entityType,
77 | $exportPath . '/' . $fileName,
78 | $type,
79 | $split
80 | );
81 |
82 | // Add media files
83 | foreach ($convertedEntities['media'] as $mediaFile) {
84 | //Strip Quotes if any
85 | $mediaFile = str_replace(['"',""","'"], '', $mediaFile);
86 | $absMediaPath = $this->fileManager->getMediaPath($mediaFile);
87 | if ($this->file->fileExists($absMediaPath, true)) {
88 | $zipArchive->addFile($absMediaPath, self::MEDIA_ARCHIVE_PATH . '/' . $mediaFile);
89 | }
90 | }
91 |
92 | $zipArchive->close();
93 |
94 | // Clear export path
95 | $this->file->rm($relativeZipFile);
96 |
97 | return $relativeZipFile;
98 | }
99 |
100 | /**
101 | * @param array $contentArray
102 | * @param string $cmsEntityCode
103 | * @param string $zipFileName
104 | * @param string $type
105 | * @param bool $split
106 | * @return ZipArchive
107 | * @throws LocalizedException
108 | */
109 | private function putContentToZip(
110 | array $contentArray,
111 | string $cmsEntityCode,
112 | string $zipFileName,
113 | string $type,
114 | bool $split
115 | ): ZipArchive {
116 | $zipArchive = new ZipArchive();
117 | $zipArchive->open($zipFileName, ZipArchive::CREATE);
118 | $generator = $this->cmsEntityGeneratorManager->getGenerator($type);
119 |
120 | if ($split) {
121 | foreach ($contentArray[$cmsEntityCode] as $key => $content) {
122 | $payload = $generator->generate(
123 | [
124 | $cmsEntityCode => [$key => $content]
125 | ]
126 | );
127 |
128 | $zipArchive->addFromString(
129 | sprintf(
130 | '%s_%s_%s.%s',
131 | self::FILENAME,
132 | $this->prepareEntityPartName($cmsEntityCode),
133 | $key,
134 | $type
135 | ),
136 | $payload
137 | );
138 | }
139 | } else {
140 | $fullContent = [];
141 | foreach ($contentArray[$cmsEntityCode] as $key => $content) {
142 | $entityContent = [
143 | $cmsEntityCode => [$key => $content]
144 | ];
145 | $fullContent = array_merge_recursive($fullContent, $entityContent);
146 | }
147 | $payload = $generator->generate($fullContent);
148 | $zipArchive->addFromString(
149 | sprintf('%s_%s.%s', self::FILENAME, $this->prepareEntityPartName($cmsEntityCode), $type),
150 | $payload
151 | );
152 | }
153 |
154 | return $zipArchive;
155 | }
156 |
157 | /**
158 | * Prepare entity part name
159 | *
160 | * @param string $cmsEntityCode
161 | *
162 | * @return string
163 | */
164 | private function prepareEntityPartName(string $cmsEntityCode): string
165 | {
166 | return substr($cmsEntityCode, 0, strlen($cmsEntityCode) - 1) . '_data';
167 | }
168 | }
169 |
--------------------------------------------------------------------------------
/Model/Content/Generator/CmsEntityGeneratorInterface.php:
--------------------------------------------------------------------------------
1 | self::PAGE_SCHEMA_NAME,
16 | CmsEntityConverterInterface::BLOCK_ENTITY_CODE => self::BLOCK_SCHEMA_NAME
17 | ];
18 |
19 | const MAIN_ENTITY_NODE_NAME = 'cms';
20 | const STORES_ENTITY_NODE_NAME = 'stores';
21 |
22 | /**
23 | * Call generate action
24 | *
25 | * @param array $data
26 | *
27 | * @return string
28 | */
29 | public function generate(array $data): string;
30 | }
31 |
--------------------------------------------------------------------------------
/Model/Content/Generator/CmsEntityGeneratorManager.php:
--------------------------------------------------------------------------------
1 | generators = $generators;
24 | }
25 |
26 | /**
27 | * @inheritdoc
28 | */
29 | public function getGenerator($type): CmsEntityGeneratorInterface
30 | {
31 | if (isset($this->generators[$type])) {
32 | return $this->generators[$type];
33 | }
34 | throw new LocalizedException(__("Can't find generator"));
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/Model/Content/Generator/Json/Generator.php:
--------------------------------------------------------------------------------
1 | serializerInterface = $serializerInterface;
24 | }
25 |
26 | /**
27 | * @param array $data
28 | *
29 | * @return string
30 | */
31 | public function generate(array $data): string
32 | {
33 | return $this->serializerInterface->serialize($data);
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/Model/ContentVersion.php:
--------------------------------------------------------------------------------
1 | _init(ResourceContentVersion::class);
19 | }
20 |
21 | /**
22 | * @inheritdoc
23 | */
24 | public function getType(): ?string
25 | {
26 | return $this->getData(self::TYPE);
27 | }
28 |
29 | /**
30 | * @inheritdoc
31 | */
32 | public function setType(string $type): ContentVersionInterface
33 | {
34 | return $this->setData(self::TYPE, $type);
35 | }
36 |
37 | /**
38 | * @inheritdoc
39 | */
40 | public function getIdentifier(): ?string
41 | {
42 | return $this->getData(self::IDENTIFIER);
43 | }
44 |
45 | /**
46 | * @inheritdoc
47 | */
48 | public function setIdentifier(string $identifier): ContentVersionInterface
49 | {
50 | return $this->setData(self::IDENTIFIER, $identifier);
51 | }
52 |
53 | /**
54 | * @inheritdoc
55 | */
56 | public function getVersion(): ?string
57 | {
58 | return $this->getData(self::VERSION);
59 | }
60 |
61 | /**
62 | * @inheritdoc
63 | */
64 | public function setVersion(string $version): ContentVersionInterface
65 | {
66 | return $this->setData(self::VERSION, $version);
67 | }
68 |
69 | /**
70 | * @inheritdoc
71 | */
72 | public function getStoreIds(): ?string
73 | {
74 | return $this->getData(self::STORE_IDS);
75 | }
76 |
77 | /**
78 | * @inheritdoc
79 | */
80 | public function setStoreIds(string $storeIds): ContentVersionInterface
81 | {
82 | return $this->setData(self::STORE_IDS, $storeIds);
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/Model/ContentVersionRepository.php:
--------------------------------------------------------------------------------
1 | resource = $resource;
63 | $this->contentVersionFactory = $contentVersionFactory;
64 | $this->contentVersionCollectionFactory = $contentVersionCollectionFactory;
65 | $this->searchResultsFactory = $searchResultsFactory;
66 | $this->collectionProcessor = $collectionProcessor;
67 | }
68 |
69 | /**
70 | * @inheritdoc
71 | */
72 | public function save(ContentVersionInterface $contentVersion): ContentVersionInterface
73 | {
74 | try {
75 | $this->resource->save($contentVersion);
76 |
77 | return $this->get($contentVersion->getId());
78 | } catch (\Exception $exception) {
79 | throw new CouldNotSaveException(
80 | __('Could not save the contentVersion: %1', $exception->getMessage())
81 | );
82 | }
83 | }
84 |
85 | /**
86 | * @inheritdoc
87 | */
88 | public function get(string $id): ContentVersionInterface
89 | {
90 | $contentVersion = $this->contentVersionFactory->create();
91 | $this->resource->load($contentVersion, $id);
92 | if (!$contentVersion->getId()) {
93 | throw new NoSuchEntityException(__('content_version with id "%1" does not exist.', $id));
94 | }
95 | return $contentVersion;
96 | }
97 |
98 | /**
99 | * @inheritdoc
100 | */
101 | public function getList(SearchCriteriaInterface $searchCriteria): SearchResultsInterface
102 | {
103 | $collection = $this->contentVersionCollectionFactory->create();
104 |
105 | $this->collectionProcessor->process($searchCriteria, $collection);
106 |
107 | $searchResult = $this->searchResultsFactory->create();
108 | $searchResult->setItems($collection->getItems());
109 | $searchResult->setTotalCount($collection->getSize());
110 | $searchResult->setSearchCriteria($searchCriteria);
111 |
112 | return $searchResult;
113 | }
114 |
115 | /**
116 | * @inheritdoc
117 | */
118 | public function delete(ContentVersionInterface $contentVersion): bool
119 | {
120 | try {
121 | $contentVersionModel = $this->contentVersionFactory->create();
122 | $this->resource->load($contentVersionModel, $contentVersion->getId());
123 | $this->resource->delete($contentVersionModel);
124 | } catch (\Exception $exception) {
125 | throw new CouldNotDeleteException(__(
126 | 'Could not delete the content_version: %1',
127 | $exception->getMessage()
128 | ));
129 | }
130 | return true;
131 | }
132 |
133 | /**
134 | * @inheritdoc
135 | */
136 | public function deleteById($id): bool
137 | {
138 | return $this->delete($this->get($id));
139 | }
140 | }
141 |
--------------------------------------------------------------------------------
/Model/Dir/Reader.php:
--------------------------------------------------------------------------------
1 | getFilesIterator($filename, Dir::MODULE_ETC_DIR);
27 | }
28 |
29 | /**
30 | * @inheridoc
31 | */
32 | private function getFilesIterator($filename, $subDir = '')
33 | {
34 | if (!isset($this->fileIterators[$subDir][$filename])) {
35 | $this->fileIterators[$subDir][$filename] = $this->fileIteratorFactory->create(
36 | $this->getFiles($filename, $subDir)
37 | );
38 | }
39 | return $this->fileIterators[$subDir][$filename];
40 | }
41 |
42 | /**
43 | * @inheridoc
44 | */
45 | private function getFiles($filename, $subDir = '')
46 | {
47 | $result = [];
48 | foreach ($this->modulesList->getNames() as $moduleName) {
49 | try {
50 | $moduleSubDir = $this->getModuleDir($subDir, $moduleName);
51 | } catch (\InvalidArgumentException $e) {
52 | continue;
53 | }
54 | $file = $moduleSubDir . '/' . $filename;
55 | $directoryRead = $this->readFactory->create($moduleSubDir);
56 | $path = $directoryRead->getRelativePath($file);
57 | if ($directoryRead->isExist($path)) {
58 | $result[] = $file;
59 | }
60 | // === Overdose - the override part BEGIN ===
61 | if ($filename === BlockReader::FILE_NAME || $filename === PageReader::FILE_NAME) {
62 | $pattern = substr($filename, 0, strlen($filename) - 4);
63 | $dir = $moduleSubDir . '/' . ReaderAbstract::OD_CONFIG_DIR_NAME;
64 | $directoryRead = $this->readFactory->create($dir);
65 | $path = $directoryRead->getRelativePath($dir);
66 | if ($directoryRead->isExist($path)) {
67 | $odFiles = array_filter($directoryRead->read($path), function ($file) use ($pattern) {
68 | return strpos($file, $pattern) === 0 && substr($file, -4) === '.xml';
69 | });
70 | array_walk($odFiles, function (&$file) use ($dir) {
71 | $file = $dir . '/' . $file;
72 | });
73 | $result = array_merge($odFiles, $result);
74 | }
75 | }
76 | // === Overdose - the override part END ===
77 | }
78 | return $result;
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/Model/EntityManagement.php:
--------------------------------------------------------------------------------
1 | repositoryList = $repositoryList;
41 | $this->factoryList = $factoryList;
42 | }
43 |
44 | /**
45 | * Get repository
46 | *
47 | * @return BlockRepositoryInterface|PageRepositoryInterface
48 | * @throws LocalizedException
49 | */
50 | public function getRepository(string $type)
51 | {
52 | if (isset($this->repositoryList[$type])) {
53 | return $this->repositoryList[$type];
54 | }
55 | throw new LocalizedException(__('Expected entity instance not found.'));
56 | }
57 |
58 | /**
59 | * Get repository
60 | *
61 | * @return BlockInterface|PageInterface
62 | * @throws LocalizedException
63 | */
64 | public function getFactory(string $type)
65 | {
66 | if (isset($this->factoryList[$type])) {
67 | return $this->factoryList[$type]->create();
68 | }
69 | throw new LocalizedException(__('Expected entity instance not found.'));
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/Model/OptionSource/CmsMode.php:
--------------------------------------------------------------------------------
1 | __('Overwrite existing'),
22 | 'value' => ContentImportInterface::OD_CMS_MODE_UPDATE
23 | ],
24 | [
25 | 'label' => __('Skip existing'),
26 | 'value' => ContentImportInterface::OD_CMS_MODE_SKIP
27 | ]
28 | ];
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/Model/OptionSource/MediaMode.php:
--------------------------------------------------------------------------------
1 | __('Do not import'),
22 | 'value' => ContentImportInterface::OD_MEDIA_MODE_NONE
23 | ],
24 | [
25 | 'label' => __('Overwrite existing'),
26 | 'value' => ContentImportInterface::OD_MEDIA_MODE_UPDATE
27 | ],
28 | [
29 | 'label' => __('Skip existing'),
30 | 'value' => ContentImportInterface::OD_MEDIA_MODE_SKIP
31 | ]
32 | ];
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/Model/OptionSource/Methods.php:
--------------------------------------------------------------------------------
1 | __('By Periods'),
20 | 'value' => Config::PERIOD
21 | ],
22 | [
23 | 'label' => __('Older Than'),
24 | 'value' => Config::OLDER_THAN
25 | ]
26 | ];
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/Model/OptionSource/Periods.php:
--------------------------------------------------------------------------------
1 | __('Days'),
20 | 'value' => Config::DAY
21 | ],
22 | [
23 | 'label' => __('Weeks'),
24 | 'value' => Config::WEEK
25 | ],
26 | [
27 | 'label' => __('Months'),
28 | 'value' => Config::MONTH
29 | ],
30 | [
31 | 'label' => __('Years'),
32 | 'value' => Config::YEAR
33 | ]
34 | ];
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/Model/ResourceModel/ContentVersion.php:
--------------------------------------------------------------------------------
1 | _init('od_cmscontent_version', 'id');
21 | }
22 |
23 | /**
24 | * @inheritDoc
25 | */
26 | protected function _beforeSave(AbstractModel $object)
27 | {
28 | if (empty($storeIds = $object->getData(ContentVersionInterface::STORE_IDS))) {
29 | $storeIds = '0';
30 | } else {
31 | $storeIdsArray = explode(',', $storeIds);
32 | if (in_array('0', $storeIdsArray)) {
33 | $storeIds = '0';
34 | }
35 | }
36 |
37 | $object->setData(ContentVersionInterface::STORE_IDS, $storeIds);
38 |
39 | return parent::_beforeSave($object);
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/Model/ResourceModel/ContentVersion/Collection.php:
--------------------------------------------------------------------------------
1 | _init(ContentVersion::class, ResourceContentVersion::class);
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/Model/Service/GetCmsEntityItems.php:
--------------------------------------------------------------------------------
1 | searchCriteriaBuilder = $searchCriteriaBuilder;
54 | $this->filterBuilder = $filterBuilder;
55 | $this->filterGroupBuilder = $filterGroupBuilder;
56 | $this->entityManagement = $entityManagement;
57 | }
58 |
59 | /**
60 | * Get Cms items
61 | *
62 | * /**
63 | * @param string $type
64 | * @param string $identifier
65 | * @param array $storeIds
66 | *
67 | * @return array
68 | * @throws LocalizedException
69 | */
70 | public function execute(string $type, string $identifier, array $storeIds): array
71 | {
72 | $repository = $this->getCmsRepository($type);
73 |
74 | $searchCriteria = $this->prepareSearchCriteria($identifier, $storeIds);
75 |
76 | return $repository->getList($searchCriteria)->getItems();
77 | }
78 |
79 | /**
80 | * Retrieve repository for cms-block or cms-page
81 | *
82 | * @param string $type
83 | *
84 | * @return BlockRepositoryInterface|PageRepositoryInterface
85 | * @throws LocalizedException
86 | */
87 | private function getCmsRepository(string $type)
88 | {
89 | return $this->entityManagement->getRepository($type);
90 | }
91 |
92 | /**
93 | * Prepare search criteria
94 | *
95 | * @param string $identifier
96 | * @param array $storeIds
97 | *
98 | * @return SearchCriteria
99 | */
100 | private function prepareSearchCriteria(string $identifier, array $storeIds): SearchCriteria
101 | {
102 | $filtersGroups = [];
103 |
104 | $filterIdentifier = $this->filterBuilder
105 | ->setField('identifier')
106 | ->setConditionType('eq')
107 | ->setValue($identifier)
108 | ->create();
109 | $filterGroup1 = $this->filterGroupBuilder
110 | ->setFilters([$filterIdentifier])
111 | ->create();
112 | $filtersGroups[] = $filterGroup1;
113 |
114 | $filterStore = $this->filterBuilder
115 | ->setField('store_id')
116 | ->setConditionType('in')
117 | ->setValue($storeIds)
118 | ->create();
119 | $filterGroup2 = $this->filterGroupBuilder
120 | ->setFilters([$filterStore])
121 | ->create();
122 | $filtersGroups[] = $filterGroup2;
123 |
124 | return $this->searchCriteriaBuilder
125 | ->setFilterGroups($filtersGroups)
126 | ->create();
127 | }
128 | }
129 |
--------------------------------------------------------------------------------
/Model/Service/GetContentVersions.php:
--------------------------------------------------------------------------------
1 | searchCriteriaBuilder = $searchCriteriaBuilder;
62 | $this->filterBuilder = $filterBuilder;
63 | $this->filterGroupBuilder = $filterGroupBuilder;
64 | $this->contentVersionRepository = $contentVersionRepository;
65 | $this->logger = $logger;
66 | }
67 |
68 | /**
69 | * Retrieves all CMS content records from DB based on type (0 - blocks, 1-pages), filtered by identifiers
70 | *
71 | * @param int $type
72 | * @param array $filterIds
73 | *
74 | * @return array
75 | */
76 | public function execute(int $type, array $filterIds): array
77 | {
78 | $result = [];
79 |
80 | try {
81 | $searchCriteria = $this->prepareSearchCriteria($type, $filterIds);
82 |
83 | $searchResult = $this->contentVersionRepository->getList($searchCriteria)->getItems();
84 |
85 | /** @var ContentVersion $item */
86 | foreach ($searchResult as $item) {
87 | $storePart = str_replace(',', '_', $item->getStoreIds());
88 | $key = $item->getIdentifier() . '_' . $storePart;
89 | $result[$key] = $item;
90 | }
91 | return $result;
92 | } catch (LocalizedException $e) {
93 | $this->logger->critical(__('Something went wrong during getting content versions'));
94 | }
95 | return $result;
96 | }
97 |
98 | /**
99 | * Prepare search criteria
100 | *
101 | * @param int $type
102 | * @param array $filterIds
103 | *
104 | * @return SearchCriteria
105 | */
106 | private function prepareSearchCriteria(int $type, array $filterIds): SearchCriteria
107 | {
108 | $filtersGroups = [];
109 |
110 | $filterType = $this->filterBuilder
111 | ->setField(ContentVersionInterface::TYPE)
112 | ->setConditionType('eq')
113 | ->setValue($type)
114 | ->create();
115 | $filterGroup1 = $this->filterGroupBuilder
116 | ->setFilters([$filterType])
117 | ->create();
118 | $filtersGroups[] = $filterGroup1;
119 |
120 | if (count($filterIds)) {
121 | $filterId = $this->filterBuilder
122 | ->setField(ContentVersionInterface::IDENTIFIER)
123 | ->setConditionType('in')
124 | ->setValue(implode(",", $filterIds))
125 | ->create();
126 | $filterGroup2 = $this->filterGroupBuilder
127 | ->setFilters([$filterId])
128 | ->create();
129 | $filtersGroups[] = $filterGroup2;
130 | }
131 |
132 | return $this->searchCriteriaBuilder
133 | ->setFilterGroups($filtersGroups)
134 | ->create();
135 | }
136 | }
137 |
--------------------------------------------------------------------------------
/Model/StoreManagement.php:
--------------------------------------------------------------------------------
1 | storeRepositoryInterface = $storeRepositoryInterface;
24 | }
25 |
26 | /**
27 | * Get store ids by codes
28 | * @param array $storeCodes
29 | * @return array
30 | */
31 | public function getStoreIdsByCodes(array $storeCodes): array
32 | {
33 | $storeCodes = $this->filterStoresByStoreCodes($storeCodes);
34 | $storeIds = [];
35 | foreach ($storeCodes as $storeCode) {
36 | if ($storeCode == 'admin') {
37 | $storeIds[] = 0;
38 | } else {
39 | try {
40 | $store = $this->storeRepositoryInterface->get($storeCode);
41 | if ($store && $store->getId()) {
42 | $storeIds[] = $store->getId();
43 | }
44 | } catch (NoSuchEntityException $exception) {
45 | continue;
46 | }
47 | }
48 | }
49 | return $storeIds;
50 | }
51 |
52 | /**
53 | * Filter stores passed to import by existed
54 | *
55 | * @param array $storeCodes
56 | *
57 | * @return array
58 | */
59 | public function filterStoresByStoreCodes(array $storeCodes): array
60 | {
61 | $filteredStores = [];
62 | $allStores = $this->storeRepositoryInterface->getList();
63 | foreach ($storeCodes as $storeCode) {
64 | if (array_key_exists($storeCode, $allStores)) {
65 | $filteredStores[] = $storeCode;
66 | }
67 | }
68 | return $filteredStores;
69 | }
70 |
71 | /**
72 | * Filter stores passed to import by existed
73 | *
74 | * @param array $storeIds
75 | *
76 | * @return array
77 | */
78 | public function filterStoresByStoreIds(array $storeIds): array
79 | {
80 | $filteredStores = [];
81 | $allStores = $this->storeRepositoryInterface->getList();
82 | foreach ($allStores as $store) {
83 | if (in_array($store->getId(), $storeIds)) {
84 | $filteredStores[] = $store->getId();
85 | }
86 | }
87 | return $filteredStores;
88 | }
89 | }
90 |
--------------------------------------------------------------------------------
/Observer/CmsSaveBefore.php:
--------------------------------------------------------------------------------
1 | BackupManager::TYPE_CMS_BLOCK,
20 | 'cms_page_save_before' => BackupManager::TYPE_CMS_PAGE,
21 | ];
22 |
23 | /**
24 | * Add keys to check if data was changed
25 | *
26 | * @var array
27 | */
28 | private $keysToCheck = [
29 | 'identifier',
30 | 'title',
31 | 'content',
32 | 'content_heading',
33 | ];
34 |
35 | /**
36 | * @var BackupManager
37 | */
38 | private $backupManager;
39 |
40 | /**
41 | * CmsSaveBefore constructor.
42 | *
43 | * @param BackupManager $backupManager
44 | */
45 | public function __construct(
46 | BackupManager $backupManager
47 | ) {
48 | $this->backupManager = $backupManager;
49 | }
50 |
51 | /**
52 | * @inheritDoc
53 | */
54 | public function execute(Observer $observer)
55 | {
56 | $eventName = $observer->getEvent()->getName();
57 | if (empty($this->eventsTypeMap[$eventName])) {
58 | return;
59 | }
60 |
61 | $cmsObject = $observer->getEvent()->getData('data_object');
62 | if ($this->hasImportantDataChanges($cmsObject)) {
63 | $this->backupManager->createBackup($this->eventsTypeMap[$eventName], $cmsObject);
64 | }
65 | }
66 |
67 | /**
68 | * Check if cms object was changed
69 | *
70 | * @param $cmsObject
71 | *
72 | * @return bool
73 | */
74 | private function hasImportantDataChanges($cmsObject): bool
75 | {
76 | foreach ($this->keysToCheck as $key) {
77 | if ($cmsObject->getOrigData() && $cmsObject->getData($key) !== $cmsObject->getOrigData($key)) {
78 | return true;
79 | }
80 | }
81 | return false;
82 | }
83 | }
84 |
--------------------------------------------------------------------------------
/Observer/DeleteContentVersion.php:
--------------------------------------------------------------------------------
1 | ContentVersionInterface::TYPE_PAGE,
23 | 'cms_block_delete_commit_after' => ContentVersionInterface::TYPE_BLOCK,
24 | ];
25 |
26 | /**
27 | * @var ContentVersionManagementInterface
28 | */
29 | private $contentVersionManagement;
30 |
31 | /**
32 | * @param ContentVersionManagementInterface $contentVersionManagement
33 | */
34 | public function __construct(
35 | ContentVersionManagementInterface $contentVersionManagement
36 | ) {
37 | $this->contentVersionManagement = $contentVersionManagement;
38 | }
39 |
40 | /**
41 | * @inheritDoc
42 | */
43 | public function execute(Observer $observer): void
44 | {
45 | $eventName = $observer->getEvent()->getName();
46 | if (!array_key_exists($eventName, $this->eventsTypeMap)) {
47 | return;
48 | }
49 | /** @var PageInterface|BlockInterface $cmsModel */
50 | $cmsModel = $observer->getEvent()->getData('data_object');
51 | $this->contentVersionManagement->deleteContentVersion(
52 | $cmsModel->getIdentifier(),
53 | $this->eventsTypeMap[$eventName],
54 | $cmsModel->getStores()
55 | );
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/Setup/Recurring.php:
--------------------------------------------------------------------------------
1 | contentVersionManagement = $contentVersionManagement;
25 | }
26 |
27 | /**
28 | * @inheritDoc
29 | */
30 | public function install(SchemaSetupInterface $setup, ModuleContextInterface $context)
31 | {
32 | $this->contentVersionManagement->processAll();
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/Test/Unit/Controller/Adminhtml/History/ViewTest.php:
--------------------------------------------------------------------------------
1 | resultFactoryMock = $this->getMockBuilder(ResultFactory::class)
45 | ->disableOriginalConstructor()
46 | ->getMock();
47 | $this->pageMock = $this->getMockBuilder(Page::class)
48 | ->disableOriginalConstructor()
49 | ->getMock();
50 | $this->pageConfigMock = $this->getMockBuilder(Config::class)
51 | ->disableOriginalConstructor()
52 | ->getMock();
53 | $this->pageTitleMock = $this->getMockBuilder(Title::class)
54 | ->disableOriginalConstructor()
55 | ->getMock();
56 | $this->contextMock = $this->getMockBuilder(Context::class)
57 | ->disableOriginalConstructor()
58 | ->getMock();
59 | }
60 |
61 | public function testExecute()
62 | {
63 | $this->resultFactoryMock->expects($this->once())
64 | ->method('create')
65 | ->with(ResultFactory::TYPE_PAGE)
66 | ->willReturn($this->pageMock);
67 | $this->pageMock->expects($this->once())
68 | ->method('getConfig')
69 | ->willReturn($this->pageConfigMock);
70 | $this->pageConfigMock->expects($this->once())
71 | ->method('getTitle')
72 | ->willReturn($this->pageTitleMock);
73 | $this->pageTitleMock->expects($this->once())
74 | ->method('prepend')
75 | ->with('Backup Preview');
76 | $this->contextMock->expects($this->once())
77 | ->method('getResultFactory')
78 | ->willReturn($this->resultFactoryMock);
79 |
80 | $controller = new View($this->contextMock);
81 | $this->assertSame($this->pageMock, $controller->execute());
82 |
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/Test/Unit/Controller/Adminhtml/Import/ImportTest.php:
--------------------------------------------------------------------------------
1 | requestMock = $this->getMockBuilder(RequestInterface::class)
50 | ->disableOriginalConstructor()
51 | ->getMockForAbstractClass();
52 | $this->importExportMock = $this->getMockBuilder(ContentImportInterface::class)
53 | ->disableOriginalConstructor()
54 | ->getMockForAbstractClass();
55 | $this->messageManagerMock = $this->getMockBuilder(ManagerInterface::class)
56 | ->disableOriginalConstructor()
57 | ->getMockForAbstractClass();
58 | $this->redirectFactoryMock = $this->getMockBuilder(RedirectFactory::class)
59 | ->disableOriginalConstructor()
60 | ->getMock();
61 | $this->redirectMock = $this->getMockBuilder(Redirect::class)
62 | ->disableOriginalConstructor()
63 | ->getMock();
64 | $this->contextMock = $this->getMockBuilder(Context::class)
65 | ->disableOriginalConstructor()
66 | ->getMock();
67 | }
68 |
69 | public function testExecute()
70 | {
71 | $upload[] = [
72 | 'file' => 'filename',
73 | 'path' => 'temppath'
74 | ];
75 | $cmsMode = ContentImportInterface::OD_CMS_MODE_UPDATE;
76 | $mediaMode = ContentImportInterface::OD_MEDIA_MODE_NONE;
77 |
78 | $this->requestMock->expects($this->atLeast(3))
79 | ->method('getParam')
80 | ->withConsecutive(['cms_import_mode'], ['media_import_mode'], ['upload'])
81 | ->willReturnOnConsecutiveCalls(
82 | $cmsMode,
83 | $mediaMode,
84 | $upload
85 | );
86 |
87 | $this->importExportMock->expects($this->once())
88 | ->method('setCmsModeOption')
89 | ->with($cmsMode)
90 | ->willReturn($this->importExportMock);
91 |
92 | $this->importExportMock->expects($this->once())
93 | ->method('setMediaModeOption')
94 | ->with($mediaMode)
95 | ->willReturn($this->importExportMock);
96 |
97 | $this->importExportMock->expects($this->once())
98 | ->method('importContentFromZipFile')
99 | ->with($upload[0]['path'] . DIRECTORY_SEPARATOR . $upload[0]['file'], true)
100 | ->willReturn(1);
101 |
102 | $this->messageManagerMock->expects($this->once())
103 | ->method('addSuccessMessage');
104 |
105 | $this->redirectFactoryMock->expects($this->once())
106 | ->method('create')
107 | ->willReturn($this->redirectMock);
108 |
109 | $this->redirectMock->expects($this->once())
110 | ->method('setPath')
111 | ->with('*/*/index')
112 | ->willReturnSelf();
113 |
114 | $this->contextMock->expects($this->once())
115 | ->method('getRequest')
116 | ->willReturn($this->requestMock);
117 |
118 | $this->contextMock->expects($this->once())
119 | ->method('getMessageManager')
120 | ->willReturn($this->messageManagerMock);
121 |
122 | $controller = new Import(
123 | $this->contextMock,
124 | $this->importExportMock,
125 | $this->redirectFactoryMock
126 | );
127 |
128 | $this->assertSame($this->redirectMock, $controller->execute());
129 | }
130 | }
131 |
--------------------------------------------------------------------------------
/Test/Unit/Controller/Adminhtml/Import/IndexTest.php:
--------------------------------------------------------------------------------
1 | resultFactoryMock = $this->getMockBuilder(ResultFactory::class)
45 | ->disableOriginalConstructor()
46 | ->getMock();
47 | $this->resultPageMock = $this->getMockBuilder(Page::class)
48 | ->disableOriginalConstructor()
49 | ->getMock();
50 | $this->pageConfigMock = $this->getMockBuilder(Config::class)
51 | ->disableOriginalConstructor()
52 | ->getMock();
53 | $this->pageTitleMock = $this->getMockBuilder(Title::class)
54 | ->disableOriginalConstructor()
55 | ->getMock();
56 | $this->contextMock = $this->getMockBuilder(Context::class)
57 | ->disableOriginalConstructor()
58 | ->getMock();
59 | }
60 |
61 | /**
62 | * @return void
63 | */
64 | public function testExecute()
65 | {
66 | $this->resultFactoryMock->expects($this->once())
67 | ->method('create')
68 | ->with(ResultFactory::TYPE_PAGE)
69 | ->willReturn($this->resultPageMock);
70 |
71 | $this->resultPageMock->expects($this->once())
72 | ->method('setActiveMenu')
73 | ->with('Overdose_CMSContent::import')
74 | ->willReturnSelf();
75 |
76 | $this->resultPageMock->expects($this->atLeast(2))
77 | ->method('addBreadcrumb')
78 | ->withConsecutive(['CMS', 'CMS'], ['Import CMS', 'Import CMS'])
79 | ->willReturnSelf();
80 |
81 | $this->resultPageMock->expects($this->atLeast(2))
82 | ->method('getConfig')
83 | ->willReturn($this->pageConfigMock);
84 |
85 | $this->pageConfigMock->expects($this->atLeast(2))
86 | ->method('getTitle')
87 | ->willReturn($this->pageTitleMock);
88 |
89 | $this->pageTitleMock->expects($this->atLeast(2))
90 | ->method('prepend')
91 | ->withConsecutive(['CMS'], ['CMS Import']);
92 |
93 | $this->contextMock->expects($this->once())
94 | ->method('getResultFactory')
95 | ->willReturn($this->resultFactoryMock);
96 |
97 | $controller = new Index($this->contextMock);
98 |
99 | $this->assertSame($this->resultPageMock, $controller->execute());
100 |
101 | }
102 | }
103 |
--------------------------------------------------------------------------------
/Test/Unit/Controller/Adminhtml/Import/UploadTest.php:
--------------------------------------------------------------------------------
1 | resultJsonFactoryMock = $this->getMockBuilder(JsonFactory::class)
50 | ->disableOriginalConstructor()
51 | ->getMock();
52 | $this->resultJsonMock = $this->getMockBuilder(Json::class)
53 | ->disableOriginalConstructor()
54 | ->getMock();
55 | $this->configMock = $this->getMockBuilder(Config::class)
56 | ->disableOriginalConstructor()
57 | ->getMock();
58 | $this->uploaderFactoryMock = $this->getMockBuilder(UploaderFactory::class)
59 | ->disableOriginalConstructor()
60 | ->getMock();
61 | $this->uploaderMock = $this->getMockBuilder(Uploader::class)
62 | ->disableOriginalConstructor()
63 | ->getMock();
64 | $this->messageManagerMock = $this->getMockBuilder(ManagerInterface::class)
65 | ->disableOriginalConstructor()
66 | ->getMockForAbstractClass();
67 | }
68 |
69 | /**
70 | * @return void
71 | */
72 | public function testExecute()
73 | {
74 | $backupDir = '';
75 |
76 | $this->resultJsonFactoryMock->expects($this->once())
77 | ->method('create')
78 | ->willReturn($this->resultJsonMock);
79 |
80 | $this->resultJsonMock->expects($this->once())
81 | ->method('setData')
82 | ->with([])
83 | ->willReturnSelf();
84 |
85 | $this->uploaderFactoryMock->expects($this->once())
86 | ->method('create')
87 | ->with(['fileId' => 'upload'])
88 | ->willReturn($this->uploaderMock);
89 |
90 | $this->uploaderMock->expects($this->once())
91 | ->method('setAllowedExtensions')
92 | ->with(['zip'])
93 | ->willReturnSelf();
94 |
95 | $this->uploaderMock->expects($this->once())
96 | ->method('setAllowRenameFiles')
97 | ->with(true)
98 | ->willReturnSelf();
99 |
100 | $this->uploaderMock->expects($this->once())
101 | ->method('save')
102 | ->with($backupDir . DIRECTORY_SEPARATOR . 'import')
103 | ->willReturn([]);
104 |
105 | $this->configMock->expects($this->once())
106 | ->method('getBackupsDir')
107 | ->willReturn($backupDir);
108 |
109 | $controller = new Upload(
110 | $this->configMock,
111 | $this->resultJsonFactoryMock,
112 | $this->uploaderFactoryMock,
113 | $this->messageManagerMock
114 | );
115 |
116 | $this->assertSame($this->resultJsonMock, $controller->execute());
117 | }
118 | }
119 |
--------------------------------------------------------------------------------
/Test/Unit/Model/BackupManagerTest.php:
--------------------------------------------------------------------------------
1 | configMock = $this->getMockBuilder(Config::class)
55 | ->disableOriginalConstructor()
56 | ->getMock();
57 | $this->fileMock = $this->getMockBuilder(FileManagerInterface::class)
58 | ->disableOriginalConstructor()
59 | ->getMock();
60 | $this->fileDriverMock = $this->getMockBuilder(File::class)
61 | ->disableOriginalConstructor()
62 | ->getMock();
63 | $this->loggerMock = $this->getMockBuilder(LoggerInterface::class)
64 | ->disableOriginalConstructor()
65 | ->getMock();
66 | $this->cmsBlockModelMock = $this->getMockBuilder(Block::class)
67 | ->disableOriginalConstructor()
68 | ->getMock();
69 | $this->cmsPageModelMock = $this->getMockBuilder(Page::class)
70 | ->disableOriginalConstructor()
71 | ->getMock();
72 | $this->json = new Json();
73 |
74 | $this->model = new BackupManager(
75 | $this->fileDriverMock,
76 | $this->fileMock,
77 | $this->configMock,
78 | $this->json,
79 | $this->loggerMock
80 | );
81 | }
82 |
83 | /**
84 | * @return void
85 | */
86 | public function testCreateBackupWithBlock()
87 | {
88 | $this->configMock->expects($this->once())
89 | ->method('isEnabled')
90 | ->willReturn(true);
91 |
92 | $this->fileMock->expects($this->once())
93 | ->method('writeData');
94 |
95 | $this->cmsBlockModelMock->expects($this->atLeastOnce())
96 | ->method('getStores')
97 | ->willReturn([0, 1]);
98 |
99 | $this->cmsBlockModelMock->expects($this->atLeastOnce())
100 | ->method('getIdentifier')
101 | ->willReturn('1');
102 |
103 | $this->cmsBlockModelMock->expects($this->atLeastOnce())
104 | ->method('getOrigData')
105 | ->withConsecutive(['title'], ['content'])
106 | ->willReturnOnConsecutiveCalls('Lorem Ipsum Dolor Sit Amet.');
107 |
108 | $this->model->setCmsObject($this->cmsBlockModelMock);
109 | $this->assertSame(
110 | $this->model,
111 | $this->model->createBackup('cms_block', $this->cmsBlockModelMock)
112 | );
113 | }
114 |
115 | /**
116 | * @return void
117 | */
118 | public function testCreateBackupWithPage()
119 | {
120 | $this->configMock->expects($this->once())
121 | ->method('isEnabled')
122 | ->willReturn(true);
123 |
124 | $this->fileMock->expects($this->once())
125 | ->method('writeData');
126 |
127 | $this->cmsPageModelMock->expects($this->atLeastOnce())
128 | ->method('getStores')
129 | ->willReturn([0, 1]);
130 |
131 | $this->cmsPageModelMock->expects($this->atLeastOnce())
132 | ->method('getIdentifier')
133 | ->willReturn('1');
134 |
135 | $this->cmsPageModelMock->expects($this->atLeastOnce())
136 | ->method('getOrigData')
137 | ->withConsecutive(['title'], ['content'])
138 | ->willReturnOnConsecutiveCalls('Lorem Ipsum Dolor Sit Amet.');
139 |
140 | $this->model->setCmsObject($this->cmsPageModelMock);
141 | $this->assertSame(
142 | $this->model,
143 | $this->model->createBackup('cms_page', $this->cmsPageModelMock)
144 | );
145 | }
146 | }
147 |
--------------------------------------------------------------------------------
/Test/Unit/Model/Config/App/FileResolverTest.php:
--------------------------------------------------------------------------------
1 | readerMock = $this->getMockBuilder(Reader::class)
47 | ->disableOriginalConstructor()
48 | ->getMock();
49 |
50 | $this->fileSystemMock = $this->getMockBuilder(Filesystem::class)
51 | ->disableOriginalConstructor()
52 | ->getMock();
53 |
54 | $this->iteratorFactoryMock = $this->getMockBuilder(FileIteratorFactory::class)
55 | ->onlyMethods(['create'])
56 | ->disableOriginalConstructor()
57 | ->getMock();
58 |
59 | $this->readInterfaceMock = $this->getMockBuilder(ReadInterface::class)
60 | ->disableOriginalConstructor()
61 | ->getMock();
62 |
63 | $this->fileIteratorMock = $this->getMockBuilder(FileIterator::class)
64 | ->disableOriginalConstructor()
65 | ->getMock();
66 |
67 | $this->sutObject = new FileResolver(
68 | $this->readerMock,
69 | $this->fileSystemMock,
70 | $this->iteratorFactoryMock
71 | );
72 | }
73 |
74 | /**
75 | * @dataProvider dataProviderForGet
76 | * @param string $filename
77 | * @param string $scope
78 | */
79 | public function testGet($filename, $scope)
80 | {
81 | if ($scope == 'primary') {
82 | $this->fileSystemMock->expects($this->once())
83 | ->method('getDirectoryRead')
84 | ->willReturn($this->readInterfaceMock);
85 |
86 | $this->readInterfaceMock->expects($this->once())
87 | ->method('search')
88 | ->willReturn(['subFileOne', 'subFileTwo']);
89 |
90 | $this->readInterfaceMock->expects($this->exactly(2))
91 | ->method('getAbsolutePath')
92 | ->willReturnOnConsecutiveCalls(['absPathOne', 'absPathTwo']);
93 |
94 | $this->iteratorFactoryMock->expects($this->once())
95 | ->method('create')
96 | ->willReturn($this->fileIteratorMock);
97 | } else {
98 | $this->readerMock->expects($this->once())
99 | ->method('getConfigurationFiles')
100 | ->willReturn($this->fileIteratorMock);
101 | }
102 |
103 | $this->assertSame($this->fileIteratorMock, $this->sutObject->get($filename, $scope));
104 | }
105 |
106 | /**
107 | * Data provider for get method.
108 | * @return string[][]
109 | */
110 | public function dataProviderForGet(): array
111 | {
112 | return [
113 | 'case_1_no_scope' => ['fileOne', ''],
114 | 'case_2_primary_scope' => ['fileTwo', 'primary'],
115 | 'case_3_global_scope' => ['fileThree', 'global']
116 | ];
117 | }
118 | }
119 |
--------------------------------------------------------------------------------
/Test/Unit/Model/Config/ConverterAbstractTest.php:
--------------------------------------------------------------------------------
1 | converterObject = new Converter();
22 | }
23 |
24 | /**
25 | * @dataProvider dataProviderForConvert
26 | */
27 | public function testConvert($xmlContent)
28 | {
29 | $domDocument = new DOMDocument();
30 | $domDocument->loadXML($xmlContent);
31 |
32 | $result = $this->converterObject->convert($domDocument);
33 | $expected = [
34 | 'some-identifier_0' => [
35 | 'identifier' => 'some-identifier',
36 | 'title' => 'Some block title',
37 | 'content' => '
Content here
',
38 | 'version' => '1.0.0',
39 | 'is_active' => '1',
40 | 'store_ids' => ''
41 | ],
42 | 'some-identifier-2_1_2_3' => [
43 | 'identifier' => 'some-identifier-2',
44 | 'title' => 'Some block title 2',
45 | 'content' => 'Content here 2
',
46 | 'version' => '1.0.1',
47 | 'is_active' => '1',
48 | 'store_ids' => '1,2,3'
49 | ]
50 | ];
51 |
52 | $this->assertSame($expected, $result);
53 | }
54 |
55 | /**
56 | * data provider for convert function. The xmlContent value is hardcoded and taken from sample cms_block_data.xml
57 | * file present in etc/od_cms directory.
58 | * @return string[][]
59 | */
60 | public function dataProviderForConvert(): array
61 | {
62 | $xmlContent = 'Some block titleContent here]]>1.0.01Some block title 2Content here 2]]>1.0.111,2,3';
63 | return [[$xmlContent]];
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/Test/Unit/Model/ConfigTest.php:
--------------------------------------------------------------------------------
1 | scopeConfig = $this->createMock(ScopeConfigInterface::class);
31 | $this->directoryList = $this->createMock(DirectoryList::class);
32 |
33 | $this->config = $this->createMock(Config::class);
34 | }
35 |
36 | /**
37 | * Test getBackupsDir
38 | *
39 | * @return void
40 | */
41 | public function testGetBackupsDir()
42 | {
43 | $this->directoryList->expects($this->once())
44 | ->method('getPath')
45 | ->willReturn('var');
46 |
47 | $config = new Config(
48 | $this->scopeConfig,
49 | $this->directoryList
50 | );
51 |
52 | $result = $config->getBackupsDir();
53 |
54 | $expected = 'var' . DIRECTORY_SEPARATOR . Config::CMS_DIR;
55 |
56 | $this->assertEquals($expected, $result);
57 | }
58 |
59 | /**
60 | * Test getExportPath
61 | *
62 | * @return void
63 | */
64 | public function testGetExportPath()
65 | {
66 | $this->directoryList->expects($this->once())
67 | ->method('getPath')
68 | ->willReturn('var');
69 |
70 | $config = new Config(
71 | $this->scopeConfig,
72 | $this->directoryList
73 | );
74 |
75 | $result = $config->getExportPath();
76 |
77 | $expected = 'var' . DIRECTORY_SEPARATOR . Config::CMS_DIR . DIRECTORY_SEPARATOR . Config::EXPORT_PATH;
78 |
79 | $this->assertEquals($expected, $result);
80 | }
81 |
82 | /**
83 | * Test getExtractPath
84 | *
85 | * @return void
86 | */
87 | public function testGetExtractPath()
88 | {
89 | $this->directoryList->expects($this->once())
90 | ->method('getPath')
91 | ->willReturn('var');
92 |
93 | $config = new Config(
94 | $this->scopeConfig,
95 | $this->directoryList
96 | );
97 |
98 | $result = $config->getExtractPath();
99 |
100 | $expected = 'var' . DIRECTORY_SEPARATOR . Config::CMS_DIR . DIRECTORY_SEPARATOR . Config::EXTRACT_PATH;
101 |
102 | $this->assertEquals($expected, $result);
103 | }
104 | }
105 |
--------------------------------------------------------------------------------
/Test/Unit/Model/Service/ClearCMSHistoryTest.php:
--------------------------------------------------------------------------------
1 | fileMock = $this->getMockBuilder(File::class)
56 | ->disableOriginalConstructor()
57 | ->getMock();
58 | $this->dateTimeMock = $this->getMockBuilder(DateTime::class)
59 | ->disableOriginalConstructor()
60 | ->getMock();
61 | $this->configMock = $this->getMockBuilder(Config::class)
62 | ->disableOriginalConstructor()
63 | ->getMock();
64 | $this->loggerInterfaceMock = $this->getMockBuilder(LoggerInterface::class)
65 | ->disableOriginalConstructor()
66 | ->getMock();
67 |
68 | $this->serviceModel = new ClearCMSHistory(
69 | $this->dateTimeMock,
70 | $this->configMock,
71 | $this->loggerInterfaceMock,
72 | $this->fileMock
73 | );
74 | }
75 |
76 | /**
77 | * Test clear action by providing two files in mock objects and expecting count of two returned in result.
78 | * @return void
79 | * @throws \Magento\Framework\Exception\FileSystemException
80 | */
81 | public function testExecute()
82 | {
83 | $outerDirs = ['parentOne', 'parentTwo'];
84 | $files = ['fileOne', 'fileTwo'];
85 |
86 | $this->fileMock->expects($this->atLeastOnce())
87 | ->method('readDirectory')
88 | ->willReturnOnConsecutiveCalls($outerDirs, $files, $files, $files, $files);
89 |
90 | $this->fileMock->expects($this->atLeastOnce())
91 | ->method('isFile')
92 | ->willReturn(true);
93 |
94 | $this->configMock->expects($this->atLeastOnce())
95 | ->method('getBackupsDir')
96 | ->willReturn('');
97 |
98 | $this->configMock->expects($this->atLeastOnce())
99 | ->method('getMethodType')
100 | ->willReturn(Config::PERIOD);
101 |
102 | $this->dateTimeMock->expects($this->atLeastOnce())
103 | ->method('gmtTimestamp')
104 | ->willReturn(1659355018);
105 |
106 | $this->assertEquals(2, $this->serviceModel->execute(Config::TYPE_BLOCK));
107 | }
108 | }
109 |
--------------------------------------------------------------------------------
/Test/Unit/Model/StoreManagementTest.php:
--------------------------------------------------------------------------------
1 | storeRepositoryMock = $this->getMockBuilder(StoreRepositoryInterface::class)
34 | ->disableOriginalConstructor()
35 | ->getMock();
36 | $this->storeInterfaceMock = $this->getMockBuilder(StoreInterface::class)
37 | ->disableOriginalConstructor()
38 | ->getMock();
39 | $this->model = new StoreManagement(
40 | $this->storeRepositoryMock
41 | );
42 | }
43 |
44 | /**
45 | * @return array
46 | */
47 | public function dataProviderGetStoreIdsByCodes(): array
48 | {
49 | return [
50 | 'store_not_found_case' => [
51 | 'storeCodes' => ['us'],
52 | 'expectedResult' => []
53 | ],
54 | 'store_found_case' => [
55 | 'storeCodes' => ['nz'],
56 | 'expectedResult' => [1]
57 | ],
58 | 'admin_case' => [
59 | 'storeCodes' => ['admin'],
60 | 'expectedResult' => [0]
61 | ],
62 | 'multi_store_case' => [
63 | 'storeCodes' => ['admin', 'nz', 'us', 'au'],
64 | 'expectedResult' => [0, 1, 2]
65 | ]
66 | ];
67 | }
68 |
69 | /**
70 | * @dataProvider dataProviderGetStoreIdsByCodes
71 | * @return void
72 | */
73 | public function testGetStoreIdsByCodes($storeCodes, $expectedResult)
74 | {
75 | $this->storeInterfaceMock
76 | ->method('getId')
77 | ->willReturn(1);
78 |
79 | $auStore = $this->getMockBuilder(StoreInterface::class)
80 | ->disableOriginalConstructor()
81 | ->getMock();
82 | $auStore->method('getId')
83 | ->willReturn(2);
84 |
85 | $this->storeRepositoryMock
86 | ->method('get')
87 | ->withConsecutive(['nz'], ['au'])
88 | ->willReturnOnConsecutiveCalls($this->storeInterfaceMock, $auStore);
89 |
90 | $this->storeRepositoryMock->expects($this->atLeastOnce())
91 | ->method('getList')
92 | ->willReturn(['admin' => 'adminStore', 'nz' => 'nzStore', 'au' => 'auStore']);
93 |
94 | $this->assertEquals($expectedResult, $this->model->getStoreIdsByCodes($storeCodes));
95 | }
96 |
97 | /**
98 | * @return array
99 | */
100 | public function dataProviderFilterStoresByStoreCodes(): array
101 | {
102 | return [
103 | 'store_not_found_case' => [
104 | 'storeCodes' => ['pk'],
105 | 'expectedResult' => []
106 | ],
107 | 'store_found_case' => [
108 | 'storeCodes' => ['nz'],
109 | 'expectedResult' => ['nz']
110 | ],
111 | 'admin_case' => [
112 | 'storeCodes' => ['admin'],
113 | 'expectedResult' => ['admin']
114 | ],
115 | 'multi_store_case' => [
116 | 'storeCodes' => ['admin', 'au', 'nz', 'ua'],
117 | 'expectedResult' => ['admin', 'nz']
118 | ]
119 | ];
120 | }
121 |
122 | /**
123 | * @dataProvider dataProviderFilterStoresByStoreCodes
124 | * @return void
125 | */
126 | public function testFilterStoresByStoreCodes($storeCodes, $expectedResult)
127 | {
128 | $this->storeRepositoryMock->expects($this->once())
129 | ->method('getList')
130 | ->willReturn(['admin' => 'adminStore', 'nz' => 'nzStore']);
131 |
132 | $this->assertSame($expectedResult, $this->model->filterStoresByStoreCodes($storeCodes));
133 | }
134 |
135 | /**
136 | * @return array
137 | */
138 | public function dataProviderFilterStoresByStoreIds(): array
139 | {
140 | return [
141 | 'store_not_found_case' => [
142 | 'storeIds' => [77],
143 | 'expectedResult' => []
144 | ],
145 | 'store_found_case' => [
146 | 'storeIds' => [1],
147 | 'expectedResult' => [1]
148 | ],
149 | 'admin_case' => [
150 | 'storeIds' => [0],
151 | 'expectedResult' => [0]
152 | ],
153 | 'multi_store_case' => [
154 | 'storeIds' => [0, 1, 2, 77],
155 | 'expectedResult' => [0, 1, 2]
156 | ]
157 | ];
158 | }
159 |
160 | /**
161 | * @dataProvider dataProviderFilterStoresByStoreIds
162 | * @return void
163 | */
164 | public function testFilterStoresByStoreIds($storeIds, $expectedResult)
165 | {
166 | $adminStore = $this->getMockBuilder(StoreInterface::class)
167 | ->disableOriginalConstructor()
168 | ->getMock();
169 | $adminStore->expects($this->atLeastOnce())
170 | ->method('getId')
171 | ->willReturn(0);
172 |
173 | $nzStore = $this->getMockBuilder(StoreInterface::class)
174 | ->disableOriginalConstructor()
175 | ->getMock();
176 | $nzStore->expects($this->atLeastOnce())
177 | ->method('getId')
178 | ->willReturn(1);
179 |
180 | $auStore = $this->getMockBuilder(StoreInterface::class)
181 | ->disableOriginalConstructor()
182 | ->getMock();
183 | $auStore->expects($this->atLeastOnce())
184 | ->method('getId')
185 | ->willReturn(2);
186 |
187 | $this->storeRepositoryMock->expects($this->atLeastOnce())
188 | ->method('getList')
189 | ->willReturn(['admin' => $adminStore, 'nz' => $nzStore, 'au' => $auStore]);
190 |
191 | $this->assertSame($expectedResult, $this->model->filterStoresByStoreIds($storeIds));
192 | }
193 | }
194 |
--------------------------------------------------------------------------------
/Test/Unit/Observer/CmsSaveBeforeTest.php:
--------------------------------------------------------------------------------
1 | cmsSaveBefore = $this->getMockBuilder(CmsSaveBefore::class)
28 | ->disableOriginalConstructor(true)
29 | ->getMock();
30 | }
31 |
32 | /**
33 | * Test function: HasImportantDataChanges
34 | *
35 | * @param $cmsEntity
36 | * @param bool $expected
37 | * @dataProvider formHasImportantDataChangesProvider
38 | *
39 | * @return void
40 | * @throws \ReflectionException
41 | */
42 | public function testHasImportantDataChanges($cmsEntity, bool $expected)
43 | {
44 | $result = $this->invokeMethod(
45 | $this->cmsSaveBefore,
46 | 'hasImportantDataChanges',
47 | [
48 | $cmsEntity
49 | ]
50 | );
51 |
52 | $this->assertEquals($expected, $result);
53 | }
54 |
55 | /**
56 | * Provider testHasImportantDataChanges
57 | *
58 | * @return array
59 | */
60 | public function formHasImportantDataChangesProvider(): array
61 | {
62 | $testCases = [];
63 |
64 | $dataModel = $this->getMockBuilder(AbstractModel::class)
65 | ->disableOriginalConstructor()
66 | ->getMockForAbstractClass();
67 |
68 | $dataModelCase1 = clone $dataModel;
69 | $dataModelCase1->setData([
70 | 'identifier' => 'no-route',
71 | 'title' => 'no-route',
72 | 'content' => 'no-route',
73 | 'content_heading' => 'no-route',
74 | ]);
75 | $dataModelCase1->setOrigData('identifier', 'no-route');
76 | $dataModelCase1->setOrigData('title', 'no-route');
77 | $dataModelCase1->setOrigData('content', 'no-route');
78 | $dataModelCase1->setOrigData('content_heading', 'no-route');
79 |
80 | $testCases['case_1_true'] = [
81 | 'cmsEntity' => $dataModelCase1,
82 | 'expected' => false
83 | ];
84 |
85 | $dataModelCase2 = clone $dataModel;
86 | $dataModelCase2->setData([
87 | 'identifier' => 'no-route2',
88 | 'title' => 'no-route',
89 | 'content' => 'no-route',
90 | 'content_heading' => 'no-route',
91 | ]);
92 | $dataModelCase2->setOrigData('identifier', 'no-route');
93 | $dataModelCase2->setOrigData('title', 'no-route');
94 | $dataModelCase2->setOrigData('content', 'no-route');
95 | $dataModelCase2->setOrigData('content_heading', 'no-route');
96 |
97 | $testCases['case_2_false'] = [
98 | 'cmsEntity' => $dataModelCase2,
99 | 'expected' => true
100 | ];
101 |
102 | $dataModelCase3 = clone $dataModel;
103 | $dataModelCase3->setData([
104 | 'identifier' => 'no-route2',
105 | 'title' => 'no-route',
106 | 'content' => 'no-route',
107 | 'content_heading' => 'no-route',
108 | ]);
109 |
110 | $testCases['case_3_false'] = [
111 | 'cmsEntity' => $dataModelCase3,
112 | 'expected' => false
113 | ];
114 |
115 | return $testCases;
116 | }
117 |
118 | /**
119 | * Invoke private method
120 | *
121 | * @param $object
122 | * @param $methodName
123 | * @param array $parameters
124 | *
125 | * @return mixed
126 | * @throws \ReflectionException
127 | */
128 | protected function invokeMethod(&$object, $methodName, array $parameters = [])
129 | {
130 | $reflection = new \ReflectionClass(get_class($object));
131 | $method = $reflection->getMethod($methodName);
132 | $method->setAccessible(true);
133 |
134 | return $method->invokeArgs($object, $parameters);
135 | }
136 | }
137 |
--------------------------------------------------------------------------------
/Ui/DataProvider/HistoryView.php:
--------------------------------------------------------------------------------
1 | backupManager = $backupManager;
68 | $this->jsonFormatter = $jsonFormatter;
69 | $this->file = $file;
70 | $this->request = $request;
71 | }
72 |
73 | /**
74 | * Get data
75 | *
76 | * @return array
77 | */
78 | public function getData(): array
79 | {
80 | $data = [];
81 | if ($backupItemIdentifier = $this->request->getParam('bc_identifier')) {
82 | $parsedArray = $this->jsonFormatter->decode($this->getBackupContent(), true);
83 | $data[$backupItemIdentifier] = [
84 | 'identifier' => $parsedArray['identifier'] ?? '',
85 | 'title' => $parsedArray['title'] ?? '',
86 | 'content' => $parsedArray['content'] ?? ''
87 | ];
88 | }
89 |
90 | return $data;
91 | }
92 |
93 | /**
94 | * Retrieve content from backup
95 | *
96 | * @return mixed
97 | */
98 | public function getBackupContent()
99 | {
100 | $itemIdentifier = $this->request->getParam('bc_identifier');
101 | $itemName = $this->request->getParam('item');
102 | $itemType = $this->request->getParam('bc_type');
103 | $storeId = $this->request->getParam('store_id');
104 |
105 | if (!is_null($storeId)) {
106 | $path = $this->backupManager->getBackupPathByStoreId($itemType, $itemIdentifier, (int)$storeId)
107 | . DIRECTORY_SEPARATOR . $itemName;
108 | } else {
109 | $path = $this->backupManager->getBackupPath($itemType, $itemIdentifier)
110 | . DIRECTORY_SEPARATOR . $itemName;
111 | }
112 |
113 | return $this->file->readData($path);
114 | }
115 |
116 | /**
117 | * @inheritDoc
118 | */
119 | public function addFilter(Filter $filter)
120 | {
121 | return $this;
122 | }
123 | }
124 |
--------------------------------------------------------------------------------
/Ui/DataProvider/Import.php:
--------------------------------------------------------------------------------
1 | =102.0",
14 | "overdose/module-core": "*"
15 | },
16 | "autoload": {
17 | "psr-4": {
18 | "Overdose\\CMSContent\\": ""
19 | },
20 | "files": [
21 | "registration.php"
22 | ]
23 | },
24 | "version": "2.0.5"
25 | }
26 |
--------------------------------------------------------------------------------
/etc/acl.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/etc/adminhtml/events.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
6 |
7 |
8 |
10 |
11 |
12 |
14 |
15 |
16 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/etc/adminhtml/menu.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
12 |
13 |
--------------------------------------------------------------------------------
/etc/adminhtml/routes.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/etc/adminhtml/system.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | od_core
7 | Overdose_CMSContent::config_overdose_cmscontent
8 |
9 |
10 |
11 |
12 |
13 | Magento\Config\Model\Config\Source\Yesno
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 | In case enabled: old backups will be deleted by cron according to selected deleting method.
24 | Magento\Config\Model\Config\Source\Yesno
25 |
26 |
27 |
28 | Overdose\CMSContent\Model\OptionSource\Methods
29 | By Periods: Left one file per a period (weeks, months and years):
31 | - One history item per year for older than year.
32 | - One history item per month for older than month but yonger 12m.
33 | - One history item per week for older than week but yonger one month.
34 | Files younger than one week, excluded.
35 | Older Than: Delete files older than 'Period' and it number.
36 | ]]>
37 |
38 |
39 |
40 | Overdose\CMSContent\Model\OptionSource\Periods
41 |
42 | older_than
43 |
44 |
45 |
46 |
47 |
48 | older_than
49 |
50 |
51 |
52 | 1
53 |
54 |
55 |
56 |
57 |
58 |
59 | Magento\Cron\Model\Config\Source\Frequency
60 | Overdose\CMSContent\Model\Config\Cron\SaveValue
61 |
62 |
63 |
64 |
67 |
68 |
69 | 1
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 | Magento\Config\Model\Config\Source\Yesno
79 |
80 |
81 | 1
82 |
83 |
84 |
85 |
86 |
87 |
--------------------------------------------------------------------------------
/etc/cms_block_data.xsd:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/etc/cms_page_data.xsd:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/etc/config.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | 0
7 |
8 |
9 |
10 | 0
11 | older_than
12 | 5
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/etc/crontab.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | crontab/default/jobs/cms_content_delete_backups/schedule/cron_expr
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/etc/db_schema.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/etc/module.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/etc/od_cms/cms_block_data.xml.sample:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Some block title
6 | Content here ]]>
7 | 1.0.0
8 | 1
9 |
10 |
11 |
12 | Some block title 2
13 | Content here 2]]>
14 | 1.0.1
15 | 1
16 | 1,2,3
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/etc/od_cms/cms_page_data.xml.sample:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Some title
6 | Content here ]]>
7 | 1.0.0
8 | 1
9 |
10 |
11 |
12 | Some title 2
13 | Some content heading 2
14 | Content here 2]]>
15 | 2columns-left
16 | 1.0.1
17 | 1
18 | 1,2,3
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/etc/od_cms/cms_page_data_no-route.xml.sample:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | no-route
6 | 404 Not Found
7 | 2columns-right
8 | Page keywords
9 | Page description
10 | Whoops, our bad...
11 |
12 | The page you requested was not found, and we have a fine guess why.
13 |
14 |
15 | - If you typed the URL directly, please make sure the spelling is correct.
16 | - If you clicked on a link to get here, the link is outdated.
17 |
18 |
19 |
20 | - What can you do?
21 | - Have no fear, help is near! There are many ways you can get back on track with Magento Store.
22 | -
23 |
24 | - Go back to the previous page.
25 | - Use the search bar at the top of the page to search for your products.
26 | - Follow these links to get you back on track!
Store Home | My Account
27 | ]]>
28 | 0
29 |
30 |
31 |
32 |
33 |
34 |
35 | 1
36 | 0
37 | 1.0.1
38 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/registration.php:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/view/adminhtml/layout/cmscontent_import_index.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/view/adminhtml/templates/cms/backup/history.phtml:
--------------------------------------------------------------------------------
1 |
2 | getBackups(); ?>
3 |
4 |
7 |
8 |
9 |
10 |
11 | = __('No records found'); ?>
12 |
13 |
14 |
15 |
21 |
22 |
23 |
24 | =__('This Action will rewrite existing content of the page/block from backup.')?>
25 | =__('Do you want to proceed?')?>
26 |
27 |
50 |
51 |
--------------------------------------------------------------------------------
/view/adminhtml/templates/history/view.phtml:
--------------------------------------------------------------------------------
1 |
6 | = $block->getBackupContent(); ?>
7 |
--------------------------------------------------------------------------------
/view/adminhtml/ui_component/cms_block_form.xml:
--------------------------------------------------------------------------------
1 |
2 |
21 |
--------------------------------------------------------------------------------
/view/adminhtml/ui_component/cms_block_listing.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | -
8 |
- json_export
9 | - Export to JSON file
10 |
11 |
12 |
13 |
14 |
15 |
16 | -
17 |
- json_split_export
18 | - Export to split JSON files
19 |
20 |
21 |
22 |
23 |
24 |
25 | -
26 |
- xml_export
27 | - Export to XML file
28 |
29 |
30 |
31 |
32 |
33 |
34 | -
35 |
- xml_split_export
36 | - Export to split XML files
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/view/adminhtml/ui_component/cms_page_form.xml:
--------------------------------------------------------------------------------
1 |
2 |
22 |
--------------------------------------------------------------------------------
/view/adminhtml/ui_component/cms_page_listing.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | -
8 |
- export_json
9 | - Export JSON
10 |
11 |
12 |
13 |
14 |
15 |
16 | -
17 |
- export_json_split
18 | - Export as split JSON files
19 |
20 |
21 |
22 |
23 |
24 |
25 | -
26 |
- export_xml
27 | - Export XML
28 |
29 |
30 |
31 |
32 |
33 |
34 | -
35 |
- export_xml_split
36 | - Export as split XML files
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/view/adminhtml/ui_component/cmscontent_import_form.xml:
--------------------------------------------------------------------------------
1 |
2 |
88 |
--------------------------------------------------------------------------------
/view/adminhtml/ui_component/overdose_cmscontent_historyview_form.xml:
--------------------------------------------------------------------------------
1 |
2 |
85 |
--------------------------------------------------------------------------------