From 1ad76c04f97bbb1305a08ad436b2fc33dd4256b5 Mon Sep 17 00:00:00 2001 From: Flavio Copes Date: Tue, 7 Jun 2016 23:10:34 +0200 Subject: [PATCH] Re #394 update slug, route and raw route with appended number when doing a copy of the page (#537) Does not change the page title, just the page route property if set in the header, to avoid two pages with the same slug / route / raw route. Handles ordering too, and pages with custom slug. Independently updates the slug and the folder name. --- CHANGELOG.md | 2 + classes/controller.php | 142 +++++++++++++++++++++++++++++++---------- 2 files changed, 111 insertions(+), 33 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2c7f2bf8..819592ba 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,8 @@ * Fix for Gemini Scrollbar CSS breaking layout in IE 9+ [#644](https://github.com/getgrav/grav-plugin-admin/issues/644) * Fall back to english for UI language if admin's language is not set [#641](https://github.com/getgrav/grav-plugin-admin/issues/641) * List field has the wrong label/field width. Switched to "1/3 | 2/3" like all other fields. + * Correctly set the page slug on page copy. Avoids having two pages with the same slug [#394](https://github.com/getgrav/grav-plugin-admin/issues/394) + * When copying a page, if there's a page prefix (used for ordering), update the value to avoid having two pages with the same order number [#429](https://github.com/getgrav/grav-plugin-admin/issues/429) * Fixed size of dropdown text in responsive views to be readable [#647](https://github.com/getgrav/grav-plugin-admin/issues/647) # v1.1.0-rc.2 diff --git a/classes/controller.php b/classes/controller.php index 99895838..20cf91c4 100644 --- a/classes/controller.php +++ b/classes/controller.php @@ -1419,27 +1419,13 @@ class AdminController return $found; } - - /** - * Handles creating an empty page folder (without markdown file) + * Get the next available ordering number in a folder * - * @return bool True if the action was performed. + * @return string the correct order string to prepend */ - public function taskSaveNewFolder() + private function getNextOrderInFolder($path) { - if (!$this->authorizeTask('save', $this->dataPermissions())) { - return false; - } - - $data = (array) $this->data; - - if ($data['route'] == '/') { - $path = $this->grav['locator']->findResource('page://'); - } else { - $path = $page = $this->grav['page']->find($data['route'])->path(); - } - $files = Folder::all($path, ['recursive' => false]); $highestOrder = 0; @@ -1463,6 +1449,30 @@ class AdminController $orderOfNewFolder = '0' . $orderOfNewFolder; } + return $orderOfNewFolder; + } + + /** + * Handles creating an empty page folder (without markdown file) + * + * @return bool True if the action was performed. + */ + public function taskSaveNewFolder() + { + if (!$this->authorizeTask('save', $this->dataPermissions())) { + return false; + } + + $data = (array) $this->data; + + if ($data['route'] == '/') { + $path = $this->grav['locator']->findResource('page://'); + } else { + $path = $this->grav['page']->find($data['route'])->path(); + } + + $orderOfNewFolder = $this->getNextOrderInFolder($path); + Folder::mkdir($path . '/' . $orderOfNewFolder . '.' . $data['folder']); Cache::clearCache('standard'); @@ -1526,8 +1536,6 @@ class AdminController /** @var Page $obj */ $obj = $this->admin->page(true); - - // Ensure route is prefixed with a forward slash. $route = '/' . ltrim($route, '/'); @@ -1705,6 +1713,60 @@ class AdminController return true; } + /** + * Find the first available $item ('slug' | 'folder') for a page + * Used when copying a page, to determine the first available slot + * + * @param string $item + * @param Page $page + * + * @return string The first available slot + */ + protected function findFirstAvailable($item, $page) + { + if (!$page->parent()->children()) { + return $page->$item(); + } + + $withoutPrefix = function ($string) { + $match = preg_split('/^[0-9]+\./u', $string, 2, PREG_SPLIT_DELIM_CAPTURE); + return isset($match[1]) ? $match[1] : $match[0]; + }; + + $withoutPostfix = function ($string) { + $match = preg_split('/-(\d+)$/', $string, 2, PREG_SPLIT_DELIM_CAPTURE); + return $match[0]; + }; + $appendedNumber = function ($string) { + $match = preg_split('/-(\d+)$/', $string, 2, PREG_SPLIT_DELIM_CAPTURE); + $append = (isset($match[1]) ? (int)$match[1] + 1 : 2); + return $append; + }; + + $highest = 1; + $siblings = $page->parent()->children(); + $findCorrectAppendedNumber = function ($item, $page_item, $highest) use ($siblings, &$findCorrectAppendedNumber, &$withoutPrefix) { + foreach ($siblings as $sibling) { + if ($withoutPrefix($sibling->$item()) == ($highest === 1 ? $page_item : $page_item . '-' . $highest)) { + $highest = $findCorrectAppendedNumber($item, $page_item, $highest + 1); + return $highest; + } + } + return $highest; + }; + + $base = $withoutPrefix($withoutPostfix($page->$item())); + + $return = $base; + $highest = $findCorrectAppendedNumber($item, $base, $highest); + + if ($highest > 1) { + $return .= '-' . $highest; + } + + return $return; + } + /** * Save page as a new copy. * @@ -1726,35 +1788,49 @@ class AdminController /** @var Pages $pages */ $pages = $this->grav['pages']; - // And then get the current page. + // Get the current page. $page = $this->admin->page(true); - // Find new parent page in order to build the path. $parent = $page->parent() ?: $pages->root(); - // Make a copy of the current page and fill the updated information into it. $page = $page->copy($parent); + + if ($page->order()) { + $order = $this->getNextOrderInFolder($page->parent()->path()); + } + $this->preparePage($page); // Make sure the header is loaded in case content was set through raw() (expert mode) $page->header(); - // Deal with folder naming conflicts, but limit number of searches to 99. - $break = 99; - while ($break > 0 && file_exists($page->filePath())) { - $break--; - $match = preg_split('/-(\d+)$/', $page->path(), 2, PREG_SPLIT_DELIM_CAPTURE); - $page->path($match[0] . '-' . (isset($match[1]) ? (int)$match[1] + 1 : 2)); - // Reset slug and route. For now we do not support slug twig variable on save. - $page->slug(''); + if ($page->order()) { + $page->order($order); } - $page->save(); + $folder = $this->findFirstAvailable('folder', $page); + $slug = $this->findFirstAvailable('slug', $page); + $page->path($page->parent()->path() . DS . $page->order() . $folder); + $page->route($page->parent()->route() . '/' . $slug); + $page->rawRoute($page->parent()->rawRoute() . '/' . $slug); + + $page->save(false); + + $redirect = $this->view . $page->rawRoute(); + $header = $page->header(); + + if (isset($header->slug)) { + $match = preg_split('/-(\d+)$/', $header->slug, 2, PREG_SPLIT_DELIM_CAPTURE); + $header->slug = $match[0] . '-' . (isset($match[1]) ? (int)$match[1] + 1 : 2); + } + + $page->header($header); + + $page->save(); // Enqueue message and redirect to new location. $this->admin->setMessage($this->admin->translate('PLUGIN_ADMIN.SUCCESSFULLY_COPIED'), 'info'); - $parent_route = $parent->route() ? '/' . ltrim($parent->route(), '/') : ''; - $this->setRedirect($this->view . $parent_route . '/' . $page->slug()); + $this->setRedirect($redirect); } catch (\Exception $e) { throw new \RuntimeException('Copying page failed on error: ' . $e->getMessage());