sterzycom/kirby/core/pages.php

330 lines
No EOL
8.1 KiB
PHP

<?php
/**
* Pages
*
* @package Kirby CMS
* @author Bastian Allgeier <bastian@getkirby.com>
* @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));
}
}