* @link http://getkirby.com * @copyright Bastian Allgeier * @license http://getkirby.com/license */ abstract class PagesAbstract extends Collection { static public $methods = array(); /** * Constructor */ public function __construct($data = array()) { foreach($data as $object) { $this->add($object); } } public function __call($method, $arguments) { if(isset(static::$methods[$method])) { array_unshift($arguments, clone $this); return call(static::$methods[$method], $arguments); } else { return $this->get($method); } } /** * Adds a single page object to the * collection by id or the entire object * * @param mixed $page */ public function add($page) { if(is_a($page, 'Collection')) { foreach($page as $object) $this->add($object); } else if(is_string($page) and $object = page($page)) { $this->data[$object->id()] = $object; } else if(is_a($page, 'Page')) { $this->data[$page->id()] = $page; } return $this; } /** * Returns a new collection of pages without the given pages * * @param args any number of uris or page elements, passed as individual arguments * @return object a new collection without the pages */ public function not() { $collection = clone $this; foreach(func_get_args() as $uri) { if(is_array($uri) or $uri instanceof Traversable) { foreach($uri as $u) { $collection = $collection->not($u); } } else if(is_a($uri, 'Page')) { // unset by Page object unset($collection->data[$uri->id()]); } else if(isset($collection->data[$uri])) { // unset by URI unset($collection->data[$uri]); } else if($page = $collection->findBy('uid', $uri)) { // unset by UID unset($collection->data[$page->id()]); } } return $collection; } public function find() { $args = func_get_args(); if(!count($args)) { return false; } if(count($args) === 1 and is_array($args[0])) { $args = $args[0]; } if(count($args) > 1) { $pages = new static(); foreach($args as $id) { if($page = $this->find($id)) { $pages->data[$page->id()] = $page; } } return $pages; } else { // get the first argument and remove slashes $id = trim($args[0], '/'); // fast access to direct uris return isset($this->data[$id]) ? $this->data[$id] : null; } } /** * Find a single page by a given value * * @param string $field * @param string $value * @return Page */ public function findBy($field, $value) { foreach($this->data as $page) { if($page->$field() == $value) return $page; } return false; } /** * Find the open page in a set * * @return Page */ public function findOpen() { return $this->findBy('isOpen', true); } /** * Filters the collection by visible pages * * @return Children */ public function visible() { $collection = clone $this; return $collection->filterBy('isVisible', true); } /** * Filters the collection by invisible pages * * @return Children */ public function invisible() { $collection = clone $this; return $collection->filterBy('isInvisible', true); } /** * Checks if a page is in a set of children * * @param Page | string $page * @return boolean */ public function has($page) { $uri = is_string($page) ? $page : $page->id(); return parent::has($uri); } /** * Native search method to search for anything within the collection */ public function search($query, $params = array()) { if(is_string($params)) { $params = array('fields' => str::split($params, '|')); } $defaults = array( 'minlength' => 2, 'fields' => array(), 'words' => false, 'score' => array() ); $options = array_merge($defaults, $params); $collection = clone $this; $searchwords = preg_replace('/(\s)/u',',', $query); $searchwords = str::split($searchwords, ',', $options['minlength']); if(!empty($options['stopwords'])) { $searchwords = array_diff($searchwords, $options['stopwords']); } if(empty($searchwords)) return $collection->limit(0); $searchwords = array_map(function($value) use($options) { return $options['words'] ? '\b' . preg_quote($value) . '\b' : preg_quote($value); }, $searchwords); $preg = '!(' . implode('|', $searchwords) . ')!i'; $results = $collection->filter(function($page) use($query, $searchwords, $preg, $options) { $data = $page->content()->toArray(); $keys = array_keys($data); if(!empty($options['fields'])) { $keys = array_intersect($keys, $options['fields']); } $page->searchHits = 0; $page->searchScore = 0; foreach($keys as $key) { $score = a::get($options['score'], $key, 1); // check for a match if($matches = preg_match_all($preg, $data[$key], $r)) { $page->searchHits += $matches; $page->searchScore += $matches * $score; // check for full matches if($matches = preg_match_all('!' . preg_quote($query) . '!i', $data[$key], $r)) { $page->searchScore += $matches * $score; } } } return $page->searchHits > 0 ? true : false; }); $results = $results->sortBy('searchScore', SORT_DESC); return $results; } /** * Returns files from all pages * * @return object A collection of all files of the pages (not of their subpages) */ public function files() { $files = new Collection(); foreach($this->data as $page) { foreach($page->files() as $file) { $files->append($page->id() . '/' . strtolower($file->filename()), $file); } } return $files; } // File type filters public function images() { return $this->files()->filterBy('type', 'image'); } public function videos() { return $this->files()->filterBy('type', 'video'); } public function documents() { return $this->files()->filterBy('type', 'document'); } public function audio() { return $this->files()->filterBy('type', 'audio'); } public function code() { return $this->files()->filterBy('type', 'code'); } public function archives() { return $this->files()->filterBy('type', 'archive'); } /** * Groups the pages by a given field * * @param string $field * @param bool $i (ignore upper/lowercase for group names) * @return object A collection with an item for each group and a Pages object for each group */ public function groupBy($field, $i = true) { $groups = array(); foreach($this->data as $key => $item) { $value = $item->content()->get($field)->value(); // make sure that there's always a proper value to group by if(!$value) throw new Exception('Invalid grouping value for key: ' . $key); // ignore upper/lowercase for group names if($i) $value = str::lower($value); if(!isset($groups[$value])) { // create a new entry for the group if it does not exist yet $groups[$value] = new Pages(array($key => $item)); } else { // add the item to an existing group $groups[$value]->set($key, $item); } } return new Collection($groups); } /** * Converts the pages collection * into a plain array * * @param closure $callback Filter callback for each item * @return array */ public function toArray($callback = null) { $data = array(); foreach($this as $page) { $data[] = is_string($page) ? $page : $page->toArray($callback); } return $data; } /** * Converts the pages collection * into a json string * * @param closure $callback Filter callback for each item * @return string */ public function toJson($callback = null) { return json_encode($this->toArray($callback)); } }