From 7331bac1321ad545f18c04295f5c42448345dca2 Mon Sep 17 00:00:00 2001 From: Ryan Cramer Date: Fri, 26 Oct 2018 12:18:46 -0400 Subject: [PATCH] Addition of a $page->numParents() method/property which reflects the number of parents the page has, aka depth in the tree. This commit also has several small adjustments and fixes, including a fix for the issue introduced last week that caused issues with WireArray in versions of PHP prior to 7.x --- wire/core/Functions.php | 23 +++++++++++ wire/core/Page.php | 15 +++++++ wire/core/PageTraversal.php | 37 +++++++++++++++-- wire/core/User.php | 6 +-- wire/core/WireArray.php | 41 +++++++++---------- wire/core/WireFileTools.php | 1 + .../FieldtypeComments.module | 2 + .../FieldtypeOptions/SelectableOption.php | 2 +- .../Inputfield/InputfieldCheckbox.module | 6 ++- .../ProcessPageLister.module | 18 +++++++- .../ProcessPagesExportImport.module | 8 +++- 11 files changed, 126 insertions(+), 33 deletions(-) diff --git a/wire/core/Functions.php b/wire/core/Functions.php index a849aee9..ffb18990 100644 --- a/wire/core/Functions.php +++ b/wire/core/Functions.php @@ -1031,3 +1031,26 @@ function wireRegion($key, $value = null) { return $result; } +/** + * Create new WireArray, add given $items to it, and return it + * + * @param array|WireArray $items + * @return WireArray + * + */ +function WireArray($items = array()) { + return WireArray::newInstance($items); +} + +/** + * Create new PageArray, add given $items (pages) to it, and return it + * + * @param array|PageArray $items + * @return WireArray + * + */ +function PageArray($items = array()) { + return PageArray::newInstance($items); +} + + diff --git a/wire/core/Page.php b/wire/core/Page.php index 23e52116..e4b66b31 100644 --- a/wire/core/Page.php +++ b/wire/core/Page.php @@ -611,6 +611,7 @@ class Page extends WireData implements \Countable, WireMatchable { 'namePrevious' => 'p', 'next' => 'm', 'numChildren' => 's', + 'numParents' => 'm', 'numDescendants' => 'm', 'numLinks' => 't', 'numReferences' => 't', @@ -2191,6 +2192,20 @@ class Page extends WireData implements \Countable, WireMatchable { return $this->traversal()->parents($this, $selector); } + /** + * Return number of parents (depth relative to homepage) that this page has, optionally filtered by a selector + * + * For example, homepage has 0 parents and root level pages have 1 parent (which is the homepage), and the + * number increases the deeper the page is in the pages structure. + * + * @param string $selector Optional selector to filter by (default='') + * @return int Number of parents + * + */ + public function numParents($selector = '') { + return $this->traversal()->numParents($this, $selector); + } + /** * Return all parents from current page till the one matched by $selector * diff --git a/wire/core/PageTraversal.php b/wire/core/PageTraversal.php index 7d82c811..441abe70 100644 --- a/wire/core/PageTraversal.php +++ b/wire/core/PageTraversal.php @@ -174,6 +174,28 @@ class PageTraversal { return strlen($selector) ? $parents->filter($selector) : $parents; } + /** + * Return number of parents (depth relative to homepage) that this page has, optionally filtered by a selector + * + * For example, homepage has 0 parents and root level pages have 1 parent (which is the homepage), and the + * number increases the deeper the page is in the pages structure. + * + * @param Page $page + * @param string $selector Optional selector to filter by (default='') + * @return int Number of parents + * + */ + public function numParents(Page $page, $selector = '') { + $num = 0; + $parent = $page->parent(); + while($parent && $parent->id) { + if($selector !== '' && !$parent->matches($selector)) continue; + $num++; + $parent = $parent->parent(); + } + return $num; + } + /** * Return all parent from current till the one matched by $selector * @@ -660,6 +682,7 @@ class PageTraversal { $languages = $options['languages'] ? $page->wire('languages') : null; $slashUrls = $page->template->slashUrls; $httpHostUrl = $options['http'] ? $page->wire('input')->httpHostUrl() : ''; + $urls = array(); if($options['language'] && $languages) { if(!$options['language'] instanceof Page) { @@ -682,6 +705,7 @@ class PageTraversal { // add in historical URLs if($options['past'] && $modules->isInstalled('PagePathHistory')) { + /** @var PagePathHistory $history */ $history = $modules->get('PagePathHistory'); $rootUrl = $page->wire('config')->urls->root; $pastPaths = $history->getPathHistory($page, array( @@ -691,11 +715,13 @@ class PageTraversal { foreach($pastPaths as $pathInfo) { $key = ''; if(!empty($pathInfo['language'])) { + /** @var Language $language */ + $language = $pathInfo['language']; if($options['languages']) { - $key .= $pathInfo['language']->name . ';'; + $key .= $language->name . ';'; } else { // they asked to have multi-language excluded - if(!$pathInfo['language']->isDefault()) continue; + if(!$language->isDefault()) continue; } } $key .= wireDate('c', $pathInfo['date']); @@ -774,6 +800,7 @@ class PageTraversal { */ public function referencing(Page $page, $field = false, $getCount = false) { $fieldName = ''; + $byField = null; if(is_bool($field) || is_null($field)) { $byField = $field ? true : false; } else if(is_string($field)) { @@ -793,7 +820,7 @@ class PageTraversal { foreach($page->template->fieldgroup as $f) { if($fieldName && $field->name != $fieldName) continue; if(!$f->type instanceof FieldtypePage) continue; - if($byField) $itemsByField[$f->name] = $this->wire('pages')->newPageArray(); + if($byField) $itemsByField[$f->name] = $page->wire('pages')->newPageArray(); $value = $page->get($f->name); if($value instanceof Page && $value->id) { $items->add($value); @@ -916,6 +943,7 @@ class PageTraversal { $next = $page; do { + /** @var Page $next */ $next = $siblings->getNext($next, false); if(empty($selector) || !$next || $next->matches($selector)) break; } while($next && $next->id); @@ -959,6 +987,7 @@ class PageTraversal { $prev = $page; do { + /** @var Page $prev */ $prev = $siblings->getPrev($prev, false); if(empty($selector) || !$prev || $prev->matches($selector)) break; } while($prev && $prev->id); @@ -1006,7 +1035,7 @@ class PageTraversal { * @param Page $page * @param string|array $selector Optional selector. When specified, will filter the found siblings. * @param PageArray $siblings Optional siblings to use instead of the default. - * @return Page|NullPage Returns all matching pages before this one. + * @return PageArray * */ public function prevAllSiblings(Page $page, $selector = '', PageArray $siblings = null) { diff --git a/wire/core/User.php b/wire/core/User.php index 8e06c4d9..8a07b97f 100644 --- a/wire/core/User.php +++ b/wire/core/User.php @@ -60,7 +60,7 @@ class User extends Page { * } * ~~~~~ * - * @param string|Role|int May be Role name, object or ID. + * @param string|Role|int $role May be Role name, object or ID. * @return bool * */ @@ -107,7 +107,7 @@ class User extends Page { * $user->save(); * ~~~~~ * - * @param string|int|Role Maybe Role name, object, or ID. + * @param string|int|Role $role May be Role name, object, or ID. * @return bool Returns false if role not recognized, true otherwise * */ @@ -131,7 +131,7 @@ class User extends Page { * $user->save(); * ~~~~~ * - * @param string|int|Role May be Role name, object or ID. + * @param string|int|Role $role May be Role name, object or ID. * @return bool false if role not recognized, true otherwise * */ diff --git a/wire/core/WireArray.php b/wire/core/WireArray.php index 6678dacf..7dbf3513 100644 --- a/wire/core/WireArray.php +++ b/wire/core/WireArray.php @@ -16,7 +16,7 @@ * https://processwire.com * * @method WireArray and($item) - * @method WireArray new($items = array()) + * @method static WireArray new($items = array()) * @property int $count Number of items * @property Wire|null $first First item * @property Wire|null $last Last item @@ -2446,39 +2446,36 @@ class WireArray extends Wire implements \IteratorAggregate, \ArrayAccess, \Count // multiple items specified as arguments $items = $arguments; } - $a = new $class(); - if(WireArray::iterable($items)) { - $a->import($items); - } else if($items !== null) { - $a->add($items); - } - return $a; + return self::newInstance($items, $class); } else { throw new WireException("Unrecognized static method: $class::$name()"); } } /** - * Create a new WireArray instance of this type, optionally adding $items to it + * Create new instance of this class * - * This method call be called statically or non-statically, but is primarily useful as a static method call. + * Method for internal use, use `$a = WireArray::new($items)` or `$a = WireArrray($items)` instead. * - * ~~~~~~ - * // Create new WireArray with a, b and c items - * $a = WireArray::new([ 'a', 'b', 'c' ]); + * #pw-internal * - * // This also works when called statically (array syntax can optionally be omitted) - * $a = WireArray::new('a', 'b', 'c'); - * ~~~~~~ - * - * @param array|WireArray|mixed|null $items Items (or item) to add to new WireArray + * @param array|WireArray|null $items Items to add or omit (null) for none + * @param string $class Class name to instantiate or omit for called class * @return WireArray - * @since 3.0.117 * */ - public function ___new($items = null) { - $a = self::new($items); - $this->wire($a); + public static function newInstance($items = null, $class = '') { + if(empty($class)) $class = get_called_class(); + /** @var WireArray $a */ + $a = new $class(); + if($items instanceof WireArray) { + $items->wire($a); + $a->import($items); + } else if(is_array($items)) { + $a->import($items); + } else if($items !== null) { + $a->add($items); + } return $a; } } diff --git a/wire/core/WireFileTools.php b/wire/core/WireFileTools.php index 34401dbe..b0cb93a1 100644 --- a/wire/core/WireFileTools.php +++ b/wire/core/WireFileTools.php @@ -386,6 +386,7 @@ class WireFileTools extends Wire { for($i = 0; $i < $zip->numFiles; $i++) { $name = $zip->getNameIndex($i); + if(strpos($name, '..') !== false) continue; if($zip->extractTo($dst, $name)) { $names[$i] = $name; $filename = $dst . ltrim($name, '/'); diff --git a/wire/modules/Fieldtype/FieldtypeComments/FieldtypeComments.module b/wire/modules/Fieldtype/FieldtypeComments/FieldtypeComments.module index 472efb5c..3a550c71 100644 --- a/wire/modules/Fieldtype/FieldtypeComments/FieldtypeComments.module +++ b/wire/modules/Fieldtype/FieldtypeComments/FieldtypeComments.module @@ -1814,6 +1814,8 @@ class FieldtypeComments extends FieldtypeMulti { $updatePropertyCounts = array(); $skipUpdateProperties = array('id', 'created_user', 'created_users_id'); + if(!$comments) return null; + foreach($comments as $comment) { if($comment->status == Comment::statusSpam) continue; $key = $this->getCommentExportKey($comment); diff --git a/wire/modules/Fieldtype/FieldtypeOptions/SelectableOption.php b/wire/modules/Fieldtype/FieldtypeOptions/SelectableOption.php index a700ff05..497cbf6b 100644 --- a/wire/modules/Fieldtype/FieldtypeOptions/SelectableOption.php +++ b/wire/modules/Fieldtype/FieldtypeOptions/SelectableOption.php @@ -103,7 +103,7 @@ class SelectableOption extends WireData { // implements LanguagesValueInterface * @return string * */ - protected function getProperty($property) { + public function getProperty($property) { if($this->wire('languages')) { $language = $this->wire('user')->language; if($language->isDefault()) { diff --git a/wire/modules/Inputfield/InputfieldCheckbox.module b/wire/modules/Inputfield/InputfieldCheckbox.module index e606c79b..2c1ac76b 100644 --- a/wire/modules/Inputfield/InputfieldCheckbox.module +++ b/wire/modules/Inputfield/InputfieldCheckbox.module @@ -89,9 +89,13 @@ class InputfieldCheckbox extends Inputfield { $attrs = $this->getAttributes(); $attrs['value'] = $this->checkedValue; + if($this->getSetting('entityEncodeLabel') !== false) { + $label = $this->entityEncode($label); + } + $out = ""; + "$label"; return $out; } diff --git a/wire/modules/Process/ProcessPageLister/ProcessPageLister.module b/wire/modules/Process/ProcessPageLister/ProcessPageLister.module index 9fff7f79..3d144d06 100644 --- a/wire/modules/Process/ProcessPageLister/ProcessPageLister.module +++ b/wire/modules/Process/ProcessPageLister/ProcessPageLister.module @@ -72,6 +72,7 @@ * @method string executeConfig() ListerPro * @method string executeActions() ListerPro * @method string executeSave() ListerPro + * @method string renderExtraTabs() #pw-hooker * * * @todo make system fields hookable for output like markupValue is for custom fields @@ -1823,7 +1824,7 @@ class ProcessPageLister extends Process implements ConfigurableModule { /** * Render additional tabs, setup so that descending classes can use as a template method - * + * * @return string * */ @@ -1832,9 +1833,24 @@ class ProcessPageLister extends Process implements ConfigurableModule { $resetLabel = $this->_('Reset filters and columns to default'); $out = "
"; $out .= "
"; + $out .= $this->renderExtraTabs(); return $out; } + /** + * Optionally hook this if you want to add additional tabs + * + * See renderExtras() method above for examples. + * + * #pw-hooker + * + * @return string Markup for extra tabs + * + */ + public function ___renderExtraTabs() { + return ''; + } + /** * Prepare the session values for external assets * diff --git a/wire/modules/Process/ProcessPagesExportImport/ProcessPagesExportImport.module b/wire/modules/Process/ProcessPagesExportImport/ProcessPagesExportImport.module index 0c7b3e30..4f2d4dd4 100644 --- a/wire/modules/Process/ProcessPagesExportImport/ProcessPagesExportImport.module +++ b/wire/modules/Process/ProcessPagesExportImport/ProcessPagesExportImport.module @@ -605,7 +605,13 @@ class ProcessPagesExportImport extends Process { $path = rtrim($importParentPath . substr($path, strlen($missingParentPath)), '/') . '/'; } } - if($path == $item['path']) $path = $importParentPath . trim($path, '/') . '/'; + if($path == $item['path']) { + if(strpos($path, $importParentPath) === 0) { + // parent already present in path + } else { + $path = $importParentPath . trim($path, '/') . '/'; + } + } $a['pages'][$key]['path'] = $path; } }