/** * Load items from the database table and return them in the same type class that getAll() returns * A selector string or Selectors may be provided so that this can be used as a find() by descending classes that don't load all items at once. * * @param Selectors|string|null $selectors Selectors or a selector string to find, or NULL to load all. * @return WireArray Returns the same type as specified in the getAll() method. * */ protected function ___load(WireArray $items, $selectors = null) { $query = $this->getLoadQuery($selectors); $result = $this->fuel('db')->query($query); $lookupField = $this->getLookupField(); while ($row = $result->fetch_assoc()) { $item = $this->makeBlankItem(); foreach ($row as $field => $value) { if ($field == $lookupField) { $item->addLookupItem($value); } else { $item->{$field} = $value; } } if ($items->has($item)) { // LEFT JOIN is adding more elements of the same item, i.e. from lookup table $items->get($item)->addLookupItem($row[$lookupField]); // $items->get($item)->setArray($row); } else { $items->add($item); } } if ($result) { $result->free(); } $items->setTrackChanges(true); return $items; }
/** * Load items from the database table and return them in the same type class that getAll() returns * A selector string or Selectors may be provided so that this can be used as a find() by descending classes that don't load all items at once. * * @param Selectors|string|null $selectors Selectors or a selector string to find, or NULL to load all. * @return WireArray Returns the same type as specified in the getAll() method. * */ protected function ___load(WireArray $items, $selectors = null) { $query = $this->getLoadQuery($selectors); $result = $this->fuel('db')->query($query); $lookupField = $this->getLookupField(); while ($row = $result->fetch_assoc()) { $item = $this->makeBlankItem(); $lookupValue = $row[$lookupField]; unset($row[$lookupField]); $item->addLookupItem($lookupValue, $row); foreach ($row as $field => $value) { $item->{$field} = $value; } if ($items->has($item)) { // LEFT JOIN is adding more elements of the same item, i.e. from lookup table // if the item is already present in $items, then use the existing one rather // and throw out the one we just created $item = $items->get($item); $item->addLookupItem($lookupValue, $row); } else { // add a new item $items->add($item); } } if ($result) { $result->free(); } $items->setTrackChanges(true); return $items; }
/** * Load items from the database table and return them in the same type class that getAll() returns * * A selector string or Selectors may be provided so that this can be used as a find() by descending classes that don't load all items at once. * * @param WireArray $items * @param Selectors|string|null $selectors Selectors or a selector string to find, or NULL to load all. * @return WireArray Returns the same type as specified in the getAll() method. * */ protected function ___load(WireArray $items, $selectors = null) { $query = $this->getLoadQuery($selectors); $database = $this->wire('database'); $sql = $query->getQuery(); $stmt = $database->prepare($sql); $stmt->execute(); $lookupField = $this->getLookupField(); while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) { $item = $this->makeBlankItem(); $lookupValue = $row[$lookupField]; unset($row[$lookupField]); $item->addLookupItem($lookupValue, $row); foreach ($row as $field => $value) { $item->{$field} = $value; } if ($items->has($item)) { // LEFT JOIN is adding more elements of the same item, i.e. from lookup table // if the item is already present in $items, then use the existing one rather // and throw out the one we just created $item = $items->get($item); $item->addLookupItem($lookupValue, $row); } else { // add a new item $items->add($item); } } $stmt->closeCursor(); $items->setTrackChanges(true); return $items; }
/** * Import items into this WireArray. * * @throws WireException * @param string|WireArray $items Items to import. * @return WireArray This instance. * */ public function import($items) { if (is_string($items)) { $this->extractString($items); } else { return parent::import($items); } }
public function add($item) { if ($item->flags & Notice::debug) { if (!$this->fuel('config')->debug) { return $this; } } return parent::add($item); }
public function add($item) { if ($item instanceof Page) { $page = $item; $item = new Breadcrumb(); $item->title = $page->get("title|name"); $item->url = $page->url; } return parent::add($item); }
/** * Extends the find capability of WireArray to descend into the Inputfield children * */ public function find($selector) { $a = parent::find($selector); foreach ($this as $item) { if (!$item instanceof InputfieldWrapper) { continue; } $children = $item->children(); if (count($children)) { $a->import($children->find($selector)); } } return $a; }
/** * Is the given WireArray identical to this one? * * @param WireArray $items * @param bool|int $strict * When true (default), compares items, item object instances, order, and any other data contained in WireArray. * When false, compares only items in the WireArray resolve to the same order and values (though not object instances). * @return bool * */ public function isIdentical(WireArray $items, $strict = true) { if ($items === $this) { return true; } if ($items->className() != $this->className()) { return false; } if (!$strict) { return (string) $this === (string) $items; } $a1 = $this->getArray(); $a2 = $items->getArray(); if ($a1 === $a2) { // all items match $d1 = $this->data(); $d2 = $items->data(); if ($d1 === $d2) { // all data matches return true; } } return false; }
/** * Find modules based on a selector string and ensure any ModulePlaceholders are loaded in the returned result * * @param string $selector * @return Modules * */ public function find($selector) { $a = parent::find($selector); if ($a) { foreach ($a as $key => $value) { $a[$key] = $this->get($value->class); } } return $a; }
public function getPrev($item, $strict = true) { $this->preload(); return parent::getPrev($item, $strict); }
/** * Is the given class name installed? * * @param string $class * @return bool * */ public function isInstalled($class) { return parent::get($class) !== null; }
public function __set($key, $value) { // we must have both this and set() per behavior of WireArray::__set() // which throws exceptions if attempting to set a property if (SelectableOption::isProperty($key)) { return $this->setProperty($key, $value); } return parent::__set($key, $value); }
public function add($item) { if ($item->flags & Notice::debug) { if (!$this->wire('config')->debug) { return $this; } } // check for duplicates $dup = false; foreach ($this as $notice) { if ($notice->text == $item->text && $notice->flags == $item->flags) { $dup = true; } } if ($dup) { return $this; } if ($item->flags & Notice::log || $item->flags & Notice::logOnly) { $this->addLog($item); if ($item->flags & Notice::logOnly) { return $this; } } return parent::add($item); }
/** * Set the roles that are allowed to view or edit this field on pages * * Applicable only if the flagAccess is set to this field's flags. * * @param string $type Must be either "view" or "edit" * @param PageArray|array|null $roles May be a PageArray of Role objects or an array of Role IDs * * @throws WireException if given invalid argument * */ public function setRoles($type, $roles) { if (empty($roles)) { $roles = array(); } if (!WireArray::iterable($roles)) { throw new WireException("setRoles expects PageArray or array of Role IDs"); } $ids = array(); foreach ($roles as $role) { if (is_int($role) || is_string($role) && ctype_digit("{$role}")) { $ids[] = (int) $role; } else { if ($role instanceof Role) { $ids[] = (int) $role->id; } } } if ($type == 'view') { $guestID = $this->wire('config')->guestUserRolePageID; // if guest is present, then that's inclusive of all, no need to store others in viewRoles if (in_array($guestID, $ids)) { $ids = array($guestID); } if ($this->viewRoles != $ids) { $this->viewRoles = $ids; $this->trackChange('viewRoles'); } } else { if ($type == 'edit') { if ($this->editRoles != $ids) { $this->editRoles = $ids; $this->trackChange('editRoles'); } } else { throw new WireException("setRoles expects either 'view' or 'edit' (arg 0)"); } } }
public function error($text, $flags = 0) { $this->log($text); return parent::error($text, $flags); }
public function getPrev($item) { $this->preload(); return parent::getPrev($item); }
/** * Filter out Pages that don't match the selector. * * This is applicable to and destructive to the WireArray. * * @param string|Selectors $selectors AttributeSelector string to use as the filter. * @param bool $not Make this a "not" filter? (default is false) * @return WireArray reference to current [filtered] instance * */ protected function filterData($selectors, $not = false) { if (is_string($selectors) && $selectors[0] === '/') { $selectors = "path={$selectors}"; } return parent::filterData($selectors, $not); }
/** * Given an array or CSV string of Page IDs, return a PageArray * * Optionally specify an $options array rather than a template for argument 2. When present, the 'template' and 'parent_id' arguments may be provided * in the given $options array. These options may be specified: * * LOAD OPTIONS (argument 2 array): * - cache: boolean, default=true. place loaded pages in memory cache? * - getFromCache: boolean, default=true. Allow use of previously cached pages in memory (rather than re-loading it from DB)? * - template: instance of Template (see $template argument) * - parent_id: integer (see $parent_id argument) * - getNumChildren: boolean, default=true. Specify false to disable retrieval and population of 'numChildren' Page property. * - getOne: boolean, default=false. Specify true to return just one Page object, rather than a PageArray. * - autojoin: boolean, default=true. Allow use of autojoin option? * - joinFields: array, default=empty. Autojoin the field names specified in this array, regardless of field settings (requires autojoin=true). * - joinSortfield: boolean, default=true. Whether the 'sortfield' property will be joined to the page. * - findTemplates: boolean, default=true. Determine which templates will be used (when no template specified) for more specific autojoins. * - pageClass: string, default=auto-detect. Class to instantiate Page objects with. Leave blank to determine from template. * - pageArrayClass: string, default=PageArray. PageArray-derived class to store pages in (when 'getOne' is false). * * Use the $options array for potential speed optimizations: * - Specify a 'template' with your call, when possible, so that this method doesn't have to determine it separately. * - Specify false for 'getNumChildren' for potential speed optimization when you know for certain pages will not have children. * - Specify false for 'autojoin' for potential speed optimization in certain scenarios (can also be a bottleneck, so be sure to test). * - Specify false for 'joinSortfield' for potential speed optimization when you know the Page will not have children or won't need to know the order. * - Specify false for 'findTemplates' so this method doesn't have to look them up. Potential speed optimization if you have few autojoin fields globally. * - Note that if you specify false for 'findTemplates' the pageClass is assumed to be 'Page' unless you specify something different for the 'pageClass' option. * * @param array|WireArray|string $_ids Array of IDs or CSV string of IDs * @param Template|array|null $template Specify a template to make the load faster, because it won't have to attempt to join all possible fields... just those used by the template. * Optionally specify an $options array instead, see the method notes above. * @param int|null $parent_id Specify a parent to make the load faster, as it reduces the possibility for full table scans. * This argument is ignored when an options array is supplied for the $template. * @return PageArray|Page Returns Page only if the 'getOne' option is specified, otherwise always returns a PageArray. * @throws WireException * */ public function getById($_ids, $template = null, $parent_id = null) { static $instanceID = 0; $options = array('cache' => true, 'getFromCache' => true, 'template' => null, 'parent_id' => null, 'getNumChildren' => true, 'getOne' => false, 'autojoin' => true, 'findTemplates' => true, 'joinSortfield' => true, 'joinFields' => array(), 'pageClass' => '', 'pageArrayClass' => 'PageArray'); if (is_array($template)) { // $template property specifies an array of options $options = array_merge($options, $template); $template = $options['template']; $parent_id = $options['parent_id']; } else { if (!is_null($template) && !$template instanceof Template) { throw new WireException('getById argument 2 must be Template or $options array'); } } $pageArrayClass = $options['pageArrayClass']; if (!is_null($parent_id) && !is_int($parent_id)) { // convert Page object or string to integer id $parent_id = (int) (string) $parent_id; } if (!is_null($template) && !is_object($template)) { // convert template string or id to Template object $template = $this->wire('templates')->get($template); } if (is_string($_ids)) { // convert string of IDs to array if (strpos($_ids, '|') !== false) { $_ids = explode('|', $_ids); } else { $_ids = explode(",", $_ids); } } else { if (is_int($_ids)) { $_ids = array($_ids); } } if (!WireArray::iterable($_ids) || !count($_ids)) { // return blank if $_ids isn't iterable or is empty return $options['getOne'] ? new NullPage() : new $pageArrayClass(); } if (is_object($_ids)) { $_ids = $_ids->getArray(); } // ArrayObject or the like $loaded = array(); // array of id => Page objects that have been loaded $ids = array(); // sanitized version of $_ids // sanitize ids and determine which pages we can pull from cache foreach ($_ids as $key => $id) { $id = (int) $id; if ($id < 1) { continue; } if ($options['getFromCache'] && ($page = $this->getCache($id))) { // page is already available in the cache $loaded[$id] = $page; } else { if (isset(Page::$loadingStack[$id])) { // if the page is already in the process of being loaded, point to it rather than attempting to load again. // the point of this is to avoid a possible infinite loop with autojoin fields referencing each other. $loaded[$id] = Page::$loadingStack[$id]; // cache the pre-loaded version so that other pages referencing it point to this instance rather than loading again $this->cache($loaded[$id]); } else { $loaded[$id] = ''; // reserve the spot, in this order $ids[(int) $key] = $id; // queue id to be loaded } } } $idCnt = count($ids); // idCnt contains quantity of remaining page ids to load if (!$idCnt) { // if there are no more pages left to load, we can return what we've got if ($options['getOne']) { return count($loaded) ? reset($loaded) : new NullPage(); } $pages = new $pageArrayClass(); $pages->import($loaded); return $pages; } $database = $this->wire('database'); $idsByTemplate = array(); if (is_null($template) && $options['findTemplates']) { // template was not defined with the function call, so we determine // which templates are used by each of the pages we have to load $sql = "SELECT id, templates_id FROM pages WHERE "; if ($idCnt == 1) { $sql .= "id=" . (int) reset($ids); } else { $sql .= "id IN(" . implode(",", $ids) . ")"; } $query = $database->prepare($sql); $result = $this->executeQuery($query); if ($result) { while ($row = $query->fetch(PDO::FETCH_NUM)) { list($id, $templates_id) = $row; $id = (int) $id; $templates_id = (int) $templates_id; if (!isset($idsByTemplate[$templates_id])) { $idsByTemplate[$templates_id] = array(); } $idsByTemplate[$templates_id][] = $id; } } $query->closeCursor(); } else { if (is_null($template)) { // no template provided, and autojoin not needed (so we don't need to know template) $idsByTemplate = array(0 => $ids); } else { // template was provided $idsByTemplate = array($template->id => $ids); } } foreach ($idsByTemplate as $templates_id => $ids) { if ($templates_id && (!$template || $template->id != $templates_id)) { $template = $this->wire('templates')->get($templates_id); } if ($template) { $fields = $template->fieldgroup; } else { $fields = $this->wire('fields'); } $query = new DatabaseQuerySelect(); $sortfield = $template ? $template->sortfield : ''; $joinSortfield = empty($sortfield) && $options['joinSortfield']; $query->select("false AS isLoaded, pages.templates_id AS templates_id, pages.*, " . ($joinSortfield ? 'pages_sortfields.sortfield, ' : '') . ($options['getNumChildren'] ? '(SELECT COUNT(*) FROM pages AS children WHERE children.parent_id=pages.id) AS numChildren' : '')); if ($joinSortfield) { $query->leftjoin('pages_sortfields ON pages_sortfields.pages_id=pages.id'); } $query->groupby('pages.id'); if ($options['autojoin'] && $this->autojoin) { foreach ($fields as $field) { if (!empty($options['joinFields']) && in_array($field->name, $options['joinFields'])) { // joinFields option specified to force autojoin this field } else { if (!($field->flags & Field::flagAutojoin)) { continue; } // autojoin not enabled for field if ($fields instanceof Fields && !($field->flags & Field::flagGlobal)) { continue; } // non-fieldgroup, autojoin only if global flag is set } $table = $database->escapeTable($field->table); if (!$field->type || !$field->type->getLoadQueryAutojoin($field, $query)) { continue; } // autojoin not allowed $query->leftjoin("{$table} ON {$table}.pages_id=pages.id"); // QA } } if (!is_null($parent_id)) { $query->where("pages.parent_id=" . (int) $parent_id); } if ($template) { $query->where("pages.templates_id=" . (int) $template->id); } // QA $query->where("pages.id IN(" . implode(',', $ids) . ") "); // QA $query->from("pages"); $stmt = $query->prepare(); $this->executeQuery($stmt); $class = $options['pageClass']; if (empty($class)) { if ($template) { $class = $template->pageClass && class_exists($template->pageClass) ? $template->pageClass : 'Page'; } else { $class = 'Page'; } } if ($class != 'Page' && !class_exists($class)) { $this->error("Class '{$class}' for Pages::getById() does not exist", Notice::log); $class = 'Page'; } try { while ($page = $stmt->fetchObject($class, array($template))) { $page->instanceID = ++$instanceID; $page->setIsLoaded(true); $page->setIsNew(false); $page->setTrackChanges(true); $page->setOutputFormatting($this->outputFormatting); $loaded[$page->id] = $page; if ($options['cache']) { $this->cache($page); } } } catch (Exception $e) { $error = $e->getMessage() . " [pageClass={$class}, template={$template}]"; if ($this->wire('user')->isSuperuser()) { $this->error($error); } $this->wire('log')->error($error); $this->trackException($e, false); } $stmt->closeCursor(); $template = null; } if ($options['getOne']) { return count($loaded) ? reset($loaded) : new NullPage(); } $pages = new $pageArrayClass(); return $pages->import($loaded); }
/** * Prepare selectors for filtering * * Template method for descending classes to modify selectors if needed * * @param Selectors $selectors * */ protected function filterDataSelectors(Selectors $selectors) { // @todo make it remove references to include= statements since not applicable in-memory parent::filterDataSelectors($selectors); }
public function add($item) { $item->page = $this->page; return parent::add($item); }
/** * Delete/remove a Pagefile item * * Deletes the filename associated with the Pagefile and removes it from this Pagefiles instance. * * @param Pagefile $item * @return this * */ public function remove($item) { if (is_string($item)) { $item = $this->get($item); } if (!$this->isValidItem($item)) { throw new WireException("Invalid type to {$this->className}::remove(item)"); } if (!count($this->unlinkQueue)) { $this->hookIDs[] = $this->page->filesManager->addHookBefore('save', $this, 'hookPageSave'); } $this->unlinkQueue[] = $item; parent::remove($item); return $this; }
/** * Given a Fieldtype name (or class name) return the instantiated Fieldtype module. * * If the requested Fieldtype is not already installed, it will be installed here automatically. * * @param string $key Fieldtype name or class name, or dynamic property of Fieldtypes * @return Fieldtype|null * */ public function get($key) { if (strpos($key, 'Fieldtype') !== 0) { $key = "Fieldtype" . ucfirst($key); } if (!($fieldtype = parent::get($key))) { $fieldtype = $this->modules->get($key); } if ($fieldtype instanceof ModulePlaceholder) { $fieldtype = $this->modules->get($fieldtype->className()); $this->set($key, $fieldtype); } return $fieldtype; }
/** * Add one or more of parents that this PagesType represents * * @param array|int|string|Page $parents Single or array of Page objects, IDs, or paths * */ public function addParents($parents) { if (!WireArray::iterable($parents)) { $parents = array($parents); } foreach ($parents as $parent) { if (is_int($parent)) { $id = $parent; } else { if (is_string($parent) && ctype_digit($parent)) { $id = (int) $parent; } else { if (is_string($parent)) { $parent = $this->wire('pages')->findOne($parent, array('loadOptions' => array('autojoin' => false))); $id = $parent->id; } else { if (is_object($parent) && $parent instanceof Page) { $id = $parent->id; } } } } if ($id) { $this->parents[$id] = $id; } } if (empty($this->parent_id)) { $this->parent_id = reset($this->parents); } // legacy deprecated }
/** * Remove a Permission from this Role * * Permission may be specified by name, id or Permission instance * * @param string|id|Permission $item * @return this * */ public function remove($item) { if (!is_object($item)) { $item = $this->getFuel('permissions')->get($item); } return parent::remove($item); }
/** * Load items from the database table and return them in the same type class that getAll() returns * A selector string or Selectors may be provided so that this can be used as a find() by descending classes that don't load all items at once. * * @param Selectors|string|null $selectors Selectors or a selector string to find, or NULL to load all. * @return WireArray Returns the same type as specified in the getAll() method. * */ protected function ___load(WireArray $items, $selectors = null) { $query = $this->getLoadQuery($selectors); $result = $this->getFuel('db')->query($query); while ($row = $result->fetch_assoc()) { $item = $this->makeBlankItem(); foreach ($row as $field => $value) { if ($field == 'data') { if ($value) { $value = $this->decodeData($value); } else { continue; } } $item->{$field} = $value; } $item->setTrackChanges(true); $items->add($item); } $result->free(); $items->setTrackChanges(true); return $items; }
/** * Overridden ProcessArray set * * @param string $key * @param string|int|object $value * @return Fieldgroup $this * */ public function set($key, $value) { if ($key == 'id') { $value = (int) $value; } else { if ($key == 'name') { $value = $this->fuel('sanitizer')->name($value); } } if (isset($this->settings[$key])) { if ($this->settings[$key] !== $value) { $this->trackChange($key); } $this->settings[$key] = $value; } else { return parent::set($key, $value); } return $this; }
/** * Overridden ProcessArray set * * @param string $key * @param string|int|object $value * @return Fieldgroup $this * @throws WireException if passed invalid data * */ public function set($key, $value) { if ($key == 'data') { return $this; } // we don't have a data field here if ($key == 'id') { $value = (int) $value; } else { if ($key == 'name') { $value = $this->wire('sanitizer')->name($value); } } if (isset($this->settings[$key])) { if ($this->settings[$key] !== $value) { $this->trackChange($key, $this->settings[$key], $value); } $this->settings[$key] = $value; } else { return parent::set($key, $value); } return $this; }
public function add($item) { if ($item->flags & Notice::debug) { if (!$this->wire('config')->debug) { return $this; } } if (is_array($item->text)) { $item->text = "<pre>" . trim(print_r($this->sanitizeArray($item->text), true)) . "</pre>"; $item->flags = $item->flags | Notice::allowMarkup; } else { if (is_object($item->text) && $item->text instanceof Wire) { $item->text = "<pre>" . $this->wire('sanitizer')->entities(print_r($item->text, true)) . "</pre>"; $item->flags = $item->flag | Notice::allowMarkup; } else { if (is_object($item->text)) { $item->text = (string) $item->text; } } } // check for duplicates $dup = false; foreach ($this as $notice) { if ($notice->text == $item->text && $notice->flags == $item->flags) { $dup = true; } } if ($dup) { return $this; } if ($item->flags & Notice::warning && !$item instanceof NoticeWarning) { // if given a warning of either NoticeMessage or NoticeError, convert it to a NoticeWarning // this is in support of legacy code, as NoticeWarning didn't used to exist $warning = new NoticeWarning($item->text, $item->flags); $warning->class = $item->class; $warning->timestamp = $item->timestamp; $item = $warning; } if (self::logAllNotices || $item->flags & Notice::log || $item->flags & Notice::logOnly) { $this->addLog($item); $item->flags = $item->flags & ~Notice::log; // remove log flag, to prevent it from being logged again if ($item->flags & Notice::logOnly) { return $this; } } return parent::add($item); }
/** * Convert any value to a string * * @param mixed $value * @return string * */ protected function valueToString($value) { if (is_object($value) && ($value instanceof Pagefiles || $value instanceof Pagefile)) { return $this->objectToString($value); } else { if (WireArray::iterable($value)) { return $this->arrayToString($value); } else { if (is_object($value)) { return $this->objectToString($value); } else { return $this->wire('sanitizer')->entities1($value); } } } }
/** * debugInfo PHP 5.6+ magic method * * @return array * */ public function __debugInfo() { $info = parent::__debugInfo(); if ($this->getLimit()) { $info['pager'] = $this->getPaginationString(); } $info['total'] = $this->getTotal(); $info['start'] = $this->getStart(); $info['limit'] = $this->getLimit(); return $info; }