From 844bdd0311c0ea46dbc0175d511600802313b6d4 Mon Sep 17 00:00:00 2001 From: Gregor Harlan Date: Tue, 20 Mar 2018 13:06:13 +0100 Subject: [PATCH] =?UTF-8?q?Wenn=20deployed,=20dann=20manche=20Pages=20sch?= =?UTF-8?q?=C3=BCtzen=20(#23)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- boot.php | 4 ++ lib/api_protected_page.php | 54 +++++++++++++++ lib/handler.php | 108 ++++++++++++++++++++++++++++++ package.yml | 31 ++++++--- pages/system.ydeploy.php | 131 +++++++++++++++++++++++++++++++++++++ 5 files changed, 320 insertions(+), 8 deletions(-) create mode 100644 lib/api_protected_page.php diff --git a/boot.php b/boot.php index 4003684..d02ba76 100644 --- a/boot.php +++ b/boot.php @@ -10,3 +10,7 @@ rex_extension::register('PAGE_BODY_ATTR', 'rex_ydeploy_handler::addBodyClasses'); rex_extension::register('OUTPUT_FILTER', 'rex_ydeploy_handler::addBadge'); + +if (rex_ydeploy::factory()->isDeployed()) { + rex_extension::register('PAGES_PREPARED', 'rex_ydeploy_handler::protectPages'); +} diff --git a/lib/api_protected_page.php b/lib/api_protected_page.php new file mode 100644 index 0000000..0a2f4ec --- /dev/null +++ b/lib/api_protected_page.php @@ -0,0 +1,54 @@ +isAdmin()) { + throw new rex_api_exception('Protected pages can be (un)locked only by admins.'); + } + + $action = rex_get('action', 'string'); + + if (!in_array($action, ['lock', 'unlock'], true)) { + throw new rex_api_exception('Supported protected page actions are "lock" and "unlock", but "'.$action.'" given.'); + } + + $protectedPage = rex_get('protected_page', 'string'); + + $foundPage = null; + foreach (rex_ydeploy_handler::getProtectedPages() as $page => $subpages) { + // `yform/manager/table_edit` must not match `yform/man` + // so we add slashes to avoid this + if (0 === strpos($protectedPage.'/', $page.'/')) { + $foundPage = $page; + + break; + } + } + + if (!$foundPage) { + throw new rex_api_exception('The page "'.$protectedPage.'" is not protected.'); + } + + if ('unlock' === $action) { + rex_ydeploy_handler::unlockPage($foundPage); + } else { + rex_ydeploy_handler::lockPage($foundPage); + } + + if ($redirect = rex_get('redirect', 'string')) { + rex_response::sendRedirect($redirect); + } + + $result = new rex_api_result(true); + $result->setRequiresReboot(true); + + return $result; + } + + protected function requiresCsrfProtection() + { + return true; + } +} diff --git a/lib/handler.php b/lib/handler.php index 8a0d0cb..51051aa 100644 --- a/lib/handler.php +++ b/lib/handler.php @@ -48,4 +48,112 @@ public static function addBadge(rex_extension_point $ep) return str_replace('', $badge.'', $ep->getSubject()); } + + public static function protectPages() + { + $unlockedPages = self::getUnlockedPages(); + + foreach (self::getProtectedPages() as $page => $subpages) { + $page = rex_be_controller::getPageObject($page); + + if (!$page) { + continue; + } + + if (isset($unlockedPages[$page->getFullKey()])) { + self::handleUnlockedPage($page, $subpages); + + continue; + } + + if (!is_array($subpages)) { + self::protectPage($page); + + continue; + } + + foreach ($subpages as $subpage) { + $subpage = $page->getSubpage($subpage); + + if ($subpage) { + self::protectPage($subpage); + } + } + } + } + + public static function getProtectedPages(): array + { + return rex_addon::get('ydeploy')->getProperty('config')['protected_pages']; + } + + public static function getUnlockedPages(): array + { + return rex_session('ydeploy_unlocked_pages', 'array', []); + } + + public static function unlockPage(string $page) + { + $unlockedPages = self::getUnlockedPages(); + $unlockedPages[$page] = true; + rex_set_session('ydeploy_unlocked_pages', $unlockedPages); + } + + public static function lockPage(string $page) + { + $unlockedPages = self::getUnlockedPages(); + unset($unlockedPages[$page]); + rex_set_session('ydeploy_unlocked_pages', $unlockedPages); + } + + private static function protectPage(rex_be_page $page) + { + if (rex_be_controller::getCurrentPage() && $page->isActive()) { + rex_be_controller::setCurrentPage('system/ydeploy'); + } + + $page->setHidden(true); + + // If page is first subpage of other page, then the other page must be also hidden + while ($parent = $page->getParent()) { + $subpages = $parent->getSubpages(); + + if ($page !== reset($subpages)) { + break; + } + + $parent->setHidden(true); + $page = $parent; + } + } + + private static function handleUnlockedPage(rex_be_page $page, array $subpages = null) + { + if (!rex_be_controller::getCurrentPage() || !$page->isActive()) { + return; + } + + if (is_array($subpages)) { + $subpage = substr(rex_be_controller::getCurrentPage(), strlen($page->getFullKey()) + 1); + $subpage = explode('/', $subpage, 2)[0]; + + if (!in_array($subpage, $subpages, true)) { + return; + } + } + + rex_extension::register('PAGE_TITLE_SHOWN', function (rex_extension_point $ep) { + $url = rex_url::backendPage('system/ydeploy', rex_api_ydeploy_protected_page::getUrlParams() + [ + 'action' => 'lock', + 'protected_page' => rex_be_controller::getCurrentPage(), + ]); + $error = rex_view::error(' + The page '.rex_escape(rex_be_controller::getCurrentPage()).' is protected in deployed instances, but currently unlocked. Changes via this page should be made in development instances only!

+ + Lock and leave this page + '); + + return $ep->getSubject().$error; + }); + } } diff --git a/package.yml b/package.yml index c907829..481f2d1 100644 --- a/package.yml +++ b/package.yml @@ -11,6 +11,15 @@ conflicts: packages: developer: '<3.7' +console_commands: + ydeploy:diff: rex_ydeploy_command_diff + ydeploy:migrate: rex_ydeploy_command_migrate + +pages: + system/ydeploy: + title: YDeploy + pjax: false + config: fixtures: tables: @@ -29,11 +38,17 @@ config: redactor2_profiles: ~ yform_field: ~ yform_table: ~ - -console_commands: - ydeploy:diff: rex_ydeploy_command_diff - ydeploy:migrate: rex_ydeploy_command_migrate - -pages: - system/ydeploy: - title: YDeploy + protected_pages: + install: ~ + markitup: ~ + media_manager: ~ + metainfo: ~ + modules: ~ + packages: ~ + redactor2: ~ + templates: ~ + yform/manager: + - table_edit + - table_migrate + - tableset_import + - table_field diff --git a/pages/system.ydeploy.php b/pages/system.ydeploy.php index e864df3..b5b2eba 100644 --- a/pages/system.ydeploy.php +++ b/pages/system.ydeploy.php @@ -29,3 +29,134 @@ $fragment->setVar('title', 'Info'); $fragment->setVar('body', $content, false); echo $fragment->parse('core/page/section.php'); + +if (!$ydeploy->isDeployed()) { + return; +} + +$apiUrl = function (string $action, string $page, string $redirect = null) { + $params = rex_api_ydeploy_protected_page::getUrlParams(); + $params['action'] = $action; + $params['protected_page'] = $page; + + if ($redirect) { + $params['redirect'] = $redirect; + } + + return rex_url::currentBackendPage($params); +}; + +$calledPage = rex_request('page', 'string'); + +if ('system/ydeploy' !== $calledPage) { + $redirect = rex_context::fromGet()->getUrl([], false); + $url = $apiUrl('unlock', $calledPage, $redirect); + + echo rex_view::error(' + The called page '.rex_escape($calledPage).' is protected in deployed instances because it should be used only in development instances.

+ Unlock and open it anyway + '); +} + +$content = ''; + +$pages = []; + +foreach (rex_ydeploy_handler::getProtectedPages() as $page => $subpages) { + $page = rex_be_controller::getPageObject($page); + + if (!$page) { + continue; + } + + $icon = $page->getIcon(); + $root = $page; + while ($parent = $root->getParent()) { + $root = $parent; + $icon = $icon ?: $parent->getIcon(); + } + + // create non-hidden fake page + if ($root instanceof rex_be_page_main) { + $fakePage = new rex_be_page_main($root->getBlock(), $page->getFullKey(), $page->getTitle()); + $fakePage->setPrio($root->getPrio()); + } else { + $fakePage = new rex_be_page($page->getFullKey(), $page->getTitle()); + } + + // rex_be_navigation does not provide the page keys in navigation items + // so we misuse the href for the page key + $fakePage->setHref($page->getFullKey()); + $fakePage->setIcon($icon); + + $pages[$root->getKey()][] = $fakePage; +} + +$navi = rex_be_navigation::factory(); + +// add fake pages to navigation, but use original order from rex_be_controller +foreach (rex_be_controller::getPages() as $key => $page) { + if (!isset($pages[$key])) { + continue; + } + + foreach ($pages[$key] as $fakePage) { + $navi->addPage($fakePage); + } +} + +$unlockedPages = rex_ydeploy_handler::getUnlockedPages(); + +foreach ($navi->getNavigation() as $block) { + $content .= ' + + + '.$block['headline']['title'].' + + '; + + foreach ($block['navigation'] as $page) { + if (isset($unlockedPages[$page['href']])) { + $url = $apiUrl('lock', $page['href']); + $action = ' Unlocked'; + + $action2 = 'Open'; + } else { + $url = $apiUrl('unlock', $page['href']); + $action = ' Locked'; + + $url = $apiUrl('unlock', $page['href'], rex_url::backendPage($page['href'])); + $action2 = 'Unlock & open'; + } + + $content .= ' + + + '.rex_escape($page['href']).' + '.$page['title'].' + '.$action.' + '.$action2.' + + '; + } +} + +$content = ' + + + + + + + + + + + '.$content.' + +
KeyTitle
'; + +$fragment = new rex_fragment(); +$fragment->setVar('title', 'Protected Pages'); +$fragment->setVar('content', $content, false); +echo $fragment->parse('core/page/section.php');