1
0
mirror of https://github.com/processwire/processwire.git synced 2025-08-10 16:54:44 +02:00

Some cleanup in ProcessPageList.module, plus attempt compromise fix identified for ProcessPageEdit breadcrumb starting from issue #22.

This commit is contained in:
Ryan Cramer
2017-03-15 14:12:27 -04:00
parent 770c717baa
commit e1928c9e3c
2 changed files with 202 additions and 87 deletions

View File

@@ -2230,11 +2230,12 @@ class ProcessPageEdit extends Process implements WirePageEditor, ConfigurableMod
if($this->page && $this->page->id != 1) $this->wire('breadcrumbs')->shift(); // shift off the 'Pages' breadcrumb if($this->page && $this->page->id != 1) $this->wire('breadcrumbs')->shift(); // shift off the 'Pages' breadcrumb
$page = $this->page ? $this->page : $this->parent; $page = $this->page ? $this->page : $this->parent;
if($this->masterPage) $page = $this->masterPage; if($this->masterPage) $page = $this->masterPage;
$lastID = (int) $this->wire('session')->get('ProcessPageList', 'lastID');
$numParents = $page->parents->count(); $numParents = $page->parents->count();
foreach($page->parents() as $cnt => $p) { foreach($page->parents() as $cnt => $p) {
$url = "../?open=$p->id"; $url = "../?open=$p->id";
if($cnt == $numParents-1) $url = "../"; if($cnt == $numParents-1 && $p->id == $lastID) $url = "../";
$this->breadcrumb($url, $p->get("title|name")); $this->breadcrumb($url, $p->get("title|name"));
} }

View File

