Rev 1 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | Download
<?php namespace ProcessWire;/*** ProcessWire Page** Page is the class used by all instantiated pages and it provides functionality for:** 1. Providing get/set access to the Page's properties* 2. Accessing the related hierarchy of pages (i.e. parents, children, sibling pages)** ProcessWire 3.x, Copyright 2022 by Ryan Cramer* https://processwire.com** #pw-summary Class used by all Page objects in ProcessWire.* #pw-summary-languages Multi-language methods require these core modules: `LanguageSupport`, `LanguageSupportFields`, `LanguageSupportPageNames`.* #pw-summary-system Most system properties directly correspond to columns in the `pages` database table.* #pw-summary-previous Provides access to the previously set runtime value of some Page properties.* #pw-order-groups common,traversal,manipulation,date-time,access,output-rendering,status,constants,languages,system,advanced,hooks* #pw-use-constants* #pw-var $page* #pw-body =* The `$page` API variable represents the current page being viewed. However, the documentation* here also applies to all Page objects that you may work with in the API. We use `$page` as the most common example* throughout the documentation, but you can substitute that with any variable name representing a Page.* #pw-body** @link http://processwire.com/api/ref/page/ Offical $page Documentation* @link http://processwire.com/api/selectors/ Official Selectors Documentation** @property int $id The numbered ID of the current page #pw-group-system #pw-group-common* @property string $name The name assigned to the page, as it appears in the URL #pw-group-system #pw-group-common* @property string|null $namePrevious Previous name, if changed. Null or blank string if not. #pw-group-previous* @property string $title The page’s title (headline) text* @property string $path The page’s URL path from the homepage (i.e. /about/staff/ryan/)* @property string $url The page’s URL path from the server's document root* @property array $urls All URLs the page is accessible from, whether current, former and multi-language. #pw-group-urls* @property string $httpUrl Same as $page->url, except includes scheme (http or https) and hostname.* @property Page|string|int $parent The parent Page object or a NullPage if there is no parent. For assignment, you may also use the parent path (string) or id (integer). #pw-group-traversal* @property Page|null $parentPrevious Previous parent, if parent was changed. Null if not. #pw-group-previous* @property int $parent_id The numbered ID of the parent page or 0 if homepage or not assigned. #pw-group-system* @property int $templates_id The numbered ID of the template usedby this page. #pw-group-system* @property PageArray $parents All the parent pages down to the root (homepage). Returns a PageArray. #pw-group-common #pw-group-traversal* @property Page $rootParent The parent page closest to the homepage (typically used for identifying a section) #pw-group-traversal* @property Template|string $template The Template object this page is using. The template name (string) may also be used for assignment.* @property Template|null $templatePrevious Previous template, if template was changed. Null if not. #pw-group-previous* @property Fieldgroup $fields All the Fields assigned to this page (via its template). Returns a Fieldgroup. #pw-advanced* @property int $numChildren The number of children (subpages) this page has, with no exclusions (fast). #pw-group-traversal* @property int $hasChildren The number of visible children this page has. Excludes unpublished, no-access, hidden, etc. #pw-group-traversal* @property int $numVisibleChildren Verbose alias of $hasChildren #pw-internal* @property int $numDescendants Number of descendants (quantity of children, and their children, and so on). @since 3.0.116 #pw-group-traversal* @property int $numParents Number of parent pages (i.e. depth) @since 3.0.117 #pw-group-traversal* @property PageArray $children All the children of this page. Returns a PageArray. See also $page->children($selector). #pw-group-traversal* @property Page|NullPage $child The first child of this page. Returns a Page. See also $page->child($selector). #pw-group-traversal* @property PageArray $siblings All the sibling pages of this page. Returns a PageArray. See also $page->siblings($selector). #pw-group-traversal* @property Page $next This page's next sibling page, or NullPage if it is the last sibling. See also $page->next($pageArray). #pw-group-traversal* @property Page $prev This page's previous sibling page, or NullPage if it is the first sibling. See also $page->prev($pageArray). #pw-group-traversal* @property int $created Unix timestamp of when the page was created. #pw-group-date-time #pw-group-system* @property string $createdStr Date/time when the page was created (formatted date/time string). #pw-group-date-time* @property int $modified Unix timestamp of when the page was last modified. #pw-group-date-time #pw-group-system* @property string $modifiedStr Date/time when the page was last modified (formatted date/time string). #pw-group-date-time* @property int $published Unix timestamp of when the page was published. #pw-group-date-time #pw-group-system* @property string $publishedStr Date/time when the page was published (formatted date/time string). #pw-group-date-time* @property int $created_users_id ID of created user. #pw-group-system #pw-group-users* @property User|NullPage $createdUser The user that created this page. Returns a User or a NullPage. #pw-group-users* @property int $modified_users_id ID of last modified user. #pw-group-system #pw-group-users* @property User|NullPage $modifiedUser The user that last modified this page. Returns a User or a NullPage. #pw-group-users* @property PagefilesManager $filesManager The object instance that manages files for this page. #pw-group-files* @property string $filesPath Get the disk path to store files for this page, creating it if it does not exist. #pw-group-files* @property string $filesUrl Get the URL to store files for this page, creating it if it does not exist. #pw-group-files* @property bool $hasFiles Does this page have one or more files in its files path? #pw-group-files* @property bool $outputFormatting Whether output formatting is enabled or not. #pw-advanced* @property int $sort Sort order of this page relative to siblings (applicable when manual sorting is used). #pw-group-system* @property int $index Index of this page relative to its siblings, regardless of sort (starting from 0). #pw-group-traversal* @property string $sortfield Field that a page is sorted by relative to its siblings (default="sort", which means drag/drop manual) #pw-group-system* @property null|array _statusCorruptedFields Field names that caused the page to have Page::statusCorrupted status. #pw-internal* @property int $status Page status flags. #pw-group-system #pw-group-status* @property int|null $statusPrevious Previous status, if status was changed. Null if not. #pw-group-status #pw-group-previous* @property string statusStr Returns space-separated string of status names active on this page. #pw-group-status* @property Fieldgroup $fieldgroup Fieldgroup used by page template. Shorter alias for $page->template->fieldgroup (same as $page->fields) #pw-advanced* @property string $editUrl URL that this page can be edited at. #pw-group-urls* @property string $editURL Alias of $editUrl. #pw-internal* @property PageRender $render May be used for field markup rendering like $page->render->title. #pw-advanced* @property bool|string $loaderCache Whether or not pages loaded as a result of this one may be cached by PagesLoaderCache. #pw-internal* @property PageArray $references Return pages that are referencing the given one by way of Page references. #pw-group-traversal* @property int $numReferences Total number of pages referencing this page with Page reference fields. #pw-group-traversal* @property int $hasReferences Number of visible pages (to current user) referencing this page with page reference fields. #pw-group-traversal* @property PageArray $referencing Return pages that this page is referencing by way of Page reference fields. #pw-group-traversal* @property int $numReferencing Total number of other pages this page is pointing to (referencing) with Page fields. #pw-group-traversal* @property PageArray $links Return pages that link to this one contextually in Textarea/HTML fields. #pw-group-traversal* @property int $numLinks Total number of pages manually linking to this page in Textarea/HTML fields. #pw-group-traversal* @property int $hasLinks Number of visible pages (to current user) linking to this page in Textarea/HTML fields. #pw-group-traversal* @property int $instanceID #pw-internal* @property bool $quietMode #pw-internal* @property WireData|null $_meta #pw-internal* @property WireData $meta #pw-internal*** @property Page|null $_cloning Internal runtime use, contains Page being cloned (source), when this Page is the new copy (target). #pw-internal* @property int |null $_inserted Populated with time() value of when new page was inserted into DB, only for page created in this request. #pw-internal* @property bool|null $_hasAutogenName Internal runtime use, set by Pages class when page as auto-generated name. #pw-internal* @property bool|null $_forceSaveParents Internal runtime/debugging use, force a page to refresh its pages_parents DB entries on save(). #pw-internal* @property float|null $_pfscore Internal PageFinder fulltext match score when page found/loaded from relevant query. #pw-internal** Methods added by PageRender.module:* -----------------------------------* @method string|mixed render($fieldName = '') Returns rendered page markup. If given a $fieldName argument, it behaves same as the renderField() method. #pw-group-output-rendering** Methods added by PagePermissions.module:* ----------------------------------------* @method bool viewable($field = '', $checkTemplateFile = true) Returns true if the page (and optionally field) is viewable by the current user, false if not. #pw-group-access* @method bool editable($field = '', $checkPageEditable = true) Returns true if the page (and optionally field) is editable by the current user, false if not. #pw-group-access* @method bool publishable() Returns true if the page is publishable by the current user, false if not. #pw-group-access* @method bool listable() Returns true if the page is listable by the current user, false if not. #pw-group-access* @method bool deleteable() Returns true if the page is deleteable by the current user, false if not. #pw-group-access* @method bool deletable() Alias of deleteable(). #pw-group-access* @method bool trashable($orDeleteable = false) Returns true if the page is trashable by the current user, false if not. #pw-group-access* @method bool restorable() Returns true if page is in the trash and is capable of being restored to its original location. @since 3.0.107 #pw-group-access* @method bool addable($pageToAdd = null) Returns true if the current user can add children to the page, false if not. Optionally specify the page to be added for additional access checking. #pw-group-access* @method bool moveable($newParent = null) Returns true if the current user can move this page. Optionally specify the new parent to check if the page is moveable to that parent. #pw-group-access* @method bool sortable() Returns true if the current user can change the sort order of the current page (within the same parent). #pw-group-access* @property bool $viewable #pw-group-access* @property bool $editable #pw-group-access* @property bool $publishable #pw-group-access* @property bool $deleteable #pw-group-access* @property bool $deletable #pw-group-access* @property bool $trashable #pw-group-access* @property bool $addable #pw-group-access* @property bool $moveable #pw-group-access* @property bool $sortable #pw-group-access* @property bool $listable #pw-group-access** Methods added by PagePathHistory.module (installed by default)* --------------------------------------------------------------* @method bool addUrl($url, $language = null) Add a new URL that redirects to this page and save immediately (returns false if already taken). #pw-group-urls #pw-group-manipulation* @method bool removeUrl($url) Remove a URL that redirects to this page and save immediately. #pw-group-urls #pw-group-manipulation* Note: you can use the $page->urls() method to get URLs added by PagePathHistory.** Methods added by LanguageSupport.module (not installed by default)* -----------------------------------------------------------------* @method Page setLanguageValue($language, $field, $value) Set value for field in language (requires LanguageSupport module). $language may be ID, language name or Language object. Field should be field name (string). #pw-group-languages* @method Page getLanguageValue($language, $field) Get value for field in language (requires LanguageSupport module). $language may be ID, language name or Language object. Field should be field name (string). #pw-group-languages** Methods added by LanguageSupportPageNames.module (not installed by default)* ---------------------------------------------------------------------------* @method string localName($language = null, $useDefaultWhenEmpty = false) Return the page name in the current user’s language, or specify $language argument (Language object, name, or ID), or TRUE to use default page name when blank (instead of 2nd argument). #pw-group-languages* @method string localPath($language = null) Return the page path in the current user's language, or specify $language argument (Language object, name, or ID). #pw-group-languages #pw-group-urls* @method string localUrl($language = null) Return the page URL in the current user's language, or specify $language argument (Language object, name, or ID). #pw-group-languages #pw-group-urls* @method string localHttpUrl($language = null) Return the page URL (including scheme and hostname) in the current user's language, or specify $language argument (Language object, name, or ID). #pw-group-languages #pw-group-urls** Methods added by PageFrontEdit.module (not always installed by default)* -----------------------------------------------------------------------* @method string|bool|mixed edit($key = null, $markup = null, $modal = null) Get front-end editable field output or get/set status.** Methods added by ProDrafts.module (if installed)* ------------------------------------------------* @method ProDraft|\ProDraft|int|string|Page|array draft($key = null, $value = null) Helper method for drafts (added by ProDrafts). #pw-advanced** Hookable methods* ----------------* @method mixed getUnknown($key) Last stop to find a property that we haven't been able to locate. Hook this method to provide a handler. #pw-hooker* @method Page rootParent() Get parent closest to homepage. #pw-internal* @method void loaded() Called when page is loaded. #pw-internal* @method void setEditor(WirePageEditor $editor) #pw-internal* @method string getIcon() #pw-internal* @method string getMarkup($key) Return the markup value for a given field name or {tag} string. #pw-internal* @method string|mixed renderField($fieldName, $file = '') Returns rendered field markup, optionally with file relative to templates/fields/. #pw-internal* @method string|mixed renderValue($value, $file) Returns rendered markup for $value using $file relative to templates/fields/. #pw-internal* @method PageArray references($selector = '', $field = '') Return pages that are pointing to this one by way of Page reference fields. #pw-group-traversal* @method PageArray links($selector = '', $field = '') Return pages that link to this one contextually in Textarea/HTML fields. #pw-group-traversal* @method string|mixed if($key, $yes, $no = '') If value is available for $key return or call $yes condition (with optional $no condition)** Alias/alternate methods* -----------------------* @method PageArray descendants($selector = '', array $options = array()) Find descendant pages, alias of `Page::find()`, see that method for details. @since 3.0.116 #pw-group-traversal* @method Page|NullPage descendant($selector = '', array $options = array()) Find one descendant page, alias of `Page::findOne()`, see that method for details. @since 3.0.116 #pw-group-traversal**/class Page extends WireData implements \Countable, WireMatchable {/** The following constant flags are specific to a Page's 'status' field. A page can have 1 or more flags using bitwise logic.* Status levels 1024 and above are excluded from search by the core. Status levels 16384 and above are runtime only and not* stored in the DB unless for logging or page history.** The status levels 16384 and above can safely be changed as needed as they are runtime only.** Please note that all other statuses are reserved for future use.**//*** Base status for pages, represents boolean true (1) or false (0) as flag with other statuses, for internal use purposes only* #pw-internal**/const statusOn = 1;/*** Reserved status (internal use)* #pw-internal**/const statusReserved = 2;/*** Indicates page is locked for changes (name: "locked")**/const statusLocked = 4;/*** Page is for the system and may not be deleted or have its id changed (name: "system-id").* #pw-internal**/const statusSystemID = 8;/*** Page is for the system and may not be deleted or have its id, name, template or parent changed (name: "system").* #pw-internal**/const statusSystem = 16;/*** Page has a globally unique name and no other pages may have the same name* #pw-internal**/const statusUnique = 32;/*** Page has pending draft changes (name: "draft").* #pw-internal**/const statusDraft = 64;/*** Page is flagged as incomplete, needing review, or having some issue* ProcessPageEdit uses this status to indicate an error message occurred during last internactive save* #pw-internal* @since 3.0.127**/const statusFlagged = 128;const statusIncomplete = 128; // alias of statusFlagged/*** Deprecated, was never used, but kept in case any modules referenced it* #pw-internal* @deprecated**/const statusVersions = 128;/*** Reserved for internal use* #pw-internal* @since 3.0.127**/const statusInternal = 256;/*** Page is temporary. 1+ day old unpublished pages with this status may be automatically deleted (name: "temp").* Applies only if this status is combined with statusUnpublished.* #pw-internal**/const statusTemp = 512;/*** Page is hidden and excluded from page finding methods unless overridden by selector (name: "hidden").**/const statusHidden = 1024;/*** Page is unpublished (not publicly visible) and excluded from page finding methods unless overridden (name: "unpublished").**/const statusUnpublished = 2048;/*** Page is in the trash.* #pw-internal**/const statusTrash = 8192; // page is in the trash/*** Page is deleted (runtime status only, as deleted pages aren't saved in the database)* #pw-internal**/const statusDeleted = 16384;/*** Page is in a state where system flags may be overridden (runtime only)* #pw-internal**/const statusSystemOverride = 32768;/*** Page was corrupted at runtime and is NOT saveable.* #pw-internal**/const statusCorrupted = 131072;/*** Maximum possible page status, to use only for runtime comparisons - do not assign this to a page.* #pw-internal**/const statusMax = 9999999;/*** The Template this page is using (object)** @var Template|null**/protected $template;/*** The previous template used by the page, if it was changed during runtime.** Allows Pages::save() to delete data that's no longer used.** @var Template|null**/private $templatePrevious;/*** Parent Page - Instance of Page** @var Page|null**/protected $_parent = null;/*** Parent ID for lazy loading purposes** @var int**/protected $_parent_id = 0;/*** Traversal siblings/items set by setTraversalItems() to force usage in some page traversal calls** @var PageArray|null**/protected $traversalPages = null;/*** The previous parent used by the page, if it was changed during runtime.** Allows Pages::save() to identify when the parent has changed** @var Page|null**/private $parentPrevious;/*** The previous name used by this page, if it changed during runtime.** @var string**/private $namePrevious;/*** The previous status used by this page, if it changed during runtime.** @var int**/private $statusPrevious;/*** Reference to the Page's template file, used for output. Instantiated only when asked for.** @var TemplateFile|null**/private $output;/*** Instance of PagefilesManager, which manages and migrates file versions for this page** Only instantiated upon request, so access only from filesManager() method in Page class.* Outside API can use $page->filesManager.** @var PagefilesManager|null**/private $filesManager = null;/*** Field data that queues while the page is loading.** Once setIsLoaded(true) is called, this data is processed and instantiated into the Page and the fieldDataQueue is emptied (and no longer relevant)** @var array**/protected $fieldDataQueue = array();/*** Field names that should wakeup and sanitize on first access (populated when isLoaded==false)** These are most likely field names designated as autoload for this page.** @var array of (field name => raw field value)**/protected $wakeupNameQueue = array();/*** Is this a new page (not yet existing in the database)?** @var bool**/protected $isNew = true;/*** Is this Page finished loading from the DB (i.e. Pages::getById)?** When false, it is assumed that any values set need to be woken up.* When false, it also assumes that built-in properties (like name) don't need to be sanitized.** Note: must be kept in the 'true' state. Pages::getById sets it to false before populating data and then back to true when done.** @var bool**/protected $isLoaded = true;/*** Lazy load state of page** - int: Page is pending lazy loading, and not yet populated.* - false: Page is not lazy loading.* - true: Page was lazy loading and has already loaded.** @var bool**/protected $lazyLoad = false;/*** Whether or not pages loaded by this one are allowed to be cached by PagesLoaderCache class** @var bool|string Bool for yes/no or string for yes w/group name where page cached/cleared with others having same group name.**/protected $loaderCache = true;/*** Is this page allowing it's output to be formatted?** If so, the page may not be saveable because calls to $page->get(field) are returning versions of* variables that may have been formatted at runtime for output. An exception will be thrown if you* attempt to set the value of a formatted field when $outputFormatting is on.** Output formatting should be turned off for pages that you are manipulating and saving.* Whereas it should be turned on for pages that are being used for output on a public site.* Having it on means that Textformatters and any other output formatters will be executed* on any values returned by this page. Likewise, any values you set to the page while outputFormatting* is set to true are considered potentially corrupt.** @var bool**/protected $outputFormatting = false;/*** A unique instance ID assigned to the page at the time it's loaded (for debugging purposes only)** @var int**/protected $instanceID = 0;/*** IDs for all the instances of pages, used for debugging and testing.** Indexed by $instanceID => $pageID** @var array**/static public $instanceIDs = array();/*** Stack of ID indexed Page objects that are currently in the loading process.** Used to avoid possible circular references when multiple pages referencing each other are being populated at the same time.** @var array**/static public $loadingStack = array();/*** Controls the behavior of Page::__isset function (no longer in use)** @var bool* @deprecated No longer in use**/static public $issetHas = false;/*** The current page number, starting from 1** @deprecated, use $input->pageNum instead.** @var int**/protected $pageNum = 1;/*** When true, exceptions won't be thrown when values are set before templates** @var bool**/protected $quietMode = false;/*** Cached User that created this page** @var User|null**/protected $_createdUser = null;/*** Cached User that last modified the page** @var User|null**/protected $_modifiedUser = null;/*** Page-specific settings which are either saved in pages table, or generated at runtime.** @var array**/protected $settings = array('id' => 0,'name' => '','status' => 1,'numChildren' => 0,'sort' => -1,'sortfield' => 'sort','modified_users_id' => 0,'created_users_id' => 0,'created' => 0,'modified' => 0,'published' => 0,);/*** Page meta data** @var null|WireDataDB**/protected $_meta = null;/*** Create a new page in memory.** @param Template $tpl Template object this page should use.**/public function __construct(Template $tpl = null) {if($tpl !== null) {$tpl->wire($this);$this->template = $tpl;}$this->useFuel(false); // prevent fuel from being in local scope$this->parentPrevious = null;$this->templatePrevious = null;$this->statusPrevious = null;}/*** Destruct this page instance**/public function __destruct() {if($this->instanceID) {// remove from the record of instanceID, so that we have record of page's that HAVEN'T been destructed.unset(self::$instanceIDs[$this->instanceID]);}}/*** Clone this page instance**/public function __clone() {$track = $this->trackChanges();$this->setTrackChanges(false);if($this->filesManager) {$this->filesManager = clone $this->filesManager;$this->filesManager->setPage($this);}$this->_meta = null;$template = $this->template();if($template) {foreach($template->fieldgroup as $field) {$name = $field->name;if(!$field->type) continue;if(!$field->type->isAutoload() && !isset($this->data[$name])) continue; // important for draft loading$value = $this->get($name);// no need to clone non-objects, as they've already been cloned// no need to clone Page objects as we still want to reference the original pageif(!is_object($value) || $value instanceof Page) continue;$value2 = clone $value;$this->set($name, $value2); // commit cloned value// if value is Pagefiles, then tell it the new pageif($value2 instanceof PageFieldValueInterface) $value2->setPage($this);}}$this->instanceID .= ".clone";if($track) $this->setTrackChanges(true);parent::__clone();}/*** Set the value of a page property** You can set properties to a page using either `$page->set('property', $value);` or `$page->property = $value;`.** ~~~~~* // Set the page title using set() method* $page->set('title', 'About Us');** // Set the page title directly (equivalent to the above)* $page->title = 'About Us';* ~~~~~** #pw-group-common* #pw-group-manipulation** @param string $key Name of property to set* @param mixed $value Value to set* @return Page|WireData Reference to this Page* @see __set* @throws WireException**/public function set($key, $value) {if(isset(PageProperties::$basePropertiesAlternates[$key])) $key = PageProperties::$basePropertiesAlternates[$key];if($this->isLoaded && ($key === 'id' || $key === 'name') && $this->settings[$key] && $value != $this->settings[$key]) {$sys = $this->settings['status'] & Page::statusSystem;$sysID = $this->settings['status'] & Page::statusSystemID;if(($key === 'id' && ($sys || $sysID)) || ($key === 'name' && $sys)) {throw new WireException("You may not modify '$key' on page #$this->id ($this->path) because it is a system page");}}switch($key) {/** @noinspection PhpMissingBreakStatementInspection */case 'id':if(!$this->isLoaded) Page::$loadingStack[(int) $value] = $this;// no break is intentionalcase 'sort':case 'numChildren':$value = (int) $value;if($this->isLoaded && $this->settings[$key] !== $value) $this->trackChange($key, $this->settings[$key], $value);$this->settings[$key] = $value;break;case 'status':$this->setStatus($value);break;case 'statusPrevious':$this->statusPrevious = is_null($value) ? null : (int) $value;break;case 'name':$this->setName($value);break;case 'parent':case 'parent_id':if(is_object($value) && $value instanceof Page) {// ok$this->setParent($value);} else if($value && !$this->_parent && (!$this->_parent_id || !$this->isLoaded) &&($key == 'parent_id' || is_int($value) || (is_string($value) && ctype_digit("$value")))) {// store only parent ID so that parent is lazy loaded,// but only if parent hasn't already been previously loaded$this->_parent_id = (int) $value;} else if($value && (is_string($value) || is_int($value))) {$value = $this->_pages('get', $value);$this->setParent($value);}break;case 'parentPrevious':if(is_null($value) || $value instanceof Page) $this->parentPrevious = $value;break;case 'template':case 'templates_id':if($key === 'templates_id' && $this->template && $this->template->id == $value) break;if($key === 'templates_id') $value = $this->wire()->templates->get((int) $value);$this->setTemplate($value);break;case 'created':case 'modified':case 'published':if($value === null) $value = 0;if($value && !ctype_digit("$value")) $value = $this->wire()->datetime->strtotime($value);$value = (int) $value;if($this->isLoaded && $this->settings[$key] !== $value) $this->trackChange($key, $this->settings[$key], $value);$this->settings[$key] = $value;break;case 'created_users_id':case 'modified_users_id':$value = (int) $value;if($this->isLoaded && $this->settings[$key] !== $value) $this->trackChange($key, $this->settings[$key], $value);$this->settings[$key] = $value;break;case 'createdUser':case 'modifiedUser':$this->setUser($value, str_replace('User', '', $key));break;case 'sortfield':$template = $this->template();if($template && $template->sortfield) break;$value = $this->wire()->pages->sortfields()->decode($value);if($this->isLoaded && $this->settings[$key] != $value) $this->trackChange($key, $this->settings[$key], $value);$this->settings[$key] = $value;break;case 'isLoaded':$this->setIsLoaded($value);break;case 'pageNum':// note: pageNum is deprecated, use $input->pageNum instead/** @noinspection PhpDeprecationInspection */$this->pageNum = ((int) $value) > 1 ? (int) $value : 1;break;case 'instanceID':$this->instanceID = $value;self::$instanceIDs[$value] = $this->settings['id'];break;case 'loaderCache':$this->loaderCache = is_bool($value) || ctype_digit("$value") ? (bool) $value : (string) $value;break;default:if(isset(PageProperties::$languageProperties[$key])) {list($property, $languageId) = PageProperties::$languageProperties[$key];if($property === 'name') {$this->setName($value, $languageId); // i.e. name1234} else if($property === 'status') {parent::set($key, (int) $value); // i.e. status1234}} else {if($this->quietMode && !$this->template) return parent::set($key, $value);$this->setFieldValue($key, $value, $this->isLoaded);}}return $this;}/*** Quietly set the value of a page property.** Set a value to a page without tracking changes and without exceptions.* Otherwise same as set().** #pw-advanced* #pw-group-manipulation** @param string $key* @param mixed $value* @return $this**/public function setQuietly($key, $value) {if(isset($this->settings[$key]) && is_int($value)) {// allow integer-only values in $this->settings to be set directly in quiet mode$this->settings[$key] = $value;} else {$this->quietMode = true;parent::setQuietly($key, $value);$this->quietMode = false;}return $this;}/*** Force setting a value, skipping over any checks or errors** Enables setting a value when page has no template assigned, for example.** #pw-internal** @param string $key Name of field/property to set* @param mixed $value Value to set* @return Page|WireData Returns reference to this page**/public function setForced($key, $value) {if(isset($this->settings[$key])) {$this->settings[$key] = $value;} else {parent::set($key, $value);}return $this;}/*** Set the value of a field that is defined in the page's Fieldgroup** This may not be called when outputFormatting is on.** This is for internal use. API should generally use the set() method, but this is kept public for the minority of instances where it's useful.** #pw-internal** @param string $key* @param mixed $value* @param bool $load Should the existing value be loaded for change comparisons? (applicable only to non-autoload fields)* @return Page|WireData Returns reference to this Page* @throws WireException**/public function setFieldValue($key, $value, $load = true) {if(!$this->template()) {throw new WireException("You must assign a template to the page before setting custom field values ($key)");}// if the page is not yet loaded and a '__' field was set, then we queue it so that the loaded() method can// instantiate all those fields knowing that all parts of them are present for wakeup.if(!$this->isLoaded && strpos($key, '__')) {list($key, $subKey) = explode('__', $key, 2);if(!isset($this->fieldDataQueue[$key])) $this->fieldDataQueue[$key] = array();$this->fieldDataQueue[$key][$subKey] = $value;return $this;}// check if the given key resolves to a Field or not$field = $this->getField($key);if(!$field) {// not a known/saveable field, let them use it for runtime storage$valPrevious = parent::get($key);if($valPrevious !== null && is_null(parent::get("-$key")) && $valPrevious !== $value) {// store previous value (if set) in a "-$key" versionparent::setQuietly("-$key", $valPrevious);}return parent::set($key, $value);}// if a null value is set, then ensure the proper blank type is set to the fieldif($value === null) {return parent::set($key, $field->type->getBlankValue($this, $field));}// if the page is currently loading from the database, we assume that any set values are 'raw' and need to be woken upif(!$this->isLoaded) {// queue for wakeup and sanitize on first field access$this->wakeupNameQueue[$key] = $key;// page is currently loading, so we don't need to continue any furtherreturn parent::set($key, $value);}// check if the field hasn't been already loadedif(is_null(parent::get($key))) {// this field is not currently loaded. if the $load param is true, then ...// retrieve old value first in case it's not autojoined so that change comparisons and save's workif($load) $this->get($key);} else if(isset($this->wakeupNameQueue[$key])) {// autoload value: we don't yet have a "woke" value suitable for change detection, so let it wakeupif($this->trackChanges() && $load) {// if changes are being tracked, load existing value for comparison$this->getFieldValue($key);} else {// if changes aren't being tracked, the existing value can be discardedunset($this->wakeupNameQueue[$key]);}} else {// check if the field is corrupted$isCorrupted = false;if(is_object($value) && $value instanceof PageFieldValueInterface) {// value indicates it is already formatted, so would corrupt the page for savingif($value->formatted()) $isCorrupted = true;} else if($this->outputFormatting) {// check if value is modified by being formatted$result = $field->type->_callHookMethod('formatValue', array($this, $field, $value));if($result != $value) $isCorrupted = true;}if($isCorrupted) {// The field has been loaded or dereferenced from the API, and this field changes when formatters are applied to it.// There is a good chance they are trying to set a formatted value, and we don't allow this situation because the// possibility of data corruption is high. We set the Page::statusCorrupted status so that Pages::save() can abort.$this->set('status', $this->status | self::statusCorrupted);$corruptedFields = $this->get('_statusCorruptedFields');if(!is_array($corruptedFields)) $corruptedFields = array();$corruptedFields[$field->name] = $field->name;$this->set('_statusCorruptedFields', $corruptedFields);}}// isLoaded so sanitizeValue can determine if it can perform a typecast rather than a full sanitization (when helpful)// we don't use setIsLoaded() so as to avoid triggering any other functions$isLoaded = $this->isLoaded;if(!$load) $this->isLoaded = false;// ensure that the value is in a safe format and set it$value = $field->type->sanitizeValue($this, $field, $value);// Silently restore isLoaded stateif(!$load) $this->isLoaded = $isLoaded;return parent::set($key, $value);}/*** Get the value of a Page property (see details for several options)** This method can accept a simple property name, and also much more:** - You can retrieve a value using either `$page->get('property');` or `$page->property`.* - Get the first populated property by specifying multiple properties separated by a pipe, i.e. `headline|title`.* - Get multiple properties in a string by specifying a string `{property}` tags, i.e. `{title}: {summary}`.* - Specify a selector string to get the first matching child page, i.e. `created>=today`.* - This method can also retrieve sub-properties of object properties, i.e. `parent.title`.** ~~~~~* // retrieve the title using get…* $title = $page->get('title');** // …or retrieve using direct access* $title = $page->title;** // retrieve headline if populated, otherwise title* $headline = $page->get('headline|title');** // retrieve title, created date, and summary, formatted in a string* $str = $page->get('{createdStr}: {title} - {summary}');** // example of getting a sub-property: title of parent page* $title = $page->get('parent.title');* ~~~~~** @param string $key Name of property, format string or selector, per the details above.* @return mixed Value of found property, or NULL if not found.* @see __get()**/public function get($key) {// if lazy load pending, load the page nowif($this->lazyLoad && $key !== 'id' && is_int($this->lazyLoad)) $this->_lazy(true);if(is_array($key)) $key = implode('|', $key);if(isset(PageProperties::$basePropertiesAlternates[$key])) {$key = PageProperties::$basePropertiesAlternates[$key];}if(isset(PageProperties::$baseProperties[$key])) {$type = PageProperties::$baseProperties[$key];if($type === 'p') {// local propertyreturn $this->$key;} else if($type === 'm') {// local methodreturn $this->{$key}();} else if($type === 'n') {// local method, possibly overridden by $fieldif(!$this->wire()->fields->get($key)) return $this->{$key}();} else if($type === 's') {// settings propertyreturn $this->settings[$key];} else if($type === 't') {// map to method in PageTraversal, if not overridden by fieldif(!$this->wire()->fields->get($key)) return $this->traversal()->{$key}($this);} else if($type) {// defined local methodreturn $this->{$type}();}}switch($key) {case 'parent':$value = $this->_parent ? $this->_parent : $this->parent();break;case 'parent_id':$value = $this->_parent ? $this->_parent->id : 0;if(!$value) $value = $this->_parent_id;break;case 'templates_id':$template = $this->template();$value = $template ? $template->id : 0;break;case 'fieldgroup':$template = $this->template();$value = $template ? $template->fieldgroup : null;break;case 'modifiedUser':case 'createdUser':$value = $this->getUser($key);if($value->id) $value->of($this->of());break;case 'urlSegment':// deprecated, but kept for backwards compatibility$value = $this->wire()->input->urlSegment1;break;case 'statusStr':$value = implode(' ', $this->status(true));break;case 'modifiedStr':case 'createdStr':case 'publishedStr':$value = $this->settings[str_replace('Str', '', $key)];$value = $value ? wireDate($this->wire()->config->dateFormat, $value) : '';break;case 'render':$value = $this->wire()->modules->get('PageRender'); /** @var PageRender $value */$value->setPropertyPage($this);break;case 'loaderCache':$value = $this->loaderCache;break;case '_meta':$value = $this->_meta; // null or WireDataDBbreak;default:if($key && isset($this->settings[(string)$key])) return $this->settings[$key];if($key === 'meta' && !$this->wire()->fields->get('meta')) return $this->meta(); // always WireDataDBif(!ctype_alnum("$key")) {// populate a formatted string with {tag} varsif(strpos($key, '{') !== false && strpos($key, '}')) return $this->getMarkup($key);// populate a markup requested field like '_fieldName_'$ulpos = strpos($key, '_');if($ulpos === 0 && substr($key, -1) === '_' && !$this->wire()->fields->get($key)) {if($this->wire()->sanitizer->fieldName($key) == $key) return $this->renderField(substr($key, 1, -1));}if(strpos($key, '|') !== false) {$value = $this->getFieldFirstValue($key);if($value !== null) return $value;}$value = $this->getFieldValue($key);if($value !== null) return $value;// if there is a selector, we'll assume they are using the get() method to get a childif(Selectors::stringHasOperator($key)) return $this->child($key);// check if it's a field.subfield propertyif(strpos($key, '.')) {$value = $this->getFieldSubfieldValue($key);if($value !== null) return $value;}if($ulpos !== false && strpos($key, '_OR_')) {// convert '_OR_' to '|'$value = $this->getFieldFirstValue(str_replace('_OR_', '|', $key));if($value !== null) return $value;}} else {$value = $this->getFieldValue($key);if($value !== null) return $value;}// optionally let a hook look at itif($this->wire()->hooks->isHooked('Page::getUnknown()')) {$value = $this->getUnknown($key);}}return $value;}/*** Get a Field object in context or NULL if not valid for this page** Field in context is only returned when output formatting is on.** #pw-advanced** @param string|int|Field $field* @return Field|null* @todo determine if we can always retrieve in context regardless of output formatting.**/public function getField($field) {$template = $this->template();$fieldgroup = $template ? $template->fieldgroup : null;if(!$fieldgroup) return null;if($this->outputFormatting && $fieldgroup->hasFieldContext($field)) {$value = $fieldgroup->getFieldContext($field);} else {$value = $fieldgroup->getField($field);}return $value;}/*** Returns a FieldsArray of all Field objects in the context of this Page** Unlike $page->fieldgroup (or its alias $page->fields), the fields returned from* this method are in the context of this page/template. Meaning returned Field* objects may have some properties that are different from the Field outside of* the context of this page.** #pw-advanced** @return FieldsArray of Field objects**/public function getFields() {$template = $this->template();if(!$template) return new FieldsArray();$fields = new FieldsArray();/** @var Fieldgroup $fieldgroup */$fieldgroup = $template->fieldgroup;foreach($fieldgroup as $field) {if($fieldgroup->hasFieldContext($field)) {$field = $fieldgroup->getFieldContext($field);}if($field) $fields->add($field);}return $fields;}/*** Returns whether or not given $field name, ID or object is valid for this Page** Note that this only indicates validity, not whether the field is populated.** #pw-advanced** @param int|string|Field|array $field Field name, object or ID to check.* - In 3.0.126+ this may also be an array or pipe "|" separated string of field names to check.* @return bool|string True if valid, false if not.* - In 3.0.126+ returns first matching field name if given an array of field names or pipe separated string of field names.**/public function hasField($field) {$template = $this->template();if(!$template) return false;if(is_string($field) && strpos($field, '|') !== false) {$field = explode('|', $field);}if(is_array($field)) {$result = false;foreach($field as $f) {$f = trim($f);if(!empty($f) && $this->hasField($f)) $result = $f;if($result) break;}} else {$result = $template->fieldgroup->hasField($field);}return $result;}/*** If given a field.subfield string, returns the associated value** This is like the getDot() method, but with additional protection during output formatting.** @param $key* @return mixed|null**/protected function getFieldSubfieldValue($key) {if(!strpos($key, '.')) return null;// we allow any field.subfield properties when output formatting is offif(!$this->outputFormatting()) return $this->getDot($key);// allow limited access to field.subfield properties when output formatting is on// we only allow known custom fields, and only 1 level of subfieldlist($key1, $key2) = explode('.', $key, 2);// test if any parts of key can potentially refer to API variables$api = false;foreach(explode('.', $key2) as $k) {if($this->wire($k)) $api = true;if($api) break;}if($api) return null; // do not allow dereference of API variablesif(isset(PageProperties::$traversalReturnTypes[$key1])) {if(PageProperties::$traversalReturnTypes[$key1] === PageProperties::typePage) {return $this->$key1()->get($key2);}}$field = $this->getField($key1);if(!$field || ($field->flags & Field::flagSystem)) return null;// get first part of value$value = $this->get($key1);if($value === null) return null;// then get second part of valueif($value instanceof WireData) {$value = $value->get($key2);} else if($value instanceof Wire) {$value = $this->getDot($key);}return $value;}/*** Hookable method called when a request to a field was made that didn't match anything** Hooks that want to inject something here should hook after and modify the $event->return.** #pw-hooker** @param string $key Name of property.* @return null|mixed Returns null if property not known, or a value if it is.**/public function ___getUnknown($key) {// $key unused is intentional, for access by hooksif($key) {}return null;}/*** Handles get() method requests for properties that include a period like "field.subfield"** Typically these resolve to objects, and the subfield is pulled from the object.* Currently we only allow this dot syntax when output formatting is off. This limitation may be removed* but we have to consider potential security implications before doing so.** #pw-internal** @param string $key Property name in field.subfield format* @return null|mixed Returns null if not found or invalid. Returns property value on success.**/public function getDot($key) {if(strpos($key, '.') === false) return $this->get($key);$of = $this->outputFormatting();if($of) $this->setOutputFormatting(false);$value = self::_getDot($key, $this);if($of) $this->setOutputFormatting(true);return $value;}/*** Given a Multi Key, determine if there are multiple keys requested and return the first non-empty value** A Multi Key is a string with multiple field names split by pipes, i.e. headline|title** Example: browser_title|headline|title - Return the value of the first field that is non-empty** @param string $multiKey* @param bool $getKey Specify true to get the first matching key (name) rather than value* @return null|mixed Returns null if no values match, or if there aren't multiple keys split by "|" chars**/protected function getFieldFirstValue($multiKey, $getKey = false) {// looking multiple keys split by "|" chars, and not an '=' selectorif(strpos($multiKey, '|') === false || strpos($multiKey, '=') !== false) return null;$value = null;$keys = explode('|', $multiKey);foreach($keys as $key) {$v = $this->getUnformatted($key);if(is_array($v) || $v instanceof WireArray) {// array or WireArrayif(!count($v)) continue;} else if(is_object($v)) {// like LanguagesPageFieldValue$str = trim((string) $v);if(!strlen($str)) continue;} else if(is_string($v)) {$v = trim($v);}if($v) {if($this->outputFormatting) {$v = $this->get($key);}if($v) {$value = $getKey ? $key : $v;break;}}}return $value;}/*** Get the value for a non-native page field, and call upon Fieldtype to join it if not autojoined** @param string $key Name of field to get* @param string $selector Optional selector to filter load by...* ...or, if not in selector format, it becomes an __invoke() argument for object values .* @return null|mixed**/protected function getFieldValue($key, $selector = '') {$template = $this->template();if(!$template) return parent::get($key);$field = $this->getField($key);$value = parent::get($key);if(!$field) return $value; // likely a runtime field, not part of our data/** @var Fieldtype $fieldtype */$fieldtype = $field->type;$invokeArgument = '';if($value !== null && isset($this->wakeupNameQueue[$key])) {$value = $fieldtype->_callHookMethod('wakeupValue', array($this, $field, $value));$value = $fieldtype->sanitizeValue($this, $field, $value);$trackChanges = $this->trackChanges(true);$this->setTrackChanges(false);parent::set($key, $value);$this->setTrackChanges($trackChanges);unset($this->wakeupNameQueue[$key]);}if($field->useRoles && $this->outputFormatting) {// API access may be limited when output formatting is ONif($field->flags & Field::flagAccessAPI) {// API access always allowed because of flag} else if($this->viewable($field)) {// User has view permission for this field} else {// API access is denied when output formatting is ON// so just return a blank value as defined by the Fieldtype// note: we do not store this blank value in the Page, so that// the real value can potentially be loaded later without output formatting$value = $fieldtype->getBlankValue($this, $field);return $this->formatFieldValue($field, $value);}}if($value !== null && empty($selector)) {// if the non-filtered value is already loaded, return itreturn $this->formatFieldValue($field, $value);}$track = $this->trackChanges();$this->setTrackChanges(false);if(!$fieldtype) return null;if($selector && !Selectors::stringHasSelector($selector)) {// if selector argument provided, but isn't valid, we assume it// to instead be an argument for the value's __invoke() method$invokeArgument = $selector;$selector = '';}if($selector) {$value = $fieldtype->loadPageFieldFilter($this, $field, $selector);} else {// $value = $field->type->loadPageField($this, $field);$value = $fieldtype->_callHookMethod('loadPageField', array($this, $field));}if($value === null) {$value = $fieldtype->getDefaultValue($this, $field);} else {$value = $fieldtype->_callHookMethod('wakeupValue', array($this, $field, $value));//$value = $field->type->wakeupValue($this, $field, $value);}// turn off output formatting and set the field value, which may apply additional changes$outputFormatting = $this->outputFormatting;if($outputFormatting) $this->setOutputFormatting(false);$this->setFieldValue($key, $value, false);if($outputFormatting) $this->setOutputFormatting(true);$value = parent::get($key);// prevent storage of value if it was filtered when loadedif(!empty($selector)) $this->__unset($key);if(is_object($value) && $value instanceof Wire && !$value instanceof Page) $value->resetTrackChanges(true);if($track) $this->setTrackChanges(true);$value = $this->formatFieldValue($field, $value);if($invokeArgument && is_object($value) && method_exists($value, '__invoke')) {$value = $value->__invoke($invokeArgument);}return $value;}/*** Return a value consistent with the page’s output formatting state** This is primarily for use as a helper to the getFieldValue() method.** @param Field $field* @param mixed $value* @return mixed**/protected function formatFieldValue(Field $field, $value) {$hasInterface = is_object($value) && $value instanceof PageFieldValueInterface;if($hasInterface) {$value->setPage($this);$value->setField($field);}if($this->outputFormatting) {// output formatting is enabled so return a formatted value//$value = $field->type->formatValue($this, $field, $value);$value = $field->type->_callHookMethod('formatValue', array($this, $field, $value));// check again for interface since value may now be differentif($hasInterface) $hasInterface = is_object($value) && $value instanceof PageFieldValueInterface;if($hasInterface) $value->formatted(true);} else if($hasInterface && $value->formatted()) {// unformatted requested, and value is already formatted so load a fresh copy$this->__unset($field->name);$value = $this->getFieldValue($field->name);}return $value;}/*** If value is available for $key return or call $yes condition (with optional $no condition)** This merges the capabilities of an if() statement, get() and getMarkup() methods in one,* plus some useful PW type-specific logic, providing a useful output shortcut. It many situations* it enables you to accomplish on one-line of code what might have otherwise taken multiple lines* of code. Use this when looking for a useful shortcut and this one fits your need, otherwise* use a regular PHP if() statement.** This function is primarily intended for conditionally outputting some formatted string value or* markup, however its use is not limited to that, as you can specify whatever you’d like for the* $yes and $no conditions. The examples section best describes potential usages of this method,* so I recommend looking at those before reading all the details of this method.** Note that the logic is a little bit smarter for PW than a regular PHP if() statement in these ways:** - If value resolves to any kind of *empty* `WireArray` (like a `PageArray`) the NO condition is used.* If the WireArray is populated with at least one item then the YES condition is used. So this if()* method (unlike PHP if) requires that not only is the value present, but it is also populated.** - If value resolves to a `NullPage` the NO condition is used.** The `$key` argument may be any of the following:** - A field name, in which case we will use the value of that field on this page. If the value is* empty the NO condition will be used, otherwise the YES condition will be used. You can use any* format for the field name that the `Page::get()` method accepts, so subfields and OR field* statements are also okay, i.e. `categories.count`, `field1|field2|field3', etc.** - A selector string that must match this page in order to return the YES condition. If it does not* match then the NO condition will be used.** - A boolean, integer, digit string or PHP array. If considered empty by PHP it will return the NO* condition, otherwise it will return the YES condition.** The `$yes` and `$no` arguments (the conditional actions) may be any of the following:** - Any string value that you’d like (HTML markup is fine too).** - A field name that is present on this page, or optionally the word “value” to refer to the field* specified in the `$key` argument. Either way, makes this method return the actual field value as it* exists on the page, rather than a string/markup version of it. Note that if this word (“value”) is* used for the argument then of course the `$key` argument must be a field name (not a selector string).** - Any callable inline function that returns the value you want this function to return.** - A string containing one or more `{field}` placeholders, where you replace “field” with a field name.* These are in turn populated by the `Page::getMarkup()` method. You can also use `{field.subfield}`* and `{field1|field2|field3}` type placeholder strings.** - A string containing `{val}` or `{value}` where they will be replaced with the markup value of the* field name given in the $key argument.** - If you omit the `$no` argument an empty string is assumed.** - If you omit both the `$yes` and `$no` arguments, then boolean is assumed (true for yes, false for no),* which makes this method likewise return a boolean. The only real reason to do this would be to take* advantage of the method’s slightly different behavior than regular PHP if() statements (i.e. treating* empty WireArray or NullPage objects as false conditions).** ~~~~~* // if summary is populated, output it in an paragraph* echo $page->if("summary", "<p class='summary'>{summary}</p>");** // same as above, but shows you can specify {value} to assume field in $key arg* echo $page->if("summary", "<p class='summary'>{value}</p>");** // if price is populated, format for output, otherwise ask them to call for price* echo $page->if("price", function($val) { return '$' . number_format($val); }, "Please call");** // you can also use selector strings* echo $page->if("inventory>10", "In stock", "Limited availability");** // output an <img> tag for the first image on the page, or blank if none* echo $page->if("images", function($val) { return "<img src='{$val->first->url}'>"; });* ~~~~~** @param string|bool|int $key Name of field to check, selector string to evaluate, or boolean/int to evalute* @param string|callable|mixed $yes If value for $key is present, return or call this* @param string|callable|mixed $no If value for $key is empty, return or call this* @return mixed|string|bool* @since 3.0.126**/public function ___if($key, $yes = '', $no = '') {return $this->comparison()->_if($this, $key, $yes, $no);}/*** Return the markup value for a given field name or {tag} string** 1. If given a field name (or `name.subname` or `name1|name2|name3`) it will return the* markup value as defined by the fieldtype.* 2. If given a string with field names referenced in `{tags}`, it will populate those* tags and return the populated string.** #pw-advanced** @param string $key Field name or markup string with field {name} tags in it* @return string* @see Page::getText()**/public function ___getMarkup($key) {if(strpos($key, '{') !== false && strpos($key, '}')) {// populate a string with {tags}// note that the wirePopulateStringTags() function calls back on this method// to retrieve the markup values for each of the found field namesreturn wirePopulateStringTags($key, $this);}if(strpos($key, '|') !== false) {$key = $this->getFieldFirstValue($key, true);if(!$key) return '';}if($this->wire()->sanitizer->name($key) != $key) {// not a possible field namereturn '';}$parts = strpos($key, '.') ? explode('.', $key) : array($key);$value = $this;do {$name = array_shift($parts);$field = $this->getField($name);if(!$field && $this->wire($name)) {// disallow API vars$value = '';break;}if($value instanceof Page) {$value = $value->getFormatted($name);} else if($value instanceof WireData) {$value = $value->get($name);} else {$value = $value->$name;}if($field && count($parts) < 2) {// this is a field that will provide its own formatted value$subname = count($parts) == 1 ? array_shift($parts) : '';if(!$subname || !$this->wire($subname)) {$value = $field->type->markupValue($this, $field, $value, $subname);}}} while(is_object($value) && count($parts));if(is_object($value)) {if($value instanceof Page) $value = $value->getFormatted('title|name');if($value instanceof PageArray) $value = $value->getMarkup();}if(!is_string($value)) $value = (string) $value;return $value;}/*** Same as getMarkup() except returned value is plain text** If no `$entities` argument is provided, returned value is entity encoded when output formatting* is on, and not entity encoded when output formatting is off.** #pw-advanced** @param string $key Field name or string with field {name} tags in it.* @param bool $oneLine Specify true if returned value must be on single line.* @param bool|null $entities True to entity encode, false to not. Null for auto, which follows page's outputFormatting state.* @return string* @see Page::getMarkup()**/public function getText($key, $oneLine = false, $entities = null) {$value = $this->getMarkup($key);$length = strlen($value);if(!$length) return '';$options = array('entities' => ($entities === null ? $this->outputFormatting() : (bool) $entities));if($oneLine) {$value = $this->wire()->sanitizer->markupToLine($value, $options);} else {$value = $this->wire()->sanitizer->markupToText($value, $options);}// if stripping tags from non-empty value made it empty, just indicate that it was markup and lengthif(!strlen(trim($value))) $value = "markup($length)";return $value;}/*** Set the unformatted value of a field, regardless of current output formatting state** Use this when setting an unformatted value to a page that has (or might have) output formatting enabled.* This will save you the steps of checking the output formatting state, turning it off, setting the value,* and turning it back on again (if it was on). Note that the output formatting distinction matters for some* field types and not others, just depending on the case—this method is safe to use either way.** Make sure you do not use this to set an already formatted value to a Page (like some text that has been* entity encoded). This method skips over some of the checks that might otherwise flag the page as corrupted.** ~~~~~* // good usage* $page->setUnformatted('title', 'This & That');** // bad usage* $page->setUnformatted('title', 'This & That');* ~~~~~** #pw-advanced** @param string $key* @param mixed $value* @return self* @since 3.0.169* @throws WireException if given an object value that indicates it is already formatted.* @see Page::getUnformatted(), Page::of(), Page::setOutputFormatting(), Page::outputFormatting()**/public function setUnformatted($key, $value) {if(is_object($value) && $value instanceof PageFieldValueInterface && $value->formatted()) {throw new WireException("Cannot use formatted-value with Page::setUnformatted($key, formatted-value);");}$outputFormatting = $this->outputFormatting;if($outputFormatting) $this->setOutputFormatting(false);$this->set($key, $value);if($outputFormatting) $this->setOutputFormatting(true);return $this;}/*** Get the unformatted value of a field, regardless of current output formatting state** When a page’s output formatting state is off, `$page->get('property')` or `$page->property` will* produce the same result as this method call.** ~~~~~* // Get the 'body' field without any text formatters applied* $body = $page->getUnformatted('body');* ~~~~~** #pw-advanced** @param string $key Field or property name to retrieve* @return mixed* @see Page::getFormatted(), Page::of(), Page::setOutputFormatting(), Page::outputFormatting()**/public function getUnformatted($key) {$outputFormatting = $this->outputFormatting;if($outputFormatting) $this->setOutputFormatting(false);$value = $this->get($key);if($outputFormatting) $this->setOutputFormatting(true);return $value;}/*** Get the formatted value of a field, regardless of output formatting state** When a page's output formatting state is on, `$page->get('property')` or `$page->property` will* produce the same result as this method call.** ~~~~~* // Get the formatted 'body' field (text formatters applied)* $body = $page->getFormatted('body');* ~~~~~** #pw-advanced** @param string $key Field or property name to retrieve* @return mixed* @see Page::getUnformatted(), Page::of()**/public function getFormatted($key) {$outputFormatting = $this->outputFormatting;if(!$outputFormatting) $this->setOutputFormatting(true);$value = $this->get($key);if(!$outputFormatting) $this->setOutputFormatting(false);return $value;}/*** Direct access get method** @param string $key* @return mixed* @see get()**/public function __get($key) {return $this->get($key);}/*** Direct access set method** @param string $key* @param mixed $value* @see set()**/public function __set($key, $value) {$this->set($key, $value);}/*** If method call resulted in no handler, this hookable method is called.** If you want to override this method with a hook, see the example below.* ~~~~~* $wire->addHookBefore('Wire::callUnknown', function(HookEvent $event) {* // Get information about unknown method that was called* $methodObject = $event->object;* $methodName = $event->arguments(0); // string* $methodArgs = $event->arguments(1); // array* // The replace option replaces the method and blocks the exception* $event->replace = true;* // Now do something with the information you have, for example* // you might want to populate a value to $event->return if* // you want the unknown method to return a value.* });* ~~~~~** #pw-hooker** @param string $method Requested method name* @param array $arguments Arguments provided* @return null|mixed Return value of method (if applicable)* @throws WireException* @see Wire::callUnknown()**/protected function ___callUnknown($method, $arguments) {if($this->hasField($method)) {if(count($arguments)) {return $this->getFieldValue($method, $arguments[0]);} else {return $this->get($method);}} else if(isset(PageProperties::$baseMethodAlternates[$method])) {return call_user_func_array(array($this, PageProperties::$baseMethodAlternates[$method]), $arguments);} else {return parent::___callUnknown($method, $arguments);}}/*** Set the status setting, with some built-in protections** This method is also used when you set status directly, i.e. `$page->status = $value;`.** ~~~~~* // set status to unpublished* $page->setStatus('unpublished');** // set status to hidden and unpublished* $page->setStatus('hidden, unpublished');** // set status to hidden + unpublished using Page constant bitmask* $page->setStatus(Page::statusHidden | Page::statusUnpublished);* ~~~~~** #pw-advanced* #pw-group-manipulation** @param int|array|string Status value, array of status names or values, or status name string.* @return self* @see Page::addStatus(), Page::removeStatus()**/protected function setStatus($value) {if(!is_int($value)) {// status provided as something other than integerif(is_string($value) && !ctype_digit($value)) {// string of one or more status namesif(strpos($value, ',') !== false) $value = str_replace(array(', ', ','), ' ', $value);$value = explode(' ', strtolower($value));}if(is_array($value)) {// array of status names or numbers$status = 0;foreach($value as $v) {if(is_int($v) || ctype_digit("$v")) { // integer$status = $status | ((int) $v);} else if(is_string($v) && isset(PageProperties::$statuses[$v])) { // string (status name)$status = $status | PageProperties::$statuses[$v];}}if($status) $value = $status;}// note if $value started as an integer string, i.e. "123", it gets passed through to below}$value = (int) $value;$override = $this->settings['status'] & Page::statusSystemOverride;if(!$override) {if($this->settings['status'] & Page::statusSystemID) $value = $value | Page::statusSystemID;if($this->settings['status'] & Page::statusSystem) $value = $value | Page::statusSystem;}if($this->settings['status'] != $value && $this->isLoaded) {$this->trackChange('status', $this->settings['status'], $value);if($this->statusPrevious === null) {$this->statusPrevious = $this->settings['status'];}}$this->settings['status'] = $value;if($value & Page::statusDeleted) {// disable any instantiated filesManagers after page has been marked deleted// example: uncache method polls filesManager$this->filesManager = null;}return $this;}/*** Set the page name, optionally for specific language** ~~~~~* // Set page name (default language)* $page->setName('my-page-name');** // This is equivalent to the above* $page->name = 'my-page-name';** // Set page name for Spanish language* $page->setName('la-cerveza', 'es');* ~~~~~** #pw-group-manipulation** @param string $value Page name that you want to set* @param Language|string|int|null $language Set language for name (can also be language name or string in format "name1234")* @return $this**/public function setName($value, $language = null) {$key = 'name';$charset = $this->wire()->config->pageNameCharset;$sanitizer = $this->wire()->sanitizer;if($this->isLoaded) {if(is_int($language)) {$key .= $language;$existingValue = $this->get($key);} else if($language && $language !== 'name') {// update $key to contain language ID when applicable$languages = $this->wire()->languages;if($languages) {if(!is_object($language)) {if(strpos($language, 'name') === 0) $language = (int) substr($language, 4);$language = $languages->getLanguage($language);if(!$language || !$language->id || $language->isDefault()) $language = '';}if(!$language) return $this;$key .= $language->id;}$existingValue = $this->get($key);} else {$existingValue = isset($this->settings[$key]) ? $this->settings[$key] : '';}// name is being set after page has already been loadedif($charset === 'UTF8') {// UTF8 page names allowed but decoding not allowed$value = $sanitizer->pageNameUTF8($value);} else if(empty($existingValue)) {// ascii, and beautify if there is no existing value$value = $sanitizer->pageName($value, true);} else {// ascii page name and do not beautify$value = $sanitizer->pageName($value, false);}if($existingValue !== $value && !$this->quietMode) {// set the namePrevious property when the main 'name' has changedif($key === 'name' && $existingValue && empty($this->namePrevious)) {$this->namePrevious = $existingValue;}// track the change$this->trackChange($key, $existingValue, $value);}} else {// name being set while page is loadingif($charset === 'UTF8' && strpos($value, 'xn-') === 0) {// allow decode of UTF8 name while page is loading$value = $sanitizer->pageName($value, Sanitizer::toUTF8);} else {// regular ascii page name while page is loading, do nothing to it}if($language) {if(ctype_digit("$language")) {$key = "name$language";} else if(is_string($language)) {$key = $language; // i.e. name1234}}}if($key === 'name') {$this->settings[$key] = $value;} else if($this->quietMode || !$this->isLoaded) {parent::set($key, $value);} else {$this->setFieldValue($key, $value, $this->isLoaded); // i.e. name1234}return $this;}/*** Set this Page's Template** ~~~~~* // The following 3 lines are equivalent* $page->setTemplate('basic-page');* $page->template = 'basic-page';* $page->templates = $templates->get('basic-page');* ~~~~~** #pw-internal** @param Template|int|string $tpl May be Template object, name or ID.* @return $this* @throws WireException if given invalid arguments or template not allowed for page**/protected function setTemplate($tpl) {if(!is_object($tpl)) $tpl = $this->wire()->templates->get($tpl);if(!$tpl instanceof Template) throw new WireException("Invalid value sent to Page::setTemplate");if($this->template && $this->template->id != $tpl->id && $this->isLoaded) {if($this->settings['status'] & Page::statusSystem) {throw new WireException("Template changes are disallowed on page $this->id because it has system status");}if(is_null($this->templatePrevious)) $this->templatePrevious = $this->template;$this->trackChange('template', $this->template, $tpl);}if($tpl->sortfield) $this->settings['sortfield'] = $tpl->sortfield;$this->template = $tpl;return $this;}/*** Get or set template** @param null|Template|string|int $template* @return Template|null* @since 3.0.181**/public function template($template = null) {if($template !== null) $this->setTemplate($template);return $this->template;}/*** Set this page's parent Page** #pw-internal** @param Page $parent* @return $this* @throws WireException if given impossible $parent or parent changes aren't allowed**/public function setParent(Page $parent) {if($this->_parent && $this->_parent->id == $parent->id) return $this;if($parent->id && $this->id == $parent->id || $parent->parents->has($this)) {throw new WireException("Page cannot be its own parent");}if($this->isLoaded && $this->id) {if(!$this->_parent) $this->parent(); // force it to load$this->trackChange('parent', $this->_parent, $parent);if(($this->_parent && $this->_parent->id) && $this->_parent->id != $parent->id) {if($this->settings['status'] & Page::statusSystem) {throw new WireException("Parent changes are disallowed on page $this->id because it has system status");}if(is_null($this->parentPrevious)) $this->parentPrevious = $this->_parent;}}$this->_parent = $parent;$this->_parent_id = $parent->id;return $this;}/*** Set either the createdUser or the modifiedUser** @param User|int|string $user User object or integer/string representation of User* @param string $userType Must be either 'created' or 'modified'* @return $this* @throws WireException**/protected function setUser($user, $userType) {if(!$user instanceof User) {if(is_object($user)) {$user = null;} else {$user = $this->wire()->users->get($user);}}// if they are setting an invalid user or unknown user, then the Page defaults to the super userif(!$user || !$user->id || !$user instanceof User) {$user = $this->wire()->users->get($this->wire()->config->superUserPageID);}if(strpos($userType, 'created') === 0) {$field = 'created_users_id';$this->_createdUser = $user;} else if(strpos($userType, 'modified') === 0) {$field = 'modified_users_id';$this->_modifiedUser = $user;} else {throw new WireException("Unknown user type in Page::setUser(user, type)");}$existingUserID = $this->settings[$field];if($existingUserID != $user->id) $this->trackChange($field, $existingUserID, $user->id);$this->settings[$field] = $user->id;return $this;}/*** Get page’s created or modified user** @param string $userType One of 'created' or 'modified'* @return User|NullPage**/protected function getUser($userType) {if($userType === 'created' || strpos($userType, 'created') === 0) {$userType = 'created';} else if($userType === 'modified' || strpos($userType, 'modified') === 0) {$userType = 'modified';} else {return $this->wire(new NullPage());}$property = '_' . $userType . 'User';$user = $this->$property;// if we already have the user, return it nowif($user) return $user;$key = $userType . '_users_id';$uid = (int) $this->settings[$key];if(!$uid) return $this->wire(new NullPage());$u = $this->wire()->user;if($u && $uid === $u->id) {// ok use current user $user$user = $u;} else {// get user$users = $this->wire()->users;if($users) $user = $users->get($uid);}if(!$user) $user = $this->wire(new NullPage());$this->$property = $user; // cache to _createdUser or _modifiedUserreturn $user;}/*** Find descendant pages matching given selector** This is the same as `Pages::find()` except that the results are limited to descendents of this Page.** ~~~~~* // Find all unpublished pages underneath the current page* $items = $page->find("status=unpublished");* ~~~~~** #pw-group-common* #pw-group-traversal** @param string|array $selector Selector string or array* @param array $options Same as the $options array passed to $pages->find().* @return PageArray* @see Pages::find()**/public function find($selector = '', $options = array()) {if(!$this->numChildren) {return $this->wire()->pages->newPageArray();}if(is_string($selector)) {$selector = trim("has_parent={$this->id}, $selector", ", ");} else if(is_array($selector)) {$selector["has_parent"] = $this->id;}return $this->_pages('find', $selector, $options);}/*** Find one descendant page matching given selector** This is the same as `Pages::findOne()` except that the match is always a descendant of page it is called on.** ~~~~~* // Find the most recently modified descendant page* $item = $page->findOne("sort=-modified");* ~~~~~** #pw-group-common* #pw-group-traversal** @param string|array $selector Selector string or array* @param array $options Optional options to modify default bheavior, see options for `Pages::find()`.* @return Page|NullPage Returns Page when found, or NullPage when nothing found.* @see Pages::findOne(), Page::child()* @since 3.0.116**/public function findOne($selector = '', $options = array()) {if(!$this->numChildren) {return $this->wire()->pages->newNullPage();}if(is_string($selector)) {$selector = trim("has_parent={$this->id}, $selector", ", ");} else if(is_array($selector)) {$selector["has_parent"] = $this->id;}return $this->_pages('findOne', $selector, $options);}/*** Return this page’s children, optionally filtered by a selector** By default, hidden, unpublished and no-access pages are excluded unless `include=x` (where "x" is desired status) is specified.* If a selector isn't needed, children can also be accessed directly by property with `$page->children`.** ~~~~~* // Render navigation for all child pages below this one* foreach($page->children() as $child) {* echo "<li><a href='$child->url'>$child->title</a></li>";* }* ~~~~~* ~~~~~* // Retrieve just the 3 newest children* $newest = $page->children("limit=3, sort=-created");* ~~~~~** #pw-group-common* #pw-group-traversal** @param string $selector Selector to use, or omit to return all children.* @param array $options Optional options to modify behavior, the same as those provided to Pages::find.* @return PageArray|array Returns PageArray for most cases. Returns regular PHP array if using the findIDs option.* @see Page::child(), Page::find(), Page::numChildren(), Page::hasChildren()**/public function children($selector = '', $options = array()) {return $this->traversal()->children($this, $selector, $options);}/*** Return number of all children, optionally with conditions** Use this over the `$page->numChildren` property when you want to specify a selector, or when you want the result to* include only visible children. See the options for the $selector argument.** When you want to retrieve all children with no exclusions or conditions, use the `$page->numChildren` property instead.** ~~~~~* // Find how many children were modified in the last week* $qty = $page->numChildren("modified>='-1 WEEK'");* ~~~~~** #pw-group-common* #pw-group-traversal** @param bool|string|array $selector* - When not specified, result includes all children without conditions, same as $page->numChildren property.* - When a string or array, a selector is assumed and quantity will be counted based on selector.* - When boolean true, number includes only visible children (excludes unpublished, hidden, no-access, etc.)* - When boolean false, number includes all children without conditions, including unpublished, hidden, no-access, etc.* @return int Number of children* @see Page::hasChildren(), Page::children(), Page::child()**/public function numChildren($selector = null) {if(!$this->settings['numChildren'] && $selector === null) return 0;return $this->traversal()->numChildren($this, $selector);}/*** Return the number of visible children, optionally with conditions** This method is similar to `$page->numChildren()` except that the default behavior is to exclude non-visible children.** This method may be more convenient for front-end navigation use than the `$page->numChildren()` method because* it only includes the count of visible children. By visible, we mean children that are not hidden, unpublished,* or non-accessible due to access control.** ~~~~~* // Determine if we should show navigation to children* if($page->hasChildren()) {* // Yes, we should show navigation to children* }* ~~~~~** #pw-group-common* #pw-group-traversal** @param bool|string|array $selector* - When not specified, result is quantity of visible children (excludes unpublished, hidden, no-access, etc.)* - When a string or array, a selector is assumed and quantity will be counted based on selector.* - When boolean true, number includes only visible children (this is the default behavior, so no need to specify this value).* - When boolean false, number includes all children without conditions, including unpublished, hidden, no-access, etc.* @return int Number of children**/public function hasChildren($selector = true) {return $this->numChildren($selector);}/*** Return the page’s first single child that matches the given selector.** Same as `$page->children()` but returns a single Page object or NullPage (with id=0) rather than a PageArray.* Meaning, this method will only ever return one Page.** ~~~~~* // Get the newest created child page* $newestChild = $page->child("sort=-created");* ~~~~~** #pw-group-common* #pw-group-traversal** @param string|array|int $selector Selector to use, or blank to return the first child.* @param array $options Optional options per Pages::find* @return Page|NullPage* @see Page::children()**/public function child($selector = '', $options = array()) {return $this->traversal()->child($this, $selector, $options);}/*** Return this page’s parent Page, or–if given a selector–the closest matching parent.** Omit all arguments if you just want to retrieve the parent of this page, which would be the same as the* `$page->parent` property. To retrieve the closest parent matching your selector, specify either a selector* string or array.** ~~~~~* // Retrieve the parent* $parent = $page->parent();** // Retrieve the closest parent using template "products"* $parent = $page->parent("template=products");* ~~~~~** #pw-group-common* #pw-group-traversal** @param string|array $selector Optional selector. When used, it returns the closest parent matching the selector.* @return Page Returns a Page or a NullPage when there is no parent or the selector string did not match any parents.**/public function parent($selector = '') {if(!$this->_parent) {if($this->_parent_id) {$this->_parent = $this->_pages('get', (int) $this->_parent_id);} else {return $this->wire()->pages->newNullPage();}}if(empty($selector)) return $this->_parent;if($this->_parent->matches($selector)) return $this->_parent;if($this->_parent->parent_id) return $this->_parent->parent($selector); // recursive, in a wayreturn $this->wire()->pages->newNullPage();}/*** Return this page’s parent pages, or the parent pages matching the given selector.** This method returns all parents of this page, in order. If a selector is specified, they* will be filtered by the selector. By default, parents are returned in breadcrumb order.* In 3.0.158+ if you specify boolean true for selector argument, then it will return parents* in reverse order (closest to furthest).** ~~~~~* // Render breadcrumbs* foreach($page->parents() as $parent) {* echo "<li><a href='$parent->url'>$parent->title</a></li>";* }* ~~~~~* ~~~~~* // Return all parents, excluding the homepage* $parents = $page->parents("template!=home");* ~~~~~* ~~~~~* // Return parents in reverse order (closest to furthest, 3.0.158+)* $parents = $page->parents(true);* ~~~~~** #pw-group-common* #pw-group-traversal** @param string|array|bool $selector Optional selector string to filter parents by or boolean true for reverse order* @return PageArray All parent pages, or those matching the given selector.**/public function parents($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** This duplicates the jQuery parentsUntil() function in ProcessWire.** #pw-group-traversal** @param string|Page|array $selector May either be a selector sor Page to stop at. Results will not include this.* @param string|array $filter Optional selector to filter matched pages by* @return PageArray**/public function parentsUntil($selector = '', $filter = '') {return $this->traversal()->parentsUntil($this, $selector, $filter);}/*** Find the closest parent page matching your selector** This is like `$page->parent()` but includes the current Page in the possible pages that can be matched,* and the $selector argument is required.** #pw-group-traversal** @param string|array $selector Selector string to match.* @return Page|NullPage $selector Returns the current Page or closest parent matching the selector. Returns NullPage when no match.**/public function closest($selector) {if(empty($selector) || $this->matches($selector)) return $this;return $this->parent($selector);}/*** Get the lowest-level, non-homepage parent of this page** The rootParents typically comprise the first level of navigation on a site, and in many cases are considered* the "section" pages of the site.** ~~~~~* // Determine if we are in the "products" section of the site* if($page->rootParent()->template == 'products') {* // we are in the products section* } else {* // we are in some other section of the site* }* ~~~~~** #pw-group-common* #pw-group-traversal** @return Page**/public function ___rootParent() {return $this->traversal()->rootParent($this);}/*** Return this Page’s sibling pages, optionally filtered by a selector.** To exclude the current page in list of siblings, specify boolean false for first or second argument.** ~~~~~* // Get all sibling pages* $siblings = $page->siblings();** // Get all sibling pages, and exclude current page from the returned value* $siblings = $page->siblings(false);** // Get all siblings having the "product-featured" template, sorted by name* $featured = $page->siblings("template=product-featured, sort=name");** // Same as above, while excluding current page* $featured = $page->siblings("template=product-featured, sort=name", false);* ~~~~~** #pw-group-traversal** @param string|array|bool $selector Optional selector to filter siblings by, or omit for all siblings.* @param bool $includeCurrent Specify false to exclude current page in the returned siblings (default=true).* If no $selector argument is given, this argument may optionally be specified as the first argument.* @return PageArray**/public function siblings($selector = '', $includeCurrent = true) {if(is_bool($selector)) {$includeCurrent = $selector;$selector = '';}if(!$includeCurrent) {if(is_array($selector)) {$selector[] = array('id', '!=', $this->id);} else {if(strlen($selector)) $selector .= ", ";$selector .= "id!=$this->id";}}return $this->traversal()->siblings($this, $selector);}/*** Return number of descendants (children, grandchildren, great-grandchildren, …), optionally with conditions** Use this over the `$page->numDescendants` property when you want to specify a selector or apply* some other filter to the result (see options for `$selector` argument). If you want to include only* visible descendants specify a selector (string or array) or boolean true for the `$selector` argument,* if you don’t need a selector.** If you want to find descendant pages (rather than count), use the `Page::find()` method.** ~~~~~* // Find how many descendants were modified in the last week* $qty = $page->numDescendants("modified>='-1 WEEK'");* ~~~~~** #pw-group-traversal** @param bool|string|array $selector* - When not specified, result includes all descendants without conditions, same as $page->numDescendants property.* - When a string or array, a selector is assumed and quantity will be counted based on selector.* - When boolean true, number includes only visible descendants (excludes unpublished, hidden, no-access, etc.)* @return int Number of descendants* @see Page::numChildren(), Page::find()**/public function numDescendants($selector = null) {return $this->traversal()->numDescendants($this, $selector);}/*** Return the next sibling page** By default, hidden, unpublished and non-viewable pages are excluded. If you want them included,* be sure to specify `include=` with hidden, unpublished or all, in your selector.** ~~~~~* // Get the next sibling* $sibling = $page->next();** // Get the next newest sibling* $sibling = $page->next("created>$page->created");** // Get the next sibling, even if it isn't viewable* $sibling = $page->next("include=all");* ~~~~~** #pw-group-traversal** @param string|array $selector Optional selector. When specified, will find nearest next sibling that matches.* @param PageArray $siblings Optional siblings to use instead of the default. Avoid using this argument* as it forces this method to use the older/slower functions.* @return Page|NullPage Returns the next sibling page, or a NullPage if none found.**/public function next($selector = '', PageArray $siblings = null) {if(is_object($selector) && $selector instanceof PageArray) {$siblings = $selector;$selector = '';}if($siblings === null && $this->traversalPages) $siblings = $this->traversalPages;if($siblings) return $this->traversal()->nextSibling($this, $selector, $siblings);return $this->traversal()->next($this, $selector);}/*** Return all sibling pages after this one, optionally matching a selector** #pw-group-traversal** @param string|array|bool $selector Optional selector. When specified, will filter the found siblings.* @param bool|PageArray $getQty Return a count instead of PageArray? (boolean)* - If no $selector argument is needed, this may be specified as the first argument.* - Legacy support: You may specify a PageArray of siblings to use instead of the default (deprecated, avoid it).* @param bool $getPrev For internal use, makes this method implement the prevAll() behavior instead.* @return PageArray|int Returns all matching pages after this one, or integer if $count option specified.**/public function nextAll($selector = '', $getQty = false, $getPrev = false) {$siblings = null;if(is_object($selector) && $selector instanceof PageArray) {$siblings = $selector;$selector = '';}if(is_object($getQty) && $getQty instanceof PageArray) {$siblings = $getQty;$getQty = false;}if(is_bool($selector)) {$getQty = $selector;$selector = '';}if($siblings === null && $this->traversalPages) $siblings = $this->traversalPages;if($getPrev) {if($siblings) return $this->traversal()->prevAllSiblings($this, $selector, $siblings);return $this->traversal()->prevAll($this, $selector, array('qty' => $getQty));}if($siblings) return $this->traversal()->nextAllSiblings($this, $selector, $siblings);return $this->traversal()->nextAll($this, $selector, array('qty' => $getQty));}/*** Return all sibling pages after this one until matching the one specified** #pw-group-traversal** @param string|Page|array $selector May either be a selector or Page to stop at. Results will not include this.* @param string|array $filter Optional selector to filter matched pages by* @param PageArray $siblings Optional PageArray of siblings to use instead (avoid).* @return PageArray**/public function nextUntil($selector = '', $filter = '', PageArray $siblings = null) {if($siblings === null && $this->traversalPages) $siblings = $this->traversalPages;if($siblings) return $this->traversal()->nextUntilSiblings($this, $selector, $filter, $siblings);return $this->traversal()->nextUntil($this, $selector, $filter);}/*** Return the previous sibling page** ~~~~~* // Get the previous sibling* $sibling = $page->prev();** // Get the previous sibling having field "featured" with value of "1"* $sibling = $page->prev("featured=1");* ~~~~~** #pw-group-traversal** @param string|array $selector Optional selector. When specified, will find nearest previous sibling that matches.* @param PageArray|null $siblings Optional siblings to use instead of the default.* @return Page|NullPage Returns the previous sibling page, or a NullPage if none found.**/public function prev($selector = '', PageArray $siblings = null) {if(is_object($selector) && $selector instanceof PageArray) {$siblings = $selector;$selector = '';}if($siblings === null && $this->traversalPages) $siblings = $this->traversalPages;if($siblings) return $this->traversal()->prevSibling($this, $selector, $siblings);return $this->traversal()->prev($this, $selector);}/*** Return all sibling pages before this one, optionally matching a selector** #pw-group-traversal** @param string|array|bool $selector Optional selector. When specified, will filter the found siblings.* @param bool|PageArray $getQty Return a count instead of PageArray? (boolean)* - If no $selector argument is needed, this may be specified as the first argument.* - Legacy support: You may specify a PageArray of siblings to use instead of the default (deprecated, avoid it).* @return Page|NullPage|int Returns all matching pages before this one, or integer if $getQty requested.**/public function prevAll($selector = '', $getQty = false) {return $this->nextAll($selector, $getQty, true);}/*** Return all sibling pages before this one until matching the one specified** #pw-group-traversal** @param string|Page|array $selector May either be a selector or Page to stop at. Results will not include this.* @param string|array $filter Optional selector to filter matched pages by* @param PageArray|null $siblings Optional PageArray of siblings to use instead of default.* @return PageArray**/public function prevUntil($selector = '', $filter = '', PageArray $siblings = null) {if($siblings === null && $this->traversalPages) $siblings = $this->traversalPages;if($siblings) return $this->traversal()->prevUntilSiblings($this, $selector, $filter, $siblings);return $this->traversal()->prevUntil($this, $selector, $filter);}/*** Return pages that have Page reference fields pointing to this one (references)** By default this excludes pages that are hidden, unpublished and pages excluded due to access control for the current user.* To prevent these exclusions specify an include mode in the selector, i.e. `include=all`, or you can use* boolean `true` as a shortcut to specify that you do not want any exclusions.** #pw-group-traversal** @param string|bool $selector Optional selector to filter results by, or boolean true as shortcut for `include=all`.* @param Field|string|bool $field Optionally limit to pages using specified field (name or Field object),* - OR specify boolean TRUE to return array of PageArrays indexed by field names.* - If $field argument not specified, it searches all applicable Page fields.* @return PageArray|array* @since 3.0.107**/public function ___references($selector = '', $field = '') {return $this->traversal()->references($this, $selector, $field);}/*** Return pages linking to this one (in Textarea/HTML fields)** Applies only to Textarea fields with “html” content-type and link abstraction enabled.** #pw-group-traversal** @param string|bool $selector Optional selector to filter by or boolean true for “include=all”. (default='')* @param string|Field $field Optionally limit results to specified field. (default=all applicable Textarea fields)* @return PageArray* @since 3.0.107**/public function ___links($selector = '', $field = '') {return $this->traversal()->links($this, $selector, $field);}/*** Get languages active for this page and viewable by current user** #pw-group-languages** @return PageArray|null Returns PageArray of languages, or null if language support is not active.**/public function getLanguages() {$languages = $this->wire()->pages->newPageArray();$template = $this->template();if(!$template) return null;$templateLanguages = $template->getLanguages();if(!$templateLanguages) return null;foreach($templateLanguages as $language) {if($this->viewable($language, false)) $languages->add($language);}return $languages;}/*** Save the entire page to the database, or just a field from it** This is the same as calling `$pages->save($page);` or `$pages->saveField($page, $field)`, but calling directly* on the $page like this may be more convenient in many instances.** If you want to hook into the save operation, hook into one of the many Pages class hooks referenced in the 'See Also' section.** ~~~~~* // Save the page* $page->save();** // Save just the 'title' field from the page* $page->save('title');* ~~~~~** #pw-group-common* #pw-group-manipulation** @param Field|string $field Optional field to save (name of field or Field object)* @param array $options See Pages::save() documentation for options. You may also specify $options as the first argument if no $field is needed.* @return bool Returns true on success false on fail* @throws WireException on database error* @see Pages::save(), Pages::saveField(), Pages::saveReady(), Pages::saveFieldReady(), Pages::saved(), Pages::fieldSaved()**/public function save($field = null, array $options = array()) {$pages = $this->wire()->pages;if(is_array($field) && empty($options)) {$options = $field;$field = null;}if(empty($field)) {return $pages->save($this, $options);}if($this->hasField($field)) {// save fieldreturn $pages->saveField($this, $field, $options);}// save only native properties$options['noFields'] = true;return $pages->save($this, $options);}/*** Quickly set field value(s) and save to database** You can specify a single field and value, or an array of fields and values.** This method does not need output formatting to be turned off first, so make sure that whatever* value(s) you set are not formatted values.** ~~~~~* // Set and save the summary field* $page->setAndSave('summary', 'When nothing is done, nothing is left undone.');* ~~~~~* ~~~~~* // Set and save multiple fields* $page->setAndSave([* 'title' => 'It is Friday again',* 'subtitle' => 'Here is another new blog post',* 'body' => 'Hope you all have a great weekend!'* ]);* ~~~~~* ~~~~~* // Update a 'last_login' field after every user login* $session->addHookAfter('loginSuccess', function($event) {* $user = $event->arguments(0);* $user->setAndSave('last_login', time());* });* ~~~~~** #pw-group-manipulation* #pw-links [Blog post about setAndSave](https://processwire.com/blog/posts/processwire-2.6.9-core-updates-and-new-procache-version/)** @param array|string $key Field or property name to set, or array of one or more ['property' => $value].* @param string|int|bool|object $value Value to set, or omit if you provided an array in first argument.* @param array $options See Pages::save() for additional $options that may be specified.* @return bool Returns true on success, false on failure* @see Pages::save()**/public function setAndSave($key, $value = null, array $options = array()) {if(is_array($key)) {$values = $key;$property = count($values) == 1 ? key($values) : '';} else {$property = $key;$values = array($key => $value);}$of = $this->of();if($of) $this->of(false);foreach($values as $k => $v) {$this->set($k, $v);}if($property) {$result = $this->save($property, $options);} else {$result = $this->save($options);}if($of) $this->of(true);return $result;}/*** Delete this page from the database** This is the same as calling `$pages->delete($page)`.** ~~~~~* // Delete pages named "delete-me" that don't have children* $items = $pages->find("name=delete-me, numChildren=0");* foreach($items as $item) {* $item->delete();* }* ~~~~~* ~~~~~* // Delete a page and recursively all of its children, grandchildren, etc.* $item = $pages->get('/some-page/');* $item->delete(true);* ~~~~~** #pw-group-manipulation** @param bool $recursive If set to true, then this will attempt to delete all children too.* @return bool|int True on success, false on failure, or int quantity of pages deleted when recursive option is true.* @throws WireException when attempting to delete a page with children and $recursive option is not specified.* @see Pages::delete()**/public function delete($recursive = false) {return $this->wire()->pages->delete($this, $recursive);}/*** Move this page to the trash** This is the same as calling `$pages->trash($page)`.** ~~~~~* // Trash a page* $item = $pages->get('/some-page/');* $item->trash();* ~~~~~** #pw-group-manipulation** @return bool True on success, false on failure* @throws WireException**/public function trash() {return $this->wire()->pages->trash($this);}/*** Returns number of children page has, affected by output formatting mode.** - When output formatting is on, returns only number of visible children,* making the return value the same as the `Page::hasChildren()` method.** - When output formatting is off, returns number of all children without exclusion,* making the return value the same as the `Page::numChildren()` method.** ~~~~~* // Get number of visible children, like $page->hasChildren()* $page->of(true); // enable output formatting* $numVisible = $page->count();** // Get number of all children, like $page->numChildren()* $page->of(false); // disable output formatting* $numTotal = $page->count();* ~~~~~** #pw-advanced** @return int Quantity of children* @see Page::hasChildren(), Page::numChildren()**/#[\ReturnTypeWillChange]public function count() {if($this->outputFormatting) return $this->numChildren(true);return $this->numChildren(false);}/*** Enables iteration of the page's properties and fields with PHP’s foreach()** This fulfills PHP's IteratorAggregate interface, enabling you to interate all of the page's properties and fields.** ~~~~~* // List all properties and fields from the page* foreach($page as $name => $value) {* echo "<h3>$name</h3>";* echo "<p>$value</p>";* }* ~~~~~** #pw-advanced** @return \ArrayObject**/#[\ReturnTypeWillChange]public function getIterator() {$a = $this->settings;$template = $this->template();if($template && $template->fieldgroup) {foreach($template->fieldgroup as $field) {$a[$field->name] = $this->get($field->name);}}return new \ArrayObject($a);}/*** Has the Page changed since it was loaded?** To check if only a specific property on the page has changed, specify the property/field name as the first argument.* This method assumes that change tracking is enabled for the Page (as it is by default).* Pages that are new (i.e. don't yet exist in the database) always return true.** #pw-group-manipulation** ~~~~~* // Check if page has any changes* if($page->isChanged()) {* // There are changes to this page* $changes = $page->getChanges();* }* ~~~~~* ~~~~~* // When page is about to be saved, update summary when body has changed* $this->addHookBefore('Pages::saveReady', function($event) {* $page = $event->arguments('page');* if($page->isChanged('body')) {* // get first 300 chars from body* $summary = substr($page->body, 0, 300);* // truncate to position of last period* $period = strrpos($summary, '.');* if($period) $summary = substr($summary, 0, $period);* // populate to the page, so that summary is also saved* $page->summary = $summary;* }* });* ~~~~~** @param string $what If specified, only checks the given property for changes rather than the whole page.* @return bool* @see Wire::setTrackChanges(), Wire::getChanges(), Wire::trackChange()**/public function isChanged($what = '') {if($this->isNew()) return true;if(parent::isChanged($what)) return true;$changed = false;if($what) {$data = array_key_exists($what, $this->data) ? array($this->data[$what]) : array();} else {$data = &$this->data;}foreach($data as $key => $value) {if(is_object($value) && $value instanceof Wire) {$changed = $value->isChanged();}if($changed) break;}return $changed;}/*** Clears out any tracked changes and turns change tracking ON or OFF** Use this method when you want to clear a list of tracked changes on the page. Note that any changes are still* present, but ProcessWire no longer knows they had been changed. Meaning, the changes won't be available to* the `$page->isChanged()` and `$page->getChanges()` methods, and the changes might be skipped over if/when* the page is saved.** #pw-group-manipulation** @param bool $trackChanges True to turn change tracking ON, or false to turn OFF. Default of true is assumed.* @return $this* @see Page::isChanged(), Page::getChanges(), Page::trackChanges()**/public function resetTrackChanges($trackChanges = true) {parent::resetTrackChanges($trackChanges);foreach($this->data as $key => $value) {if(is_object($value) && $value instanceof Wire && !$value instanceof Page) {$value->resetTrackChanges($trackChanges);}}return $this;}/*** Returns the Page's ID in a string** @return string**/public function __toString() {return "{$this->id}";}/*** Returns the Page’s path from the ProcessWire installation root.** The path is always indicated from the ProcessWire installation root. Meaning, if the installation is* running from a subdirectory, then the path does not include that subdirectory, whereas the url does.* Note that path and url are identical if installation is not running from a subdirectory.** #pw-hookable* #pw-group-common* #pw-group-urls** ~~~~~* // Difference between path and url on site running from subdirectory /my-site/* echo $page->path(); // outputs: /about/contact/* echo $page->url(); // outputs: /my-site/about/contact/* ~~~~~** @return string Returns the page path, for example: `/about/contact/`* @see Page::url(), Page::httpUrl()**/public function path() {return $this->wire()->hooks->isHooked('Page::path()') ? $this->__call('path', array()) : $this->___path();}/*** Provides the hookable implementation for the path() method.** The method we're using here by having a real path() function above is slightly quicker than just letting* PW's hook handler handle it all. We're taking this approach since path() is a function that can feasibly* be called hundreds or thousands of times in a request, so we want it as optimized as possible.** #pw-internal**/protected function ___path() {if($this->id === 1) return '/';$path = '';$parents = $this->parents();foreach($parents as $parent) if($parent->id > 1) $path .= "/{$parent->name}";return $path . '/' . $this->name . '/';}/*** Returns the URL to the page (optionally with additional $options)** - This method can also be accessed by property `$page->url` (without parenthesis).** - Like `$page->path()` but comes from server document root. Path and url are identical if* installation is not running from a subdirectory.** - Use `$page->httpUrl()` if you need the URL to include scheme and hostname.** - **Need to hook this method?** While it's not directly hookable, it does use the `$page->path()`* method, which *is* hookable. As a result, you can affect the output of the url() method by* hooking the path() method instead.** ## $options argument** You can specify an `$options` argument to this method with any of the following:** - `pageNum` (int|string|bool): Specify pagination number, "+" for next pagination, "-" for previous pagination,* or boolean true (3.0.155+) for current.* - `urlSegmentStr` (string|bool): Specify a URL segment string to append, or true (3.0.155+) for current.* - `urlSegments` (array|bool): Specify array of URL segments to append (may be used instead of urlSegmentStr),* or boolean true (3.0.155+) for current. Specify associative array to use keys and values in order (3.0.155+).* - `data` (array): Array of key=value variables to form a query string.* - `http` (bool): Specify true to make URL include scheme and hostname (default=false).* - `language` (Language): Specify Language object to return URL in that Language.* - `host` (string): Force hostname to use, i.e. 'world.com' or 'hello.world.com'. The 'http' option is implied. (3.0.178+)* - `scheme` (string): Like http option, makes URL have scheme+hostname, but you specify scheme here, i.e. 'https' (3.0.178+)* Note that if you specify scheme of 'https' and $config->noHTTPS is true, the 'http' scheme will still be used.** You can also specify any of the following for `$options` as shortcuts:** - If you specify an `int` for options it is assumed to be the `pageNum` option.* - If you specify `+` or `-` for options it is assumed to be the `pageNum` “next/previous pagination” option.* - If you specify any other `string` for options it is assumed to be the `urlSegmentStr` option.* - If you specify a `boolean` (true) for options it is assumed to be the `http` option.** Please also note regarding `$options`:** - This method honors template slash settings for page, URL segments and page numbers.* - Any passed in URL segments are automatically sanitized with `Sanitizer::pageNameUTF8()`.* - If using the `pageNum` or URL segment options please also make sure these are enabled on the page’s template.* - The query string generated by any `data` variables is entity encoded when output formatting is on.* - The `language` option requires that the `LanguageSupportPageNames` module is installed.* - The prefix for page numbers honors `$config->pageNumUrlPrefix` and multi-language prefixes as well.** ~~~~~* // Using $page->url to output navigation* foreach($page->children as $child) {* echo "<li><a href='$child->url'>$child->title</a></li>";* }* ~~~~~* ~~~~~* // Difference between url() and path() on site running from subdirectory /my-site/* echo $page->url(); // outputs: /my-site/about/contact/* echo $page->path(); // outputs: /about/contact/* ~~~~~* ~~~~~* // Specify that you want a specific pagination (output: /example/page2)* echo $page->url(2);** // Get URL for next and previous pagination* echo $page->url('+'); // next* echo $page->url('-'); // prev** // Get a URL with scheme and hostname (output: http://domain.com/example/)* echo $page->url(true);** // Specify a URL segment string (output: /example/photos/1)* echo $page->url('photos/1');** // Use a URL segment array (output: /example/photos/1)* echo $page->url([* 'urlSegments' => [ 'photos', '1' ]* ]);** // Get URL in a specific language* $fr = $languages->get('fr');* echo $page->url($fr);** // Include data/query vars (output: /example/?action=view&type=photos)* echo $page->url([* 'data' => [* 'action' => 'view',* 'type' => 'photos'* ]* ]);** // Specify multiple options (output: http://domain.com/example/foo/page3?bar=baz)* echo $page->url([* 'http' => true,* 'pageNum' => 3,* 'urlSegmentStr' => 'foo',* 'data' => [ 'bar' => 'baz' ]* ]);* ~~~~~** #pw-group-common* #pw-group-urls** @param array|int|string|bool|Language|null $options Optionally specify options to modify default behavior (see method description).* @return string Returns page URL, for example: `/my-site/about/contact/`* @see Page::path(), Page::httpUrl(), Page::editUrl(), Page::localUrl()**/public function url($options = null) {if($options !== null) return $this->traversal()->urlOptions($this, $options);$url = rtrim($this->wire()->config->urls->root, '/') . $this->path();$template = $this->template();if($template && $template->slashUrls === 0 && $this->settings['id'] > 1) $url = rtrim($url, '/');return $url;}/*** Return all URLs that this page can be accessed from (excluding URL segments and pagination)** This includes the current page URL, any other language URLs (for which page is active), and* any past (historical) URLs the page was previously available at (which will redirect to it).** - Returned URLs do not include additional URL segments or pagination numbers.* - Returned URLs are indexed by language name, i.e. “default”, “fr”, “es”, etc.* - If multi-language URLs not installed, then index is just “default”.* - Past URLs are indexed by language; then ISO-8601 date, i.e. “default;2016-08-11T07:44:43-04:00”,* where the date represents the last date that URL was considered current.* - If PagePathHistory core module is not installed then past/historical URLs are excluded.* - You can disable past/historical or multi-language URLs by using the $options argument.** #pw-group-urls** @param array $options Options to modify default behavior:* - `http` (bool): Make URLs include current scheme and hostname (default=false).* - `past` (bool): Include past/historical URLs? (default=true)* - `languages` (bool): Include other language URLs when supported/available? (default=true).* - `language` (Language|int|string): Include only URLs for this language (default=null).* Note: the `languages` option must be true if using the `language` option.* @return array* @since 3.0.107* @see Page::addUrl(), page::removeUrl()**/public function urls($options = array()) {return $this->traversal()->urls($this, $options);}/*** Returns the URL to the page, including scheme and hostname** - This method is just like the `$page->url()` method except that it also includes scheme and hostname.** - This method can also be accessed at the property `$page->httpUrl` (without parenthesis).** - It is desirable to use this method when some page templates require https while others don't.* This ensures local links will always point to pages with the proper scheme. For other cases, it may* be preferable to use `$page->url()` since it produces shorter output.** ~~~~~* // Generating a link to this page using httpUrl* echo "<a href='$page->httpUrl'>$page->title</a>";* ~~~~~** #pw-group-common* #pw-group-urls** @param array $options For details on usage see `Page::url()` options argument.* @return string Returns full URL to page, for example: `https://processwire.com/about/`* @see Page::url(), Page::localHttpUrl()**/public function httpUrl($options = array()) {return $this->traversal()->httpUrl($this, $options);}/*** Return the URL necessary to edit this page** - We recommend checking that the page is editable before outputting the editUrl().* - If user opens URL in their browser and is not logged in, they must login to account with edit permission.* - This method can also be accessed by property at `$page->editUrl` (without parenthesis).** ~~~~~~* if($page->editable()) {* echo "<a href='$page->editUrl'>Edit this page</a>";* }* ~~~~~~** #pw-group-urls** @param array|bool|string $options Specify true for http option, specify name of field to find (3.0.151+), or use $options array:* - `http` (bool): True to force scheme and hostname in URL (default=auto detect).* - `language` (Language|bool): Optionally specify Language to start editor in, or boolean true to force current user language.* - `find` (string): Name of field to find in the editor (3.0.151+)* @return string URL for editing this page**/public function editUrl($options = array()) {return $this->traversal()->editUrl($this, $options);}/*** Return the field name by which children are sorted** - If sort is descending, then field name is prepended with a "-".* - Returns the value "sort" if pages are unsorted or sorted manually.* - Note the return value from this method may be different from the `Page::sortfield` (lowercase) property,* as this method considers the sort field specified with the template as well.** #pw-group-system** @return string**/public function sortfield() {$template = $this->template();$sortfield = $template ? $template->sortfield : '';if(!$sortfield) $sortfield = $this->settings['sortfield'];if(!$sortfield) $sortfield = 'sort';return $sortfield;}/*** Return the index/position of this page relative to siblings.** If given a hidden or unpublished page, that page would not usually be part of the group of siblings.* As a result, such pages will return what the value would be if they were visible (as of 3.0.121). This* may overlap with the index of other pages, since indexes are relative to visible pages, unless you* specify an include mode (see next paragraph).** If you want this method to include hidden/unpublished pages as part of the index numbers, then* specify boolean true for the $selector argument (which implies "include=all") OR specify a* selector of "include=hidden", "include=unpublished" or "include=all".** ~~~~~* $i = $page->index();* $n = $page->parent->numChildren();* echo "This page is $i out of $n total pages";* ~~~~~** #pw-group-traversal** @param bool|string|array Specify one of the following (since 3.0.121):* - Boolean true to include hidden and unpublished pages as part of the index numbers (same as "include=all").* - An "include=hidden", "include=unpublished" or "include=all" selector to include them in the index numbers.* - A string selector or selector array to filter the criteria for the returned index number.* @return int Returns index number (zero-based)* @since 3.0.24**/public function index($selector = '') {return $this->traversal()->index($this, $selector);}/*** Get the output TemplateFile object for rendering this page (internal use only)** You can retrieve the results of this by calling $page->out or $page->output** #pw-internal** @param bool $forceNew Forces it to return a new (non-cached) TemplateFile object (default=false)* @return TemplateFile**/public function output($forceNew = false) {if($this->output && !$forceNew) return $this->output;$template = $this->template();if(!$template) return null;$this->output = $this->wire(new TemplateFile());$this->output->setThrowExceptions(false);$this->output->setFilename($template->filename);$fuel = $this->wire()->fuel->getArray();$this->output->set('wire', $this->wire());foreach($fuel as $key => $value) $this->output->set($key, $value);$this->output->set('page', $this);return $this->output;}/*** Render given $fieldName using site/templates/fields/ markup file** Shorter aliases of this method include:** - `$page->render('fieldName', $file);`* - `$page->render->fieldName;`* - `$page->_fieldName_;`** This method expects that there is a file in `/site/templates/fields/` to render the field with:** - `/site/templates/fields/fieldName.php`* - `/site/templates/fields/fieldName.templateName.php`* - `/site/templates/fields/fieldName/$file.php` (using $file argument)* - `/site/templates/fields/$file.php` (using $file argument)* - `/site/templates/fields/$file/fieldName.php` (using $file argument, must have trailing slash)* - `/site/templates/fields/$file.fieldName.php` (using $file argument, must have trailing period)** Note that the examples above showing $file require that the `$file` argument is specified.** ~~~~~* // Render output for the 'images' field (assumes you have implemented an output file)* echo $page->renderField('images');* ~~~~~** #pw-group-output-rendering** @param string $fieldName May be any custom field name or native page property.* @param string $file Optionally specify file (in site/templates/fields/) to render with (may omit .php extension).* @param mixed|null $value Optionally specify value to render, otherwise it will be pulled from this $page.* @return mixed|string Returns the rendered value of the field* @see Page::render(), Page::renderValue()**/public function ___renderField($fieldName, $file = '', $value = null) {/** @var PageRender $pageRender */$pageRender = $this->wire()->modules->get('PageRender');return $pageRender->renderField($this, $fieldName, $file, $value);}/*** Render given $value using /site/templates/fields/ markup file** See the documentation for the `Page::renderField()` method for information about the `$file` argument.** ~~~~~* // Render a value using site/templates/fields/my-images.php custom output template* $images = $page->images;* echo $page->renderValue($images, 'my-images');* ~~~~~** #pw-group-output-rendering** @param mixed $value Value to render* @param string $file Optionally specify file (in site/templates/fields/) to render with (may omit .php extension)* @return mixed|string Returns rendered value**/public function ___renderValue($value, $file = '') {return $this->___renderField('', $file, $value);}/*** Return all Inputfield objects necessary to edit this page** This method returns an InputfieldWrapper object that contains all the custom Inputfield objects* required to edit this page. You may also specify a `$fieldName` argument to limit what is contained* in the returned InputfieldWrapper.** Please note this method deals only with custom fields, not system fields name 'name' or 'status', etc.,* as those are exclusive to the ProcessPageEdit page editor.** #pw-advanced** @param string|array $fieldName Optional field to limit to, typically the name of a fieldset or tab.* - Or optionally specify array of $options (See `Fieldgroup::getPageInputfields()` for options).* @return null|InputfieldWrapper Returns an InputfieldWrapper array of Inputfield objects, or NULL on failure.**/public function getInputfields($fieldName = '') {$of = $this->of();if($of) $this->of(false);$template = $this->template();$fieldgroup = $template ? $template->fieldgroup : null;if($fieldgroup) {if(is_array($fieldName) && !ctype_digit(implode('', array_keys($fieldName)))) {// fieldName is an associative array of options for Fieldgroup::getPageInputfields$wrapper = $fieldgroup->getPageInputfields($this, $fieldName);} else {$wrapper = $fieldgroup->getPageInputfields($this, '', $fieldName);}} else {$wrapper = null;}if($of) $this->of(true);return $wrapper;}/*** Get a single Inputfield for the given field name** - If requested field name refers to a single field, an Inputfield object is returned.* - If requested field name refers to a fieldset or tab, then an InputfieldWrapper representing will be returned.* - Returned Inputfield already has values populated to it.* - Please note this method deals only with custom fields, not system fields name 'name' or 'status', etc.,* as those are exclusive to the ProcessPageEdit page editor.** #pw-advanced** @param string $fieldName* @return Inputfield|InputfieldWrapper|null Returns Inputfield, or null if given field name doesn't match field for this page.**/public function getInputfield($fieldName) {$inputfields = $this->getInputfields($fieldName);if($inputfields) {$field = $this->wire()->fields->get($fieldName);if($field && $field->type instanceof FieldtypeFieldsetOpen) {// requested field name is a fieldset, returns InputfieldWrapperreturn $inputfields;} else {// requested field name is a single field, return Inputfieldreturn $inputfields->children()->first();}} else {// requested field name is not applicable to this pagereturn null;}}/*** Get front-end editable output for field (requires PageFrontEdit module to be installed)** This method requires the core `PageFrontEdit` module to be installed. If it is not installed then* it returns expected output but it is not front-end editable. This method corresponds to front-end* editing Option B. See the [front-end editor docs](https://processwire.com/docs/front-end/) for more details.* If the user does not have permission to front-end edit then returned output will not be editable.** Use `$page->edit('field_name');` instead of `$page->get('field_name');` to automatically return an editable* field value when the user is allowed to edit, or a regular field value when not. When field is* editable, hovering the value shows a different icon. **The user must double-click the area to edit.**** The 2nd and 3rd arguments are typically used only if you need to override the default presentation of* the editor or provide some kind of action or button to trigger the editor. It might also be useful if* the content to edit is not visible by default. It is recommended that you specify boolean true for the* `$modal` argument when using the `$markup` argument, which makes it open the editor in a modal window,* less likely to interfere with your front-end layout.** ~~~~~* // retrieve editable value if field_name is editable, or just value if not* $value = $page->edit('field_name');* ~~~~~** #pw-group-output-rendering* #pw-hooker** @param string|bool|null $key Name of field, omit to get editor active status, or boolean true to enable editor.* @param string|bool|null $markup Markup user should click on to edit $fieldName (typically omitted).* @param bool|null $modal Specify true to force editable region to open a modal window (typically omitted).* @return string|bool|mixed* @see https://processwire.com/docs/front-end/* @since 3.0.0 This method is added by a hook in PageFrontEdit and only shown in this class for documentation purposes.**/public function ___edit($key = null, $markup = null, $modal = null) {if($modal) {} // ignoreif($key === null || is_bool($key)) return false;if(is_string($markup)) return $markup;if($markup === false) return $this->getFormatted($key);return $this->get($key);}/*** Does this page have the given status?** This method is the preferred way to check if a page has a particular status.* The status may be specified as one of the `Page::status` constants or a string representing* one of the constants, i.e. `hidden`, `unpublished`, `locked`, and so on.** ~~~~~* // check if page has hidden status using status name* if($page->hasStatus('hidden')) { ... }** // check if page has hidden status using status constant* if($page->hasStatus(Page::statusHidden)) { ... }** // There are also method shortcuts, i.e.* if($page->isHidden()) { ... }* if($page->isUnpublished()) { ... }* if($page->isLocked()) { ... }* ~~~~~** #pw-group-status* #pw-group-common** @param int|string $status Status flag constant or string representation (hidden, locked, unpublished, etc.)* @return bool Returns true if page has the given status, or false if it doesn't.* @see Page::addStatus(), Page::removeStatus(), Page::isHidden(), Page::isUnpublished(), Page::isLocked()**/public function hasStatus($status) {if(is_string($status) && isset(PageProperties::$statuses[$status])) {$status = PageProperties::$statuses[$status];}return (bool) ($this->status & $status);}/*** Add the specified status to this page** This is the preferred way to add a new status to a page. There is also a corresponding `Page::removeStatus()` method.** ~~~~~* // Add hidden status to the page using status name* $page->addStatus('hidden');** // Add hidden status to the page using status constant* $page->addStatus(Page::statusHidden);* ~~~~~** #pw-group-status* #pw-group-manipulation** @param int|string $statusFlag Status flag constant or string representation (hidden, locked, unpublished, etc.)* @return $this* @see Page::removeStatus(), Page::hasStatus()**/public function addStatus($statusFlag) {if(is_string($statusFlag) && isset(PageProperties::$statuses[$statusFlag])) {$statusFlag = PageProperties::$statuses[$statusFlag];}$statusFlag = (int) $statusFlag;return $this->setStatus($this->status | $statusFlag);}/*** Remove the specified status from this page** This is the preferred way to remove a status from a page. There is also a corresponding `Page::addStatus()` method.** ~~~~~* // Remove hidden status from the page using status name* $page->removeStatus('hidden');** // Remove hidden status from the page using status constant* $page->removeStatus(Page::statusHidden);* ~~~~~** #pw-group-status* #pw-group-manipulation** @param int|string $statusFlag Status flag constant or string representation (hidden, locked, unpublished, etc.)* @return $this* @throws WireException If you attempt to remove `Page::statusSystem` or `Page::statusSystemID` statuses without first adding `Page::statusSystemOverride` status.* @see Page::addStatus(), Page::hasStatus()**/public function removeStatus($statusFlag) {if(is_string($statusFlag) && isset(PageProperties::$statuses[$statusFlag])) {$statusFlag = PageProperties::$statuses[$statusFlag];}$statusFlag = (int) $statusFlag;$override = $this->settings['status'] & Page::statusSystemOverride;if($statusFlag == Page::statusSystem || $statusFlag == Page::statusSystemID) {if(!$override) throw new WireException('Cannot remove statusSystem from page without statusSystemOverride');}$this->status = $this->status & ~$statusFlag;return $this;}/*** Given a selector, return whether or not this Page matches it** ~~~~~* if($page->matches("created>=" . strtotime("today"))) {* echo "This page was created today";* }* ~~~~~** @param string|Selectors|array $selector Selector to compare against (string, Selectors object, or array).* @return bool Returns true if this page matches, or false if it doesn't.**/public function matches($selector) {// This method implements the WireMatchable interfacereturn $this->comparison()->matches($this, $selector);}/*** Does this page have the specified status number or template name?** See status flag constants at top of Page class.* You may also use status names: hidden, locked, unpublished, system, systemID** #pw-internal** @param int|string|Selectors $status Status number, status name, or Template name or selector string/object* @return bool**/public function is($status) {if(is_string($status) && isset(PageProperties::$statuses[$status])) {$status = PageProperties::$statuses[$status];}return $this->comparison()->is($this, $status);}/*** Does this page have a 'hidden' status?** #pw-group-status** @return bool**/public function isHidden() {return $this->hasStatus(self::statusHidden);}/*** Does this page have a 'unpublished' status?** #pw-group-status** @return bool**/public function isUnpublished() {return $this->hasStatus(self::statusUnpublished);}/*** Does this page have a 'locked' status?** #pw-group-status** @return bool**/public function isLocked() {return $this->hasStatus(self::statusLocked);}/*** Is this Page new? (i.e. doesn't yet exist in DB)** #pw-internal** @return bool**/public function isNew() {return $this->isNew;}/*** Is the page fully loaded? (or optionally a field)** #pw-internal** @param string|null $fieldName Optionally request if a specified field is already loaded in the page* @return bool**/public function isLoaded($fieldName = null) {if($fieldName) {if($this->hasField($fieldName)) return isset($this->data[$fieldName]);return parent::get($fieldName) !== null;}return $this->isLoaded;}/*** Is this Page in the trash?** #pw-group-status** @return bool**/public function isTrash() {if($this->hasStatus(self::statusTrash)) return true;$trashPageID = (int) $this->wire()->config->trashPageID;if($this->id === (int) $trashPageID) return true;// this is so that isTrash() still returns the correct result, even if the page was just trashed and not yet savedforeach($this->parents() as $parent) if($parent->id === $trashPageID) return true;return false;}/*** Is this page public and viewable by all?** This is a state that persists regardless of user, so has nothing to do with the current user.* To be public, the page must be published and have guest view access.** #pw-advanced* #pw-hookable** @return bool True if public, false if not**/public function isPublic() {return $this->wire()->hooks->isHooked('Page::isPublic()') ? $this->__call('isPublic', array()) : $this->___isPublic();}/*** Hookable implementation for the above isPublic function** @return bool**/protected function ___isPublic() {if($this->status >= Page::statusUnpublished) return false;$template = $this->getAccessTemplate();if(!$template || !$template->hasRole('guest')) return false;return true;}/*** Get or set current status** - When manipulating status, you may prefer to use the `$page->addStatus()` and `$page->removeStatus()` methods instead.** - Use this `status()` method when you want to set multiple statuses at once, or when you want to get status rather than set it.** - You can also get or set status directly, by manipulating the `$page->status` property.** ~~~~~* // Get the current status as bitmask* $status = $page->status();** // Get an array of status names assigned to page* $statuses = $page->status(true);** // Set status by Page constant bitmask* $page->status(Page::statusHidden | Page::statusUnpublished);** // Set status by name* $page->status('unpublished');** // Set status by names* $page->status(['hidden', 'unpublished']);* ~~~~~** #pw-group-manipulation* #pw-group-status** @param bool|int $value Optionally specify one of the following:* - `true` (boolean): To return an array of status names (indexed by status number).* - `integer|string|array`: Status number(s) or status name(s) to set the current page status (same as $page->status = $value)* @param int|null $status If you specified `true` for first argument, optionally specify status value you want to use (if not the current).* @return int|array|Page If setting status, `$this` is returned. If getting status: current status or array of status names is returned.* @see Page::addStatus(), Page::removeStatus(), Page::hasStatus()**/public function status($value = false, $status = null) {if($value !== true && $value !== false) return $this->setStatus($value);if($status === null) $status = $this->status;if($value === false) return $status;return PageProperties::statusToNames($status);}/*** Set the value for isNew, i.e. doesn't exist in the DB (internal use only)** #pw-internal** @param bool @isNew* @return $this**/public function setIsNew($isNew) {$this->isNew = $isNew ? true : false;return $this;}/*** Set that the Page is fully loaded (internal use only)** Pages::getById sets this once it has completed loading the page* This method also triggers the loaded() method that hooks may listen to** #pw-internal** @param bool $isLoaded* @return $this**/public function setIsLoaded($isLoaded) {$isLoaded = !$isLoaded || $isLoaded === 'false' ? false : true;if($isLoaded) {$this->processFieldDataQueue();unset(Page::$loadingStack[$this->settings['id']]);}$this->isLoaded = $isLoaded ? true : false;if($isLoaded) {//$this->loaded();$this->_callHookMethod('loaded');}return $this;}/*** Process and instantiate any data in the fieldDataQueue** This happens after setIsLoaded(true) is called**/protected function processFieldDataQueue() {$template = $this->template();if(!$template) return;$fieldgroup = $template->fieldgroup;if(!$fieldgroup) return;foreach($this->fieldDataQueue as $key => $value) {$field = $fieldgroup->get($key);if(!$field) continue;// check for autojoin multi fields, which may have multiple values bundled into one string// as a result of an sql group_concat() functionif($field->type instanceof FieldtypeMulti && ($field->flags & Field::flagAutojoin)) {foreach($value as $k => $v) {if(is_string($v) && strpos($v, FieldtypeMulti::multiValueSeparator) !== false) {$value[$k] = explode(FieldtypeMulti::multiValueSeparator, $v);}}}// if all there is in the array is 'data', then we make that the value rather than keeping an array// this is so that Fieldtypes that only need to interact with a single value don't have to receive an array of dataif(count($value) == 1 && array_key_exists('data', $value)) $value = $value['data'];$this->setFieldValue($key, $value, false);}$this->fieldDataQueue = array(); // empty it out, no longer needed}/*** For hooks to listen to, triggered when page is loaded and ready** #pw-hooker**/public function ___loaded() { }/*** Set output formatting state of page** The output formatting state determines if a page's output is allowed to be filtered by runtime formatters.* Pages used for output should have output formatting on. Pages you intend to manipulate and save should* have it off.** ~~~~~* // Set output formatting state off, for page manipulation* $page->setOutputFormatting(false);* $page->title = 'About Us';* $page->save();** // You can also use this shorter version* $page->of(false);* ~~~~~** #pw-internal** @param bool $outputFormatting Optional, default true* @return $this* @see Page::outputFormatting(), Page::of()**/public function setOutputFormatting($outputFormatting = true) {$this->outputFormatting = $outputFormatting ? true : false;return $this;}/*** Return true if output formatting is on, false if not.** #pw-internal** @return bool True if output formatting is ON, false if OFF.* @see Page::of(), Page::setOutputFormatting()**/public function outputFormatting() {return $this->outputFormatting;}/*** Get or set the current output formatting state of the page** - Always returns the current output formatting state: true if ON, or false if OFF.** - To set the current output formatting state, provide a boolean true to turn it ON, or boolean false to turn it OFF.** - Pages used for front-end output should have output formatting turned ON.** - Pages that you are manipulating and saving should have output formatting turned OFF.** ~~~~~* // Set output formatting state off, for page manipulation* $page->of(false);* $page->title = 'About Us';* $page->save();* ~~~~~** #pw-group-common* #pw-group-output-rendering* #pw-group-manipulation** @param bool $outputFormatting If specified, sets output formatting state ON or OFF. If not specified, nothing is changed.* @return bool Current output formatting state (before this function call, if it was changed)**/public function of($outputFormatting = null) {$of = $this->outputFormatting;if(!is_null($outputFormatting)) $this->outputFormatting = $outputFormatting ? true : false;return $of;}/*** Return instance of PagefilesManager specific to this Page** #pw-group-files** @return PagefilesManager**/public function filesManager() {if($this->hasStatus(Page::statusDeleted)) return null;if(is_null($this->filesManager)) $this->filesManager = $this->wire(new PagefilesManager($this));return $this->filesManager;}/*** Does this Page use secure Pagefiles?** See also `$template->pagefileSecure` and `$config->pagefileSecure` which determine the return value.** #pw-group-files** @return bool|null Returns boolean true if yes, false if no, or null if not known* @since 3.0.166**/public function secureFiles() {if($this->wire()->config->pagefileSecure && !$this->isPublic()) return true;$template = $this->getAccessTemplate();if(!$template) return null;$value = $template->pagefileSecure;if($value < 1) return false; // 0: disabledif($value > 1) return true; // 2: files always securereturn !$this->isPublic(); // 1: secure only if page not public}/*** Does the page have a files path for storing files?** This will only check if files path exists, it will not create the path if it’s not already present.** #pw-group-files** @return bool* @since 3.0.138 Earlier versions must use the more verbose PagefilesManager::hasPath($page)* @see hasFiles(), filesManager()**/public function hasFilesPath() {return PagefilesManager::hasPath($this);}/*** Does the page have a files path and one or more files present in it?** This will only check if files exist, it will not create the directory if it’s not already present.** #pw-group-files** @return bool* @since 3.0.138 Earlier versions must use the more verbose PagefilesManager::hasFiles($page)* @see hasFilesPath(), filesPath(), filesManager()**/public function hasFiles() {return PagefilesManager::hasFiles($this);}/*** Does Page have given filename in its files directory?** @param string $file File basename or verbose hash* @param array $options* - `getPathname` (bool): Get full path + filename when would otherwise return boolean true? (default=false)* - `getPagefile` (bool): Get Pagefile object when would otherwise return boolean true? (default=false)* @return bool|string* @since 3.0.166**/public function hasFile($file, array $options = array()) {$defaults = array('getPathname' => false,'getPagefile' => false,);$file = basename($file);$options = array_merge($defaults, $options);$hasFile = PagefilesManager::hasFile($this, $file, $options['getPathname']);if($hasFile && $options['getPagefile']) {$hasFile = $this->wire()->fieldtypes->FieldtypeFile->getPagefile($this, $file);}return $hasFile;}/*** Returns the path for files, creating it if it does not yet exist** #pw-group-files** @return string* @since 3.0.138 You can also use the equivalent but more verbose `$page->filesManager()->path()` in any version* @see filesUrl(), hasFilesPath(), hasFiles(), filesManager()**/public function filesPath() {return $this->filesManager()->path();}/*** Returns the URL for files, creating it if it does not yet exist** #pw-group-files** @return string* @see filesPath(), filesManager()* @since 3.0.138 You can use the equivalent but more verbose `$page->filesManager()->url()` in any version**/public function filesUrl() {return $this->filesManager()->url();}/*** Prepare the page and it's fields for removal from runtime memory, called primarily by Pages::uncache()** #pw-internal**/public function uncache() {$trackChanges = $this->trackChanges();if($trackChanges) $this->setTrackChanges(false);$template = $this->template();if($template) {foreach($template->fieldgroup as $field) {$value = parent::get($field->name);if($value != null && is_object($value)) {parent::set($field->name, null);if(isset($this->wakeupNameQueue[$field->name])) unset($this->wakeupNameQueue[$field->name]);}}}if($this->filesManager) $this->filesManager->uncache();$this->filesManager = null;if($trackChanges) $this->setTrackChanges(true);}/*** Ensures that isset() and empty() work for this classes properties.** @param string $key* @return bool**/public function __isset($key) {if($this->isLoaded) {return $this->get($key) !== null;} else {if(isset(PageProperties::$baseProperties[$key])) return true;if(isset(PageProperties::$basePropertiesAlternates[$key])) return true;if($this->hasField($key)) return true;return false;}}/*** Returns the page from which role/access settings are inherited from** #pw-group-access** @param string $type Optionally specify one of 'view', 'edit', 'add', or 'create' (default='view')* @return Page|NullPage Returns NullPage if none found**/public function getAccessParent($type = 'view') {return $this->access()->getAccessParent($this, $type);}/*** Returns the template from which role/access settings are inherited from** #pw-group-access** @param string $type Optionally specify one of 'view', 'edit', 'add', or 'create' (default='view')* @return Template|null Returns Template object or NULL if none**/public function getAccessTemplate($type = 'view') {return $this->access()->getAccessTemplate($this, $type);}/*** Return Roles (PageArray) that have access to this page** This is determined from the page's template. If the page's template has roles turned off,* then it will go down the tree till it finds usable roles to use and inherit from.** #pw-group-access** @param string $type May be 'view', 'edit', 'create' or 'add' (default='view')* @return PageArray of Role objects**/public function getAccessRoles($type = 'view') {return $this->access()->getAccessRoles($this, $type);}/*** Returns whether this page has the given access role** Given access role may be a role name, role ID or Role object.** #pw-group-access** @param string|int|Role $role* @param string $type May be 'view', 'edit', 'create' or 'add' (default is 'view')* @return bool**/public function hasAccessRole($role, $type = 'view') {return $this->access()->hasAccessRole($this, $role, $type);}/*** Export the page's data to an array** @return array*public function ___export() {$exporter = new PageExport();return $exporter->export($this);}*//*** Export the page's data from an array** @param array $data Data to import, in the format from the export() function* @return $this*public function ___import(array $data) {$importer = new PageExport();return $importer->import($this, $data);}*//*** Is $value1 equal to $value2?** @param string $key Name of the key that triggered the check (see WireData::set)* @param mixed $value1* @param mixed $value2* @return bool**/protected function isEqual($key, $value1, $value2) {$isEqual = $value1 === $value2;if(!$isEqual && $value1 instanceof WireArray && $value2 instanceof WireArray) {// ask WireArray to compare itself to another$isEqual = $value1->isIdentical($value2, true);}if($isEqual) {if(is_object($value1) && $value1 instanceof Wire && ($value1->isChanged() || $value2->isChanged())) {$this->trackChange($key, $value1, $value2);}}return $isEqual;}/*** Return a Page helper class instance that’s common among all Page (and derived) objects in this ProcessWire instance** @param string $className* @return object|PageComparison|PageAccess|PageTraversal**/protected function getHelperInstance($className) {$instanceID = $this->wire()->getProcessWireInstanceID();if(!isset(PageProperties::$helpers[$instanceID])) {// no helpers yet for this ProcessWire instancePageProperties::$helpers[$instanceID] = array();}if(!isset(PageProperties::$helpers[$instanceID][$className])) {// helper not yet loaded, so load it$nsClassName = __NAMESPACE__ . "\\$className";$helper = new $nsClassName();if($helper instanceof WireFuelable) $this->wire($helper);PageProperties::$helpers[$instanceID][$className] = $helper;} else {// helper already ready to use$helper = PageProperties::$helpers[$instanceID][$className];}return $helper;}/*** @return PageComparison**/protected function comparison() {return $this->getHelperInstance('PageComparison');}/*** @return PageAccess**/protected function access() {return $this->getHelperInstance('PageAccess');}/*** @return PageTraversal**/protected function traversal() {return $this->getHelperInstance('PageTraversal');}/*** return PageFamily** Coming soon*protected function family() {return $this->getHelperInstance('PageFamily');}*//*** Return a translation array of all: status name => status number** This enables string shortcuts to be used for statuses elsewhere in ProcessWire** #pw-internal** @return array**/static public function getStatuses() {return PageProperties::$statuses;}/*** Tells the page what Process it is being edited by, or simply that it's being edited** #pw-internal** @param WirePageEditor $editor**/public function ___setEditor(WirePageEditor $editor) {// $this->setQuietly('_editor', $editor); // uncomment when/if needed}/*** Get or set current traversal pages (internal use)** When setting, force use of given $items (siblings) and sort order in some* traversal methods like next(), prev() and related methods. Given $items must* include this page as well before used in any traversal calls.** - To set, specify a PageArray for $items.* - To unset, specify boolean false for $items.* - To get current traversal pages omit all arguments.** #pw-internal** @param PageArray|bool|null $items Traversal pages (PageArray), boolean false to unset, or omit to get.* @return PageArray|null* @since 3.0.116**/public function traversalPages($items = null) {if($items instanceof PageArray) $this->traversalPages = $items; // setif($items === false) $this->traversalPages = null; // unsetreturn $this->traversalPages; // get}/*** Get the icon name associated with this Page (if applicable)** #pw-internal** @todo add recognized page icon field to core** @return string**/public function ___getIcon() {$template = $this->template();if(!$template) return '';if($this->hasField('process')) {$process = $this->getUnformatted('process');if($process) {$info = $this->wire()->modules->getModuleInfoVerbose($process);if(!empty($info['icon'])) return $info['icon'];}}return $template->getIcon();}/*** Return the API variable used for managing pages of this type** #pw-internal** @return Pages|PagesType**/public function getPagesManager() {return $this->wire()->pages;}/*** Get lazy loading state, set lazy load state, or trigger the page to load** $page->_lazy() to return current lazy loading state (which is page ID or boolean).* $page->_lazy(123) to set the page as a lazy loading page and establish its id (replacing 123 with actual ID).* $page->_lazy(true) to trigger the page to load.** #pw-internal** @param int|bool|null $lazy Specify one of the following:* - Page ID to establish it as lazy loading.* - Boolean true to trigger load of the page.* - Omit to just return the lazy load value.* @return bool|int Returns one of the following:* - Page ID if lazy load pending.* - Boolean true if lazy loading and already loaded.* - If load was requested in arguments, then returns true on success, false on fail.* @throws WireException**/public function _lazy($lazy = null) {if(is_null($lazy)) {// return current statereturn $this->lazyLoad;} else if(is_int($lazy)) {// set state (page ID)if($lazy > 0 && !$this->lazyLoad) {$this->lazyLoad = $lazy;$this->set('id', $lazy);}return true;} else if($lazy === true) {// load pageif(!is_int($this->lazyLoad) || $this->lazyLoad < 1) return false;$this->lazyLoad = true;$page = $this->wire()->pages->getById($this->id, array('cache' => (is_string($this->loaderCache) ? $this->loaderCache : false),'getOne' => true,'page' => $this // This. Just This.));if(!$page->id) return false;return true;} else {throw new WireException("Invalid arguments to Page::lazy()");}}/*** Handles get/find loads specific to this Page from the $pages API variable** #pw-internal** @param string $method The $pages API method to call (get, find, findOne, or count)* @param string|int $selector The selector argument of the $pages call* @param array $options Any additional options (see Pages::find for options).* @return Pages|Page|PageArray|NullPage|int* @throws WireException**/public function _pages($method = '', $selector = '', $options = array()) {if(empty($method)) return $this->wire()->pages;if(!isset($options['cache'])) $options['cache'] = $this->loaderCache;if(!isset($options['caller'])) $options['caller'] = "page._pages.$method";$result = $this->wire()->pages->$method($selector, $options);return $result;}/*public function remove($key) {parent::remove($key);if(isset($this->data[$key])) {$a = parent::get('_statusCorruptedFields');if(!is_array($a)) $a = array();$k = array_search($key, $a);if($k !== false) {unset($a[$k]);if(empty($a)) $this->removeStatus(self::statusCorrupted);parent::set('_statusCorruptedFields', $a);}}return $this;}*//*** Get or set page’s persistent meta data** This meta data is managed in the DB. Setting a value immediately saves it in the DB, while* getting a value immediately loads it from the DB. As a result, this data is independent of the* usual Page load and save operations. This is primarily for internal core use, but may be* useful for other specific non-core purposes as well.** Note that this data is tied to the page where you call it. Meta data is completely free-form* and has no connection to ProcessWire fields.** Values for meta data must be basic PHP types, whether arrays, strings, numbers, etc. Please do* not use objects for meta values at this time.** ~~~~~* // set and save a meta value* $page->meta()->set('colors', [ 'red, 'green', 'blue' ]);** // get a meta value* $colors = $page->meta()->get('colors');** // alternate shorter syntax for either of the above* $page->meta('colors', [ 'red', 'green', 'blue' ]); // set* $colors = $page->meta('colors'); // get** // delete a meta value* $page->meta()->remove('colors');** // get the WireDataDB instance that stores the meta values,* // it has all the same methods as WireData objects...* $meta = $page->meta();** // ...such as, get all values in an array:* $values = $meta->getArray();* ~~~~~** #pw-advanced** @param string|bool $key Omit to get the WireData instance or specify property name to get or set.* @param null|mixed $value Value to set for given $key or omit if getting a value.* @return WireDataDB|string|array|int|float* @since 3.0.133**/public function meta($key = '', $value = null) {/** @var Pages $pages */if($this->_meta === null) $this->_meta = $this->wire(new WireDataDB($this->id, 'pages_meta'));if(empty($key)) return $this->_meta; // return instanceif($value === null) return $this->_meta->get($key); // get valuereturn $this->_meta->set($key, $value); // set value}}