'createdStr', 'modified' => 'modifiedStr', 'published' => 'publishedStr', ); /** * Construct * * @param Page $page * @param PageArray $children * */ public function __construct(Page $page, PageArray $children) { $this->page = $page; $this->children = $children; $this->start = 0; $this->limit = 0; parent::__construct(); } /** * Wired to ProcessWire instance * */ public function wired() { $this->superuser = $this->wire('user')->isSuperuser(); $this->actionLabels = array( 'edit' => $this->_('Edit'), // Edit page action 'view' => $this->_('View'), // View page action 'add' => $this->_('New'), // New page action 'move' => $this->_('Move'), // Move page action 'empty' => $this->_('Empty'), // Empty trash page action 'pub' => $this->_('Pub'), // Publish page action 'unpub' => $this->_('Unpub'), // Unpublish page action 'hide' => $this->_('Hide'), // Hide page action 'unhide' => $this->_('Unhide'), // Unhide page action 'lock' => $this->_('Lock'), // Lock page action 'unlock' => $this->_('Unlock'), // Unlock page action 'trash' => $this->_('Trash'), // Trash page action 'restore' => $this->_('Restore'), // Restore from trash action ); require_once(dirname(__FILE__) . '/ProcessPageListActions.php'); $this->actions = $this->wire(new ProcessPageListActions()); $this->actions->setActionLabels($this->actionLabels); $this->numChildrenHook = $this->wire('hooks')->isMethodHooked($this, 'getNumChildren'); parent::wired(); } /** * Set option * * @param string $key * @param mixed $value * @return $this * */ public function setOption($key, $value) { $this->options[$key] = $value; return $this; } /** * Get option * * @param string $key * @return mixed|null * */ public function getOption($key) { return isset($this->options[$key]) ? $this->options[$key] : null; } /** * Set pagination start * * @param int $n * */ public function setStart($n) { $this->start = (int) $n; } /** * Set pagination limit * * @param int $n * */ public function setLimit($n) { $this->limit = (int) $n; } /** * Set action label by name * * @param string $key * @param string $value * */ public function setLabel($key, $value) { $this->actionLabels[$key] = $value; } /** * Set whether to use trash * * @param bool $useTrash * */ public function setUseTrash($useTrash) { $this->useTrash = (bool) $useTrash; $this->actions->setUseTrash($this->getUseTrash()); } /** * Set the default page label field/format * * @param string $pageLabelField * */ public function setPageLabelField($pageLabelField) { $this->pageLabelField = $pageLabelField; } /** * Set when pages should be hidden in page list * * @param array $hidePages IDs of pages that should be hidden * @param array $hidePagesNot Do not hide pages when state matches one or more of 'debug', 'advanced', 'superuser' * */ public function setHidePages($hidePages, $hidePagesNot) { if(is_array($hidePages)) { $this->hidePages = array(); foreach($hidePages as $id) $this->hidePages[(int) $id] = (int) $id; } if(is_array($hidePagesNot)) { $this->hidePagesNot = array(); foreach($hidePagesNot as $state) $this->hidePagesNot[$state] = $state; } } /** * Set the quantity type * * @param string $qtyType * */ public function setQtyType($qtyType) { $this->qtyType = $qtyType; } /** * Get the ProcessPageListActions instance * * @return null|ProcessPageListActions * */ public function actions() { return $this->actions; } /** * Get an array of available Page actions, indexed by $label => $url * * @param Page $page * @return array of $label => $url * */ public function ___getPageActions(Page $page) { $actions = $this->actions->getActions($page); /* * @todo force 'extras' option to be last if(isset($actions['extras'])) { $keys = array_keys($actions); $lastKey = array_pop($keys); if($lastKey !== 'extras') { $extras = $actions['extras']; unset($actions['extras']); $actions['extras'] = $extras; // move to last } } */ return $actions; } /** * Return the Page's label text, whether that originates from the Page's name, headline, title, etc. * * @param Page $page * @param array $options * - `noTags` (bool): If true, HTML will be excluded [other than for icon] in returned text value (default=false) * - `noIcon` (bool): If true, icon markup will be excluded from returned value (default=false) * @return string * */ public function ___getPageLabel(Page $page, array $options = array()) { $sanitizer = $this->wire()->sanitizer; $formatLabel = true; $label = $page->getPageListLabel(); if(!empty($label)) { // label from custom page class overrides others $formatLabel = false; } else if(strpos($this->pageLabelField, '!') === 0) { // exclamation forces this one to be used, rather than template-specific one $label = trim(ltrim($this->pageLabelField, '!')); } else { // if the page's template specifies a pageLabelField, use that, if pageLabelField doesn't start with "!" as override $label = trim($page->template->pageLabelField); // otherwise use the one specified with this instance if(!strlen($label)) $label = $this->pageLabelField; } if(!strlen($label)) $label = 'name'; $icon = $this->getPageLabelIconMarkup($page, $label); // must be called if(!empty($options['noIcon'])) $icon = ''; while(strpos($label, ' ') !== false) $label = str_replace(' ', ' ', $label); if($formatLabel) { $bracket1 = strpos($label, '{'); if($bracket1 !== false && $bracket1 < strpos($label, '}')) { // predefined format string // adjust string so that it'll work on a single line, without the markup in it $value = $page->getText($label, true, true); // oneLine=true, entities=true } else { // space delimited list of fields $value = $this->getPageLabelDelimited($page, $label, $options); } if(!strlen($value)) { $value = $sanitizer->entities($page->getUnformatted("title|name")); } } else { $value = $label; } if(!empty($options['noTags']) && strpos($value, '<') !== false) { // legacy code, appears to be impossible to reach $value = strip_tags($value); } return $icon . trim($value); } /** * Get page label icon and modify $label to remove existing icon references * * @param Page $page * @param string $label * @return string * @since 3.0.163 * */ protected function getPageLabelIconMarkup(Page $page, &$label) { $icon = $page->getIcon(); // remove any existing icon references in label if(strpos($label, 'fa-') !== false || strpos($label, 'icon-') !== false) { if(preg_match_all('/\b(?:fa|icon)-([-a-z0-9]+)(?:\s*|\b)/', $label, $matches)) { foreach($matches[0] as $key => $iconFull) { // allow first icon reference to be used if there isn't already one if(!$icon) $icon = $matches[1][$key]; $label = str_replace($iconFull, '', $label); } } } if($icon) { if(!ctype_alnum($icon) && !ctype_alnum(str_replace('-', '', $icon))) { $icon = $this->wire()->sanitizer->name($icon); } $icon = ""; } return $icon; } /** * Get page label when label format is space delimited * * @param Page $page * @param string $label * @param array $options * @return string * @since 3.0.163 * */ protected function getPageLabelDelimited(Page $page, $label, array $options) { $value = ''; // convert to array if(strpos($label, ' ')) { $fields = explode(' ', $label); } else { $fields = array($label); } foreach($fields as $field) { $field = trim($field); if(!strlen($field)) { continue; } else if(strpos($field, ".")) { list($field, $subfield) = explode(".", $field); if(isset($this->translateFields[$subfield])) $subfield = $this->translateFields[$subfield]; } else if(strpos($field, 'icon-') === 0 || strpos($field, 'fa-') === 0) { // skip over icons, which we now pull directly from page continue; } else { $subfield = ''; } if(isset($this->translateFields[$field])) $field = $this->translateFields[$field]; $v = $page->get($field); if($subfield && is_object($v)) { if($v instanceof WireArray && count($v)) $v = $v->first(); if($v instanceof Page) { $v = $v->getFormatted($subfield); // @esrch PR #965 } else if($v instanceof Template && $subfield == 'label') { $v = $v->getLabel(); } else if($v instanceof Wire) { $v = $v->$subfield; } else { // unknown $v = (string) $v; } } if(!strlen("$v")) continue; if(empty($options['noTags'])) { $value .= ""; } else if(strlen($value)) { $value .= ", "; } $value .= htmlspecialchars(strip_tags("$v"), ENT_QUOTES, "UTF-8", false); if(empty($options['noTags'])) $value .= ""; } return $value; } /** * Render child item in page list * * @param Page $page * @return array * */ abstract public function renderChild(Page $page); /** * Render page list * * @return string|array * */ abstract public function render(); /** * Get the name of this renderer (i.e. 'JSON') * * @return string * */ public function getRenderName() { return str_replace('ProcessPageListRender', '', $this->className()); } /** * Get URL to view more * * @return string * */ public function getMoreURL() { if($this->limit && ($this->numChildren($this->page, 1) > ($this->start + $this->limit))) { $start = $this->start + $this->limit; return $this->config->urls->admin . "page/list/?&id={$this->page->id}&start=$start&render=" . $this->getRenderName(); } return ''; } /** * Get children pages * * @return PageArray * */ public function getChildren() { return $this->children; } /** * Get whether or not to use trash * * @return bool * */ public function getUseTrash() { return $this->useTrash; } /** * Hook this method if you want to manipulate the numChildren count for pages * * ~~~~~ * $wire->addHookAfter('ProcessPageListRender::getNumChildren', function($event) { * $page = $event->arguments(0); * $selector = $event->arguments(1); * $event->return = $page->numChildren($selector); // your implementation here * }); * ~~~~~ * * See Page::numChildren() for details on arguments * * #pw-hooker * * @param Page $page * @param string|int|bool|null $selector * @return int * */ protected function ___getNumChildren(Page $page, $selector = null) { return $page->numChildren($selector); } /** * Return number of children for page * @param Page $page * @param string|int|bool|null $selector * @return int * */ public function numChildren(Page $page, $selector = null) { return $this->numChildrenHook ? $this->getNumChildren($page, $selector) : $page->numChildren($selector); } }