diff --git a/app/code/core/Mage/Cms/Helper/Page.php b/app/code/core/Mage/Cms/Helper/Page.php index 710b0a86658..1cd1f559e5e 100644 --- a/app/code/core/Mage/Cms/Helper/Page.php +++ b/app/code/core/Mage/Cms/Helper/Page.php @@ -174,4 +174,66 @@ public static function getUsedInStoreConfigPaths(?array $paths = []): array return $searchPaths; } + + /** + * @param self::XML_PATH_* $path + */ + public static function getConfigLabelFromConfigPath(string $path): string + { + return match ($path) { + self::XML_PATH_NO_ROUTE_PAGE => Mage::helper('cms')->__('No Route Page'), + self::XML_PATH_NO_COOKIES_PAGE => Mage::helper('cms')->__('No Cookies Page'), + self::XML_PATH_HOME_PAGE => Mage::helper('cms')->__('Home Page'), + default => $path, + }; + } + + /** + * @param Mage_Adminhtml_Block_System_Config_Form::SCOPE_* $scope + * @throws Mage_Core_Exception + */ + public static function getScopeInfoFromConfigScope(string $scope, string $scopeId): string + { + return match ($scope) { + Mage_Adminhtml_Block_System_Config_Form::SCOPE_DEFAULT => Mage::helper('cms')->__('Default Config'), + Mage_Adminhtml_Block_System_Config_Form::SCOPE_WEBSITES => Mage::app()->getWebsite($scopeId)->getName(), + Mage_Adminhtml_Block_System_Config_Form::SCOPE_STORES => sprintf( + '%s "%s"', + Mage::app()->getStore($scopeId)->getGroup()->getName(), + Mage::app()->getStore($scopeId)->getName(), + ), + }; + } + + /** + * @throws Mage_Core_Exception + */ + public static function getValidateConfigErrorMessage(Mage_Core_Model_Resource_Db_Collection_Abstract $isUsedInConfig): string + { + $messages = []; + + $data = $isUsedInConfig->getData(); + foreach ($data as $key => $item) { + $path = $item['path']; + unset($item['config_id'], $item['path'], $item['updated_at'], $item['value']); + $data[$path][] = $item; + unset($data[$key], $key, $path); + } + + foreach ($data as $path => $items) { + $scopes = []; + foreach ($items as $item) { + $scopes[] = self::getScopeInfoFromConfigScope($item['scope'], $item['scope_id']); + } + + $messages[] = sprintf( + '"%s" (%s)', + self::getConfigLabelFromConfigPath($path), + implode(', ', $scopes), + ); + } + unset($data, $path, $items, $item, $scopes); + + return implode(', ', $messages); + } } diff --git a/app/code/core/Mage/Cms/Model/Resource/Page.php b/app/code/core/Mage/Cms/Model/Resource/Page.php index fe70583a913..09ef2627598 100644 --- a/app/code/core/Mage/Cms/Model/Resource/Page.php +++ b/app/code/core/Mage/Cms/Model/Resource/Page.php @@ -39,8 +39,9 @@ protected function _beforeDelete(Mage_Core_Model_Abstract $object) $object->setId(null); Mage::throwException( Mage::helper('cms')->__( - 'Cannot delete page, it is used in "%s".', - implode(', ', $isUsedInConfig->getColumnValues('path')), + 'Cannot delete page, it is used in configuration for %s.', + Mage::helper('adminhtml')->getUrl('adminhtml/system_config/edit', ['section' => 'web']), + Mage_Cms_Helper_Page::getValidateConfigErrorMessage($isUsedInConfig), ), ); } @@ -79,8 +80,9 @@ protected function _beforeSave(Mage_Core_Model_Abstract $object) $object->setIsActive(true); Mage::getSingleton('adminhtml/session')->addWarning( Mage::helper('cms')->__( - 'Cannot disable page, it is used in configuration "%s".', - implode(', ', $isUsedInConfig->getColumnValues('path')), + 'Cannot disable page, it is used in configuration for %s.', + Mage::helper('adminhtml')->getUrl('adminhtml/system_config/edit', ['section' => 'web']), + Mage_Cms_Helper_Page::getValidateConfigErrorMessage($isUsedInConfig), ), ); } @@ -281,7 +283,9 @@ protected function isValidPageIdentifier(Mage_Core_Model_Abstract $object) public function getUsedInStoreConfigCollection(Mage_Cms_Model_Page $page, ?array $paths = []): Mage_Core_Model_Resource_Db_Collection_Abstract { - $storeIds = (array) $page->getStoreId(); + $storeId = (array) $page->getStoreId(); # null on save + $stores = (array) $page->getStores(); # null on delete + $storeIds = array_merge($storeId, $stores); $storeIds[] = Mage_Core_Model_App::ADMIN_STORE_ID; $config = Mage::getResourceModel('core/config_data_collection') ->addFieldToFilter('value', $page->getIdentifier()) diff --git a/app/code/core/Mage/Cms/etc/system.xml b/app/code/core/Mage/Cms/etc/system.xml index 78438587b9e..bf6806e043a 100644 --- a/app/code/core/Mage/Cms/etc/system.xml +++ b/app/code/core/Mage/Cms/etc/system.xml @@ -17,7 +17,7 @@ select adminhtml/system_config_source_cms_page - 1 + 21 1 1 1 @@ -26,7 +26,7 @@ select adminhtml/system_config_source_cms_page - 2 + 22 1 1 1 @@ -35,7 +35,7 @@ select adminhtml/system_config_source_cms_page - 3 + 23 1 1 1 @@ -44,7 +44,7 @@ select adminhtml/system_config_source_yesno - 5 + 25 1 1 1 diff --git a/app/locale/en_US/Mage_Cms.csv b/app/locale/en_US/Mage_Cms.csv index 52f813fe13d..d106bdd764f 100644 --- a/app/locale/en_US/Mage_Cms.csv +++ b/app/locale/en_US/Mage_Cms.csv @@ -27,10 +27,10 @@ "CMS Static Block","CMS Static Block" "CMS Static Block Default Template","CMS Static Block Default Template" "Cannot create new directory.","Cannot create new directory." -"Cannot delete page, it is used in ""%s"".","Cannot delete page, it is used in ""%s""." +"Cannot delete page, it is used in configuration %s.","Cannot delete page, it is used in configuration %s." "Cannot delete directory %s.","Cannot delete directory %s." "Cannot delete root directory %s.","Cannot delete root directory %s." -"Cannot disable page, it is used in configuration ""%s"".","Cannot disable page, it is used in configuration ""%s""." +"Cannot disable page, it is used in configuration %s.","Cannot disable page, it is used in configuration %s." "Cannot upload file.","Cannot upload file." "Collapse All","Collapse All" "Content","Content" diff --git a/cypress/e2e/openmage/backend/cms/page.cy.js b/cypress/e2e/openmage/backend/cms/page.cy.js index c0f43f81f0f..ebd15a65641 100644 --- a/cypress/e2e/openmage/backend/cms/page.cy.js +++ b/cypress/e2e/openmage/backend/cms/page.cy.js @@ -1,4 +1,5 @@ const route = cy.testRoutes.backend.cms.page; +const validation = cy.openmage.validation; describe(`Checks admin system "${route.h3}"`, () => { beforeEach('Log in the user', () => { @@ -9,4 +10,31 @@ describe(`Checks admin system "${route.h3}"`, () => { it(`tests classes and title`, () => { cy.adminTestRoute(route); }); + + it('tests to disable a CMS page that is used in config', () => { + cy.log('Select a CMS page'); + cy.get(route._gridTable) + .contains('td', 'no-route') + .click(); + + cy.log('Disable the CMS page'); + cy.get('#page_is_active') + .select('Disabled'); + + validation.saveAction(route._buttonSaveAndContinue); + cy.get(validation._warningMessage).should('include.text', 'Cannot disable page, it is used in configuration'); + cy.get(validation._successMessage).should('include.text', 'The page has been saved.'); + cy.get('#messages').screenshot('error-disable-active-page', { overwrite: true}); + }); + + it('tests to delete a CMS page that is used in config', () => { + cy.log('Select a CMS page'); + cy.get(route._gridTable) + .contains('td', 'no-route') + .click(); + + validation.saveAction(route._buttonDelete); + cy.get(validation._errorMessage).should('include.text', 'Cannot delete page'); + cy.get('#messages').screenshot('error-delete-active-page', { overwrite: true}); + }); }); \ No newline at end of file diff --git a/cypress/support/openmage.js b/cypress/support/openmage.js index e4c67926cc9..c7e5a0c56e0 100644 --- a/cypress/support/openmage.js +++ b/cypress/support/openmage.js @@ -82,7 +82,7 @@ cy.openmage = { }, saveAction: (selector) => { cy.log('Clicking on Save button'); - cy.get(selector).click({force: true, multiple: true}); + cy.get(selector).first().click({force: true, multiple: false}); }, validateFields: (fields, validation) =>{ cy.log('Checking for error messages'); diff --git a/cypress/support/openmage/config/paths.js b/cypress/support/openmage/config/paths.js index 2470f01ecf2..337c497f3d2 100644 --- a/cypress/support/openmage/config/paths.js +++ b/cypress/support/openmage/config/paths.js @@ -73,6 +73,11 @@ cy.testRoutes = { url: 'cms_page/index', h3: 'Manage Pages', _h3: adminPage._h3, + _gridTable: '#cmsPageGrid_table', + _buttonDelete: '.form-buttons button[title="Delete Page"]', + _buttonReset: '.form-buttons button[title="Reset"]', + _buttonSave: '.form-buttons button[title="Save Page"]', + _buttonSaveAndContinue: '.form-buttons button[title="Save and Continue Edit"]', }, widget: { _id_parent: adminNav.cms, diff --git a/tests/unit/Mage/Cms/Helper/PageTest.php b/tests/unit/Mage/Cms/Helper/PageTest.php index 284e0dea6ed..f54b712a50a 100644 --- a/tests/unit/Mage/Cms/Helper/PageTest.php +++ b/tests/unit/Mage/Cms/Helper/PageTest.php @@ -20,6 +20,7 @@ final class PageTest extends OpenMageTest use CmsTrait; /** + * @covers Mage_Cms_Helper_Page::getUsedInStoreConfigPaths() * @dataProvider provideGetUsedInStoreConfigPaths * @group Helper */ @@ -27,4 +28,24 @@ public function testGetUsedInStoreConfigPaths(array $expectedResult, ?array $pat { self::assertSame($expectedResult, Subject::getUsedInStoreConfigPaths($path)); } + + /** + * @covers Mage_Cms_Helper_Page::getConfigLabelFromConfigPath() + * @dataProvider provideGetConfigLabelFromConfigPath + * @group Helper + */ + public function testGetConfigLabelFromConfigPath(string $expectedResult, string $paths): void + { + self::assertSame($expectedResult, Subject::getConfigLabelFromConfigPath($paths)); + } + + /** + * @covers Mage_Cms_Helper_Page::getScopeInfoFromConfigScope() + * @dataProvider provideGetScopeInfoFromConfigScope + * @group Helper + */ + public function testGetScopeInfoFromConfigScope(string $expectedResult, string $scope, string $scopeId): void + { + self::assertStringStartsWith($expectedResult, Subject::getScopeInfoFromConfigScope($scope, $scopeId)); + } } diff --git a/tests/unit/Traits/DataProvider/Mage/Cms/CmsTrait.php b/tests/unit/Traits/DataProvider/Mage/Cms/CmsTrait.php index 9b02b997d0e..d6df9624f30 100644 --- a/tests/unit/Traits/DataProvider/Mage/Cms/CmsTrait.php +++ b/tests/unit/Traits/DataProvider/Mage/Cms/CmsTrait.php @@ -11,6 +11,7 @@ namespace OpenMage\Tests\Unit\Traits\DataProvider\Mage\Cms; use Generator; +use Mage_Adminhtml_Block_System_Config_Form; use Mage_Cms_Helper_Page; trait CmsTrait @@ -45,6 +46,45 @@ public function provideGetUsedInStoreConfigPaths(): Generator ]; } + public function provideGetConfigLabelFromConfigPath(): Generator + { + yield 'home page' => [ + 'Home Page', + Mage_Cms_Helper_Page::XML_PATH_HOME_PAGE, + ]; + + yield 'no cookie page' => [ + 'No Cookies Page', + Mage_Cms_Helper_Page::XML_PATH_NO_COOKIES_PAGE, + ]; + + yield 'no route page' => [ + 'No Route Page', + Mage_Cms_Helper_Page::XML_PATH_NO_ROUTE_PAGE, + ]; + } + + public function provideGetScopeInfoFromConfigScope(): Generator + { + yield 'default' => [ + 'Default Config', + Mage_Adminhtml_Block_System_Config_Form::SCOPE_DEFAULT, + '1', + ]; + + yield 'websites' => [ + 'Main Website', + Mage_Adminhtml_Block_System_Config_Form::SCOPE_WEBSITES, + '1', + ]; + + yield 'stores' => [ + 'Main Website', + Mage_Adminhtml_Block_System_Config_Form::SCOPE_STORES, + '1', + ]; + } + public function provideGetShortFilename(): Generator { yield 'full length' => [