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

Various updates including optimizations to WireHooks, support for a hookable renderReadyHook method in Inputfield, cache prevention measures for Pages::findMany() per @apeisa, and some work in progress on InputfieldSelector support for improved "custom (field=value)" searches.

This commit is contained in:
Ryan Cramer
2016-12-13 14:24:01 -05:00
parent e2e8c35c2c
commit 953ca72014
14 changed files with 393 additions and 100 deletions

View File

@@ -44,16 +44,22 @@ class HookEvent extends WireData {
/** /**
* Construct the HookEvent and establish default values * Construct the HookEvent and establish default values
* *
* @param array $eventData Optional event data to start with
*
*/ */
public function __construct() { public function __construct(array $eventData = array()) {
$this->set('object', null); $data = array(
$this->set('method', ''); 'object' => null,
$this->set('arguments', array()); 'method' => '',
$this->set('return', null); 'arguments' => array(),
$this->set('replace', false); 'return' => null,
$this->set('options', array()); 'replace' => false,
$this->set('id', ''); 'options' => array(),
$this->set('cancelHooks', false); 'id' => '',
'cancelHooks' => false
);
if(!empty($eventData)) $data = array_merge($data, $eventData);
$this->data = $data;
} }
/** /**
@@ -202,7 +208,7 @@ class HookEvent extends WireData {
* ~~~~~ * ~~~~~
* *
* @param string|null $hookId * @param string|null $hookId
* @return $this * @return HookEvent|WireData $this
* *
*/ */
public function removeHook($hookId) { public function removeHook($hookId) {
@@ -227,6 +233,5 @@ class HookEvent extends WireData {
return $s; return $s;
} }
} }

View File

