1
0
mirror of https://github.com/processwire/processwire.git synced 2025-08-11 09:14:58 +02:00

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

This commit is contained in:
Ryan Cramer
2018-10-26 12:18:46 -04:00
parent b964fd1a15
commit 7331bac132
11 changed files with 126 additions and 33 deletions

View File

@@ -1031,3 +1031,26 @@ function wireRegion($key, $value = null) {
return $result; 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);
}

View File

@@ -611,6 +611,7 @@ class Page extends WireData implements \Countable, WireMatchable {
'namePrevious' => 'p', 'namePrevious' => 'p',
'next' => 'm', 'next' => 'm',
'numChildren' => 's', 'numChildren' => 's',
'numParents' => 'm',
'numDescendants' => 'm', 'numDescendants' => 'm',
'numLinks' => 't', 'numLinks' => 't',
'numReferences' => 't', 'numReferences' => 't',
@@ -2191,6 +2192,20 @@ class Page extends WireData implements \Countable, WireMatchable {
return $this->traversal()->parents($this, $selector); 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 * Return all parents from current page till the one matched by $selector
* *

View File

@@ -174,6 +174,28 @@ class PageTraversal {
return strlen($selector) ? $parents->filter($selector) : $parents; 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 * Return all parent from current till the one matched by $selector
* *
@@ -660,6 +682,7 @@ class PageTraversal {
$languages = $options['languages'] ? $page->wire('languages') : null; $languages = $options['languages'] ? $page->wire('languages') : null;
$slashUrls = $page->template->slashUrls; $slashUrls = $page->template->slashUrls;
$httpHostUrl = $options['http'] ? $page->wire('input')->httpHostUrl() : ''; $httpHostUrl = $options['http'] ? $page->wire('input')->httpHostUrl() : '';
$urls = array();
if($options['language'] && $languages) { if($options['language'] && $languages) {
if(!$options['language'] instanceof Page) { if(!$options['language'] instanceof Page) {
@@ -682,6 +705,7 @@ class PageTraversal {
// add in historical URLs // add in historical URLs
if($options['past'] && $modules->isInstalled('PagePathHistory')) { if($options['past'] && $modules->isInstalled('PagePathHistory')) {
/** @var PagePathHistory $history */
$history = $modules->get('PagePathHistory'); $history = $modules->get('PagePathHistory');
$rootUrl = $page->wire('config')->urls->root; $rootUrl = $page->wire('config')->urls->root;
$pastPaths = $history->getPathHistory($page, array( $pastPaths = $history->getPathHistory($page, array(
@@ -691,11 +715,13 @@ class PageTraversal {
foreach($pastPaths as $pathInfo) { foreach($pastPaths as $pathInfo) {
$key = ''; $key = '';
if(!empty($pathInfo['language'])) { if(!empty($pathInfo['language'])) {
/** @var Language $language */
$language = $pathInfo['language'];
if($options['languages']) { if($options['languages']) {
$key .= $pathInfo['language']->name . ';'; $key .= $language->name . ';';
} else { } else {
// they asked to have multi-language excluded // they asked to have multi-language excluded
if(!$pathInfo['language']->isDefault()) continue; if(!$language->isDefault()) continue;
} }
} }
$key .= wireDate('c', $pathInfo['date']); $key .= wireDate('c', $pathInfo['date']);
@@ -774,6 +800,7 @@ class PageTraversal {
*/ */
public function referencing(Page $page, $field = false, $getCount = false) { public function referencing(Page $page, $field = false, $getCount = false) {
$fieldName = ''; $fieldName = '';
$byField = null;
if(is_bool($field) || is_null($field)) { if(is_bool($field) || is_null($field)) {
$byField = $field ? true : false; $byField = $field ? true : false;
} else if(is_string($field)) { } else if(is_string($field)) {
@@ -793,7 +820,7 @@ class PageTraversal {
foreach($page->template->fieldgroup as $f) { foreach($page->template->fieldgroup as $f) {
if($fieldName && $field->name != $fieldName) continue; if($fieldName && $field->name != $fieldName) continue;
if(!$f->type instanceof FieldtypePage) 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); $value = $page->get($f->name);
if($value instanceof Page && $value->id) { if($value instanceof Page && $value->id) {
$items->add($value); $items->add($value);
@@ -916,6 +943,7 @@ class PageTraversal {
$next = $page; $next = $page;
do { do {
/** @var Page $next */
$next = $siblings->getNext($next, false); $next = $siblings->getNext($next, false);
if(empty($selector) || !$next || $next->matches($selector)) break; if(empty($selector) || !$next || $next->matches($selector)) break;
} while($next && $next->id); } while($next && $next->id);
@@ -959,6 +987,7 @@ class PageTraversal {
$prev = $page; $prev = $page;
do { do {
/** @var Page $prev */
$prev = $siblings->getPrev($prev, false); $prev = $siblings->getPrev($prev, false);
if(empty($selector) || !$prev || $prev->matches($selector)) break; if(empty($selector) || !$prev || $prev->matches($selector)) break;
} while($prev && $prev->id); } while($prev && $prev->id);
@@ -1006,7 +1035,7 @@ class PageTraversal {
* @param Page $page * @param Page $page
* @param string|array $selector Optional selector. When specified, will filter the found siblings. * @param string|array $selector Optional selector. When specified, will filter the found siblings.
* @param PageArray $siblings Optional siblings to use instead of the default. * @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) { public function prevAllSiblings(Page $page, $selector = '', PageArray $siblings = null) {

View File

@@ -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 * @return bool
* *
*/ */
@@ -107,7 +107,7 @@ class User extends Page {
* $user->save(); * $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 * @return bool Returns false if role not recognized, true otherwise
* *
*/ */
@@ -131,7 +131,7 @@ class User extends Page {
* $user->save(); * $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 * @return bool false if role not recognized, true otherwise
* *
*/ */

View File

@@ -16,7 +16,7 @@
* https://processwire.com * https://processwire.com
* *
* @method WireArray and($item) * @method WireArray and($item)
* @method WireArray new($items = array()) * @method static WireArray new($items = array())
* @property int $count Number of items * @property int $count Number of items
* @property Wire|null $first First item * @property Wire|null $first First item
* @property Wire|null $last Last item * @property Wire|null $last Last item
@@ -2446,39 +2446,36 @@ class WireArray extends Wire implements \IteratorAggregate, \ArrayAccess, \Count
// multiple items specified as arguments // multiple items specified as arguments
$items = $arguments; $items = $arguments;
} }
$a = new $class(); return self::newInstance($items, $class);
if(WireArray::iterable($items)) {
$a->import($items);
} else if($items !== null) {
$a->add($items);
}
return $a;
} else { } else {
throw new WireException("Unrecognized static method: $class::$name()"); 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.
* *
* ~~~~~~ * #pw-internal
* // Create new WireArray with a, b and c items
* $a = WireArray::new([ 'a', 'b', 'c' ]);
* *
* // This also works when called statically (array syntax can optionally be omitted) * @param array|WireArray|null $items Items to add or omit (null) for none
* $a = WireArray::new('a', 'b', 'c'); * @param string $class Class name to instantiate or omit for called class
* ~~~~~~
*
* @param array|WireArray|mixed|null $items Items (or item) to add to new WireArray
* @return WireArray * @return WireArray
* @since 3.0.117
* *
*/ */
public function ___new($items = null) { public static function newInstance($items = null, $class = '') {
$a = self::new($items); if(empty($class)) $class = get_called_class();
$this->wire($a); /** @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; return $a;
} }
} }

View File

@@ -386,6 +386,7 @@ class WireFileTools extends Wire {
for($i = 0; $i < $zip->numFiles; $i++) { for($i = 0; $i < $zip->numFiles; $i++) {
$name = $zip->getNameIndex($i); $name = $zip->getNameIndex($i);
if(strpos($name, '..') !== false) continue;
if($zip->extractTo($dst, $name)) { if($zip->extractTo($dst, $name)) {
$names[$i] = $name; $names[$i] = $name;
$filename = $dst . ltrim($name, '/'); $filename = $dst . ltrim($name, '/');

View File

@@ -1814,6 +1814,8 @@ class FieldtypeComments extends FieldtypeMulti {
$updatePropertyCounts = array(); $updatePropertyCounts = array();
$skipUpdateProperties = array('id', 'created_user', 'created_users_id'); $skipUpdateProperties = array('id', 'created_user', 'created_users_id');
if(!$comments) return null;
foreach($comments as $comment) { foreach($comments as $comment) {
if($comment->status == Comment::statusSpam) continue; if($comment->status == Comment::statusSpam) continue;
$key = $this->getCommentExportKey($comment); $key = $this->getCommentExportKey($comment);

View File

@@ -103,7 +103,7 @@ class SelectableOption extends WireData { // implements LanguagesValueInterface
* @return string * @return string
* *
*/ */
protected function getProperty($property) { public function getProperty($property) {
if($this->wire('languages')) { if($this->wire('languages')) {
$language = $this->wire('user')->language; $language = $this->wire('user')->language;
if($language->isDefault()) { if($language->isDefault()) {

View File

@@ -89,9 +89,13 @@ class InputfieldCheckbox extends Inputfield {
$attrs = $this->getAttributes(); $attrs = $this->getAttributes();
$attrs['value'] = $this->checkedValue; $attrs['value'] = $this->checkedValue;
if($this->getSetting('entityEncodeLabel') !== false) {
$label = $this->entityEncode($label);
}
$out = $out =
"<label><input type='checkbox' " . $this->getAttributesString($attrs) . " />" . "<label><input type='checkbox' " . $this->getAttributesString($attrs) . " />" .
"<span class='pw-no-select'>" . $this->entityEncode($label) . "</span></label>"; "<span class='pw-no-select'>$label</span></label>";
return $out; return $out;
} }

View File

@@ -72,6 +72,7 @@
* @method string executeConfig() ListerPro * @method string executeConfig() ListerPro
* @method string executeActions() ListerPro * @method string executeActions() ListerPro
* @method string executeSave() ListerPro * @method string executeSave() ListerPro
* @method string renderExtraTabs() #pw-hooker
* *
* *
* @todo make system fields hookable for output like markupValue is for custom fields * @todo make system fields hookable for output like markupValue is for custom fields
@@ -1832,9 +1833,24 @@ class ProcessPageLister extends Process implements ConfigurableModule {
$resetLabel = $this->_('Reset filters and columns to default'); $resetLabel = $this->_('Reset filters and columns to default');
$out = "<div id='ProcessListerRefreshTab' title='$refreshLabel' class='WireTab WireTabTip'></div>"; $out = "<div id='ProcessListerRefreshTab' title='$refreshLabel' class='WireTab WireTabTip'></div>";
$out .= "<div id='ProcessListerResetTab' title='$resetLabel' class='WireTab WireTabTip'></div>"; $out .= "<div id='ProcessListerResetTab' title='$resetLabel' class='WireTab WireTabTip'></div>";
$out .= $this->renderExtraTabs();
return $out; 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 * Prepare the session values for external assets
* *

View File

@@ -605,7 +605,13 @@ class ProcessPagesExportImport extends Process {
$path = rtrim($importParentPath . substr($path, strlen($missingParentPath)), '/') . '/'; $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; $a['pages'][$key]['path'] = $path;
} }
} }