@@ -8,7 +8,7 @@
* For more details about how Process modules work, please see: * For more details about how Process modules work, please see:
* /wire/core/Process.php * /wire/core/Process.php
* *
* ProcessWire 3.x, Copyright 2016 by Ryan Cramer * ProcessWire 3.x, Copyright 2017 by Ryan Cramer
* https://processwire.com * https://processwire.com
* *
* @property bool $showRootPage Whether root page (like home) should be shown. * @property bool $showRootPage Whether root page (like home) should be shown.
@@ -20,17 +20,75 @@
* @property int $hoverActionFade Milliseconds to spend fading in or out actions. * @property int $hoverActionFade Milliseconds to spend fading in or out actions.
* @property bool|int $useBookmarks Allow use of PageList bookmarks? * @property bool|int $useBookmarks Allow use of PageList bookmarks?
* *
* @method string ajaxAction($action)
* @method PageArray find($selectorString, Page $page)
*
*/ */
class ProcessPageList extends Process implements ConfigurableModule { class ProcessPageList extends Process implements ConfigurableModule {
/**
* Module information
*
* @return array
*
*/
public static function getModuleInfo() {
return array(
'title' => 'Page List',
'summary' => 'List pages in a hierarchal tree structure',
'version' => 119,
'permanent' => true,
'permission' => 'page-edit',
'icon' => 'sitemap',
'useNavJSON' => true,
);
}
/**
* @var Page|null
*
*/
protected $page; protected $page;
/**
* @var int
*
*/
protected $id; protected $id;
/**
* @var Page|null
*
*/
protected $openPage; protected $openPage;
/**
* @var int
*
*/
protected $start; protected $start;
/**
* @var string
*
*/
protected $trashLabel; protected $trashLabel;
/**
* @var string|null i.e. "JSON"
*
*/
protected $render; protected $render;
protected $allowRenderTypes = array('JSON' => 'ProcessPageListRenderJSON');
/**
* @var array
*
*/
protected $allowRenderTypes = array(
'JSON' => 'ProcessPageListRenderJSON'
);
/** /**
* Default max pages to show before pagination (configurable in the module editor) * Default max pages to show before pagination (configurable in the module editor)
@@ -44,23 +102,15 @@ class ProcessPageList extends Process implements ConfigurableModule {
*/ */
const defaultSpeed = 200; const defaultSpeed = 200;
public static function getModuleInfo() { /**
return array( * Construct and establish default config values
'title' => 'Page List', *
'summary' => 'List pages in a hierarchal tree structure', */
'version' => 118,
'permanent' => true,
'permission' => 'page-edit',
'icon' => 'sitemap',
'useNavJSON' => true,
);
}
public function __construct() { public function __construct() {
$this->showRootPage = true; $this->set('showRootPage', true);
$this->pageLabelField = 'title'; $this->set('pageLabelField', 'title');
$this->limit = self::defaultLimit; $this->set('limit', self::defaultLimit);
$this->set('useHoverActions', false); $this->set('useHoverActions', false);
$this->set('useBookmarks', false); $this->set('useBookmarks', false);
$this->set('bookmarks', array()); $this->set('bookmarks', array());
@@ -73,15 +123,20 @@ class ProcessPageList extends Process implements ConfigurableModule {
* *
*/ */
public function init() { public function init() {
parent::init(); parent::init();
$isAjax = $this->wire('config')->ajax; $config = $this->wire('config');
$this->start = isset($_GET['start']) ? (int) $_GET['start'] : 0; $input = $this->wire('input');
$this->limit = (isset($_GET['limit']) && $_GET['limit'] < $this->limit) ? (int) $_GET['limit'] : $this->limit; $isAjax = $config->ajax;
$this->render = isset($_GET['render']) ? strtoupper($this->sanitizer->name($_GET['render'])) : ''; $limit = (int) $input->get->int('limit');
if($isAjax && !$this->render && !$this->wire('input')->get('renderInputfieldAjax')) $this->render = 'JSON'; $render = $input->get('render');
$this->start = (int) $input->get->int('start'); //isset($_GET['start']) ? (int) $_GET['start'] : 0;
$this->limit = $limit && $limit < $this->limit ? $limit : $this->limit;
$this->render = $render ? strtoupper($this->wire('sanitizer')->name($render)) : '';
if($isAjax && !$this->render && !$input->get('renderInputfieldAjax')) $this->render = 'JSON';
if($this->render && !isset($this->allowRenderTypes[$this->render])) $this->render = null; if($this->render && !isset($this->allowRenderTypes[$this->render])) $this->render = null;
$settings = $this->wire('config')->pageList; $settings = $config->pageList;
if(is_array($settings)) { if(is_array($settings)) {
if(!empty($settings['useHoverActions'])) $this->set('useHoverActions', true); if(!empty($settings['useHoverActions'])) $this->set('useHoverActions', true);
$this->set('hoverActionDelay', isset($settings['hoverActionDelay']) ? (int) $settings['hoverActionDelay'] : 100); $this->set('hoverActionDelay', isset($settings['hoverActionDelay']) ? (int) $settings['hoverActionDelay'] : 100);
@@ -91,61 +146,59 @@ class ProcessPageList extends Process implements ConfigurableModule {
} }
if(!$isAjax) { if(!$isAjax) {
$this->wire('modules')->get('JqueryCore')->use('cookie'); $modules = $this->wire('modules');
$this->wire('modules')->get('JqueryCore')->use('longclick'); $jQuery = $modules->get('JqueryCore');
$this->wire('modules')->get('JqueryUI')->use('modal'); $jQuery->use('cookie');
$jQuery->use('longclick');
$modules->get('JqueryUI')->use('modal');
} }
} }
/** /**
* Execute the Page List * Execute the Page List
* *
* @return string
* @throws WireException|Wire404Exception|WirePermissionException
*
*/ */
public function ___execute() { public function ___execute() {
$langID = (int) $this->wire('input')->get->lang; $pages = $this->wire('pages');
$input = $this->wire('input');
$ajax = $this->wire('config')->ajax;
$langID = (int) $input->get('lang');
if($langID) $this->wire('user')->language = $this->languages->get($langID); if($langID) $this->wire('user')->language = $this->languages->get($langID);
$this->trashLabel = $this->_('Trash'); // Label for 'Trash' page in PageList // Overrides page title if used $this->trashLabel = $this->_('Trash'); // Label for 'Trash' page in PageList // Overrides page title if used
if(isset($_GET['id']) && $_GET['id'] == 'bookmark') $this->wire('session')->redirect('./bookmarks/'); $id = $input->get('id');
if(!$this->id) $this->id = isset($_GET['id']) ? (int) $_GET['id'] : 0; if($id === 'bookmark') $this->wire('session')->redirect('./bookmarks/');
$this->openPage = $this->input->get->open ? $this->pages->get((int) $this->input->get->open) : $this->wire('pages')->newNullPage(); $id = (int) $id;
if(!$this->id && $id > 0) $this->id = $id;
$openID = (int) $input->get('open');
$this->openPage = $openID ? $pages->get($openID) : $pages->newNullPage();
if($this->openPage->id && $this->speed > 50) $this->speed = floor($this->speed / 2); if($this->openPage->id && $this->speed > 50) $this->speed = floor($this->speed / 2);
$this->page = $this->pages->get("id=" . ($this->id ? $this->id : 1) . ", status<" . Page::statusMax); $this->page = $pages->get("id=" . ($this->id > 0 ? $this->id : 1) . ", status<" . Page::statusMax);
if(!$this->page) throw new Wire404Exception("Unable to load page {$this->id}"); if(!$this->page) throw new Wire404Exception("Unable to load page {$this->id}");
if(!$this->page->listable()) throw new WirePermissionException("You don't have access to list page {$this->page->url}"); if(!$this->page->listable()) throw new WirePermissionException("You don't have access to list page {$this->page->url}");
$this->page->setOutputFormatting(false); $this->page->setOutputFormatting(false);
if($this->wire('config')->ajax && !empty($_POST['action'])) { $action = $input->post('action');
$action = $this->wire('input')->post('action'); if($ajax && $action) {
if($action) return $this->ajaxAction($this->wire('sanitizer')->name($action)); return $this->ajaxAction($this->wire('sanitizer')->name($action));
} }
$p = $this->wire('page'); $p = $this->wire('page');
if($p->name == 'list' && $p->process == $this) { if($p->name == 'list' && $p->process == $this) {
// ensure that we use the page's title is always consistent in the admin (i.e. 'Pages' not 'Page List') // ensure that we use the page's title is always consistent in the admin (i.e. 'Pages' not 'Page List')
$p->title = $p->parent->title; $p->title = $p->parent->title;
} }
/* if($ajax && $this->id > 1 && $p->process == $this && $this->wire('input')->get('mode') != 'select') {
if($p->process == $this && $this->wire('input')->get('mode') != 'select') { // remember last requested id
// store information about the last page list so that it can later be linked to again by ProcessPageEdit $this->wire('session')->setFor($this, 'lastID', $this->id);
$n = 1;
if($this->start >= $this->limit) $n = ($this->start / $this->limit)+1;
$lastPageList = $this->session->get('lastPageList');
if(!is_array($lastPageList)) $lastPageList = array();
else $lastPageList = array_slice($lastPageList, 0, 10, true);
$lastPageList[$this->page->id] = array(
'title' => (string) $this->page->get('title|name'),
'url' => (string) $this->wire('page')->url,
'n' => $n
);
$this->session->set('lastPageList', $lastPageList);
} }
*/
return $this->render(); return $this->render();
} }
@@ -153,13 +206,19 @@ class ProcessPageList extends Process implements ConfigurableModule {
/** /**
* Render the Page List * Render the Page List
* *
* @return string
*
*/ */
protected function render() { protected function render() {
$this->setupBreadcrumbs(); $this->setupBreadcrumbs();
if($this->render) return $this->getPageListRender($this->page)->render(); if($this->render) return $this->getPageListRender($this->page)->render();
$isAjax = $this->wire('config')->ajax; $input = $this->wire('input');
$config = $this->wire('config');
$session = $this->wire('session');
$urls = $config->urls;
$isAjax = $config->ajax;
$openPageIDs = array(); $openPageIDs = array();
$openPageData = array(); $openPageData = array();
$script = ''; $script = '';
@@ -173,12 +232,12 @@ class ProcessPageList extends Process implements ConfigurableModule {
if($this->id) { if($this->id) {
// leave openPageIDs as empty array // leave openPageIDs as empty array
} else { } else {
$openPageIDs = $this->wire('input')->cookie->array('pagelist_open'); $openPageIDs = $input->cookie->array('pagelist_open');
} }
} }
if($isAjax) { if($isAjax) {
if($this->wire('input')->get('renderInputfieldAjax')) { if($input->get('renderInputfieldAjax')) {
$script = "<script>ProcessPageListInit();</script>"; $script = "<script>ProcessPageListInit();</script>";
} }
} else if(count($openPageIDs)) { } else if(count($openPageIDs)) {
@@ -203,12 +262,12 @@ class ProcessPageList extends Process implements ConfigurableModule {
$defaults = array( $defaults = array(
'containerID' => 'PageListContainer', 'containerID' => 'PageListContainer',
'ajaxURL' => $this->config->urls->admin . "page/list/", 'ajaxURL' => $urls->admin . "page/list/",
'ajaxMoveURL' => $this->config->urls->admin . "page/sort/", 'ajaxMoveURL' => $urls->admin . "page/sort/",
'rootPageID' => $this->id, 'rootPageID' => $this->id,
'openPageIDs' => $openPageIDs, 'openPageIDs' => $openPageIDs,
'openPageData' => $openPageData, 'openPageData' => $openPageData,
'openPagination' => (int) $this->input->get->n, // @todo: make it openPaginations and correspond to openPageIDs 'openPagination' => (int) $input->get('n'),
'paginationClass' => 'PageListPagination', 'paginationClass' => 'PageListPagination',
'showRootPage' => $this->showRootPage ? true : false, 'showRootPage' => $this->showRootPage ? true : false,
'limit' => $this->limit, 'limit' => $this->limit,
@@ -227,12 +286,12 @@ class ProcessPageList extends Process implements ConfigurableModule {
'ajaxNetworkError' => $this->_('Network error, please try again later'), // Network error during AJAX request 'ajaxNetworkError' => $this->_('Network error, please try again later'), // Network error during AJAX request
'ajaxUnknownError' => $this->_('Unknown error, please try again later'), // Unknown error during AJAX request 'ajaxUnknownError' => $this->_('Unknown error, please try again later'), // Unknown error during AJAX request
); );
$settings = $this->wire('config')->ProcessPageList; $settings = $config->ProcessPageList;
$settings = is_array($settings) ? array_merge($defaults, $settings) : $defaults; $settings = is_array($settings) ? array_merge($defaults, $settings) : $defaults;
$this->wire('config')->js('ProcessPageList', $settings); $config->js('ProcessPageList', $settings);
$tokenName = $this->session->CSRF->getTokenName(); $tokenName = $session->CSRF->getTokenName();
$tokenValue = $this->session->CSRF->getTokenValue(); $tokenValue = $session->CSRF->getTokenValue();
$class = $this->id ? "PageListContainerPage" : "PageListContainerRoot"; $class = $this->id ? "PageListContainerPage" : "PageListContainerRoot";
return "\n" . return "\n" .
@@ -278,44 +337,87 @@ class ProcessPageList extends Process implements ConfigurableModule {
return $renderer; return $renderer;
} }
/**
* Set the page label field
*
* @param $name
* @param $pageLabelField
*
*/
public function setPageLabelField($name, $pageLabelField) { public function setPageLabelField($name, $pageLabelField) {
$this->wire('session')->setFor($this, $name, $pageLabelField); $this->wire('session')->setFor($this, $name, $pageLabelField);
} }
/**
* Get the page label field
*
* @return string
*
*/
protected function getPageLabelField() { protected function getPageLabelField() {
$pageLabelField = ''; $pageLabelField = '';
if(isset($_GET['labelName'])) { $name = $this->wire('input')->get('labelName');
$name = $this->wire('sanitizer')->fieldName($_GET['labelName']);
if($name) {
$name = $this->wire('sanitizer')->fieldName($name);
if($name) $pageLabelField = $this->wire('session')->getFor($this, $name); if($name) $pageLabelField = $this->wire('session')->getFor($this, $name);
if($pageLabelField) $pageLabelField = '!' . $pageLabelField; // "!" means it may not be overridden by template if($pageLabelField) $pageLabelField = '!' . $pageLabelField; // "!" means it may not be overridden by template
} }
if(empty($pageLabelField)) $pageLabelField = $this->pageLabelField;
if(empty($pageLabelField)) {
$pageLabelField = $this->pageLabelField;
}
return $pageLabelField; return $pageLabelField;
} }
/**
* Process an AJAX action and return JSON string
*
* @param string $action
* @return string
* @throws WireException
*
*/
public function ___ajaxAction($action) { public function ___ajaxAction($action) {
if(!$this->page->editable()) throw new WireException("Page not editable");
if($this->page->id != $this->wire('input')->post('id')) throw new WireException("GET id does not match POST id");
$tokenName = $this->session->CSRF->getTokenName(); $input = $this->wire('input');
$tokenValue = $this->session->CSRF->getTokenValue(); $session = $this->wire('session');
$postTokenValue = $this->wire('input')->post($tokenName);
if(!$this->page->editable()) throw new WireException("Page not editable");
if($this->page->id != $input->post('id')) throw new WireException("GET id does not match POST id");
$tokenName = $session->CSRF->getTokenName();
$tokenValue = $session->CSRF->getTokenValue();
$postTokenValue = $input->post($tokenName);
if($postTokenValue === null || $postTokenValue !== $tokenValue) throw new WireException("CSRF token does not match"); if($postTokenValue === null || $postTokenValue !== $tokenValue) throw new WireException("CSRF token does not match");
$renderer = $this->getPageListRender($this->page, 0); $renderer = $this->getPageListRender($this->page, 0);
$result = $renderer->actions()->processAction($this->page, $action); $result = $renderer->actions()->processAction($this->page, $action);
if(!empty($result['updateItem'])) { if(!empty($result['updateItem'])) {
$result['child'] = $renderer->renderChild($this->page); $result['child'] = $renderer->renderChild($this->page);
unset($result['updateItem']); unset($result['updateItem']);
} }
if(!empty($result['appendItem'])) { if(!empty($result['appendItem'])) {
$newChild = $this->wire('pages')->get((int) $result['appendItem']); $newChild = $this->wire('pages')->get((int) $result['appendItem']);
$result['newChild'] = $renderer->renderChild($newChild); $result['newChild'] = $renderer->renderChild($newChild);
unset($result['appendItem']); unset($result['appendItem']);
} }
header("Content-type: application/json"); header("Content-type: application/json");
return json_encode($result); return json_encode($result);
} }
/**
* @param string $selectorString
* @param Page $page
* @return PageArray
*
*/
public function ___find($selectorString, Page $page) { public function ___find($selectorString, Page $page) {
return $page->children($selectorString); return $page->children($selectorString);
} }
@@ -323,6 +425,10 @@ class ProcessPageList extends Process implements ConfigurableModule {
/** /**
* Set a value to this Page List (see WireData) * Set a value to this Page List (see WireData)
* *
* @param string $key
* @param mixed $value
* @return Process|ProcessPageList
*
*/ */
public function set($key, $value) { public function set($key, $value) {
if($key == 'id') { // allow setting by other modules, overrides $_GET value of ID if($key == 'id') { // allow setting by other modules, overrides $_GET value of ID
@@ -491,12 +597,18 @@ class ProcessPageList extends Process implements ConfigurableModule {
/** /**
* Build a form allowing configuration of this Module * Build a form allowing configuration of this Module
* *
* @param array $data
* @return InputfieldWrapper
*
*/ */
public function getModuleConfigInputfields(array $data) { public function getModuleConfigInputfields(array $data) {
/** @var InputfieldWrapper $fields */
$fields = $this->wire(new InputfieldWrapper()); $fields = $this->wire(new InputfieldWrapper());
/** @var Modules $modules */
$modules = $this->wire('modules'); $modules = $this->wire('modules');
/** @var InputfieldText $field */
$field = $modules->get("InputfieldText"); $field = $modules->get("InputfieldText");
$field->attr('name', 'pageLabelField'); $field->attr('name', 'pageLabelField');
$field->attr('value', !empty($data['pageLabelField']) ? $data['pageLabelField'] : 'title'); $field->attr('value', !empty($data['pageLabelField']) ? $data['pageLabelField'] : 'title');
@@ -509,10 +621,10 @@ class ProcessPageList extends Process implements ConfigurableModule {
$bookmarks->addConfigInputfields($fields); $bookmarks->addConfigInputfields($fields);
$admin = $this->wire('pages')->get($this->wire('config')->adminRootPageID); $admin = $this->wire('pages')->get($this->wire('config')->adminRootPageID);
$page = $this->wire('pages')->get($admin->path . 'page/list/'); $page = $this->wire('pages')->get($admin->path . 'page/list/');
$bookmarks->checkprocessPage($page); $bookmarks->checkProcessPage($page);
$settings = $this->wire('config')->pageList;
/* /*
$settings = $this->wire('config')->pageList;
if(empty($settings['useHoverActions'])) { if(empty($settings['useHoverActions'])) {
$field = $modules->get('InputfieldCheckbox'); $field = $modules->get('InputfieldCheckbox');
$field->attr('name', 'useHoverActions'); $field->attr('name', 'useHoverActions');
@@ -527,6 +639,7 @@ class ProcessPageList extends Process implements ConfigurableModule {
$defaultNote1 = $this->_('Default value is %d.'); $defaultNote1 = $this->_('Default value is %d.');
$defaultNote2 = $this->_('If left at the default value, this setting can also be specified in the $config->pageList array.'); $defaultNote2 = $this->_('If left at the default value, this setting can also be specified in the $config->pageList array.');
/** @var InputfieldInteger $field */
$field = $modules->get("InputfieldInteger"); $field = $modules->get("InputfieldInteger");
$field->attr('name', 'limit'); $field->attr('name', 'limit');
$field->attr('value', !empty($data['limit']) ? (int) $data['limit'] : self::defaultLimit); $field->attr('value', !empty($data['limit']) ? (int) $data['limit'] : self::defaultLimit);
@@ -534,6 +647,7 @@ class ProcessPageList extends Process implements ConfigurableModule {
$field->notes = sprintf($defaultNote1, self::defaultLimit) . ' ' . $defaultNote2; $field->notes = sprintf($defaultNote1, self::defaultLimit) . ' ' . $defaultNote2;
$fields->append($field); $fields->append($field);
/** @var InputfieldInteger $field */
$field = $modules->get("InputfieldInteger"); $field = $modules->get("InputfieldInteger");
$field->attr('name', 'speed'); $field->attr('name', 'speed');
$field->attr('value', array_key_exists('speed', $data) ? (int) $data['speed'] : self::defaultSpeed); $field->attr('value', array_key_exists('speed', $data) ? (int) $data['speed'] : self::defaultSpeed);