@@ -69,7 +69,7 @@
* @property InputfieldWrapper|null $parent The parent InputfieldWrapper for this Inputfield or null if not set. #pw-internal * @property InputfieldWrapper|null $parent The parent InputfieldWrapper for this Inputfield or null if not set. #pw-internal
* @property null|bool|Fieldtype $hasFieldtype The Fieldtype using this Inputfield, or boolean false when known not to have a Fieldtype, or null when not known. #pw-group-other * @property null|bool|Fieldtype $hasFieldtype The Fieldtype using this Inputfield, or boolean false when known not to have a Fieldtype, or null when not known. #pw-group-other
* @property bool|null $useLanguages When multi-language support active, can be set to true to make it provide inputs for each language, where supported (default=false). #pw-group-behavior * @property bool|null $useLanguages When multi-language support active, can be set to true to make it provide inputs for each language, where supported (default=false). #pw-group-behavior
* @property null|bool|int $entityEncodeLabel Set to boolean false to specifically disable entity encoding of field header/label, or set to a Inputfield::textFormat constant. (default=true). #pw-group-output * @property null|bool|int $entityEncodeLabel Set to boolean false to specifically disable entity encoding of field header/label (default=true). #pw-group-output
* @property null|bool $entityEncodeText Set to boolean false to specifically disable entity encoding for other text: description, notes, etc. (default=true). #pw-group-output * @property null|bool $entityEncodeText Set to boolean false to specifically disable entity encoding for other text: description, notes, etc. (default=true). #pw-group-output
* @property int $renderValueFlags Options that can be applied to renderValue mode, see "renderValue" constants (default=0). #pw-group-output * @property int $renderValueFlags Options that can be applied to renderValue mode, see "renderValue" constants (default=0). #pw-group-output
* @property string $wrapClass Optional class name (CSS) to apply to the HTML element wrapping the Inputfield. #pw-group-other * @property string $wrapClass Optional class name (CSS) to apply to the HTML element wrapping the Inputfield. #pw-group-other
@@ -80,6 +80,7 @@
* ================ * ================
* @method string render() * @method string render()
* @method string renderValue() * @method string renderValue()
* @method void renderReadyHook(Inputfield $parent, $renderValueMode)
* @method Inputfield processInput(WireInputData $input) * @method Inputfield processInput(WireInputData $input)
* @method InputfieldWrapper getConfigInputfields() * @method InputfieldWrapper getConfigInputfields()
* @method array getConfigArray() * @method array getConfigArray()
@@ -385,7 +386,7 @@ abstract class Inputfield extends WireData implements Module {
* *
* @param string $key Name of property to set * @param string $key Name of property to set
* @param mixed $value Value of property * @param mixed $value Value of property
* @return $this * @return Inputfield|WireData
* *
*/ */
public function set($key, $value) { public function set($key, $value) {
@@ -642,7 +643,7 @@ abstract class Inputfield extends WireData implements Module {
* - String with attributes split by "+" or "|" to set them all to have the same value. * - String with attributes split by "+" or "|" to set them all to have the same value.
* - Specify boolean true to get all attributes in an associative array. * - Specify boolean true to get all attributes in an associative array.
* @param string|int|null $value Value to set (if setting), omit otherwise. * @param string|int|null $value Value to set (if setting), omit otherwise.
* @return mixed|$this If setting an attribute, it returns this instance. If getting an attribute, the attribute is returned. * @return Inputfield|array|string|int|object|float If setting an attribute, it returns this instance. If getting an attribute, the attribute is returned.
* @see Inputfield::removeAttr(), Inputfield::addClass(), Inputfield::removeClass() * @see Inputfield::removeAttr(), Inputfield::addClass(), Inputfield::removeClass()
* *
*/ */
@@ -702,7 +703,7 @@ abstract class Inputfield extends WireData implements Module {
* - Omit if getting an attribute. * - Omit if getting an attribute.
* - Value to set for $key of setting. * - Value to set for $key of setting.
* - Boolean false to remove the attribute specified for $key. * - Boolean false to remove the attribute specified for $key.
* @return string|array|$this Returns one of the following: * @return Inputfield|string|array|null Returns one of the following:
* - If getting, returns attribute value of NULL if not present. * - If getting, returns attribute value of NULL if not present.
* - If setting, returns $this. * - If setting, returns $this.
* @see Inputfield::attr(), Inputfield::addClass() * @see Inputfield::attr(), Inputfield::addClass()
@@ -1030,8 +1031,23 @@ abstract class Inputfield extends WireData implements Module {
public function renderReady(Inputfield $parent = null, $renderValueMode = false) { public function renderReady(Inputfield $parent = null, $renderValueMode = false) {
if($parent) {} if($parent) {}
if($renderValueMode) {} if($renderValueMode) {}
return $this->wire('modules')->loadModuleFileAssets($this) > 0; $result = $this->wire('modules')->loadModuleFileAssets($this) > 0;
if($this->wire('hooks')->isMethodHooked($this, 'renderReadyHook')) {
$this->renderReadyHook($parent, $renderValueMode);
} }
return $result;
}
/**
* Hookable version of renderReady(), not called unless 'renderReadyHook' is hooked
*
* Hook this method instead if you want to hook renderReady().
*
* @param Inputfield $parent
* @param bool $renderValueMode
*
*/
public function ___renderReadyHook(Inputfield $parent = null, $renderValueMode) { }
/** /**
* This hook was replaced by renderReady * This hook was replaced by renderReady
@@ -1502,7 +1518,7 @@ abstract class Inputfield extends WireData implements Module {
* @param string $what Name of property that changed * @param string $what Name of property that changed
* @param mixed $old Previous value before change * @param mixed $old Previous value before change
* @param mixed $new New value * @param mixed $new New value
* @return $this * @return Inputfield|WireData $this
* *
*/ */
public function trackChange($what, $old = null, $new = null) { public function trackChange($what, $old = null, $new = null) {

View File

@@ -124,6 +124,12 @@ class PageFinder extends Wire {
*/ */
'reverseSort' => false, 'reverseSort' => false,
/**
* Allow use of _custom="another selector" in Selectors?
*
*/
'allowCustom' => false,
); );
protected $fieldgroups; protected $fieldgroups;
@@ -293,7 +299,6 @@ class PageFinder extends Wire {
$sort = $parent->template->sortfield; $sort = $parent->template->sortfield;
if(!$sort) $sort = $parent->sortfield; if(!$sort) $sort = $parent->sortfield;
if($sort) $selectors->add(new SelectorEqual('sort', $sort)); if($sort) $selectors->add(new SelectorEqual('sort', $sort));
$hasSort = true;
} }
} }
@@ -312,23 +317,31 @@ class PageFinder extends Wire {
* @param array $options * @param array $options
* - `findOne` (bool): Specify that you only want to find 1 page and don't need info for pagination (default=false). * - `findOne` (bool): Specify that you only want to find 1 page and don't need info for pagination (default=false).
* - `findHidden` (bool): Specify that it's okay for hidden pages to be included in the results (default=false). * - `findHidden` (bool): Specify that it's okay for hidden pages to be included in the results (default=false).
* - `findUnpublished` (bool): Specify that it's okay for hidden AND unpublished pages to be included in the results (default=false). * - `findUnpublished` (bool): Specify that it's okay for hidden AND unpublished pages to be included in the
* - `findTrash` (bool): Specify that it's okay for hidden AND unpublished AND trashed pages to be included in the results (default=false). * results (default=false).
* - `findAll` (bool): Specify that no page should be excluded - results can include unpublished, trash, system, no-access pages, etc. (default=false) * - `findTrash` (bool): Specify that it's okay for hidden AND unpublished AND trashed pages to be included in the
* - `getTotal` (bool|null): Whether the total quantity of matches should be determined and accessible from getTotal() method call. * results (default=false).
* - `findAll` (bool): Specify that no page should be excluded - results can include unpublished, trash, system,
* no-access pages, etc. (default=false)
* - `getTotal` (bool|null): Whether the total quantity of matches should be determined and accessible from
* getTotal() method call.
* - null: determine automatically (default is disabled when limit=1, enabled in all other cases). * - null: determine automatically (default is disabled when limit=1, enabled in all other cases).
* - true: always calculate total. * - true: always calculate total.
* - false: never calculate total. * - false: never calculate total.
* - `getTotalType` (string): Method to use to get total, specify 'count' or 'calc' (default='calc'). * - `getTotalType` (string): Method to use to get total, specify 'count' or 'calc' (default='calc').
* - `returnQuery` (bool): When true, only the DatabaseQuery object is returned by find(), for internal use. (default=false) * - `returnQuery` (bool): When true, only the DatabaseQuery object is returned by find(), for internal use. (default=false)
* - `loadPages` (bool): This is an optimization used by the Pages::find() method, but we observe it here as we may be able to apply * - `loadPages` (bool): This is an optimization used by the Pages::find() method, but we observe it here as we
* some additional optimizations in certain cases. For instance, if loadPages=false, then we can skip retrieval of IDs and omit * may be able to apply some additional optimizations in certain cases. For instance, if loadPages=false, then
* sort fields. (default=true) * we can skip retrieval of IDs and omit sort fields. (default=true)
* - `stopBeforeID` (int): Stop loading pages once a page matching this ID is found. Page having this ID will be excluded as well (default=0). * - `stopBeforeID` (int): Stop loading pages once a page matching this ID is found. Page having this ID will be
* - `startAfterID` (int): Start loading pages once a page matching this ID is found. Page having this ID will be excluded as well (default=0). * excluded as well (default=0).
* - `startAfterID` (int): Start loading pages once a page matching this ID is found. Page having this ID will be
* excluded as well (default=0).
* - `reverseSort` (bool): Reverse whatever sort is specified. * - `reverseSort` (bool): Reverse whatever sort is specified.
* - `returnVerbose` (bool): When true, this function returns array of arrays containing page ID, parent ID, template ID and score. * - `returnVerbose` (bool): When true, this function returns array of arrays containing page ID, parent ID,
* When false, returns only an array of page IDs. True is required by most usage from Pages class. False is only for specific cases. * template ID and score. When false, returns only an array of page IDs. True is required by most usage from
* Pages class. False is only for specific cases.
* - `allowCustom` (bool): Whether or not to allow _custom='selector string' type values (default=false).
* @return array|DatabaseQuerySelect * @return array|DatabaseQuerySelect
* @throws PageFinderException * @throws PageFinderException
* *
@@ -469,6 +482,27 @@ class PageFinder extends Wire {
return $this->find($selectors, $options); return $this->find($selectors, $options);
} }
/**
* Pre-process given Selectors object
*
* @param Selectors $selectors
* @param array $options
*
*/
protected function preProcessSelectors(Selectors $selectors, $options = array()) {
if(!empty($options['allowCustom'])) {
foreach($selectors as $selector) {
$field = $selector->field;
if(!is_string($field) || $field !== '_custom') continue;
$selectors->remove($selector);
$_selectors = $this->wire(new Selectors($selector->value()));
/** @var Selectors $_selectors */
foreach($_selectors as $s) $selectors->add($s);
}
}
}
/** /**
* Pre-process the given selector to perform any necessary replacements * Pre-process the given selector to perform any necessary replacements
* *
@@ -742,6 +776,7 @@ class PageFinder extends Wire {
// $this->extraJoins = array(); // $this->extraJoins = array();
$startLimit = false; // true when the start/limit part of the query generation is done $startLimit = false; // true when the start/limit part of the query generation is done
$database = $this->wire('database'); $database = $this->wire('database');
$this->preProcessSelectors($selectors, $options);
/** @var DatabaseQuerySelect $query */ /** @var DatabaseQuerySelect $query */
$query = $this->wire(new DatabaseQuerySelect()); $query = $this->wire(new DatabaseQuerySelect());
@@ -1070,7 +1105,7 @@ class PageFinder extends Wire {
static $tableCnt = 0; static $tableCnt = 0;
$table = $database->escapeTable($field->table); $table = $database->escapeTable($field->table);
$tableAlias = $table . "__blank" . (++$tableCnt); $tableAlias = $table . "__blank" . (++$tableCnt);
$blankValue = $field->type->getBlankValue(new NullPage(), $field, $value); $blankValue = $field->type->getBlankValue(new NullPage(), $field);
$blankIsObject = is_object($blankValue); $blankIsObject = is_object($blankValue);
if($blankIsObject) $blankValue = ''; if($blankIsObject) $blankValue = '';
$blankValue = $database->escapeStr($blankValue); $blankValue = $database->escapeStr($blankValue);

View File

@@ -199,20 +199,22 @@ class Pages extends Wire {
* *
* @param string|int|array|Selectors $selector Specify selector (standard usage), but can also accept page ID or array of page IDs. * @param string|int|array|Selectors $selector Specify selector (standard usage), but can also accept page ID or array of page IDs.
* @param array|string $options One or more options that can modify certain behaviors. May be associative array or "key=value" selector string. * @param array|string $options One or more options that can modify certain behaviors. May be associative array or "key=value" selector string.
* - `findOne` (boolean): Apply optimizations for finding a single page. * - `findOne` (boolean): Apply optimizations for finding a single page (default=false).
* - `findAll` (boolean): Find all pages with no exculsions (same as include=all option). * - `findAll` (boolean): Find all pages with no exclusions, same as "include=all" option (default=false).
* - `getTotal` (boolean): Whether to set returning PageArray's "total" property (default: true except when findOne=true). * - `getTotal` (boolean): Whether to set returning PageArray's "total" property (default=true, except when findOne=true).
* - `loadPages` (boolean): Whether to populate the returned PageArray with found pages (default: true). * - `loadPages` (boolean): Whether to populate the returned PageArray with found pages (default=true).
* The only reason why you'd want to change this to false would be if you only needed the count details from * The only reason why you'd want to change this to false would be if you only needed the count details from
* the PageArray: getTotal(), getStart(), getLimit, etc. This is intended as an optimization for $pages->count(). * the PageArray: getTotal(), getStart(), getLimit, etc. This is intended as an optimization for $pages->count().
* Does not apply if $selector argument is an array. * Does not apply if $selector argument is an array.
* - `caller` (string): Optional name of calling function, for debugging purposes, i.e. pages.count * - `cache` (boolean): Allow caching of selectors and loaded pages? (default=true). Also sets loadOptions[cache].
* - `include` (string): Optional inclusion mode of 'hidden', 'unpublished' or 'all'. Default=none. Typically you would specify this * - `allowCustom` (boolean): Allow use of _custom="another selector" in given $selector? For specific uses. (default=false)
* - `caller` (string): Optional name of calling function, for debugging purposes, i.e. "pages.count" (default=blank).
* - `include` (string): Optional inclusion mode of 'hidden', 'unpublished' or 'all'. (default=none). Typically you would specify this
* directly in the selector string, so the option is mainly useful if your first argument is not a string. * directly in the selector string, so the option is mainly useful if your first argument is not a string.
* - `stopBeforeID` (int): Stop loading pages once page matching this ID is found (default=0). * - `stopBeforeID` (int): Stop loading pages once page matching this ID is found (default=0).
* - `startAfterID` (int): Start loading pages once page matching this ID is found (default=0). * - `startAfterID` (int): Start loading pages once page matching this ID is found (default=0).
* - `lazy` (bool): Specify true to force lazy loading. This is the same as using the Pages::findMany() method (default=false). * - `lazy` (bool): Specify true to force lazy loading. This is the same as using the Pages::findMany() method (default=false).
* - `loadOptions` (array): Optional assoc array of options to pass to getById() load options. * - `loadOptions` (array): Optional associative array of options to pass to getById() load options.
* @return PageArray Pages that matched the given selector. * @return PageArray Pages that matched the given selector.
* *
* Non-visible pages are excluded unless an "include=x" mode is specified in the selector * Non-visible pages are excluded unless an "include=x" mode is specified in the selector
@@ -289,6 +291,7 @@ class Pages extends Wire {
$debug = $this->debug; $debug = $this->debug;
if($debug) $this->debug(false); if($debug) $this->debug(false);
$options['lazy'] = true; $options['lazy'] = true;
if(!isset($options['cache'])) $options['cache'] = false;
$matches = $this->loader->find($selector, $options); $matches = $this->loader->find($selector, $options);
if($debug) $this->debug($debug); if($debug) $this->debug($debug);
return $matches; return $matches;

View File

@@ -154,6 +154,8 @@ class PagesLoader extends Wire {
* - findOne: boolean - apply optimizations for finding a single page * - findOne: boolean - apply optimizations for finding a single page
* - findAll: boolean - find all pages with no exculsions (same as include=all option) * - findAll: boolean - find all pages with no exculsions (same as include=all option)
* - getTotal: boolean - whether to set returning PageArray's "total" property (default: true except when findOne=true) * - getTotal: boolean - whether to set returning PageArray's "total" property (default: true except when findOne=true)
* - cache: boolean - Allow caching of selectors and pages loaded (default=true). Also sets loadOptions[cache].
* - allowCustom: boolean - Whether to allow use of "_custom=new selector" in selectors (default=false).
* - lazy: boolean - makes find() return Page objects that don't have any data populated to them (other than id and template). * - lazy: boolean - makes find() return Page objects that don't have any data populated to them (other than id and template).
* - loadPages: boolean - whether to populate the returned PageArray with found pages (default: true). * - loadPages: boolean - whether to populate the returned PageArray with found pages (default: true).
* The only reason why you'd want to change this to false would be if you only needed the count details from * The only reason why you'd want to change this to false would be if you only needed the count details from
@@ -177,6 +179,8 @@ class PagesLoader extends Wire {
$caller = isset($options['caller']) ? $options['caller'] : 'pages.find'; $caller = isset($options['caller']) ? $options['caller'] : 'pages.find';
$lazy = empty($options['lazy']) ? false : true; $lazy = empty($options['lazy']) ? false : true;
$debug = $this->debug && !$lazy; $debug = $this->debug && !$lazy;
$cachePages = isset($options['cache']) ? (bool) $options['cache'] : true;
if(!$cachePages && !isset($loadOptions['cache'])) $loadOptions['cache'] = false;
$pages = $this->findShortcut($selector, $options, $loadOptions); $pages = $this->findShortcut($selector, $options, $loadOptions);
if($pages) return $pages; if($pages) return $pages;
@@ -293,7 +297,9 @@ class PagesLoader extends Wire {
$pages->setSelectors($selectors); $pages->setSelectors($selectors);
$pages->setTrackChanges(true); $pages->setTrackChanges(true);
if($loadPages) $this->pages->cacher()->selectorCache($selectorString, $options, $pages); if($loadPages && $cachePages) {
$this->pages->cacher()->selectorCache($selectorString, $options, $pages);
}
if($debug) { if($debug) {
$this->pages->debugLog('find', $selectorString, $pages); $this->pages->debugLog('find', $selectorString, $pages);

View File

@@ -29,6 +29,7 @@ require_once(PROCESSWIRE_CORE_PATH . "Selector.php");
* #pw-body * #pw-body
* *
* @link https://processwire.com/api/selectors/ Official Selectors Documentation * @link https://processwire.com/api/selectors/ Official Selectors Documentation
* @method Selector[] getIterator()
* *
* ProcessWire 3.x, Copyright 2016 by Ryan Cramer * ProcessWire 3.x, Copyright 2016 by Ryan Cramer
* https://processwire.com * https://processwire.com
@@ -365,10 +366,9 @@ class Selectors extends WireArray {
/** /**
* Given a selector string, return an array of (field, value, operator) for each selector in the strong. * Given a selector string, populate to Selector objects in this Selectors instance
* *
* @param string $str The string containing a selector (or multiple selectors, separated by commas) * @param string $str The string containing a selector (or multiple selectors, separated by commas)
* @return array
* *
*/ */
protected function extractString($str) { protected function extractString($str) {
@@ -741,6 +741,47 @@ class Selectors extends WireArray {
return true; return true;
} }
/**
* Return array of all field names referenced in all of the Selector objects here
*
* @param bool $subfields Default is to allow "field.subfield" fields, or specify false to convert them to just "field".
* @return array Returned array has both keys and values as field names (same)
*
*/
public function getAllFields($subfields = true) {
$fields = array();
foreach($this as $selector) {
$field = $selector->field;
if(!is_array($field)) $field = array($field);
foreach($field as $f) {
if(!$subfields && strpos($f, '.')) {
list($f, $subfield) = explode('.', $f, 2);
if($subfield) {} // ignore
}
$fields[$f] = $f;
}
}
return $fields;
}
/**
* Return array of all values referenced in all Selector objects here
*
* @return array Returned array has both keys and values as field values (same)
*
*/
public function getAllValues() {
$values = array();
foreach($this as $selector) {
$value = $selector->value;
if(!is_array($value)) $value = array($value);
foreach($value as $v) {
$values[$v] = $v;
}
}
return $values;
}
/** /**
* Does the given Wire match these Selectors? * Does the given Wire match these Selectors?
* *

View File

@@ -50,7 +50,7 @@
* @property WireMailTools $mail #pw-internal * @property WireMailTools $mail #pw-internal
* @property WireFileTools $files #pw-internal * @property WireFileTools $files #pw-internal
* *
* @method changed(string $what) See Wire::___changed() * @method changed(string $what, $old = null, $new = null) See Wire::___changed()
* @method log($str = '', array $options = array()) See Wire::___log() * @method log($str = '', array $options = array()) See Wire::___log()
* @method callUnknown($method, $arguments) See Wire::___callUnknown() * @method callUnknown($method, $arguments) See Wire::___callUnknown()
* @method Wire trackException(\Exception $e, $severe = true, $text = null) * @method Wire trackException(\Exception $e, $severe = true, $text = null)
@@ -367,12 +367,15 @@ abstract class Wire implements WireTranslatable, WireFuelable, WireTrackable {
* @param $method * @param $method
* @param $arguments * @param $arguments
* @return mixed * @return mixed
* @internal
* *
*/ */
public function _callMethod($method, $arguments) { public function _callMethod($method, $arguments) {
if(empty($arguments)) {
return $this->$method();
} else {
return call_user_func_array(array($this, $method), $arguments); return call_user_func_array(array($this, $method), $arguments);
} }
}
/** /**
* Provides the gateway for calling hooks in ProcessWire * Provides the gateway for calling hooks in ProcessWire
@@ -953,7 +956,13 @@ abstract class Wire implements WireTranslatable, WireFuelable, WireTrackable {
} }
if(is_null($old) || is_null($new) || $lastValue !== $new) { if(is_null($old) || is_null($new) || $lastValue !== $new) {
/** @var WireHooks $hooks */
$hooks = $this->wire('hooks');
if(($hooks && $hooks->isHooked('changed')) || !$hooks) {
$this->changed($what, $old, $new); // triggers ___changed hook $this->changed($what, $old, $new); // triggers ___changed hook
} else {
$this->___changed($what, $old, $new);
}
} }
if($this->trackChanges & self::trackChangesValues) { if($this->trackChanges & self::trackChangesValues) {
@@ -1490,8 +1499,6 @@ abstract class Wire implements WireTranslatable, WireFuelable, WireTrackable {
/** /**
* ProcessWire instance * ProcessWire instance
* *
* This will replace static fuel in PW 3.0
*
* @var ProcessWire|null * @var ProcessWire|null
* *
*/ */
@@ -1505,7 +1512,6 @@ abstract class Wire implements WireTranslatable, WireFuelable, WireTrackable {
* #pw-internal * #pw-internal
* *
* @param ProcessWire $wire * @param ProcessWire $wire
* @return $this
* *
*/ */
public function setWire(ProcessWire $wire) { public function setWire(ProcessWire $wire) {

View File

@@ -71,23 +71,38 @@ class WireHooks {
protected $staticHooks = array(); protected $staticHooks = array();
/** /**
* A static cache of all hook method/property names for an optimization. * A cache of all hook method/property names for an optimization.
* *
* Hooked methods end with '()' while hooked properties don't. * Hooked methods end with '()' while hooked properties don't.
* *
* This does not distinguish which instance it was added to or whether it was removed. * This does not distinguish which instance it was added to or whether it was removed.
* But will use keys in the form 'fromClass::method' (with value 'method') in cases where a fromClass was specified.
* This cache exists primarily to gain some speed in our __get and __call methods. * This cache exists primarily to gain some speed in our __get and __call methods.
* *
*/ */
protected $hookMethodCache = array(); protected $hookMethodCache = array();
/**
* Same as hook method cache but for "Class::method"
*
* @var array
*
*/
protected $hookClassMethodCache = array();
/** /**
* Cache of all local hooks combined, for debugging purposes * Cache of all local hooks combined, for debugging purposes
* *
*/ */
protected $allLocalHooks = array(); protected $allLocalHooks = array();
/**
* Cached parent classes and interfaces
*
* @var array of class|interface => [ 'parentClass', 'parentClass', 'interface', 'interface', 'etc.' ]
*
*/
protected $parentClasses = array();
/** /**
* @var Config * @var Config
* *
@@ -135,6 +150,9 @@ class WireHooks {
$hooks = array(); $hooks = array();
// see if we can do a quick exit
if($method && $method !== '*' && !$this->isHookedOrParents($object, $method)) return $hooks;
// first determine which local hooks when should include // first determine which local hooks when should include
if($type !== self::getHooksStatic) { if($type !== self::getHooksStatic) {
$localHooks = $object->getLocalHooks(); $localHooks = $object->getLocalHooks();
@@ -217,8 +235,11 @@ class WireHooks {
* As a result, a true return value indicates something "might" be hooked, as opposed to be * As a result, a true return value indicates something "might" be hooked, as opposed to be
* being definitely hooked. * being definitely hooked.
* *
* If checking for a hooked method, it should be in the form "Class::method()" or "method()". * If checking for a hooked method, it should be in the form `Class::method()` or `method()` (with parenthesis).
* If checking for a hooked property, it should be in the form "Class::property" or "property". * If checking for a hooked property, it should be in the form `Class::property` or `property`.
*
* If you need to check if a method/property is hooked, including any of its parent classes, use
* the `WireHooks::isMethodHooked()`, `WireHooks::isPropertyHooked()`, or `WireHooks::hasHook()` methods instead.
* *
* @param string $method Method or property name in one of the following formats: * @param string $method Method or property name in one of the following formats:
* Class::method() * Class::method()
@@ -228,23 +249,114 @@ class WireHooks {
* @param Wire|null $instance Optional instance to check against (see hasHook method for details) * @param Wire|null $instance Optional instance to check against (see hasHook method for details)
* Note that if specifying an $instance, you may not use the Class::method() or Class::property options for $method argument. * Note that if specifying an $instance, you may not use the Class::method() or Class::property options for $method argument.
* @return bool * @return bool
* @see WireHooks::isMethodHooked(), WireHooks::isPropertyHooked(), WireHooks::hasHook()
* *
*/ */
public function isHooked($method, Wire $instance = null) { public function isHooked($method, Wire $instance = null) {
if($instance) return $this->hasHook($instance, $method); if($instance) return $this->hasHook($instance, $method);
$hooked = false;
if(strpos($method, ':') !== false) { if(strpos($method, ':') !== false) {
if(array_key_exists($method, $this->hookMethodCache)) $hooked = true; // fromClass::method() or fromClass::property $hooked = isset($this->hookClassMethodCache[$method]); // fromClass::method() or fromClass::property
} else { } else {
if(in_array($method, $this->hookMethodCache)) $hooked = true; // method() or property $hooked = isset($this->hookMethodCache[$method]); // method() or property
} }
return $hooked; return $hooked;
} }
/**
* Similar to isHooked() method but also checks parent classes for the hooked method as well
*
* This method is designed for fast determinations of whether something is hooked
*
* @param string|Wire $class
* @param string $method Name of method or property
* @param string $type May be either 'method', 'property' or 'either'
* @return bool
*
*/
protected function isHookedOrParents($class, $method, $type = 'either') {
$property = '';
$className = is_object($class) ? wireClassName($class) : $class;
if($type == 'method' || $type == 'either') {
if(strpos($method, '(') === false) $method .= '()';
if($type == 'either') $property = rtrim($method, '()');
}
if($type == 'method') {
if(!isset($this->hookMethodCache[$method])) return false; // not hooked for any class
$hooked = isset($this->hookClassMethodCache["$className::$method"]);
} else if($type == 'property') {
if(!isset($this->hookMethodCache[$property])) return false; // not hooked for any class
$hooked = isset($this->hookClassMethodCache["$className::$property"]);
} else {
if(!isset($this->hookMethodCache[$method])
&& !isset($this->hookMethodCache[$property])) return false;
$hooked = isset($this->hookClassMethodCache["$className::$property"]) ||
isset($this->hookClassMethodCache["$className::$method"]);
}
if(!$hooked) {
foreach($this->getClassParents($class) as $parentClass) {
if($type == 'method') {
if(isset($this->hookClassMethodCache["$parentClass::$method"])) {
$hooked = true;
$this->hookClassMethodCache["$class::$method"] = true;
}
} else if($type == 'property') {
if(isset($this->hookClassMethodCache["$parentClass::$property"])) {
$hooked = true;
$this->hookClassMethodCache["$class::$property"] = true;
}
} else {
if(isset($this->hookClassMethodCache["$parentClass::$method"])) {
$hooked = true;
$this->hookClassMethodCache["$class::$method"] = true;
}
if(!$hooked && isset($this->hookClassMethodCache["$parentClass::$property"])) {
$hooked = true;
$this->hookClassMethodCache["$class::$property"] = true;
}
}
if($hooked) break;
}
}
return $hooked;
}
/**
* Similar to isHooked() method but also checks parent classes for the hooked method as well
*
* This method is designed for fast determinations of whether something is hooked
*
* @param string|Wire $class
* @param string $method Name of method
* @return bool
*
*/
public function isMethodHooked($class, $method) {
return $this->isHookedOrParents($class, $method, 'method');
}
/**
* Similar to isHooked() method but also checks parent classes for the hooked property as well
*
* This method is designed for fast determinations of whether something is hooked
*
* @param string|Wire $class
* @param string $property Name of property
* @return bool
*
*/
public function isPropertyHooked($class, $property) {
return $this->isHookedOrParents($class, $property, 'property');
}
/** /**
* Similar to isHooked(), returns true if the method or property hooked, false if it isn't. * Similar to isHooked(), returns true if the method or property hooked, false if it isn't.
* *
* Accomplishes the same thing as the isHooked() method, but this is more accruate, * Accomplishes the same thing as the isHooked() method, but this is more accurate,
* and potentially slower than isHooked(). Less for optimization use, more for accuracy use. * and potentially slower than isHooked(). Less for optimization use, more for accuracy use.
* *
* It checks for both static hooks and local hooks, but only accepts a method() or property * It checks for both static hooks and local hooks, but only accepts a method() or property
@@ -270,9 +382,7 @@ class WireHooks {
} }
// quick exit when possible // quick exit when possible
if(!in_array($method, $this->hookMethodCache)) { if(!isset($this->hookMethodCache[$method])) return false;
return false;
}
$_method = rtrim($method, '()'); $_method = rtrim($method, '()');
$localHooks = $object->getLocalHooks(); $localHooks = $object->getLocalHooks();
@@ -285,12 +395,10 @@ class WireHooks {
$hooked = true; $hooked = true;
} else { } else {
// check parent classes and interfaces // check parent classes and interfaces
$classes = wireClassParents($object, false); foreach($this->getClassParents($object) as $class) {
$interfaces = wireClassImplements($object);
if(is_array($interfaces)) $classes = array_merge($interfaces, $classes);
foreach($classes as $class) {
if(!empty($this->staticHooks[$class][$_method])) { if(!empty($this->staticHooks[$class][$_method])) {
$hooked = true; $hooked = true;
$this->hookClassMethodCache["$class::$method"] = true;
break; break;
} }
} }
@@ -299,6 +407,31 @@ class WireHooks {
return $hooked; return $hooked;
} }
/**
* Get an array of parent classes and interfaces for the given object
*
* @param Wire|string $object Maybe either object instance or class name
* @param bool $cache Allow use of cache for getting or storing? (default=true)
* @return array
*
*/
public function getClassParents($object, $cache = true) {
if(is_string($object)) {
$className = $object;
} else {
$className = $object->className();
}
if($cache && isset($this->parentClasses[$className])) {
$classes = $this->parentClasses[$className];
} else {
$classes = wireClassParents($object, false);
$interfaces = wireClassImplements($object);
if(is_array($interfaces)) $classes = array_merge($interfaces, $classes);
if($cache) $this->parentClasses[$className] = $classes;
}
return $classes;
}
/** /**
* Hook a function/method to a hookable method call in this object * Hook a function/method to a hookable method call in this object
@@ -381,7 +514,6 @@ class WireHooks {
$options['fromClass'] = $fromClass; $options['fromClass'] = $fromClass;
} }
$argOpen = strpos($method, '('); $argOpen = strpos($method, '(');
if($argOpen && strpos($method, ')') > $argOpen+1) { if($argOpen && strpos($method, ')') > $argOpen+1) {
// extract argument selector match string(s), arg 0: Something::something(selector_string) // extract argument selector match string(s), arg 0: Something::something(selector_string)
@@ -468,10 +600,10 @@ class WireHooks {
); );
$hooks[$method][$priority] = $hook; $hooks[$method][$priority] = $hook;
// cacheValue is just the method() or property, cacheKey includes optional fromClass:: // cache record known hooks so they can be detected quickly
$cacheValue = $options['type'] == 'method' ? "$method()" : "$method"; $cacheValue = $options['type'] == 'method' ? "$method()" : "$method";
$cacheKey = ($options['fromClass'] ? $options['fromClass'] . '::' : '') . $cacheValue; if($options['fromClass']) $this->hookClassMethodCache["$options[fromClass]::$cacheValue"] = true;
$this->hookMethodCache[$cacheKey] = $cacheValue; $this->hookMethodCache[$cacheValue] = true;
// keep track of all local hooks combined when debug mode is on // keep track of all local hooks combined when debug mode is on
if($local && $this->config->debug) { if($local && $this->config->debug) {
@@ -534,7 +666,8 @@ class WireHooks {
$realMethod = "___$method"; $realMethod = "___$method";
if($type == 'method') $result['methodExists'] = method_exists($object, $realMethod); if($type == 'method') $result['methodExists'] = method_exists($object, $realMethod);
if(!$result['methodExists'] && !$this->hasHook($object, $method . ($type == 'method' ? '()' : ''))) { // if(!$result['methodExists'] && !$this->hasHook($object, $method . ($type == 'method' ? '()' : ''))) {
if(!$result['methodExists'] && !$this->isHookedOrParents($object, $method, $type)) {
return $result; // exit quickly when we can return $result; // exit quickly when we can
} }
@@ -557,6 +690,7 @@ class WireHooks {
if(!$hook['options'][$when]) continue; if(!$hook['options'][$when]) continue;
if(!empty($hook['options']['objMatch'])) { if(!empty($hook['options']['objMatch'])) {
/** @var Selectors $objMatch */
$objMatch = $hook['options']['objMatch']; $objMatch = $hook['options']['objMatch'];
// object match comparison to determine at runtime whether to execute the hook // object match comparison to determine at runtime whether to execute the hook
if(is_object($objMatch)) { if(is_object($objMatch)) {
@@ -571,6 +705,7 @@ class WireHooks {
$argMatches = $hook['options']['argMatch']; $argMatches = $hook['options']['argMatch'];
$matches = true; $matches = true;
foreach($argMatches as $argKey => $argMatch) { foreach($argMatches as $argKey => $argMatch) {
/** @var Selectors $argMatch */
$argVal = isset($arguments[$argKey]) ? $arguments[$argKey] : null; $argVal = isset($arguments[$argKey]) ? $arguments[$argKey] : null;
if(is_object($argMatch)) { if(is_object($argMatch)) {
// Selectors object // Selectors object
@@ -594,15 +729,16 @@ class WireHooks {
if(!$matches) continue; // don't run hook if(!$matches) continue; // don't run hook
} }
$event = new HookEvent(); $event = new HookEvent(array(
'object' => $object,
'method' => $method,
'arguments' => $arguments,
'when' => $when,
'return' => $result['return'],
'id' => $hook['id'],
'options' => $hook['options']
));
$this->wire->wire($event); $this->wire->wire($event);
$event->object = $object;
$event->method = $method;
$event->arguments = $arguments;
$event->when = $when;
$event->return = $result['return'];
$event->id = $hook['id'];
$event->options = $hook['options'];
$toObject = $hook['toObject']; $toObject = $hook['toObject'];
$toMethod = $hook['toMethod']; $toMethod = $hook['toMethod'];
@@ -624,6 +760,7 @@ class WireHooks {
} }
$toMethod($event); $toMethod($event);
} else { } else {
/** @var Wire $toObject */
if($hook['toPublic']) { if($hook['toPublic']) {
// public // public
$returnValue = $toObject->$toMethod($event); $returnValue = $toObject->$toMethod($event);
@@ -632,6 +769,7 @@ class WireHooks {
$returnValue = $toObject->_callMethod($toMethod, array($event)); $returnValue = $toObject->_callMethod($toMethod, array($event));
} }
// @todo allow for use of $returnValue as alternative to $event->return // @todo allow for use of $returnValue as alternative to $event->return
if($returnValue) {}
} }
if($profilerEvent) $profiler->stop($profilerEvent); if($profilerEvent) $profiler->stop($profilerEvent);
@@ -660,9 +798,9 @@ class WireHooks {
/** /**
* Start timing a hook and return the timer name * Start timing a hook and return the timer name
* *
* @param $object * @param Wire $object
* @param $method * @param String $method
* @param $arguments * @param array $arguments
* @return string * @return string
* *
*/ */
@@ -694,8 +832,8 @@ class WireHooks {
* } * }
* *
* @param Wire $object * @param Wire $object
* @param string|null $hookId * @param string|null $hookID
* @return $this * @return Wire
* *
*/ */
public function removeHook(Wire $object, $hookID) { public function removeHook(Wire $object, $hookID) {
@@ -707,6 +845,9 @@ class WireHooks {
$object->setLocalHooks($localHooks); $object->setLocalHooks($localHooks);
} else { } else {
unset($this->staticHooks[$hookClass][$method][$priority]); unset($this->staticHooks[$hookClass][$method][$priority]);
if(empty($this->staticHooks[$hookClass][$method])) {
unset($this->hookClassMethodCache["$hookClass::$method"]);
}
} }
} }
return $object; return $object;

View File

@@ -390,7 +390,7 @@ class FieldtypePageTable extends FieldtypeMulti implements Module {
if(!is_array($value) || !count($value) || empty($field->template_id)) return $this->getBlankValue($page, $field); if(!is_array($value) || !count($value) || empty($field->template_id)) return $this->getBlankValue($page, $field);
$template_id = $field->template_id; $template_id = $field->get('template_id');
if(!is_array($template_id)) { if(!is_array($template_id)) {
$template_id = $template_id ? array($template_id) : array(); $template_id = $template_id ? array($template_id) : array();
@@ -402,16 +402,26 @@ class FieldtypePageTable extends FieldtypeMulti implements Module {
$template = null; $template = null;
} }
if($field->sortfields) { $loadOptions = array('cache' => false);
if($template) $loadOptions['template'] = $template;
$sortfields = $field->get('sortfields');
if($sortfields) {
$selector = $template ? "template=$template, " : ""; $selector = $template ? "template=$template, " : "";
$selector .= "include=unpublished, id=" . implode('|', $value); $selector .= "include=unpublished, id=" . implode('|', $value);
foreach(explode(',', $field->sortfields) as $sortfield) { foreach(explode(',', $sortfields) as $sortfield) {
$selector .= ", sort=" . $this->wire('sanitizer')->name(trim($sortfield)); $selector .= ", sort=" . $this->wire('sanitizer')->name(trim($sortfield));
} }
$items = $this->wire('pages')->find($selector); $options = array(
'cache' => false,
'caller' => $this->className() . '::wakeupValue',
'loadOptions' => $loadOptions
);
$items = $this->wire('pages')->find($selector, $options);
} else { } else {
$items = $this->wire('pages')->getById($value, $template); $items = $this->wire('pages')->getById($value, $loadOptions);
} }
return $items; return $items;

View File

@@ -640,7 +640,17 @@ class FieldtypeRepeater extends Fieldtype implements ConfigurableModule {
*/ */
// load the repeater pages // load the repeater pages
$a = $this->wire('pages')->find($selector); $options = array(
'cache' => false,
'caller' => $this->className() . '::wakeupValue',
'loadOptions' => array(
'cache' => false,
'parent_id' => $parent_id,
'template' => $this->wire('templates')->get($template_id)
)
);
$a = $this->wire('pages')->find($selector, $options);
$class = $this->getPageArrayClass(); $class = $this->getPageArrayClass();
$pageArray = $this->wire(new $class($page, $field)); $pageArray = $this->wire(new $class($page, $field));
$pageArray->import($a); $pageArray->import($a);

View File

@@ -588,9 +588,10 @@ var InputfieldSelector = {
if(s.field == '_custom') { if(s.field == '_custom') {
if(s.isOrGroup) { if(s.isOrGroup) {
s.value = s.value.replace('(', '').replace(')', ''); s.value = s.value.replace('(', '').replace(')', '');
selector += s.field + '=' + '(' + s.value + ')'; selector += s.field + '=' + '(' + $.trim(s.value) + ')';
} else { } else {
selector += s.value; //selector += s.value;
selector += s.field + '="' + $.trim(s.value) + '"';
} }
} else { } else {
selector += s.field + s.operator + $.trim(s.value); selector += s.field + s.operator + $.trim(s.value);

File diff suppressed because one or more lines are too long

View File

@@ -273,7 +273,10 @@ class InputfieldSelector extends Inputfield implements ConfigurableModule {
} else if($action == 'subfield' && ($fieldName = $input->get->field)) { } else if($action == 'subfield' && ($fieldName = $input->get->field)) {
$fieldName = $sanitizer->name($fieldName); $fieldName = $sanitizer->name($fieldName);
if(strpos($fieldName, '.')) list($fieldName, $subfieldName) = explode('.', $fieldName); if(strpos($fieldName, '.')) {
list($fieldName, $subfieldName) = explode('.', $fieldName);
if($subfieldName) {} // ignore
}
$out = $this->renderSelectSubfield($fieldName); $out = $this->renderSelectSubfield($fieldName);
} else if($action == 'opval' && ($fieldName = $input->get->field)) { } else if($action == 'opval' && ($fieldName = $input->get->field)) {
@@ -617,6 +620,7 @@ class InputfieldSelector extends Inputfield implements ConfigurableModule {
// consider multi-language // consider multi-language
if(strpos($name, 'data') === 0 && $this->wire('languages')) { if(strpos($name, 'data') === 0 && $this->wire('languages')) {
list($unused, $languageID) = explode('data', "x$name"); list($unused, $languageID) = explode('data', "x$name");
if($unused) {}
if(ctype_digit($languageID)) { if(ctype_digit($languageID)) {
$language = $this->wire('languages')->get((int) $languageID); $language = $this->wire('languages')->get((int) $languageID);
if($language && $language->id) { if($language && $language->id) {
@@ -630,7 +634,7 @@ class InputfieldSelector extends Inputfield implements ConfigurableModule {
} else if(!empty($subfield['label'])) { } else if(!empty($subfield['label'])) {
$label = $subfield['label']; $label = $subfield['label'];
} else if(strpos($name, 'data') === 0 && ctype_digit(substr($name, 4)) && $this->wire('languages')) { } else if(strpos($name, 'data') === 0 && ctype_digit(substr($name, 4)) && $this->wire('languages')) {
$label = $this->wire('languages')->get((int) substr($name, 4))->get('title|name');
} else { } else {
$f = $this->wire('fields')->get($name); $f = $this->wire('fields')->get($name);
$label = $f ? $f->getLabel() : $name; $label = $f ? $f->getLabel() : $name;
@@ -646,6 +650,7 @@ class InputfieldSelector extends Inputfield implements ConfigurableModule {
$_subfields = array(); $_subfields = array();
foreach($subfields as $key => $subfield) { foreach($subfields as $key => $subfield) {
list($label, $name) = explode("\t", $key); list($label, $name) = explode("\t", $key);
if($label) {}
$_subfields[$name] = $subfield; $_subfields[$name] = $subfield;
} }
$subfields = $_subfields; $subfields = $_subfields;
@@ -839,6 +844,7 @@ class InputfieldSelector extends Inputfield implements ConfigurableModule {
if($settings['showFieldLabels']) { if($settings['showFieldLabels']) {
$customFields = array(); $customFields = array();
foreach($settings['customFields'] as $field) { foreach($settings['customFields'] as $field) {
/** @var Field $field */
$label = $field->getLabel(); $label = $field->getLabel();
while(isset($customFields[$label])) $label .= ' '; while(isset($customFields[$label])) $label .= ' ';
$customFields[$label] = $field; $customFields[$label] = $field;
@@ -1183,8 +1189,18 @@ class InputfieldSelector extends Inputfield implements ConfigurableModule {
&& ($field->get('findPagesCode') || $field->get('findPagesSelector'))) { && ($field->get('findPagesCode') || $field->get('findPagesSelector'))) {
// see if we can locate options purely with the parent or template // see if we can locate options purely with the parent or template
$findSelector = array("include=unpublished, limit=500, sort=title, sort=name, "); $findSelector = array("include=unpublished, limit=500, sort=title, sort=name, ");
if($field->get('parent_id')) $findSelector[] = "parent_id=" . (int) $field->get('parent_id'); $parent_ids = $field->get('parent_ids');
if($field->get('template_id')) $findSelector[] = "templates_id=" . (int) $field->get('template_id'); $template_ids = $field->get('template_ids');
if($parent_ids && count($parent_ids)) {
$findSelector[] = "parent_id=" . implode('|', $parent_ids);
} else if($field->get('parent_id')) {
$findSelector[] = "parent_id=" . (int) $field->get('parent_id');
}
if($template_ids && count($template_ids)) {
$findSelector[] = "templates_id=" . implode('|', $template_ids);
} else if($field->get('template_id')) {
$findSelector[] = "templates_id=" . (int) $field->get('template_id');
}
foreach($this->wire('pages')->find(implode(', ', $findSelector)) as $item) { foreach($this->wire('pages')->find(implode(', ', $findSelector)) as $item) {
$options[$item->id] = $inputfield->getPageLabel($item); // $item->get('title|name'); $options[$item->id] = $inputfield->getPageLabel($item); // $item->get('title|name');
} }
@@ -1386,6 +1402,7 @@ class InputfieldSelector extends Inputfield implements ConfigurableModule {
if(strpos($limitField, '.') === false) continue; if(strpos($limitField, '.') === false) continue;
if(strpos($limitField, $fieldName) !== 0) continue; if(strpos($limitField, $fieldName) !== 0) continue;
list($limitField, $limitSubfield) = explode('.', $limitField); list($limitField, $limitSubfield) = explode('.', $limitField);
if($limitField) {} // ignore
if($limitSubfield) $limitSubfields[$limitSubfield] = $limitSubfield; if($limitSubfield) $limitSubfields[$limitSubfield] = $limitSubfield;
} }
// render all the subfield options // render all the subfield options
@@ -1625,7 +1642,7 @@ class InputfieldSelector extends Inputfield implements ConfigurableModule {
* *
* @param array|string $key * @param array|string $key
* @param int|string $value * @param int|string $value
* @return $this * @return InputfieldSelector|WireData
* *
*/ */
public function setAttribute($key, $value) { public function setAttribute($key, $value) {
@@ -1913,6 +1930,7 @@ class InputfieldSelector extends Inputfield implements ConfigurableModule {
* *
*/ */
public function getModuleConfigInputfields(array $data) { public function getModuleConfigInputfields(array $data) {
if($data) {} // ignore
$form = $this->wire(new InputfieldWrapper()); $form = $this->wire(new InputfieldWrapper());
$f = $this->wire('modules')->get('InputfieldSelector'); $f = $this->wire('modules')->get('InputfieldSelector');
$f->name = 'test'; $f->name = 'test';

View File

@@ -1504,7 +1504,8 @@ class ProcessPageLister extends Process implements ConfigurableModule {
$this->finalSelector = $selector; $this->finalSelector = $selector;
try { try {
$results = $selector ? $this->wire('pages')->find($selector) : $this->wire('pages')->newPageArray(); $options = array('allowCustom' => true);
$results = $selector ? $this->wire('pages')->find($selector, $options) : $this->wire('pages')->newPageArray();
} catch(\Exception $e) { } catch(\Exception $e) {
$this->error($e->getMessage()); $this->error($e->getMessage());