update to version 2.3 and remove submodules
This commit is contained in:
parent
b11bd29981
commit
80c59a9876
402 changed files with 49907 additions and 12 deletions
6
.gitmodules
vendored
6
.gitmodules
vendored
|
@ -1,9 +1,3 @@
|
||||||
[submodule "panel"]
|
|
||||||
path = panel
|
|
||||||
url = https://github.com/getkirby/panel.git
|
|
||||||
[submodule "kirby"]
|
|
||||||
path = kirby
|
|
||||||
url = https://github.com/getkirby/kirby.git
|
|
||||||
[submodule "site/fields/markdown"]
|
[submodule "site/fields/markdown"]
|
||||||
path = site/fields/markdown
|
path = site/fields/markdown
|
||||||
url = https://github.com/JonasDoebertin/kirby-visual-markdown.git
|
url = https://github.com/JonasDoebertin/kirby-visual-markdown.git
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
# Sterzy.com
|
# Sterzy.com
|
||||||
This is the code for the http://sterzy.com website. It is based on the kirby cms (http://getkirby.com), which has it's own license agreement ([Kirby End User License Agreement](https://github.com/getkirby/starterkit/blob/master/license.md)).
|
This is the code for the http://sterzy.com website. It is based on the kirby cms (http://getkirby.com), which has it's own license agreement ([Kirby End User License Agreement](https://github.com/getkirby/starterkit/blob/master/license.md)).
|
||||||
|
|
||||||
The rest of the code is under this license: [License](https://svs.ankaa.uberspace.de/sterzy/sterzycom/blob/master/LICENSE "license"), [CC BY 4.0](http://creativecommons.org/licenses/by/4.0/).
|
The rest of the code is under this license: [License](https://svs.ankaa.uberspace.de/sterzy/sterzycom/blob/master/LICENSE "license"), [CC BY 4.0](http://creativecommons.org/licenses/by/4.0/).
|
||||||
|
|
||||||
# Installing
|
# Installing
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
define('DS', DIRECTORY_SEPARATOR);
|
define('DS', DIRECTORY_SEPARATOR);
|
||||||
|
|
||||||
// load kirby
|
// load kirby
|
||||||
|
|
1
kirby
1
kirby
|
@ -1 +0,0 @@
|
||||||
Subproject commit b45f01ba6a6cd56781a7d091a423beb04b0d0eea
|
|
69
kirby/bootstrap.php
Normal file
69
kirby/bootstrap.php
Normal file
|
@ -0,0 +1,69 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
if(!defined('KIRBY')) define('KIRBY', true);
|
||||||
|
if(!defined('DS')) define('DS', DIRECTORY_SEPARATOR);
|
||||||
|
|
||||||
|
// load the kirby toolkit
|
||||||
|
include_once(__DIR__ . DS . 'toolkit' . DS . 'bootstrap.php');
|
||||||
|
|
||||||
|
// load all core classes
|
||||||
|
load(array(
|
||||||
|
|
||||||
|
// kirby class and subclasses
|
||||||
|
'kirby' => __DIR__ . DS . 'kirby.php',
|
||||||
|
'kirby\\roots' => __DIR__ . DS . 'kirby' . DS . 'roots.php',
|
||||||
|
'kirby\\urls' => __DIR__ . DS . 'kirby' . DS . 'urls.php',
|
||||||
|
'kirby\\component' => __DIR__ . DS . 'kirby' . DS . 'component.php',
|
||||||
|
'kirby\\registry' => __DIR__ . DS . 'kirby' . DS . 'registry.php',
|
||||||
|
'kirby\\request' => __DIR__ . DS . 'kirby' . DS . 'request.php',
|
||||||
|
'kirby\\request\\params' => __DIR__ . DS . 'kirby' . DS . 'request' . DS . 'params.php',
|
||||||
|
'kirby\\request\\query' => __DIR__ . DS . 'kirby' . DS . 'request' . DS . 'query.php',
|
||||||
|
'kirby\\request\\path' => __DIR__ . DS . 'kirby' . DS . 'request' . DS . 'path.php',
|
||||||
|
|
||||||
|
// core components
|
||||||
|
'kirby\\component\\template' => __DIR__ . DS . 'kirby' . DS . 'component' . DS . 'template.php',
|
||||||
|
'kirby\\component\\thumb' => __DIR__ . DS . 'kirby' . DS . 'component' . DS . 'thumb.php',
|
||||||
|
'kirby\\component\\markdown' => __DIR__ . DS . 'kirby' . DS . 'component' . DS . 'markdown.php',
|
||||||
|
'kirby\\component\\smartypants' => __DIR__ . DS . 'kirby' . DS . 'component' . DS . 'smartypants.php',
|
||||||
|
'kirby\\component\\snippet' => __DIR__ . DS . 'kirby' . DS . 'component' . DS . 'snippet.php',
|
||||||
|
'kirby\\component\\css' => __DIR__ . DS . 'kirby' . DS . 'component' . DS . 'css.php',
|
||||||
|
'kirby\\component\\js' => __DIR__ . DS . 'kirby' . DS . 'component' . DS . 'js.php',
|
||||||
|
'kirby\\component\\tinyurl' => __DIR__ . DS . 'kirby' . DS . 'component' . DS . 'tinyurl.php',
|
||||||
|
'kirby\\component\\response' => __DIR__ . DS . 'kirby' . DS . 'component' . DS . 'response.php',
|
||||||
|
|
||||||
|
// traits
|
||||||
|
'kirby\\traits\\image' => __DIR__ . DS . 'kirby' . DS . 'traits' . DS . 'image.php',
|
||||||
|
|
||||||
|
// all core abstracts
|
||||||
|
'assetabstract' => __DIR__ . DS . 'core' . DS . 'asset.php',
|
||||||
|
'avatarabstract' => __DIR__ . DS . 'core' . DS . 'avatar.php',
|
||||||
|
'pagesabstract' => __DIR__ . DS . 'core' . DS . 'pages.php',
|
||||||
|
'childrenabstract' => __DIR__ . DS . 'core' . DS . 'children.php',
|
||||||
|
'contentabstract' => __DIR__ . DS . 'core' . DS . 'content.php',
|
||||||
|
'fieldabstract' => __DIR__ . DS . 'core' . DS . 'field.php',
|
||||||
|
'fileabstract' => __DIR__ . DS . 'core' . DS . 'file.php',
|
||||||
|
'filesabstract' => __DIR__ . DS . 'core' . DS . 'files.php',
|
||||||
|
'kirbytextabstract' => __DIR__ . DS . 'core' . DS . 'kirbytext.php',
|
||||||
|
'kirbytagabstract' => __DIR__ . DS . 'core' . DS . 'kirbytag.php',
|
||||||
|
'pageabstract' => __DIR__ . DS . 'core' . DS . 'page.php',
|
||||||
|
'roleabstract' => __DIR__ . DS . 'core' . DS . 'role.php',
|
||||||
|
'rolesabstract' => __DIR__ . DS . 'core' . DS . 'roles.php',
|
||||||
|
'siteabstract' => __DIR__ . DS . 'core' . DS . 'site.php',
|
||||||
|
'usersabstract' => __DIR__ . DS . 'core' . DS . 'users.php',
|
||||||
|
'userabstract' => __DIR__ . DS . 'core' . DS . 'user.php',
|
||||||
|
|
||||||
|
// lib
|
||||||
|
'pageextension' => __DIR__ . DS . 'lib' . DS . 'pageextension.php',
|
||||||
|
'structure' => __DIR__ . DS . 'lib' . DS . 'structure.php',
|
||||||
|
|
||||||
|
// parsedown
|
||||||
|
'parsedown' => __DIR__ . DS . 'vendors' . DS . 'parsedown.php',
|
||||||
|
'parsedownextra' => __DIR__ . DS . 'vendors' . DS . 'parsedownextra.php',
|
||||||
|
|
||||||
|
// smartypants
|
||||||
|
'smartypantstypographer_parser' => __DIR__ . DS . 'vendors' . DS . 'smartypants.php',
|
||||||
|
|
||||||
|
));
|
||||||
|
|
||||||
|
// load all helper functions
|
||||||
|
include(__DIR__ . DS . 'helpers.php');
|
18
kirby/branches/default.php
Normal file
18
kirby/branches/default.php
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
class Asset extends AssetAbstract {}
|
||||||
|
class Avatar extends AvatarAbstract {}
|
||||||
|
class Page extends PageAbstract {}
|
||||||
|
class Pages extends PagesAbstract {}
|
||||||
|
class Children extends ChildrenAbstract {}
|
||||||
|
class Content extends ContentAbstract {}
|
||||||
|
class Field extends FieldAbstract {}
|
||||||
|
class File extends FileAbstract {}
|
||||||
|
class Files extends FilesAbstract {}
|
||||||
|
class Kirbytext extends KirbytextAbstract {}
|
||||||
|
class Kirbytag extends KirbytagAbstract {}
|
||||||
|
class Role extends RoleAbstract {}
|
||||||
|
class Roles extends RolesAbstract {}
|
||||||
|
class Site extends SiteAbstract {}
|
||||||
|
class Users extends UsersAbstract {}
|
||||||
|
class User extends UserAbstract {}
|
29
kirby/branches/multilang.php
Normal file
29
kirby/branches/multilang.php
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unmodified classes
|
||||||
|
*/
|
||||||
|
class Asset extends AssetAbstract {}
|
||||||
|
class Avatar extends AvatarAbstract {}
|
||||||
|
class Pages extends PagesAbstract {}
|
||||||
|
class Children extends ChildrenAbstract {}
|
||||||
|
class Files extends FilesAbstract {}
|
||||||
|
class Kirbytext extends KirbytextAbstract {}
|
||||||
|
class Kirbytag extends KirbytagAbstract {}
|
||||||
|
class Role extends RoleAbstract {}
|
||||||
|
class Roles extends RolesAbstract {}
|
||||||
|
class Users extends UsersAbstract {}
|
||||||
|
class User extends UserAbstract {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Modified classes
|
||||||
|
*/
|
||||||
|
load(array(
|
||||||
|
'content' => __DIR__ . DS . 'multilang' . DS . 'content.php',
|
||||||
|
'field' => __DIR__ . DS . 'multilang' . DS . 'field.php',
|
||||||
|
'file' => __DIR__ . DS . 'multilang' . DS . 'file.php',
|
||||||
|
'language' => __DIR__ . DS . 'multilang' . DS . 'language.php',
|
||||||
|
'languages' => __DIR__ . DS . 'multilang' . DS . 'languages.php',
|
||||||
|
'page' => __DIR__ . DS . 'multilang' . DS . 'page.php',
|
||||||
|
'site' => __DIR__ . DS . 'multilang' . DS . 'site.php',
|
||||||
|
));
|
41
kirby/branches/multilang/content.php
Normal file
41
kirby/branches/multilang/content.php
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Content
|
||||||
|
*/
|
||||||
|
class Content extends ContentAbstract {
|
||||||
|
|
||||||
|
public $language = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*/
|
||||||
|
public function __construct($page, $root, $language) {
|
||||||
|
|
||||||
|
parent::__construct($page, $root);
|
||||||
|
|
||||||
|
$this->name = f::name($this->name);
|
||||||
|
$this->language = $language;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public function realroot() {
|
||||||
|
return dirname($this->root()) . DS . $this->name() . '.' . $this->language . '.' . f::extension($this->root());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function exists() {
|
||||||
|
return file_exists($this->realroot());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function language() {
|
||||||
|
|
||||||
|
if(!is_null($this->language)) return $this->language;
|
||||||
|
|
||||||
|
$codes = $this->page->site()->languages()->codes();
|
||||||
|
$code = f::extension(f::name($this->root));
|
||||||
|
|
||||||
|
return $this->language = in_array($code, $codes) ? $this->page->site()->languages()->find($code) : false;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
34
kirby/branches/multilang/field.php
Normal file
34
kirby/branches/multilang/field.php
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Field
|
||||||
|
*/
|
||||||
|
class Field extends FieldAbstract {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns if a field is translated in the current/provided language
|
||||||
|
* @param string $lang Language code
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
public function isTranslated($lang = null) {
|
||||||
|
|
||||||
|
$site = $this->page->site();
|
||||||
|
|
||||||
|
// use current language if $lang not set
|
||||||
|
if(is_null($lang)) $lang = $site->language()->code();
|
||||||
|
|
||||||
|
// if language is default/fallback language
|
||||||
|
if($site->language($lang)->default()) return true;
|
||||||
|
|
||||||
|
$current = $this->page->content($lang);
|
||||||
|
$default = $this->page->content($site->defaultLanguage->code);
|
||||||
|
|
||||||
|
$field = $current->get($this->key);
|
||||||
|
$untranslated = $default->get($this->key)->value();
|
||||||
|
|
||||||
|
return $field->isNotEmpty() and $field->value() !== $untranslated;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
170
kirby/branches/multilang/file.php
Normal file
170
kirby/branches/multilang/file.php
Normal file
|
@ -0,0 +1,170 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* File
|
||||||
|
*/
|
||||||
|
class File extends FileAbstract {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the full root for the content file
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function textfile($lang = null) {
|
||||||
|
return $this->page->textfile($this->filename(), $lang);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the meta information
|
||||||
|
*
|
||||||
|
* @param string $lang optional language code
|
||||||
|
* @return Content
|
||||||
|
*/
|
||||||
|
public function meta($lang = null) {
|
||||||
|
|
||||||
|
// get the content for the current language
|
||||||
|
if(is_null($lang)) {
|
||||||
|
|
||||||
|
// the current language's content can be cached
|
||||||
|
if(isset($this->cache['meta'])) return $this->cache['meta'];
|
||||||
|
|
||||||
|
// get the current content
|
||||||
|
$meta = $this->_meta($this->site->language->code);
|
||||||
|
|
||||||
|
// get the fallback content
|
||||||
|
if($this->site->language->code != $this->site->defaultLanguage->code) {
|
||||||
|
|
||||||
|
// fetch the default language content
|
||||||
|
$defaultMeta = $this->_meta($this->site->defaultLanguage->code);
|
||||||
|
|
||||||
|
// replace all missing fields with values from the default content
|
||||||
|
foreach($defaultMeta->data as $key => $field) {
|
||||||
|
if(empty($meta->data[$key]->value)) {
|
||||||
|
$meta->data[$key] = $field;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// cache the meta for this language
|
||||||
|
return $this->cache['meta'] = $meta;
|
||||||
|
|
||||||
|
// get the meta for another language
|
||||||
|
} else {
|
||||||
|
return $this->_meta($lang);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Private method to simplify meta fetching
|
||||||
|
*
|
||||||
|
* @return Content
|
||||||
|
*/
|
||||||
|
protected function _meta($lang) {
|
||||||
|
|
||||||
|
// get the inventory
|
||||||
|
$inventory = $this->page->inventory();
|
||||||
|
|
||||||
|
// try to fetch the content for this language
|
||||||
|
$meta = isset($inventory['meta'][$this->filename][$lang]) ? $inventory['meta'][$this->filename][$lang] : null;
|
||||||
|
|
||||||
|
// try to replace empty content with the default language content
|
||||||
|
if(empty($meta) and isset($inventory['meta'][$this->filename][$this->site->defaultLanguage->code])) {
|
||||||
|
$meta = $inventory['meta'][$this->filename][$this->site->defaultLanguage->code];
|
||||||
|
}
|
||||||
|
|
||||||
|
// find and cache the content for this language
|
||||||
|
return new Content($this->page, $this->page->root() . DS . $meta, $lang);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Renames the file and also its meta info txt
|
||||||
|
*
|
||||||
|
* @param string $filename
|
||||||
|
* @param boolean $safeName
|
||||||
|
*/
|
||||||
|
public function rename($name, $safeName = true) {
|
||||||
|
|
||||||
|
$filename = $this->createNewFilename($name, $safeName);
|
||||||
|
$root = $this->dir() . DS . $filename;
|
||||||
|
|
||||||
|
if(empty($name)) {
|
||||||
|
throw new Exception('The filename is missing');
|
||||||
|
}
|
||||||
|
|
||||||
|
if($root == $this->root()) return $filename;
|
||||||
|
|
||||||
|
if(file_exists($root)) {
|
||||||
|
throw new Exception('A file with that name already exists');
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!f::move($this->root(), $root)) {
|
||||||
|
throw new Exception('The file could not be renamed');
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach($this->site->languages() as $lang) {
|
||||||
|
|
||||||
|
// rename all meta files
|
||||||
|
$meta = $this->textfile($lang->code());
|
||||||
|
|
||||||
|
if(file_exists($meta)) {
|
||||||
|
f::move($meta, $this->page->textfile($filename, $lang->code()));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// reset the page cache
|
||||||
|
$this->page->reset();
|
||||||
|
|
||||||
|
// reset the basics
|
||||||
|
$this->root = $root;
|
||||||
|
$this->filename = $filename;
|
||||||
|
$this->name = $name;
|
||||||
|
$this->cache = array();
|
||||||
|
|
||||||
|
cache::flush();
|
||||||
|
|
||||||
|
return $filename;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public function update($data = array(), $lang = null) {
|
||||||
|
|
||||||
|
$data = array_merge((array)$this->meta()->toArray(), $data);
|
||||||
|
|
||||||
|
foreach($data as $k => $v) {
|
||||||
|
if(is_null($v)) unset($data[$k]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!data::write($this->textfile($lang), $data, 'kd')) {
|
||||||
|
throw new Exception('The file data could not be saved');
|
||||||
|
}
|
||||||
|
|
||||||
|
// reset the page cache
|
||||||
|
$this->page->reset();
|
||||||
|
|
||||||
|
// reset the file cache
|
||||||
|
$this->cache = array();
|
||||||
|
|
||||||
|
cache::flush();
|
||||||
|
return true;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public function delete() {
|
||||||
|
|
||||||
|
foreach($this->site->languages() as $lang) {
|
||||||
|
// delete the meta file for each language
|
||||||
|
f::remove($this->textfile($lang->code()));
|
||||||
|
}
|
||||||
|
|
||||||
|
parent::delete();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
34
kirby/branches/multilang/language.php
Normal file
34
kirby/branches/multilang/language.php
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Language
|
||||||
|
*
|
||||||
|
* A single language object
|
||||||
|
*/
|
||||||
|
class Language extends Obj {
|
||||||
|
|
||||||
|
public function __construct($site, $lang) {
|
||||||
|
|
||||||
|
$this->site = $site;
|
||||||
|
$this->code = $lang['code'];
|
||||||
|
$this->name = $lang['name'];
|
||||||
|
$this->locale = $lang['locale'];
|
||||||
|
$this->default = (isset($lang['default']) and $lang['default']);
|
||||||
|
$this->direction = (isset($lang['direction']) and $lang['direction'] == 'rtl') ? 'rtl' : 'ltr';
|
||||||
|
$this->url = isset($lang['url']) ? $lang['url'] : $lang['code'];
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public function url() {
|
||||||
|
return url::makeAbsolute($this->url, $this->site->url());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isDefault() {
|
||||||
|
return $this->default;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __toString() {
|
||||||
|
return $this->code;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
28
kirby/branches/multilang/languages.php
Normal file
28
kirby/branches/multilang/languages.php
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Languages
|
||||||
|
*
|
||||||
|
* Holds all available Language objects for the site
|
||||||
|
*/
|
||||||
|
class Languages extends Collection {
|
||||||
|
|
||||||
|
protected $site = null;
|
||||||
|
|
||||||
|
public function __construct($site) {
|
||||||
|
return $this->site = $site;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function find($code) {
|
||||||
|
return isset($this->data[$code]) ? $this->data[$code] : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function codes() {
|
||||||
|
return $this->keys();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function findDefault() {
|
||||||
|
return $this->site->defaultLanguage();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
281
kirby/branches/multilang/page.php
Normal file
281
kirby/branches/multilang/page.php
Normal file
|
@ -0,0 +1,281 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Page
|
||||||
|
*/
|
||||||
|
class Page extends PageAbstract {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the root for the content file
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function textfile($template = null, $lang = null) {
|
||||||
|
if(is_null($template)) $template = $this->intendedTemplate();
|
||||||
|
if(is_null($lang)) $lang = $this->site->language->code;
|
||||||
|
return textfile($this->diruri(), $template, $lang);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the translated URI
|
||||||
|
*/
|
||||||
|
public function uri($lang = null) {
|
||||||
|
// build the page's uri with the parent uri and the page's slug
|
||||||
|
return ltrim($this->parent->uri($lang) . '/' . $this->slug($lang), '/');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the URL key from the content file
|
||||||
|
* if available and otherwise returns the page UID
|
||||||
|
*
|
||||||
|
* @param string $lang
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function urlKey($lang = null) {
|
||||||
|
|
||||||
|
if($content = $this->content($lang)) {
|
||||||
|
// search for a translated url_key in that language
|
||||||
|
if($key = (string)a::get((array)$content->data(), 'url_key')) {
|
||||||
|
// if available, use the translated url key as slug
|
||||||
|
return $key;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->uid();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the slug for the page
|
||||||
|
* The slug is the last part of the URL path
|
||||||
|
* For multilang sites this can be translated with a URL-Key field
|
||||||
|
* in the text file for this page.
|
||||||
|
*
|
||||||
|
* @param string $lang Optional language code to get the translated slug
|
||||||
|
* @return string i.e. 01-projects returns projects
|
||||||
|
*/
|
||||||
|
public function slug($lang = null) {
|
||||||
|
|
||||||
|
$default = $this->site->defaultLanguage->code;
|
||||||
|
$current = $this->site->language->code;
|
||||||
|
|
||||||
|
// get the slug for the current language
|
||||||
|
if(is_null($lang)) {
|
||||||
|
|
||||||
|
// the current language's slug can be cached
|
||||||
|
if(isset($this->cache['slug'])) return $this->cache['slug'];
|
||||||
|
|
||||||
|
// if the current language is the default language
|
||||||
|
// simply return the uid
|
||||||
|
if($current == $default) {
|
||||||
|
return $this->cache['slug'] = $this->uid();
|
||||||
|
}
|
||||||
|
|
||||||
|
// get the translated url key
|
||||||
|
return $this->urlKey();
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
// if the passed language code is the current language code
|
||||||
|
// we can simply return the slug method without a language code specified
|
||||||
|
if($lang == $current) {
|
||||||
|
return $this->slug();
|
||||||
|
}
|
||||||
|
|
||||||
|
// the slug for the default language is just the name of the folder
|
||||||
|
if($lang == $default) {
|
||||||
|
return $this->uid();
|
||||||
|
}
|
||||||
|
|
||||||
|
// get the translated url key
|
||||||
|
return $this->urlKey($lang);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the full url for the page
|
||||||
|
*
|
||||||
|
* @param string $lang Optional language code to get the URL for that specific language on multilang sites
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function url() {
|
||||||
|
|
||||||
|
$args = func_get_args();
|
||||||
|
$lang = array_shift($args);
|
||||||
|
|
||||||
|
// for multi language sites every url needs
|
||||||
|
// to be treated specially to make sure each uid is translated properly
|
||||||
|
// and language codes are prepended if needed
|
||||||
|
if(is_null($lang)) {
|
||||||
|
// get the current language
|
||||||
|
$lang = $this->site->language->code;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Kirby is trying to remove the home folder name from the url
|
||||||
|
if($this->isHomePage()) {
|
||||||
|
return $this->site->url($lang);
|
||||||
|
} else if($this->parent->isHomePage()) {
|
||||||
|
return $this->site->url($lang) . '/' . $this->parent->slug($lang) . '/' . $this->slug($lang);
|
||||||
|
} else {
|
||||||
|
return $this->parent->url($lang) . '/' . $this->slug($lang);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Modified inventory fetcher
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function inventory() {
|
||||||
|
|
||||||
|
$inventory = parent::inventory();
|
||||||
|
$defaultLang = $this->site->defaultLanguage->code;
|
||||||
|
$expression = '!(.*?)(\.(' . implode('|', $this->site->languages->codes()) . ')|)\.' . $this->kirby->options['content.file.extension'] . '$!i';
|
||||||
|
|
||||||
|
foreach($inventory['meta'] as $key => $meta) {
|
||||||
|
$inventory['meta'][$key] = array($defaultLang => $meta);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach($inventory['content'] as $key => $content) {
|
||||||
|
|
||||||
|
preg_match($expression, $content, $match);
|
||||||
|
|
||||||
|
$file = $match[1];
|
||||||
|
$lang = isset($match[3]) ? $match[3] : null;
|
||||||
|
|
||||||
|
if(in_array($file, $inventory['files'])) {
|
||||||
|
$inventory['meta'][$file][$lang] = $content;
|
||||||
|
} else {
|
||||||
|
|
||||||
|
if(is_null($lang)) {
|
||||||
|
$lang = f::extension($file);
|
||||||
|
if(empty($lang)) $lang = $defaultLang;
|
||||||
|
}
|
||||||
|
|
||||||
|
$inventory['content'][$lang] = $content;
|
||||||
|
}
|
||||||
|
|
||||||
|
unset($inventory['content'][$key]);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// try to fill the default language with something else
|
||||||
|
if(!isset($inventory['content'][$defaultLang])) {
|
||||||
|
$inventory['content'][$defaultLang] = a::first($inventory['content']);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $inventory;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the content object for this page
|
||||||
|
*
|
||||||
|
* @param string $lang optional language code
|
||||||
|
* @return Content
|
||||||
|
*/
|
||||||
|
public function content($lang = null) {
|
||||||
|
|
||||||
|
// get the content for the current language
|
||||||
|
if(is_null($lang)) {
|
||||||
|
|
||||||
|
// the current language's content can be cached
|
||||||
|
if(isset($this->cache['content'])) return $this->cache['content'];
|
||||||
|
|
||||||
|
// get the current content
|
||||||
|
$content = $this->_content($this->site->language->code);
|
||||||
|
|
||||||
|
// get the fallback content
|
||||||
|
if($this->site->language->code != $this->site->defaultLanguage->code) {
|
||||||
|
|
||||||
|
// fetch the default language content
|
||||||
|
$defaultContent = $this->_content($this->site->defaultLanguage->code);
|
||||||
|
|
||||||
|
// replace all missing fields with values from the default content
|
||||||
|
foreach($defaultContent->data as $key => $field) {
|
||||||
|
if(empty($content->data[$key]->value)) {
|
||||||
|
$content->data[$key] = $field;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// find and cache the content for this language
|
||||||
|
return $this->cache['content'] = $content;
|
||||||
|
|
||||||
|
// get the content for another language
|
||||||
|
} else {
|
||||||
|
return $this->_content($lang);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Private method to simplify content fetching
|
||||||
|
*
|
||||||
|
* @return Content
|
||||||
|
*/
|
||||||
|
protected function _content($lang) {
|
||||||
|
|
||||||
|
// get the inventory
|
||||||
|
$inventory = $this->inventory();
|
||||||
|
|
||||||
|
// try to fetch the content for this language
|
||||||
|
$content = isset($inventory['content'][$lang]) ? $inventory['content'][$lang] : null;
|
||||||
|
|
||||||
|
// try to replace empty content with the default language content
|
||||||
|
if(empty($content) and isset($inventory['content'][$this->site->defaultLanguage->code])) {
|
||||||
|
$content = $inventory['content'][$this->site->defaultLanguage->code];
|
||||||
|
}
|
||||||
|
|
||||||
|
// find and cache the content for this language
|
||||||
|
return new Content($this, $this->root() . DS . $content, $lang);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new page object
|
||||||
|
*
|
||||||
|
* @param string $uri
|
||||||
|
* @param string $template
|
||||||
|
* @param array $data
|
||||||
|
*/
|
||||||
|
static public function create($uri, $template, $data = array()) {
|
||||||
|
return parent::create($uri, $template . '.' . site()->defaultLanguage->code, $data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update the page with a new set of data
|
||||||
|
*
|
||||||
|
* @param array $data
|
||||||
|
*/
|
||||||
|
public function update($input = array(), $lang = null) {
|
||||||
|
|
||||||
|
$data = a::update($this->content($lang)->toArray(), $input);
|
||||||
|
|
||||||
|
if(!data::write($this->textfile(null, $lang), $data, 'kd')) {
|
||||||
|
throw new Exception('The page could not be updated');
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->kirby->cache()->flush();
|
||||||
|
$this->reset();
|
||||||
|
$this->touch();
|
||||||
|
return true;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the name of the content text file / intended template
|
||||||
|
* So even if there's no such template it will return the intended name.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function intendedTemplate() {
|
||||||
|
if(isset($this->cache['intendedTemplate'])) return $this->cache['intendedTemplate'];
|
||||||
|
return $this->cache['intendedTemplate'] = $this->content($this->site->defaultLanguage()->code())->exists() ? $this->content()->name() : 'default';
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
193
kirby/branches/multilang/site.php
Normal file
193
kirby/branches/multilang/site.php
Normal file
|
@ -0,0 +1,193 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Site
|
||||||
|
*
|
||||||
|
* Modified Site object
|
||||||
|
*/
|
||||||
|
class Site extends SiteAbstract {
|
||||||
|
|
||||||
|
public $languages;
|
||||||
|
public $language;
|
||||||
|
public $defaultLanguage;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*/
|
||||||
|
public function __construct(Kirby $kirby) {
|
||||||
|
|
||||||
|
parent::__construct($kirby);
|
||||||
|
|
||||||
|
$this->languages = new Languages($this);
|
||||||
|
|
||||||
|
foreach($kirby->options['languages'] as $lang) {
|
||||||
|
|
||||||
|
$language = new Language($this, $lang);
|
||||||
|
|
||||||
|
// store the default language
|
||||||
|
if($language->default) $this->defaultLanguage = $this->language = $language;
|
||||||
|
|
||||||
|
// add the language to the collection
|
||||||
|
$this->languages->data[$language->code] = $language;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the translated URI
|
||||||
|
*/
|
||||||
|
public function uri($lang = null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function slug($lang = null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the url of the site
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function url($lang = false) {
|
||||||
|
if($lang) {
|
||||||
|
// return the specific language url
|
||||||
|
return $this->languages->find($lang)->url();
|
||||||
|
} else {
|
||||||
|
return parent::url();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Marks the site as a multilanguage site
|
||||||
|
*
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
public function multilang() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the Languages Collection
|
||||||
|
*
|
||||||
|
* @return Languages
|
||||||
|
*/
|
||||||
|
public function languages() {
|
||||||
|
return $this->languages;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the current language
|
||||||
|
* or any other language by language code
|
||||||
|
*
|
||||||
|
* @param string $code
|
||||||
|
* @return Language
|
||||||
|
*/
|
||||||
|
public function language($code = null) {
|
||||||
|
if(is_null($code)) return $this->language;
|
||||||
|
return $this->languages()->find($code);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the default language
|
||||||
|
*
|
||||||
|
* @return Language
|
||||||
|
*/
|
||||||
|
public function defaultLanguage() {
|
||||||
|
return $this->defaultLanguage;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tries to find the language for the current visitor
|
||||||
|
*
|
||||||
|
* @return Language
|
||||||
|
*/
|
||||||
|
public function visitorLanguage() {
|
||||||
|
return $this->languages()->find(visitor::acceptedLanguageCode());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the detected language
|
||||||
|
*
|
||||||
|
* @return Language
|
||||||
|
*/
|
||||||
|
public function detectedLanguage() {
|
||||||
|
|
||||||
|
if($language = $this->visitorLanguage()) {
|
||||||
|
return $language;
|
||||||
|
} else {
|
||||||
|
return $this->defaultLanguage();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the language which will be
|
||||||
|
* remembered for the next visit
|
||||||
|
*
|
||||||
|
* @return Language
|
||||||
|
*/
|
||||||
|
public function sessionLanguage() {
|
||||||
|
if($code = s::get('language') and $language = $this->languages()->find($code)) {
|
||||||
|
return $language;
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function switchLanguage(Language $language) {
|
||||||
|
|
||||||
|
s::set('language', $language->code());
|
||||||
|
|
||||||
|
if($this->language()->code() != $language->code()) {
|
||||||
|
go($this->page()->url($language->code()));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the currently active page
|
||||||
|
* and returns its page object
|
||||||
|
*
|
||||||
|
* @param string $uri
|
||||||
|
* @return Page
|
||||||
|
*/
|
||||||
|
public function visit($uri = '', $lang = null) {
|
||||||
|
|
||||||
|
// if the language code is missing or the code is invalid (TODO)
|
||||||
|
if(!in_array($lang, $this->languages()->keys())) {
|
||||||
|
$lang = $this->defaultLanguage->code;
|
||||||
|
}
|
||||||
|
|
||||||
|
// set the current language
|
||||||
|
$this->language = $this->languages()->data[$lang];
|
||||||
|
|
||||||
|
// clean the uri
|
||||||
|
$uri = trim($uri, '/');
|
||||||
|
|
||||||
|
if(empty($uri)) {
|
||||||
|
return $this->page = $this->homePage();
|
||||||
|
} else {
|
||||||
|
|
||||||
|
if($lang == $this->defaultLanguage->code and $page = $this->children()->find($uri)) {
|
||||||
|
return $this->page = $page;
|
||||||
|
} else if($page = $this->children()->findByURI($uri)) {
|
||||||
|
return $this->page = $page;
|
||||||
|
} else {
|
||||||
|
return $this->page = $this->errorPage();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the locale for the site
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function locale() {
|
||||||
|
return $this->language->locale;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
21
kirby/core/asset.php
Normal file
21
kirby/core/asset.php
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
abstract class AssetAbstract extends Media {
|
||||||
|
|
||||||
|
public $kirby = null;
|
||||||
|
|
||||||
|
use Kirby\Traits\Image;
|
||||||
|
|
||||||
|
public function __construct($path) {
|
||||||
|
$this->kirby = kirby::instance();
|
||||||
|
if(is_a($path, 'Media')) {
|
||||||
|
parent::__construct($path->root(), $path->url());
|
||||||
|
} else {
|
||||||
|
parent::__construct(
|
||||||
|
url::isAbsolute($path) ? null : $this->kirby->roots()->index() . DS . ltrim($path, DS),
|
||||||
|
url::makeAbsolute($path)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
30
kirby/core/avatar.php
Normal file
30
kirby/core/avatar.php
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
abstract class AvatarAbstract extends Media {
|
||||||
|
|
||||||
|
public $user = null;
|
||||||
|
public $kirby = null;
|
||||||
|
|
||||||
|
use Kirby\Traits\Image;
|
||||||
|
|
||||||
|
public function __construct(User $user) {
|
||||||
|
|
||||||
|
// store the parent user object
|
||||||
|
$this->user = $user;
|
||||||
|
|
||||||
|
// this should rather be coming from the user object
|
||||||
|
$this->kirby = kirby::instance();
|
||||||
|
|
||||||
|
// try to find the avatar
|
||||||
|
if($file = f::resolve($this->kirby->roots()->avatars() . DS . $user->username(), ['jpg', 'jpeg', 'gif', 'png'])) {
|
||||||
|
$filename = f::filename($file);
|
||||||
|
} else {
|
||||||
|
$filename = $user->username() . '.jpg';
|
||||||
|
$file = $this->kirby->roots()->avatars() . DS . $filename;
|
||||||
|
}
|
||||||
|
|
||||||
|
parent::__construct($file, $this->kirby->urls()->avatars() . '/' . $filename);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
207
kirby/core/children.php
Normal file
207
kirby/core/children.php
Normal file
|
@ -0,0 +1,207 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Children
|
||||||
|
*
|
||||||
|
* @package Kirby CMS
|
||||||
|
* @author Bastian Allgeier <bastian@getkirby.com>
|
||||||
|
* @link http://getkirby.com
|
||||||
|
* @copyright Bastian Allgeier
|
||||||
|
* @license http://getkirby.com/license
|
||||||
|
*/
|
||||||
|
abstract class ChildrenAbstract extends Pages {
|
||||||
|
|
||||||
|
protected $page = null;
|
||||||
|
protected $cache = array();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*/
|
||||||
|
public function __construct($page) {
|
||||||
|
$this->page = $page;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new Page object and adds it to the collection
|
||||||
|
*/
|
||||||
|
public function add($dirname) {
|
||||||
|
$page = new Page($this->page, $dirname);
|
||||||
|
$this->data[$page->id()] = $page;
|
||||||
|
return $page;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new subpage
|
||||||
|
*
|
||||||
|
* @param string $uid
|
||||||
|
* @param string $template
|
||||||
|
* @param array $data
|
||||||
|
*/
|
||||||
|
public function create($uid, $template, $data = array()) {
|
||||||
|
$page = page::create($this->page->id() . '/' . $uid, $template, $data);
|
||||||
|
$this->data[$page->id()] = $page;
|
||||||
|
return $page;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the parent page
|
||||||
|
*
|
||||||
|
* @return Page
|
||||||
|
*/
|
||||||
|
public function page() {
|
||||||
|
return $this->page;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the Children of Children
|
||||||
|
*
|
||||||
|
* @return Children
|
||||||
|
*/
|
||||||
|
public function children() {
|
||||||
|
$grandChildren = new Children($this->page);
|
||||||
|
foreach($this->data as $page) {
|
||||||
|
foreach($page->children() as $subpage) {
|
||||||
|
$grandChildren->data[$subpage->id()] = $subpage;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $grandChildren;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find a specific page by its uri
|
||||||
|
*
|
||||||
|
* @return Page or false
|
||||||
|
*/
|
||||||
|
public function find() {
|
||||||
|
|
||||||
|
$args = func_get_args();
|
||||||
|
|
||||||
|
if(!count($args)) {
|
||||||
|
return false;
|
||||||
|
} else if (count($args) === 1 and is_array($args[0])) {
|
||||||
|
$args = $args[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
if(count($args) > 1) {
|
||||||
|
$collection = new Children($this->page);
|
||||||
|
foreach($args as $id) {
|
||||||
|
if($page = $this->find($id)) {
|
||||||
|
$collection->data[$page->id()] = $page;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $collection;
|
||||||
|
} else {
|
||||||
|
|
||||||
|
// get the first argument and remove slashes
|
||||||
|
$id = trim($args[0], '/');
|
||||||
|
|
||||||
|
// build the direct uri
|
||||||
|
$directId = trim($this->page()->id() . '/' . $id, '/');
|
||||||
|
|
||||||
|
// fast access to direct uris
|
||||||
|
if(isset($this->data[$directId])) return $this->data[$directId];
|
||||||
|
|
||||||
|
$path = explode('/', $id);
|
||||||
|
$obj = $this;
|
||||||
|
$page = false;
|
||||||
|
|
||||||
|
foreach($path as $uid) {
|
||||||
|
|
||||||
|
$id = ltrim($obj->page()->id() . '/' . $uid, '/');
|
||||||
|
|
||||||
|
if(!isset($obj->data[$id])) return false;
|
||||||
|
|
||||||
|
$page = $obj->data[$id];
|
||||||
|
$obj = $page->children();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return $page;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finds pages by it's unique URI
|
||||||
|
*
|
||||||
|
* @param mixed $uri Either a single URI string or an array of URIs
|
||||||
|
* @param string $use The field, which should be used (uid or slug)
|
||||||
|
* @return mixed Either a Page object, a Pages object for multiple pages or null if nothing could be found
|
||||||
|
*/
|
||||||
|
public function findByURI() {
|
||||||
|
|
||||||
|
$args = func_get_args();
|
||||||
|
|
||||||
|
if(count($args) == 0) {
|
||||||
|
return false;
|
||||||
|
} else if(count($args) > 1) {
|
||||||
|
$collection = new Children($this->page);
|
||||||
|
foreach($args as $uri) {
|
||||||
|
$page = $this->findByURI($uri);
|
||||||
|
if($page) $collection->data[$page->id()] = $page;
|
||||||
|
}
|
||||||
|
return $collection;
|
||||||
|
} else {
|
||||||
|
|
||||||
|
// get the first argument and remove slashes
|
||||||
|
$uri = trim($args[0], '/');
|
||||||
|
$array = str::split($uri, '/');
|
||||||
|
$obj = $this;
|
||||||
|
$page = false;
|
||||||
|
|
||||||
|
foreach($array as $p) {
|
||||||
|
|
||||||
|
$next = $obj->findBy('slug', $p);
|
||||||
|
|
||||||
|
if(!$next) break;
|
||||||
|
|
||||||
|
$page = $next;
|
||||||
|
$obj = $next->children();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return ($page and $page->slug() != a::last($array)) ? false : $page;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a clean one-level collection with all
|
||||||
|
* pages, subpages, subsubpages, etc.
|
||||||
|
*
|
||||||
|
* @param object Pages object for recursive indexing
|
||||||
|
* @return Children
|
||||||
|
*/
|
||||||
|
public function index(Children $obj = null) {
|
||||||
|
|
||||||
|
if(is_null($obj)) {
|
||||||
|
if(isset($this->cache['index'])) return $this->cache['index'];
|
||||||
|
$this->cache['index'] = new Children($this->page);
|
||||||
|
$obj = $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach($obj->data as $key => $page) {
|
||||||
|
$this->cache['index']->data[$page->uri()] = $page;
|
||||||
|
$this->index($page->children());
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->cache['index'];
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extended group method
|
||||||
|
* detaches children and converts them to
|
||||||
|
* a simple pages collection
|
||||||
|
*
|
||||||
|
* @param function $callback
|
||||||
|
* @return Pages
|
||||||
|
*/
|
||||||
|
public function group($callback) {
|
||||||
|
$collection = new Pages($this);
|
||||||
|
return $collection->group($callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
151
kirby/core/content.php
Normal file
151
kirby/core/content.php
Normal file
|
@ -0,0 +1,151 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Content
|
||||||
|
*
|
||||||
|
* @package Kirby CMS
|
||||||
|
* @author Bastian Allgeier <bastian@getkirby.com>
|
||||||
|
* @link http://getkirby.com
|
||||||
|
* @copyright Bastian Allgeier
|
||||||
|
* @license http://getkirby.com/license
|
||||||
|
*/
|
||||||
|
abstract class ContentAbstract {
|
||||||
|
|
||||||
|
public $page = null;
|
||||||
|
public $root = null;
|
||||||
|
public $raw = null;
|
||||||
|
public $data = array();
|
||||||
|
public $fields = array();
|
||||||
|
public $name = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*/
|
||||||
|
public function __construct($page, $root) {
|
||||||
|
|
||||||
|
$this->page = $page;
|
||||||
|
$this->root = $root;
|
||||||
|
$this->name = pathinfo($root, PATHINFO_FILENAME);
|
||||||
|
|
||||||
|
// stop at invalid files
|
||||||
|
if(empty($this->root) or !is_file($this->root) or !is_readable($this->root)) return;
|
||||||
|
|
||||||
|
// read the content file and remove the BOM
|
||||||
|
$this->raw = str_replace(BOM, '', file_get_contents($this->root));
|
||||||
|
|
||||||
|
// explode all fields by the line separator
|
||||||
|
$fields = preg_split('!\n----\s*\n*!', $this->raw);
|
||||||
|
|
||||||
|
// loop through all fields and add them to the content
|
||||||
|
foreach($fields as $field) {
|
||||||
|
$pos = strpos($field, ':');
|
||||||
|
$key = str_replace(array('-', ' '), '_', strtolower(trim(substr($field, 0, $pos))));
|
||||||
|
|
||||||
|
// Don't add fields with empty keys
|
||||||
|
if(empty($key)) continue;
|
||||||
|
|
||||||
|
// add the key to the fields list
|
||||||
|
$this->fields[] = $key;
|
||||||
|
|
||||||
|
// add the key object
|
||||||
|
$this->data[$key] = new Field($this->page, $key, trim(substr($field, $pos+1)));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the root for the content file
|
||||||
|
*/
|
||||||
|
public function root() {
|
||||||
|
return $this->root;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the name of the content file
|
||||||
|
* without the extension. This is
|
||||||
|
* being used to determine the template for the page
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function name() {
|
||||||
|
return $this->name;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an array with all
|
||||||
|
* field names
|
||||||
|
*
|
||||||
|
* @return array3
|
||||||
|
*/
|
||||||
|
public function fields() {
|
||||||
|
return $this->fields;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the raw content from the file
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function raw() {
|
||||||
|
return $this->raw;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the entire data array
|
||||||
|
* with all field objects
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function data() {
|
||||||
|
return $this->data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the content file exists
|
||||||
|
*
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
public function exists() {
|
||||||
|
return file_exists($this->root);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a field from the content
|
||||||
|
*
|
||||||
|
* @return Field
|
||||||
|
*/
|
||||||
|
public function get($key, $arguments = null) {
|
||||||
|
|
||||||
|
// case-insensitive data fetching
|
||||||
|
$key = strtolower($key);
|
||||||
|
|
||||||
|
if(isset($this->data[$key])) {
|
||||||
|
return $this->data[$key];
|
||||||
|
} else {
|
||||||
|
// return an empty field as default
|
||||||
|
return new Field($this->page, $key);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if a field exists
|
||||||
|
*
|
||||||
|
* @param string $key
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
public function has($key) {
|
||||||
|
return isset($this->data[strtolower($key)]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __call($method, $arguments = null) {
|
||||||
|
return $this->get($method, $arguments);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function toArray() {
|
||||||
|
return array_map(function($item) {
|
||||||
|
return $item->value;
|
||||||
|
}, $this->data);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
55
kirby/core/field.php
Normal file
55
kirby/core/field.php
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Field
|
||||||
|
*
|
||||||
|
* @package Kirby CMS
|
||||||
|
* @author Bastian Allgeier <bastian@getkirby.com>
|
||||||
|
* @link http://getkirby.com
|
||||||
|
* @copyright Bastian Allgeier
|
||||||
|
* @license http://getkirby.com/license
|
||||||
|
*/
|
||||||
|
abstract class FieldAbstract {
|
||||||
|
|
||||||
|
static public $methods = array();
|
||||||
|
|
||||||
|
public $page;
|
||||||
|
public $key;
|
||||||
|
public $value;
|
||||||
|
|
||||||
|
public function __construct($page, $key, $value = '') {
|
||||||
|
$this->page = $page;
|
||||||
|
$this->key = $key;
|
||||||
|
$this->value = $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function page() {
|
||||||
|
return $this->page;
|
||||||
|
}
|
||||||
|
public function exists() {
|
||||||
|
return $this->page->content()->has($this->key);
|
||||||
|
}
|
||||||
|
public function key() {
|
||||||
|
return $this->key;
|
||||||
|
}
|
||||||
|
public function value() {
|
||||||
|
return $this->value;
|
||||||
|
}
|
||||||
|
public function isTranslated($lang = null) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
public function __toString() {
|
||||||
|
return (string)$this->value;
|
||||||
|
}
|
||||||
|
public function toString() {
|
||||||
|
return $this->value;
|
||||||
|
}
|
||||||
|
public function __call($method, $arguments = array()) {
|
||||||
|
if(isset(static::$methods[$method])) {
|
||||||
|
array_unshift($arguments, clone $this);
|
||||||
|
return call(static::$methods[$method], $arguments);
|
||||||
|
} else {
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
333
kirby/core/file.php
Normal file
333
kirby/core/file.php
Normal file
|
@ -0,0 +1,333 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* File
|
||||||
|
*
|
||||||
|
* @package Kirby CMS
|
||||||
|
* @author Bastian Allgeier <bastian@getkirby.com>
|
||||||
|
* @link http://getkirby.com
|
||||||
|
* @copyright Bastian Allgeier
|
||||||
|
* @license http://getkirby.com/license
|
||||||
|
*/
|
||||||
|
abstract class FileAbstract extends Media {
|
||||||
|
|
||||||
|
use Kirby\Traits\Image;
|
||||||
|
|
||||||
|
static public $methods = array();
|
||||||
|
|
||||||
|
public $kirby;
|
||||||
|
public $site;
|
||||||
|
public $page;
|
||||||
|
public $files;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*
|
||||||
|
* @param Files The parent files collection
|
||||||
|
* @param string The filename
|
||||||
|
*/
|
||||||
|
public function __construct(Files $files, $filename) {
|
||||||
|
|
||||||
|
$this->kirby = $files->kirby;
|
||||||
|
$this->site = $files->site;
|
||||||
|
$this->page = $files->page;
|
||||||
|
$this->files = $files;
|
||||||
|
$this->root = $this->files->page()->root() . DS . $filename;
|
||||||
|
|
||||||
|
parent::__construct($this->root);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the kirby object
|
||||||
|
*
|
||||||
|
* @return Kirby
|
||||||
|
*/
|
||||||
|
public function kirby() {
|
||||||
|
return $this->kirby;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the parent site object
|
||||||
|
*
|
||||||
|
* @return Site
|
||||||
|
*/
|
||||||
|
public function site() {
|
||||||
|
return $this->site;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the parent page object
|
||||||
|
*
|
||||||
|
* @return Page
|
||||||
|
*/
|
||||||
|
public function page() {
|
||||||
|
return $this->page;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the parent files collection
|
||||||
|
*
|
||||||
|
* @return Files
|
||||||
|
*/
|
||||||
|
public function files() {
|
||||||
|
return $this->files;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the full root for the content file
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function textfile() {
|
||||||
|
return $this->page->textfile($this->filename());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function siblings() {
|
||||||
|
return $this->files->not($this->filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function next() {
|
||||||
|
$siblings = $this->files;
|
||||||
|
$index = $siblings->indexOf($this);
|
||||||
|
if($index === false) return false;
|
||||||
|
return $this->files->nth($index+1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function hasNext() {
|
||||||
|
return $this->next();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function prev() {
|
||||||
|
$siblings = $this->files;
|
||||||
|
$index = $siblings->indexOf($this);
|
||||||
|
if($index === false) return false;
|
||||||
|
return $this->files->nth($index-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function hasPrev() {
|
||||||
|
return $this->prev();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the absolute URL for the file
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function url($raw = false) {
|
||||||
|
if($raw || empty($this->modifications)) {
|
||||||
|
return $this->page->contentUrl() . '/' . rawurlencode($this->filename);
|
||||||
|
} else {
|
||||||
|
return $this->kirby->component('thumb')->url($this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the relative URI for the image
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function uri() {
|
||||||
|
return $this->page->uri() . '/' . rawurlencode($this->filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the full directory path starting from the content folder
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function diruri() {
|
||||||
|
return $this->page->diruri() . '/' . rawurlencode($this->filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the meta information
|
||||||
|
*
|
||||||
|
* @return Content
|
||||||
|
*/
|
||||||
|
public function meta() {
|
||||||
|
|
||||||
|
if(isset($this->cache['meta'])) {
|
||||||
|
return $this->cache['meta'];
|
||||||
|
} else {
|
||||||
|
|
||||||
|
$inventory = $this->page->inventory();
|
||||||
|
$file = isset($inventory['meta'][$this->filename]) ? $this->page->root() . DS . $inventory['meta'][$this->filename] : null;
|
||||||
|
|
||||||
|
return $this->cache['meta'] = new Content($this->page, $file);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Custom modified method for files
|
||||||
|
*
|
||||||
|
* @param string $format
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function modified($format = null, $handler = null) {
|
||||||
|
return parent::modified($format, $handler ? $handler : $this->kirby->options['date.handler']);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Magic getter for all meta fields
|
||||||
|
*
|
||||||
|
* @return Field
|
||||||
|
*/
|
||||||
|
public function __call($key, $arguments = null) {
|
||||||
|
if(isset(static::$methods[$key])) {
|
||||||
|
if(!$arguments) $arguments = array();
|
||||||
|
array_unshift($arguments, clone $this);
|
||||||
|
return call(static::$methods[$key], $arguments);
|
||||||
|
} else {
|
||||||
|
return $this->meta()->get($key, $arguments);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates a new filename for a given name
|
||||||
|
* and makes sure to handle badly given extensions correctly
|
||||||
|
*
|
||||||
|
* @param string $name
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function createNewFilename($name, $safeName = true) {
|
||||||
|
|
||||||
|
$name = basename($safeName ? f::safeName($name) : $name);
|
||||||
|
$ext = f::extension($name);
|
||||||
|
|
||||||
|
// remove possible extensions
|
||||||
|
if(in_array($ext, f::extensions())) {
|
||||||
|
$name = f::name($name);
|
||||||
|
}
|
||||||
|
|
||||||
|
return trim($name . '.' . $this->extension(), '.');
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Renames the file and also its meta info txt
|
||||||
|
*
|
||||||
|
* @param string $filename
|
||||||
|
* @param boolean $safeName
|
||||||
|
*/
|
||||||
|
public function rename($name, $safeName = true) {
|
||||||
|
|
||||||
|
$filename = $this->createNewFilename($name, $safeName);
|
||||||
|
$root = $this->dir() . DS . $filename;
|
||||||
|
|
||||||
|
if(empty($name)) {
|
||||||
|
throw new Exception('The filename is missing');
|
||||||
|
}
|
||||||
|
|
||||||
|
if($root == $this->root()) return $filename;
|
||||||
|
|
||||||
|
if(file_exists($root)) {
|
||||||
|
throw new Exception('A file with that name already exists');
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!f::move($this->root(), $root)) {
|
||||||
|
throw new Exception('The file could not be renamed');
|
||||||
|
}
|
||||||
|
|
||||||
|
$meta = $this->textfile();
|
||||||
|
|
||||||
|
if(file_exists($meta)) {
|
||||||
|
f::move($meta, $this->page->textfile($filename));
|
||||||
|
}
|
||||||
|
|
||||||
|
// reset the page cache
|
||||||
|
$this->page->reset();
|
||||||
|
|
||||||
|
// reset the basics
|
||||||
|
$this->root = $root;
|
||||||
|
$this->filename = $filename;
|
||||||
|
$this->name = $name;
|
||||||
|
$this->cache = array();
|
||||||
|
|
||||||
|
cache::flush();
|
||||||
|
|
||||||
|
return $filename;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public function update($data = array()) {
|
||||||
|
|
||||||
|
$data = array_merge((array)$this->meta()->toArray(), $data);
|
||||||
|
|
||||||
|
foreach($data as $k => $v) {
|
||||||
|
if(is_null($v)) unset($data[$k]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!data::write($this->textfile(), $data, 'kd')) {
|
||||||
|
throw new Exception('The file data could not be saved');
|
||||||
|
}
|
||||||
|
|
||||||
|
// reset the page cache
|
||||||
|
$this->page->reset();
|
||||||
|
|
||||||
|
// reset the file cache
|
||||||
|
$this->cache = array();
|
||||||
|
|
||||||
|
cache::flush();
|
||||||
|
return true;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public function delete() {
|
||||||
|
|
||||||
|
// delete the meta file
|
||||||
|
f::remove($this->textfile());
|
||||||
|
|
||||||
|
if(!f::remove($this->root())) {
|
||||||
|
throw new Exception('The file could not be deleted');
|
||||||
|
}
|
||||||
|
|
||||||
|
cache::flush();
|
||||||
|
return true;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get formatted date fields
|
||||||
|
*
|
||||||
|
* @param string $format
|
||||||
|
* @param string $field
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function date($format = null, $field = 'date') {
|
||||||
|
if($timestamp = strtotime($this->meta()->$field())) {
|
||||||
|
if(is_null($format)) {
|
||||||
|
return $timestamp;
|
||||||
|
} else {
|
||||||
|
return $this->kirby->options['date.handler']($format, $timestamp);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts the entire file object into
|
||||||
|
* a plain PHP array
|
||||||
|
*
|
||||||
|
* @param closure $callback Filter callback
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function toArray($callback = null) {
|
||||||
|
|
||||||
|
$data = parent::toArray();
|
||||||
|
|
||||||
|
// add the meta content
|
||||||
|
$data['meta'] = $this->meta()->toArray();
|
||||||
|
|
||||||
|
if(is_null($callback)) {
|
||||||
|
return $data;
|
||||||
|
} else {
|
||||||
|
return array_map($callback, $data);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
137
kirby/core/files.php
Normal file
137
kirby/core/files.php
Normal file
|
@ -0,0 +1,137 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Files
|
||||||
|
*
|
||||||
|
* @package Kirby CMS
|
||||||
|
* @author Bastian Allgeier <bastian@getkirby.com>
|
||||||
|
* @link http://getkirby.com
|
||||||
|
* @copyright Bastian Allgeier
|
||||||
|
* @license http://getkirby.com/license
|
||||||
|
*/
|
||||||
|
abstract class FilesAbstract extends Collection {
|
||||||
|
|
||||||
|
static public $methods = array();
|
||||||
|
|
||||||
|
public $kirby = null;
|
||||||
|
public $site = null;
|
||||||
|
public $page = null;
|
||||||
|
|
||||||
|
public function __construct($page) {
|
||||||
|
$this->kirby = $page->kirby;
|
||||||
|
$this->site = $page->site;
|
||||||
|
$this->page = $page;
|
||||||
|
$inventory = $page->inventory();
|
||||||
|
|
||||||
|
foreach($inventory['files'] as $filename) {
|
||||||
|
$file = new File($this, $filename);
|
||||||
|
$this->data[strtolower($file->filename())] = $file;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public function kirby() {
|
||||||
|
return $this->kirby;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function site() {
|
||||||
|
return $this->site;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function page() {
|
||||||
|
return $this->page;
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
||||||
|
$files = clone $this;
|
||||||
|
$files->data = array();
|
||||||
|
foreach($args as $filename) {
|
||||||
|
$file = $this->find($filename);
|
||||||
|
if(!empty($file)) {
|
||||||
|
$files->data[$filename] = $file;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $files;
|
||||||
|
} else {
|
||||||
|
$filename = strtolower($args[0]);
|
||||||
|
return isset($this->data[$filename]) ? $this->data[$filename] : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a new collection of files without the given files
|
||||||
|
*
|
||||||
|
* @param args any number of filenames or file objects, passed as individual arguments
|
||||||
|
* @return object a new collection without the files
|
||||||
|
*/
|
||||||
|
public function not() {
|
||||||
|
$collection = clone $this;
|
||||||
|
foreach(func_get_args() as $filename) {
|
||||||
|
if(is_array($filename) or $filename instanceof Traversable) {
|
||||||
|
foreach($filename as $f) {
|
||||||
|
$collection = $collection->not($f);
|
||||||
|
}
|
||||||
|
} else if(is_a($filename, 'Media')) {
|
||||||
|
// unset by Media object
|
||||||
|
unset($collection->data[strtolower($filename->filename())]);
|
||||||
|
} else {
|
||||||
|
unset($collection->data[strtolower($filename)]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $collection;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts the files 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 $file) {
|
||||||
|
$data[] = $file->toArray($callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $data;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts the files 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));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
173
kirby/core/kirbytag.php
Normal file
173
kirby/core/kirbytag.php
Normal file
|
@ -0,0 +1,173 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Kirbytag
|
||||||
|
*
|
||||||
|
* @package Kirby CMS
|
||||||
|
* @author Bastian Allgeier <bastian@getkirby.com>
|
||||||
|
* @link http://getkirby.com
|
||||||
|
* @copyright Bastian Allgeier
|
||||||
|
* @license http://getkirby.com/license
|
||||||
|
*/
|
||||||
|
abstract class KirbytagAbstract {
|
||||||
|
|
||||||
|
protected $page;
|
||||||
|
protected $kirbytext;
|
||||||
|
protected $name;
|
||||||
|
protected $html;
|
||||||
|
protected $attr = array();
|
||||||
|
|
||||||
|
public function __construct($kirbytext, $name, $tag) {
|
||||||
|
|
||||||
|
if(is_null($kirbytext)) $kirbytext = new Kirbytext('');
|
||||||
|
|
||||||
|
$this->page = $kirbytext->field->page;
|
||||||
|
$this->kirbytext = $kirbytext;
|
||||||
|
$this->name = $name;
|
||||||
|
$this->html = kirbytext::$tags[$name]['html'];
|
||||||
|
|
||||||
|
// get a list with all attributes
|
||||||
|
$attributes = isset(kirbytext::$tags[$name]['attr']) ? (array)kirbytext::$tags[$name]['attr'] : array();
|
||||||
|
|
||||||
|
// add the name as first attribute
|
||||||
|
array_unshift($attributes, $name);
|
||||||
|
|
||||||
|
if(is_array($tag)) {
|
||||||
|
foreach($attributes as $key) {
|
||||||
|
if(isset($tag[$key])) $this->attr[$key] = $tag[$key];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
|
||||||
|
// extract all attributes
|
||||||
|
$search = preg_split('!(' . implode('|', $attributes) . '):!i', $tag, false, PREG_SPLIT_DELIM_CAPTURE|PREG_SPLIT_NO_EMPTY);
|
||||||
|
$num = 0;
|
||||||
|
|
||||||
|
foreach($search AS $key) {
|
||||||
|
|
||||||
|
if(!isset($search[$num+1])) break;
|
||||||
|
|
||||||
|
$key = trim($search[$num]);
|
||||||
|
$value = trim($search[$num+1]);
|
||||||
|
|
||||||
|
$this->attr[$key] = $value;
|
||||||
|
$num = $num+2;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the parent active page
|
||||||
|
*
|
||||||
|
* @return object Page
|
||||||
|
*/
|
||||||
|
public function page() {
|
||||||
|
return $this->page;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the parent kirbytext object
|
||||||
|
*
|
||||||
|
* @return object Kirbytext
|
||||||
|
*/
|
||||||
|
public function kirbytext() {
|
||||||
|
return $this->kirbytext;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the field object
|
||||||
|
*
|
||||||
|
* @return object Field
|
||||||
|
*/
|
||||||
|
public function field() {
|
||||||
|
return $this->kirbytext->field();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tries to find all related files for the current page
|
||||||
|
*
|
||||||
|
* @return object Files
|
||||||
|
*/
|
||||||
|
public function files() {
|
||||||
|
return $this->page->files();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tries to find a file for the given url/uri
|
||||||
|
*
|
||||||
|
* @param string $url a full path to a file or just a filename for files form the current active page
|
||||||
|
* @return object File
|
||||||
|
*/
|
||||||
|
public function file($url) {
|
||||||
|
|
||||||
|
// if this is an absolute url cancel
|
||||||
|
if(preg_match('!(http|https)\:\/\/!i', $url)) return false;
|
||||||
|
|
||||||
|
// skip urls without extensions
|
||||||
|
if(!preg_match('!\.[a-z0-9]+$!i',$url)) return false;
|
||||||
|
|
||||||
|
// relative url
|
||||||
|
if(str::contains($url, '/')) {
|
||||||
|
|
||||||
|
$path = dirname($url);
|
||||||
|
$filename = basename($url);
|
||||||
|
|
||||||
|
if($page = page($path) and $file = $page->file($filename)) {
|
||||||
|
return $file;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// try to get all files for the current page
|
||||||
|
$files = $this->files();
|
||||||
|
|
||||||
|
// cancel if no files are available
|
||||||
|
if(!$files) return false;
|
||||||
|
|
||||||
|
// try to find the file
|
||||||
|
return $files->find($url);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a specific attribute by key or all attributes
|
||||||
|
* by passing no key at all.
|
||||||
|
*
|
||||||
|
* @param mixed $key
|
||||||
|
* @param mixed $default
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function attr($key = null, $default = null) {
|
||||||
|
if(is_null($key)) return $this->attr;
|
||||||
|
return isset($this->attr[$key]) ? $this->attr[$key] : $default;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Smart getter for the applicable target attribute.
|
||||||
|
* This will watch for popup or target attributes and return
|
||||||
|
* a proper target value if available.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function target() {
|
||||||
|
if(empty($this->attr['popup']) and empty($this->attr['target'])) return false;
|
||||||
|
return empty($this->attr['popup']) ? $this->attr['target'] : '_blank';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function html() {
|
||||||
|
if(!is_callable($this->html)) {
|
||||||
|
return (string)$this->html;
|
||||||
|
} else {
|
||||||
|
return call_user_func_array($this->html, array($this));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __toString() {
|
||||||
|
return (string)$this->html();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
107
kirby/core/kirbytext.php
Normal file
107
kirby/core/kirbytext.php
Normal file
|
@ -0,0 +1,107 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Kirbytext
|
||||||
|
*
|
||||||
|
* @package Kirby CMS
|
||||||
|
* @author Bastian Allgeier <bastian@getkirby.com>
|
||||||
|
* @link http://getkirby.com
|
||||||
|
* @copyright Bastian Allgeier
|
||||||
|
* @license http://getkirby.com/license
|
||||||
|
*/
|
||||||
|
abstract class KirbytextAbstract {
|
||||||
|
|
||||||
|
static public $tags = array();
|
||||||
|
static public $pre = array();
|
||||||
|
static public $post = array();
|
||||||
|
|
||||||
|
public $field;
|
||||||
|
|
||||||
|
public function __construct($field) {
|
||||||
|
|
||||||
|
if(is_a($field, 'Field')) {
|
||||||
|
$this->field = $field;
|
||||||
|
} else if(is_array($field)) {
|
||||||
|
throw new Exception('Kirbytext cannot handle arrays');
|
||||||
|
} else if(empty($field) or is_string($field)) {
|
||||||
|
$this->field = new Field(page(), null, $field);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public function field() {
|
||||||
|
return $this->field;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function parse() {
|
||||||
|
|
||||||
|
if(!$this->field) return '';
|
||||||
|
|
||||||
|
$text = $this->field->value;
|
||||||
|
|
||||||
|
// pre filters
|
||||||
|
foreach(static::$pre as $filter) {
|
||||||
|
$text = call_user_func_array($filter, array($this, $text));
|
||||||
|
}
|
||||||
|
|
||||||
|
// tagsify
|
||||||
|
$text = preg_replace_callback('!(?=[^\]])\([a-z0-9_-]+:.*?\)!is', array($this, 'tag'), $text);
|
||||||
|
|
||||||
|
// markdownify
|
||||||
|
$text = kirby::instance()->component('markdown')->parse($text);
|
||||||
|
|
||||||
|
// smartypantsify
|
||||||
|
$text = kirby::instance()->component('smartypants')->parse($text);
|
||||||
|
|
||||||
|
// post filters
|
||||||
|
foreach(static::$post as $filter) {
|
||||||
|
$text = call_user_func_array($filter, array($this, $text));
|
||||||
|
}
|
||||||
|
|
||||||
|
return $text;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public function tag($input) {
|
||||||
|
|
||||||
|
// remove the brackets
|
||||||
|
$tag = trim(rtrim(ltrim($input[0], '('), ')'));
|
||||||
|
$name = trim(substr($tag, 0, strpos($tag, ':')));
|
||||||
|
|
||||||
|
// if the tag is not installed return the entire string
|
||||||
|
if(!isset(static::$tags[$name])) return $input[0];
|
||||||
|
|
||||||
|
try {
|
||||||
|
$tag = new Kirbytag($this, $name, $tag);
|
||||||
|
return $tag->html();
|
||||||
|
} catch(Exception $e) {
|
||||||
|
// broken tags will be ignored
|
||||||
|
return $input[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static public function install($root) {
|
||||||
|
|
||||||
|
if(!is_dir($root)) return false;
|
||||||
|
|
||||||
|
foreach(scandir($root) as $file) {
|
||||||
|
if(pathinfo($file, PATHINFO_EXTENSION) == 'php') {
|
||||||
|
$name = pathinfo($file, PATHINFO_FILENAME);
|
||||||
|
$tag = include($root . DS . $file);
|
||||||
|
if(is_array($tag)) Kirbytext::$tags[$name] = $tag;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __toString() {
|
||||||
|
try {
|
||||||
|
return $this->parse();
|
||||||
|
} catch(Exception $e) {
|
||||||
|
// on massive render bugs the entire text will be returned
|
||||||
|
return $this->field->value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
1443
kirby/core/page.php
Normal file
1443
kirby/core/page.php
Normal file
File diff suppressed because it is too large
Load diff
330
kirby/core/pages.php
Normal file
330
kirby/core/pages.php
Normal file
|
@ -0,0 +1,330 @@
|
||||||
|
<?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));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
102
kirby/core/role.php
Normal file
102
kirby/core/role.php
Normal file
|
@ -0,0 +1,102 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Role
|
||||||
|
*
|
||||||
|
* @package Kirby CMS
|
||||||
|
* @author Bastian Allgeier <bastian@getkirby.com>
|
||||||
|
* @link http://getkirby.com
|
||||||
|
* @copyright Bastian Allgeier
|
||||||
|
* @license http://getkirby.com/license
|
||||||
|
*/
|
||||||
|
abstract class RoleAbstract {
|
||||||
|
|
||||||
|
protected $id = null;
|
||||||
|
protected $name = null;
|
||||||
|
protected $panel = false;
|
||||||
|
protected $permissions = array(
|
||||||
|
'panel.access' => true,
|
||||||
|
'panel.site.update' => true,
|
||||||
|
'panel.page.create' => true,
|
||||||
|
'panel.page.update' => true,
|
||||||
|
'panel.page.move' => true,
|
||||||
|
'panel.page.sort' => true,
|
||||||
|
'panel.page.hide' => true,
|
||||||
|
'panel.page.delete' => true,
|
||||||
|
'panel.file.upload' => true,
|
||||||
|
'panel.file.replace' => true,
|
||||||
|
'panel.file.update' => true,
|
||||||
|
'panel.file.delete' => true,
|
||||||
|
'panel.user.add' => true,
|
||||||
|
'panel.user.edit' => true,
|
||||||
|
'panel.user.role' => true,
|
||||||
|
'panel.user.delete' => true,
|
||||||
|
);
|
||||||
|
|
||||||
|
public $default = false;
|
||||||
|
|
||||||
|
public function __construct($data = array()) {
|
||||||
|
|
||||||
|
if(!isset($data['id'])) throw new Exception('The role id is missing');
|
||||||
|
if(!isset($data['name'])) throw new Exception('The role name is missing');
|
||||||
|
|
||||||
|
// required data
|
||||||
|
$this->id = $data['id'];
|
||||||
|
$this->name = $data['name'];
|
||||||
|
|
||||||
|
if(isset($data['permissions']) and is_array($data['permissions'])) {
|
||||||
|
$this->permissions = a::merge($this->permissions, $data['permissions']);
|
||||||
|
} else if(isset($data['permissions']) and $data['permissions'] === false) {
|
||||||
|
$this->permissions = array_fill_keys(array_keys($this->permissions), false);
|
||||||
|
} else {
|
||||||
|
$this->permissions = $this->permissions;
|
||||||
|
}
|
||||||
|
|
||||||
|
// fallback permissions support for old 'panel' role variable
|
||||||
|
if(isset($data['panel']) and is_bool($data['panel'])) {
|
||||||
|
$this->permissions['panel.access'] = $data['panel'];
|
||||||
|
}
|
||||||
|
|
||||||
|
// is this role the default role?
|
||||||
|
if(isset($data['default'])) {
|
||||||
|
$this->default = $data['default'] === true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public function id() {
|
||||||
|
return $this->id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function name() {
|
||||||
|
return $this->name;
|
||||||
|
}
|
||||||
|
|
||||||
|
// support for old 'panel' role permission
|
||||||
|
public function hasPanelAccess() {
|
||||||
|
return $this->hasPermission('panel.access');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function hasPermission($target) {
|
||||||
|
if($this->id == 'admin') {
|
||||||
|
return true;
|
||||||
|
} else if(isset($this->permissions[$target]) and $this->permissions[$target] === true) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isDefault() {
|
||||||
|
return $this->default;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function users() {
|
||||||
|
return kirby::instance()->site()->users()->filterBy('role', $this->id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __toString() {
|
||||||
|
return (string)$this->id;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
84
kirby/core/roles.php
Normal file
84
kirby/core/roles.php
Normal file
|
@ -0,0 +1,84 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Roles
|
||||||
|
*
|
||||||
|
* @package Kirby CMS
|
||||||
|
* @author Bastian Allgeier <bastian@getkirby.com>
|
||||||
|
* @link http://getkirby.com
|
||||||
|
* @copyright Bastian Allgeier
|
||||||
|
* @license http://getkirby.com/license
|
||||||
|
*/
|
||||||
|
abstract class RolesAbstract extends Collection {
|
||||||
|
|
||||||
|
// cache for the default role
|
||||||
|
protected $default = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*/
|
||||||
|
public function __construct() {
|
||||||
|
|
||||||
|
$roles = kirby::instance()->option('roles');
|
||||||
|
|
||||||
|
// set the default set of roles, if roles are not configured
|
||||||
|
if(empty($roles)) {
|
||||||
|
$roles = array(
|
||||||
|
array(
|
||||||
|
'id' => 'admin',
|
||||||
|
'name' => 'Admin',
|
||||||
|
'default' => true
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'id' => 'editor',
|
||||||
|
'name' => 'Editor',
|
||||||
|
'permissions' => array(
|
||||||
|
'panel.access' => true,
|
||||||
|
'panel.site.update' => false,
|
||||||
|
'panel.page.create' => true,
|
||||||
|
'panel.page.update' => true,
|
||||||
|
'panel.page.move' => true,
|
||||||
|
'panel.page.sort' => true,
|
||||||
|
'panel.page.hide' => true,
|
||||||
|
'panel.page.delete' => true,
|
||||||
|
'panel.file.upload' => true,
|
||||||
|
'panel.file.replace' => true,
|
||||||
|
'panel.file.update' => true,
|
||||||
|
'panel.file.delete' => true,
|
||||||
|
'panel.user.add' => false,
|
||||||
|
'panel.user.edit' => false,
|
||||||
|
'panel.user.role' => false,
|
||||||
|
'panel.user.delete' => false
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach($roles as $role) {
|
||||||
|
$role = new Role($role);
|
||||||
|
$this->data[$role->id()] = $role;
|
||||||
|
}
|
||||||
|
|
||||||
|
// check for a valid admin role
|
||||||
|
if(!isset($this->data['admin'])) {
|
||||||
|
throw new Exception('There must be an admin role');
|
||||||
|
}
|
||||||
|
|
||||||
|
// check for a valid default role
|
||||||
|
if(!$this->findDefault()) {
|
||||||
|
$this->data['admin']->default = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the default role for new users
|
||||||
|
*
|
||||||
|
* @return Role
|
||||||
|
*/
|
||||||
|
public function findDefault() {
|
||||||
|
if(!is_null($this->default)) return $this->default;
|
||||||
|
return $this->default = $this->findBy('isDefault', true);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
337
kirby/core/site.php
Normal file
337
kirby/core/site.php
Normal file
|
@ -0,0 +1,337 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Site
|
||||||
|
*
|
||||||
|
* @package Kirby CMS
|
||||||
|
* @author Bastian Allgeier <bastian@getkirby.com>
|
||||||
|
* @link http://getkirby.com
|
||||||
|
* @copyright Bastian Allgeier
|
||||||
|
* @license http://getkirby.com/license
|
||||||
|
*/
|
||||||
|
abstract class SiteAbstract extends Page {
|
||||||
|
|
||||||
|
// the current page
|
||||||
|
public $page = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public function __construct(Kirby $kirby) {
|
||||||
|
|
||||||
|
$this->kirby = $kirby;
|
||||||
|
$this->url = $kirby->urls()->index();
|
||||||
|
$this->depth = 0;
|
||||||
|
$this->uri = '';
|
||||||
|
$this->site = $this;
|
||||||
|
$this->page = null;
|
||||||
|
|
||||||
|
// build ugly urls if rewriting is disabled
|
||||||
|
if($this->kirby->options['rewrite'] === false) {
|
||||||
|
$this->url .= '/index.php';
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->root = $kirby->roots()->content();
|
||||||
|
$this->dirname = basename($this->root);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cleans the temporary internal cache
|
||||||
|
*/
|
||||||
|
public function reset() {
|
||||||
|
$this->cache = array();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The id is an empty string in case of the site object
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function id() {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The base diruri is bascially just an empty string
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function diruri() {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the base url for the site
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function url() {
|
||||||
|
return $this->url;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the full URL for the content folder
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function contentUrl() {
|
||||||
|
return $this->kirby()->urls()->content();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if this object is the main site
|
||||||
|
*
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
public function isSite() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the usable template
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function template() {
|
||||||
|
return 'site';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The site has no template
|
||||||
|
*
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
public function templateFile() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the intended template
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function intendedTemplate() {
|
||||||
|
return 'site';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Again, the site has no template!
|
||||||
|
*
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
public function intendedTemplateFile() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* There can't be a template for the site
|
||||||
|
* Didn't you still get it yet?
|
||||||
|
*
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
public function hasTemplate() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the currently active page
|
||||||
|
* and returns its page object
|
||||||
|
*
|
||||||
|
* @param string $uri
|
||||||
|
* @return Page
|
||||||
|
*/
|
||||||
|
public function visit($uri = '') {
|
||||||
|
|
||||||
|
$uri = trim($uri, '/');
|
||||||
|
|
||||||
|
if(empty($uri)) {
|
||||||
|
return $this->page = $this->homePage();
|
||||||
|
} else {
|
||||||
|
if($page = $this->children()->find($uri)) {
|
||||||
|
return $this->page = $page;
|
||||||
|
} else {
|
||||||
|
return $this->page = $this->errorPage();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the currently active page or any other page by uri
|
||||||
|
*
|
||||||
|
* @param string $uri Optional uri to get any page on the site
|
||||||
|
* @return Page
|
||||||
|
*/
|
||||||
|
public function page($uri = null) {
|
||||||
|
if(is_null($uri)) {
|
||||||
|
return is_null($this->page) ? $this->page = $this->homePage() : $this->page;
|
||||||
|
} else {
|
||||||
|
return $this->children()->find($uri);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Alternative for $this->children()
|
||||||
|
*
|
||||||
|
* @return Children
|
||||||
|
*/
|
||||||
|
public function pages() {
|
||||||
|
return $this->children();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builds a breadcrumb collection
|
||||||
|
*
|
||||||
|
* @return Children
|
||||||
|
*/
|
||||||
|
public function breadcrumb() {
|
||||||
|
|
||||||
|
if(isset($this->cache['breadcrumb'])) return $this->cache['breadcrumb'];
|
||||||
|
|
||||||
|
// get all parents and flip the order
|
||||||
|
$crumb = $this->page()->parents()->flip();
|
||||||
|
|
||||||
|
// add the home page
|
||||||
|
$crumb->prepend($this->homePage()->uri(), $this->homePage());
|
||||||
|
|
||||||
|
// add the active page
|
||||||
|
$crumb->append($this->page()->uri(), $this->page());
|
||||||
|
|
||||||
|
return $this->cache['breadcrumb'] = $crumb;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Alternative for $this->page()
|
||||||
|
*
|
||||||
|
* @return Page
|
||||||
|
*/
|
||||||
|
public function activePage() {
|
||||||
|
return $this->page();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the error page object
|
||||||
|
*
|
||||||
|
* @return Page
|
||||||
|
*/
|
||||||
|
public function errorPage() {
|
||||||
|
if(isset($this->cache['errorPage'])) return $this->cache['errorPage'];
|
||||||
|
return $this->cache['errorPage'] = $this->children()->find($this->kirby->options['error']);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the home page object
|
||||||
|
*
|
||||||
|
* @return Page
|
||||||
|
*/
|
||||||
|
public function homePage() {
|
||||||
|
if(isset($this->cache['homePage'])) return $this->cache['homePage'];
|
||||||
|
return $this->cache['homePage'] = $this->children()->find($this->kirby->options['home']);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the locale for the site
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function locale() {
|
||||||
|
return isset($this->kirby->options['locale']) ? $this->kirby->options['locale'] : 'en_US';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the site is a multi language site
|
||||||
|
*
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
public function multilang() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Placeholder for multilanguage sites
|
||||||
|
*/
|
||||||
|
public function languages() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Placeholder for multilanguage sites
|
||||||
|
*/
|
||||||
|
public function language() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Placeholder for multilanguage sites
|
||||||
|
*/
|
||||||
|
public function defaultLanguage() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the detected language
|
||||||
|
*/
|
||||||
|
public function detectedLanguage() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a collection of all users
|
||||||
|
*
|
||||||
|
* @return Users
|
||||||
|
*/
|
||||||
|
public function users() {
|
||||||
|
return new Users();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the current user
|
||||||
|
*
|
||||||
|
* @param string $username Optional way to search for a single user
|
||||||
|
* @return User
|
||||||
|
*/
|
||||||
|
public function user($username = null) {
|
||||||
|
if(is_null($username)) return User::current();
|
||||||
|
try {
|
||||||
|
return new User($username);
|
||||||
|
} catch(Exception $e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a collection of all roles
|
||||||
|
*
|
||||||
|
* @return Roles
|
||||||
|
*/
|
||||||
|
public function roles() {
|
||||||
|
return new Roles();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the last modification date of all pages
|
||||||
|
* in the content folder.
|
||||||
|
*
|
||||||
|
* @param mixed $format
|
||||||
|
* @param mixed $handler
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function modified($format = null, $handler = null) {
|
||||||
|
return dir::modified($this->root, $format, $handler ? $handler : $this->kirby->options['date.handler']);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if any content of the site has been
|
||||||
|
* modified after the given unix timestamp
|
||||||
|
* This is mainly used to auto-update the cache
|
||||||
|
*
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
public function wasModifiedAfter($time) {
|
||||||
|
return dir::wasModifiedAfter($this->root(), $time);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
349
kirby/core/user.php
Normal file
349
kirby/core/user.php
Normal file
|
@ -0,0 +1,349 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Users
|
||||||
|
*
|
||||||
|
* @package Kirby CMS
|
||||||
|
* @author Bastian Allgeier <bastian@getkirby.com>
|
||||||
|
* @link http://getkirby.com
|
||||||
|
* @copyright Bastian Allgeier
|
||||||
|
* @license http://getkirby.com/license
|
||||||
|
*/
|
||||||
|
abstract class UserAbstract {
|
||||||
|
|
||||||
|
protected $username = null;
|
||||||
|
protected $cache = array();
|
||||||
|
protected $data = null;
|
||||||
|
|
||||||
|
public function __construct($username) {
|
||||||
|
|
||||||
|
$this->username = str::slug(basename($username));
|
||||||
|
|
||||||
|
// check if the account file exists
|
||||||
|
if(!file_exists($this->file())) {
|
||||||
|
throw new Exception('The user account could not be found');
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the username
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function username() {
|
||||||
|
return $this->username;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get all data for the user
|
||||||
|
*/
|
||||||
|
public function data() {
|
||||||
|
|
||||||
|
if(!is_null($this->data)) return $this->data;
|
||||||
|
|
||||||
|
// get all data from the account file
|
||||||
|
$this->data = data::read($this->file(), 'yaml');
|
||||||
|
|
||||||
|
// make sure all keys are lowercase
|
||||||
|
$this->data = array_change_key_case($this->data, CASE_LOWER);
|
||||||
|
|
||||||
|
// remove garbage
|
||||||
|
unset($this->data[0]);
|
||||||
|
|
||||||
|
// add the username
|
||||||
|
$this->data['username'] = $this->username;
|
||||||
|
|
||||||
|
// return the data array
|
||||||
|
return $this->data;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __get($key) {
|
||||||
|
return a::get($this->data(), strtolower($key));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __call($key, $arguments = null) {
|
||||||
|
return $this->__get($key);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function role() {
|
||||||
|
|
||||||
|
$roles = kirby::instance()->site()->roles();
|
||||||
|
$data = $this->data();
|
||||||
|
|
||||||
|
if(empty($data['role'])) {
|
||||||
|
// apply the default role, if no role is stored for the user
|
||||||
|
$data['role'] = $roles->findDefault()->id();
|
||||||
|
}
|
||||||
|
|
||||||
|
// return the role by id
|
||||||
|
if($role = $roles->get($data['role'])) {
|
||||||
|
return $role;
|
||||||
|
} else {
|
||||||
|
return $roles->findDefault();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public function hasRole() {
|
||||||
|
$roles = func_get_args();
|
||||||
|
return in_array($this->role()->id(), $roles);
|
||||||
|
}
|
||||||
|
|
||||||
|
// support for old 'panel' role permission
|
||||||
|
public function hasPanelAccess() {
|
||||||
|
return $this->role()->hasPermission('panel.access');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function hasPermission($target) {
|
||||||
|
return $this->role()->hasPermission($target);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isAdmin() {
|
||||||
|
return $this->role()->id() == 'admin';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function avatar() {
|
||||||
|
|
||||||
|
if(isset($this->cache['avatar'])) return $this->cache['avatar'];
|
||||||
|
|
||||||
|
$avatar = new Avatar($this);
|
||||||
|
|
||||||
|
return $this->cache['avatar'] = $avatar->exists() ? $avatar : false;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public function avatarRoot($extension = 'jpg') {
|
||||||
|
return kirby::instance()->roots()->avatars() . DS . $this->username() . '.' . $extension;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function gravatar($size = 256) {
|
||||||
|
return gravatar($this->email(), $size);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function file() {
|
||||||
|
return kirby::instance()->roots()->accounts() . DS . $this->username() . '.php';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function textfile() {
|
||||||
|
return $this->file();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function exists() {
|
||||||
|
return file_exists($this->file());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function generateKey() {
|
||||||
|
return str::random(64);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function generateSecret($key) {
|
||||||
|
return sha1($this->username() . $key);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function login($password) {
|
||||||
|
|
||||||
|
static::logout();
|
||||||
|
|
||||||
|
if(!password::match($password, $this->password)) return false;
|
||||||
|
|
||||||
|
// create a new session id
|
||||||
|
s::regenerateId();
|
||||||
|
|
||||||
|
$key = $this->generateKey();
|
||||||
|
$secret = $this->generateSecret($key);
|
||||||
|
|
||||||
|
s::set('kirby_auth_secret', $secret);
|
||||||
|
s::set('kirby_auth_username', $this->username());
|
||||||
|
|
||||||
|
cookie::set(
|
||||||
|
s::$name . '_auth',
|
||||||
|
$key,
|
||||||
|
s::$cookie['lifetime'],
|
||||||
|
s::$cookie['path'],
|
||||||
|
s::$cookie['domain'],
|
||||||
|
s::$cookie['secure'],
|
||||||
|
s::$cookie['httponly']
|
||||||
|
);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static public function logout() {
|
||||||
|
s::destroy();
|
||||||
|
cookie::remove(s::$name . '_auth');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function is($user) {
|
||||||
|
if(!is_a($user, 'User')) return false;
|
||||||
|
return $this->username() === $user->username();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isCurrent() {
|
||||||
|
return $this->is(static::current());
|
||||||
|
}
|
||||||
|
|
||||||
|
static public function validate($data = array(), $mode = 'insert') {
|
||||||
|
|
||||||
|
if($mode == 'insert') {
|
||||||
|
|
||||||
|
if(empty($data['username'])) {
|
||||||
|
throw new Exception('Invalid username');
|
||||||
|
}
|
||||||
|
|
||||||
|
if(empty($data['password'])) {
|
||||||
|
throw new Exception('Invalid password');
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!empty($data['email']) and !v::email($data['email'])) {
|
||||||
|
throw new Exception('Invalid email');
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public function update($data = array()) {
|
||||||
|
|
||||||
|
// sanitize the given data
|
||||||
|
$data = $this->sanitize($data, 'update');
|
||||||
|
|
||||||
|
// validate the updated dataset
|
||||||
|
$this->validate($data, 'update');
|
||||||
|
|
||||||
|
// don't update the username
|
||||||
|
unset($data['username']);
|
||||||
|
|
||||||
|
// create a new hash for the password
|
||||||
|
if(!empty($data['password'])) {
|
||||||
|
$data['password'] = password::hash($data['password']);
|
||||||
|
}
|
||||||
|
|
||||||
|
// merge with existing fields
|
||||||
|
$this->data = array_merge($this->data(), $data);
|
||||||
|
|
||||||
|
foreach($this->data as $key => $value) {
|
||||||
|
if(is_null($value)) unset($this->data[$key]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// save the new user data
|
||||||
|
static::save($this->file(), $this->data);
|
||||||
|
|
||||||
|
// return the updated user project
|
||||||
|
return $this;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public function delete() {
|
||||||
|
|
||||||
|
if($avatar = $this->avatar()) {
|
||||||
|
$avatar->delete();
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!f::remove($this->file())) {
|
||||||
|
throw new Exception('The account could not be deleted');
|
||||||
|
} else {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static public function sanitize($data, $mode = 'insert') {
|
||||||
|
|
||||||
|
// all usernames must be lowercase
|
||||||
|
$data['username'] = str::slug(a::get($data, 'username'));
|
||||||
|
|
||||||
|
// convert all keys to lowercase
|
||||||
|
$data = array_change_key_case($data, CASE_LOWER);
|
||||||
|
|
||||||
|
// return the cleaned up data
|
||||||
|
return $data;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new user
|
||||||
|
*
|
||||||
|
* @param array $user
|
||||||
|
* @return User
|
||||||
|
*/
|
||||||
|
static public function create($data = array()) {
|
||||||
|
|
||||||
|
// sanitize the given data for the new user
|
||||||
|
$data = static::sanitize($data, 'insert');
|
||||||
|
|
||||||
|
// validate the dataset
|
||||||
|
static::validate($data, 'insert');
|
||||||
|
|
||||||
|
// create the file root
|
||||||
|
$file = kirby::instance()->roots()->accounts() . DS . $data['username'] . '.php';
|
||||||
|
|
||||||
|
// check for an existing username
|
||||||
|
if(file_exists($file)) {
|
||||||
|
throw new Exception('The username is taken');
|
||||||
|
}
|
||||||
|
|
||||||
|
// create a new hash for the password
|
||||||
|
if(!empty($data['password'])) {
|
||||||
|
$data['password'] = password::hash($data['password']);
|
||||||
|
}
|
||||||
|
|
||||||
|
static::save($file, $data);
|
||||||
|
|
||||||
|
// return the created user project
|
||||||
|
return new static($data['username']);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static protected function save($file, $data) {
|
||||||
|
|
||||||
|
$yaml = '<?php if(!defined(\'KIRBY\')) exit ?>' . PHP_EOL . PHP_EOL;
|
||||||
|
$yaml .= data::encode($data, 'yaml');
|
||||||
|
|
||||||
|
if(!f::write($file, $yaml)) {
|
||||||
|
throw new Exception('The user account could not be saved');
|
||||||
|
} else {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static public function unauthorize() {
|
||||||
|
s::remove('kirby_auth_secret');
|
||||||
|
s::remove('kirby_auth_username');
|
||||||
|
cookie::remove('kirby_auth');
|
||||||
|
}
|
||||||
|
|
||||||
|
static public function current() {
|
||||||
|
|
||||||
|
$cookey = cookie::get(s::$name . '_auth');
|
||||||
|
$username = s::get('kirby_auth_username');
|
||||||
|
|
||||||
|
if(empty($cookey)) {
|
||||||
|
static::unauthorize();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(s::get('kirby_auth_secret') !== sha1($username . $cookey)) {
|
||||||
|
static::unauthorize();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// find the logged in user by token
|
||||||
|
try {
|
||||||
|
$user = new static($username);
|
||||||
|
return $user;
|
||||||
|
} catch(Exception $e) {
|
||||||
|
static::unauthorize();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __toString() {
|
||||||
|
return (string)$this->username;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
38
kirby/core/users.php
Normal file
38
kirby/core/users.php
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Users
|
||||||
|
*
|
||||||
|
* @package Kirby CMS
|
||||||
|
* @author Bastian Allgeier <bastian@getkirby.com>
|
||||||
|
* @link http://getkirby.com
|
||||||
|
* @copyright Bastian Allgeier
|
||||||
|
* @license http://getkirby.com/license
|
||||||
|
*/
|
||||||
|
abstract class UsersAbstract extends Collection {
|
||||||
|
|
||||||
|
public function __construct() {
|
||||||
|
|
||||||
|
$root = kirby::instance()->roots()->accounts();
|
||||||
|
|
||||||
|
foreach(dir::read($root) as $file) {
|
||||||
|
|
||||||
|
// skip invalid account files
|
||||||
|
if(f::extension($file) != 'php') continue;
|
||||||
|
|
||||||
|
$user = new User(f::name($file));
|
||||||
|
$this->append($user->username(), $user);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public function create($data) {
|
||||||
|
return user::create($data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function find($username) {
|
||||||
|
return $this->findBy('username', $username);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
279
kirby/extensions/methods.php
Normal file
279
kirby/extensions/methods.php
Normal file
|
@ -0,0 +1,279 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts the field value to valid html
|
||||||
|
* @param Field $field The calling Kirby Field instance
|
||||||
|
* @param boolean $keepTags Don't touch valid html tags
|
||||||
|
* @return Field
|
||||||
|
*/
|
||||||
|
field::$methods['html'] = field::$methods['h'] = function($field, $keepTags = true) {
|
||||||
|
$field->value = html($field->value, $keepTags);
|
||||||
|
return $field;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Escapes unwanted characters in the field value
|
||||||
|
* to protect from possible xss attacks or other
|
||||||
|
* unwanted side effects in your html code
|
||||||
|
* @param Field $field The calling Kirby Field instance
|
||||||
|
* @param string $context html|attr|css|js|url
|
||||||
|
* @return Field
|
||||||
|
*/
|
||||||
|
field::$methods['escape'] = field::$methods['esc'] = function($field, $context = 'html') {
|
||||||
|
$field->value = esc($field->value, $context);
|
||||||
|
return $field;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts html entities and specialchars in the field
|
||||||
|
* value to valid xml entities
|
||||||
|
* @param Field $field The calling Kirby Field instance
|
||||||
|
* @return Field
|
||||||
|
*/
|
||||||
|
field::$methods['xml'] = field::$methods['x'] = function($field) {
|
||||||
|
$field->value = xml($field->value);
|
||||||
|
return $field;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses the field value as kirbytext
|
||||||
|
* @param Field $field The calling Kirby Field instance
|
||||||
|
* @return Field
|
||||||
|
*/
|
||||||
|
field::$methods['kirbytext'] = field::$methods['kt'] = function($field) {
|
||||||
|
$field->value = kirbytext($field);
|
||||||
|
return $field;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses the field value as markdown
|
||||||
|
* @param Field $field The calling Kirby Field instance
|
||||||
|
* @return Field
|
||||||
|
*/
|
||||||
|
field::$methods['markdown'] = field::$methods['md'] = function($field) {
|
||||||
|
$field->value = markdown($field->value);
|
||||||
|
return $field;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts the field value to lower case
|
||||||
|
* @param Field $field The calling Kirby Field instance
|
||||||
|
* @return Field
|
||||||
|
*/
|
||||||
|
field::$methods['lower'] = function($field) {
|
||||||
|
$field->value = str::lower($field->value);
|
||||||
|
return $field;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts the field value to upper case
|
||||||
|
* @param Field $field The calling Kirby Field instance
|
||||||
|
* @return Field
|
||||||
|
*/
|
||||||
|
field::$methods['upper'] = function($field) {
|
||||||
|
$field->value = str::upper($field->value);
|
||||||
|
return $field;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Applies the widont rule to avoid single
|
||||||
|
* words on the last line
|
||||||
|
* @param Field $field The calling Kirby Field instance
|
||||||
|
* @return Field
|
||||||
|
*/
|
||||||
|
field::$methods['widont'] = function($field) {
|
||||||
|
$field->value = widont($field->value);
|
||||||
|
return $field;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a simple text excerpt without formats
|
||||||
|
* @param Field $field The calling Kirby Field instance
|
||||||
|
* @param integer $chars The desired excerpt length
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
field::$methods['excerpt'] = function($field, $chars = 140, $mode = 'chars') {
|
||||||
|
return excerpt($field, $chars, $mode);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shortens the field value by the given length
|
||||||
|
* @param Field $field The calling Kirby Field instance
|
||||||
|
* @param integer $length The desired string length
|
||||||
|
* @param string $rep The attached ellipsis character if the string is longer
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
field::$methods['short'] = function($field, $length, $rep = '…') {
|
||||||
|
return str::short($field->value, $length, $rep);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the string length of the field value
|
||||||
|
* @param Field $field The calling Kirby Field instance
|
||||||
|
* @return integer
|
||||||
|
*/
|
||||||
|
field::$methods['length'] = function($field) {
|
||||||
|
return str::length($field->value);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the word count for the field value
|
||||||
|
* @param Field $field The calling Kirby Field instance
|
||||||
|
* @return integer
|
||||||
|
*/
|
||||||
|
field::$methods['words'] = function($field) {
|
||||||
|
return str_word_count(strip_tags($field->value));
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Splits the field value by the given separator
|
||||||
|
* @param Field $field The calling Kirby Field instance
|
||||||
|
* @param string $separator The string to split the field value by
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
field::$methods['split'] = function($field, $separator = ',') {
|
||||||
|
return str::split($field->value, $separator);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses the field value as yaml and returns an array
|
||||||
|
* @param Field $field The calling Kirby Field instance
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
field::$methods['yaml'] = function($field) {
|
||||||
|
return yaml($field->value);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the field value is empty
|
||||||
|
* @param Field $field The calling Kirby Field instance
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
field::$methods['empty'] = field::$methods['isEmpty'] = function($field) {
|
||||||
|
return empty($field->value);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the field value is not empty
|
||||||
|
* @param Field $field The calling Kirby Field instance
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
field::$methods['isNotEmpty'] = function($field) {
|
||||||
|
return !$field->isEmpty();
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a page object from a uri in a field
|
||||||
|
* @param Field $field The calling Kirby Field instance
|
||||||
|
* @return Collection
|
||||||
|
*/
|
||||||
|
field::$methods['toPage'] = function($field) {
|
||||||
|
return page($field->value);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns all page objects from a yaml list or a $sep separated string in a field
|
||||||
|
* @param Field $field The calling Kirby Field instance
|
||||||
|
* @return Collection
|
||||||
|
*/
|
||||||
|
field::$methods['pages'] = field::$methods['toPages'] = function($field, $sep = null) {
|
||||||
|
|
||||||
|
if($sep !== null) {
|
||||||
|
$array = $field->split($sep);
|
||||||
|
} else {
|
||||||
|
$array = $field->yaml();
|
||||||
|
}
|
||||||
|
|
||||||
|
return $field->site()->pages()->find($array);
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a file object from a filename in a field
|
||||||
|
* @param Field $field The calling Kirby Field instance
|
||||||
|
* @return Collection
|
||||||
|
*/
|
||||||
|
field::$methods['toFile'] = function($field) {
|
||||||
|
return $field->page()->file($field->value);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds 'or' method to Field objects, which allows getting a field
|
||||||
|
* value or getting back a default value if the field is empty.
|
||||||
|
* @author fvsch <florent@fvsch.com>
|
||||||
|
* @param Field $field The calling Kirby Field instance
|
||||||
|
* @param mixed $fallback Fallback value returned if field is empty
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
field::$methods['or'] = function($field, $fallback = null) {
|
||||||
|
return $field->empty() ? $fallback : $field;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Filter the Field value, or a fallback value if the Field is empty,
|
||||||
|
* to get a boolean value. '1', 'on', 'true' or 'yes' will be true,
|
||||||
|
* and everything else will be false.
|
||||||
|
* @author fvsch <florent@fvsch.com>
|
||||||
|
* @param Field $field The calling Kirby Field instance
|
||||||
|
* @param boolean $default Default value returned if field is empty
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
field::$methods['bool'] = field::$methods['isTrue'] = function($field, $default = false) {
|
||||||
|
$val = $field->empty() ? $default : $field->value;
|
||||||
|
return filter_var($val, FILTER_VALIDATE_BOOLEAN);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the field content is false
|
||||||
|
* @param Field $field The calling Kirby Field instance
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
field::$methods['isFalse'] = function($field) {
|
||||||
|
return !$field->bool();
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get an integer value for the Field.
|
||||||
|
* @author fvsch <florent@fvsch.com>
|
||||||
|
* @param Object(Field) [$field] The calling Kirby Field instance
|
||||||
|
* @param integer [$default] Default value returned if field is empty
|
||||||
|
* @return integer
|
||||||
|
*/
|
||||||
|
field::$methods['int'] = function($field, $default = 0) {
|
||||||
|
$val = $field->empty() ? $default : $field->value;
|
||||||
|
return intval($val);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a float value for the Field
|
||||||
|
* @param Field $field The calling Kirby Field instance
|
||||||
|
* @param int $default Default value returned if field is empty
|
||||||
|
* @return float
|
||||||
|
*/
|
||||||
|
field::$methods['float'] = function($field, $default = 0) {
|
||||||
|
$val = $field->empty() ? $default : $field->value;
|
||||||
|
return floatval($val);
|
||||||
|
};
|
||||||
|
|
||||||
|
field::$methods['toStructure'] = field::$methods['structure'] = function($field) {
|
||||||
|
return structure($field->yaml(), $field->page());
|
||||||
|
};
|
||||||
|
|
||||||
|
field::$methods['link'] = function($field, $attr1 = array(), $attr2 = array()) {
|
||||||
|
$a = new Brick('a', $field->value());
|
||||||
|
|
||||||
|
if(is_string($attr1)) {
|
||||||
|
$a->attr('href', url($attr1));
|
||||||
|
$a->attr($attr2);
|
||||||
|
} else {
|
||||||
|
$a->attr('href', $field->page()->url());
|
||||||
|
$a->attr($attr1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $a;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
field::$methods['toUrl'] = field::$methods['url'] = function($field) {
|
||||||
|
return url($field->value());
|
||||||
|
};
|
309
kirby/extensions/tags.php
Normal file
309
kirby/extensions/tags.php
Normal file
|
@ -0,0 +1,309 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
// date tag
|
||||||
|
kirbytext::$tags['date'] = array(
|
||||||
|
'attr' => array(),
|
||||||
|
'html' => function($tag) {
|
||||||
|
return strtolower($tag->attr('date')) == 'year' ? date('Y') : date($tag->attr('date'));
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
// email tag
|
||||||
|
kirbytext::$tags['email'] = array(
|
||||||
|
'attr' => array(
|
||||||
|
'class',
|
||||||
|
'title',
|
||||||
|
'text',
|
||||||
|
'rel'
|
||||||
|
),
|
||||||
|
'html' => function($tag) {
|
||||||
|
return html::email($tag->attr('email'), html($tag->attr('text')), array(
|
||||||
|
'class' => $tag->attr('class'),
|
||||||
|
'title' => $tag->attr('title'),
|
||||||
|
'rel' => $tag->attr('rel'),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
// file tag
|
||||||
|
kirbytext::$tags['file'] = array(
|
||||||
|
'attr' => array(
|
||||||
|
'text',
|
||||||
|
'class',
|
||||||
|
'title',
|
||||||
|
'rel',
|
||||||
|
'target',
|
||||||
|
'popup'
|
||||||
|
),
|
||||||
|
'html' => function($tag) {
|
||||||
|
|
||||||
|
// build a proper link to the file
|
||||||
|
$file = $tag->file($tag->attr('file'));
|
||||||
|
$text = $tag->attr('text');
|
||||||
|
|
||||||
|
if(!$file) return $text;
|
||||||
|
|
||||||
|
// use filename if the text is empty and make sure to
|
||||||
|
// ignore markdown italic underscores in filenames
|
||||||
|
if(empty($text)) $text = str_replace('_', '\_', $file->name());
|
||||||
|
|
||||||
|
return html::a($file->url(), html($text), array(
|
||||||
|
'class' => $tag->attr('class'),
|
||||||
|
'title' => html($tag->attr('title')),
|
||||||
|
'rel' => $tag->attr('rel'),
|
||||||
|
'target' => $tag->target(),
|
||||||
|
));
|
||||||
|
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
// image tag
|
||||||
|
kirbytext::$tags['image'] = array(
|
||||||
|
'attr' => array(
|
||||||
|
'width',
|
||||||
|
'height',
|
||||||
|
'alt',
|
||||||
|
'text',
|
||||||
|
'title',
|
||||||
|
'class',
|
||||||
|
'imgclass',
|
||||||
|
'linkclass',
|
||||||
|
'caption',
|
||||||
|
'link',
|
||||||
|
'target',
|
||||||
|
'popup',
|
||||||
|
'rel'
|
||||||
|
),
|
||||||
|
'html' => function($tag) {
|
||||||
|
|
||||||
|
$url = $tag->attr('image');
|
||||||
|
$alt = $tag->attr('alt');
|
||||||
|
$title = $tag->attr('title');
|
||||||
|
$link = $tag->attr('link');
|
||||||
|
$caption = $tag->attr('caption');
|
||||||
|
$file = $tag->file($url);
|
||||||
|
|
||||||
|
// use the file url if available and otherwise the given url
|
||||||
|
$url = $file ? $file->url() : url($url);
|
||||||
|
|
||||||
|
// alt is just an alternative for text
|
||||||
|
if($text = $tag->attr('text')) $alt = $text;
|
||||||
|
|
||||||
|
// try to get the title from the image object and use it as alt text
|
||||||
|
if($file) {
|
||||||
|
|
||||||
|
if(empty($alt) and $file->alt() != '') {
|
||||||
|
$alt = $file->alt();
|
||||||
|
}
|
||||||
|
|
||||||
|
if(empty($title) and $file->title() != '') {
|
||||||
|
$title = $file->title();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// at least some accessibility for the image
|
||||||
|
if(empty($alt)) $alt = ' ';
|
||||||
|
|
||||||
|
// link builder
|
||||||
|
$_link = function($image) use($tag, $url, $link, $file) {
|
||||||
|
|
||||||
|
if(empty($link)) return $image;
|
||||||
|
|
||||||
|
// build the href for the link
|
||||||
|
if($link == 'self') {
|
||||||
|
$href = $url;
|
||||||
|
} else if($file and $link == $file->filename()) {
|
||||||
|
$href = $file->url();
|
||||||
|
} else if($tag->file($link)) {
|
||||||
|
$href = $tag->file($link)->url();
|
||||||
|
} else {
|
||||||
|
$href = $link;
|
||||||
|
}
|
||||||
|
|
||||||
|
return html::a(url($href), $image, array(
|
||||||
|
'rel' => $tag->attr('rel'),
|
||||||
|
'class' => $tag->attr('linkclass'),
|
||||||
|
'title' => $tag->attr('title'),
|
||||||
|
'target' => $tag->target()
|
||||||
|
));
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
// image builder
|
||||||
|
$_image = function($class) use($tag, $url, $alt, $title) {
|
||||||
|
return html::img($url, array(
|
||||||
|
'width' => $tag->attr('width'),
|
||||||
|
'height' => $tag->attr('height'),
|
||||||
|
'class' => $class,
|
||||||
|
'title' => $title,
|
||||||
|
'alt' => $alt
|
||||||
|
));
|
||||||
|
};
|
||||||
|
|
||||||
|
if(kirby()->option('kirbytext.image.figure') or !empty($caption)) {
|
||||||
|
$image = $_link($_image($tag->attr('imgclass')));
|
||||||
|
$figure = new Brick('figure');
|
||||||
|
$figure->addClass($tag->attr('class'));
|
||||||
|
$figure->append($image);
|
||||||
|
if(!empty($caption)) {
|
||||||
|
$figure->append('<figcaption>' . html($caption) . '</figcaption>');
|
||||||
|
}
|
||||||
|
return $figure;
|
||||||
|
} else {
|
||||||
|
$class = trim($tag->attr('class') . ' ' . $tag->attr('imgclass'));
|
||||||
|
return $_link($_image($class));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
// link tag
|
||||||
|
kirbytext::$tags['link'] = array(
|
||||||
|
'attr' => array(
|
||||||
|
'text',
|
||||||
|
'class',
|
||||||
|
'title',
|
||||||
|
'rel',
|
||||||
|
'lang',
|
||||||
|
'target',
|
||||||
|
'popup'
|
||||||
|
),
|
||||||
|
'html' => function($tag) {
|
||||||
|
|
||||||
|
$link = url($tag->attr('link'), $tag->attr('lang'));
|
||||||
|
$text = $tag->attr('text');
|
||||||
|
|
||||||
|
if(empty($text)) {
|
||||||
|
$text = $link;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(str::isURL($text)) {
|
||||||
|
$text = url::short($text);
|
||||||
|
}
|
||||||
|
|
||||||
|
return html::a($link, $text, array(
|
||||||
|
'rel' => $tag->attr('rel'),
|
||||||
|
'class' => $tag->attr('class'),
|
||||||
|
'title' => $tag->attr('title'),
|
||||||
|
'target' => $tag->target(),
|
||||||
|
));
|
||||||
|
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
// tel tag
|
||||||
|
kirbytext::$tags['tel'] = array(
|
||||||
|
'attr' => array(
|
||||||
|
'text',
|
||||||
|
'class',
|
||||||
|
'title'
|
||||||
|
),
|
||||||
|
'html' => function($tag) {
|
||||||
|
|
||||||
|
$text = $tag->attr('text');
|
||||||
|
$tel = str_replace(array('/', ' ', '-'), '', $tag->attr('tel'));
|
||||||
|
|
||||||
|
if(empty($text)) $text = $tag->attr('tel');
|
||||||
|
|
||||||
|
return html::a('tel:' . $tel, html($text), array(
|
||||||
|
'rel' => $tag->attr('rel'),
|
||||||
|
'class' => $tag->attr('class'),
|
||||||
|
'title' => html($tag->attr('title'))
|
||||||
|
));
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
// twitter tag
|
||||||
|
kirbytext::$tags['twitter'] = array(
|
||||||
|
'attr' => array(
|
||||||
|
'class',
|
||||||
|
'title',
|
||||||
|
'text',
|
||||||
|
'rel',
|
||||||
|
'target',
|
||||||
|
'popup',
|
||||||
|
),
|
||||||
|
'html' => function($tag) {
|
||||||
|
|
||||||
|
// get and sanitize the username
|
||||||
|
$username = str_replace('@', '', $tag->attr('twitter'));
|
||||||
|
|
||||||
|
// build the profile url
|
||||||
|
$url = 'https://twitter.com/' . $username;
|
||||||
|
|
||||||
|
// sanitize the link text
|
||||||
|
$text = $tag->attr('text', '@' . $username);
|
||||||
|
|
||||||
|
// build the final link
|
||||||
|
return html::a($url, $text, array(
|
||||||
|
'class' => $tag->attr('class'),
|
||||||
|
'title' => $tag->attr('title'),
|
||||||
|
'rel' => $tag->attr('rel'),
|
||||||
|
'target' => $tag->target(),
|
||||||
|
));
|
||||||
|
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
kirbytext::$tags['youtube'] = array(
|
||||||
|
'attr' => array(
|
||||||
|
'width',
|
||||||
|
'height',
|
||||||
|
'class',
|
||||||
|
'caption'
|
||||||
|
),
|
||||||
|
'html' => function($tag) {
|
||||||
|
|
||||||
|
$caption = $tag->attr('caption');
|
||||||
|
|
||||||
|
if(!empty($caption)) {
|
||||||
|
$figcaption = '<figcaption>' . escape::html($caption) . '</figcaption>';
|
||||||
|
} else {
|
||||||
|
$figcaption = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return '<figure class="' . $tag->attr('class', kirby()->option('kirbytext.video.class', 'video')) . '">' . embed::youtube($tag->attr('youtube'), array(
|
||||||
|
'width' => $tag->attr('width', kirby()->option('kirbytext.video.width')),
|
||||||
|
'height' => $tag->attr('height', kirby()->option('kirbytext.video.height')),
|
||||||
|
'options' => kirby()->option('kirbytext.video.youtube.options')
|
||||||
|
)) . $figcaption . '</figure>';
|
||||||
|
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
kirbytext::$tags['vimeo'] = array(
|
||||||
|
'attr' => array(
|
||||||
|
'width',
|
||||||
|
'height',
|
||||||
|
'class',
|
||||||
|
'caption'
|
||||||
|
),
|
||||||
|
'html' => function($tag) {
|
||||||
|
|
||||||
|
$caption = $tag->attr('caption');
|
||||||
|
|
||||||
|
if(!empty($caption)) {
|
||||||
|
$figcaption = '<figcaption>' . escape::html($caption) . '</figcaption>';
|
||||||
|
} else {
|
||||||
|
$figcaption = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return '<figure class="' . $tag->attr('class', kirby()->option('kirbytext.video.class', 'video')) . '">' . embed::vimeo($tag->attr('vimeo'), array(
|
||||||
|
'width' => $tag->attr('width', kirby()->option('kirbytext.video.width')),
|
||||||
|
'height' => $tag->attr('height', kirby()->option('kirbytext.video.height')),
|
||||||
|
'options' => kirby()->option('kirbytext.video.vimeo.options')
|
||||||
|
)) . $figcaption . '</figure>';
|
||||||
|
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
kirbytext::$tags['gist'] = array(
|
||||||
|
'attr' => array(
|
||||||
|
'file'
|
||||||
|
),
|
||||||
|
'html' => function($tag) {
|
||||||
|
return embed::gist($tag->attr('gist'), $tag->attr('file'));
|
||||||
|
}
|
||||||
|
);
|
327
kirby/helpers.php
Normal file
327
kirby/helpers.php
Normal file
|
@ -0,0 +1,327 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Embeds a snippet from the snippet folder
|
||||||
|
*
|
||||||
|
* @param string $file
|
||||||
|
* @param mixed $data array or object
|
||||||
|
* @param boolean $return
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
function snippet($file, $data = array(), $return = false) {
|
||||||
|
return kirby::instance()->component('snippet')->render($file, $data, $return);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builds a css link tag for relative or absolute urls
|
||||||
|
*
|
||||||
|
* @param string $url
|
||||||
|
* @param string $media
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
function css() {
|
||||||
|
return call([kirby::instance()->component('css'), 'tag'], func_get_args());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builds a script tag for relative or absolute links
|
||||||
|
*
|
||||||
|
* @param string $src
|
||||||
|
* @param boolean $async
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
function js($src, $async = false) {
|
||||||
|
return call([kirby::instance()->component('js'), 'tag'], func_get_args());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Global markdown parser shortcut
|
||||||
|
*
|
||||||
|
* @param string $text
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
function markdown($text) {
|
||||||
|
return kirby::instance()->component('markdown')->parse($text);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Global smartypants parser shortcut
|
||||||
|
*
|
||||||
|
* @param string $text
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
function smartypants($text) {
|
||||||
|
return kirby::instance()->component('smartypants')->parse($text);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts a string to Kirbytext
|
||||||
|
*
|
||||||
|
* @param Field $field
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
function kirbytext($field) {
|
||||||
|
return (string)new Kirbytext($field);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the Kirby class singleton
|
||||||
|
*
|
||||||
|
* @return Kirby
|
||||||
|
*/
|
||||||
|
function kirby($class = null) {
|
||||||
|
return kirby::instance($class);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the site object
|
||||||
|
*
|
||||||
|
* @return Site
|
||||||
|
*/
|
||||||
|
function site() {
|
||||||
|
return kirby::instance()->site();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns either the current page or any page for a given uri
|
||||||
|
*
|
||||||
|
* @return Page
|
||||||
|
*/
|
||||||
|
function page() {
|
||||||
|
return call_user_func_array(array(kirby::instance()->site(), 'page'), func_get_args());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper to build page collections
|
||||||
|
*
|
||||||
|
* @param array $data
|
||||||
|
*/
|
||||||
|
function pages($data = array()) {
|
||||||
|
return new Pages($data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an excerpt without html and kirbytext
|
||||||
|
*
|
||||||
|
* @param mixed $text Variable object or string
|
||||||
|
* @param int $length The number of characters which should be included in the excerpt
|
||||||
|
* @param array $params an array of options for kirbytext: array('markdown' => true, 'smartypants' => true)
|
||||||
|
* @return string The shortened text
|
||||||
|
*/
|
||||||
|
function excerpt($text, $length = 140, $mode = 'chars') {
|
||||||
|
|
||||||
|
if(strtolower($mode) == 'words') {
|
||||||
|
$text = str::excerpt(kirbytext($text), 0);
|
||||||
|
|
||||||
|
if(str_word_count($text, 0) > $length) {
|
||||||
|
$words = str_word_count($text, 2);
|
||||||
|
$pos = array_keys($words);
|
||||||
|
$text = str::substr($text, 0, $pos[$length]) . '...';
|
||||||
|
}
|
||||||
|
return $text;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
return str::excerpt(kirbytext($text), $length);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper to create correct text file names for content files
|
||||||
|
*
|
||||||
|
* @param string $uri
|
||||||
|
* @param string $template
|
||||||
|
* @param string $lang
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
function textfile($uri, $template, $lang = null) {
|
||||||
|
|
||||||
|
$curi = '';
|
||||||
|
$parts = str::split($uri, '/');
|
||||||
|
$parent = site();
|
||||||
|
|
||||||
|
foreach($parts as $p) {
|
||||||
|
|
||||||
|
if($parent and $child = $parent->children()->find($p)) {
|
||||||
|
$curi .= '/' . $child->dirname();
|
||||||
|
$parent = $child;
|
||||||
|
} else {
|
||||||
|
$curi .= '/' . $p;
|
||||||
|
$parent = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
$uri = ltrim($curi, '/');
|
||||||
|
$root = kirby::instance()->roots()->content();
|
||||||
|
$ext = kirby::instance()->option('content.file.extension', 'txt');
|
||||||
|
return $root . DS . r(!empty($uri), str_replace('/', DS, $uri) . DS) . $template . r($lang, '.' . $lang) . '.' . $ext;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Renders a kirbytag
|
||||||
|
*
|
||||||
|
* @param array $attr
|
||||||
|
* @return Kirbytag
|
||||||
|
*/
|
||||||
|
function kirbytag($attr) {
|
||||||
|
return new Kirbytag(null, key($attr), $attr);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builds a Youtube video iframe
|
||||||
|
*
|
||||||
|
* @param string $url
|
||||||
|
* @param mixed $width
|
||||||
|
* @param mixed $height
|
||||||
|
* @param string $class
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
function youtube($url, $width = null, $height = null, $class = null) {
|
||||||
|
return kirbytag(array(
|
||||||
|
'youtube' => $url,
|
||||||
|
'width' => $width,
|
||||||
|
'height' => $height,
|
||||||
|
'class' => $class
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builds a Vimeo video iframe
|
||||||
|
*
|
||||||
|
* @param string $url
|
||||||
|
* @param mixed $width
|
||||||
|
* @param mixed $height
|
||||||
|
* @param string $class
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
function vimeo($url, $width = null, $height = null, $class = null) {
|
||||||
|
return kirbytag(array(
|
||||||
|
'vimeo' => $url,
|
||||||
|
'width' => $width,
|
||||||
|
'height' => $height,
|
||||||
|
'class' => $class
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builds a Twitter link
|
||||||
|
*
|
||||||
|
* @param string $username
|
||||||
|
* @param string $text
|
||||||
|
* @param string $title
|
||||||
|
* @param string $class
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
function twitter($username, $text = null, $title = null, $class = null) {
|
||||||
|
return kirbytag(array(
|
||||||
|
'twitter' => $username,
|
||||||
|
'text' => $text,
|
||||||
|
'title' => $title,
|
||||||
|
'class' => $class
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Embeds a Github Gist
|
||||||
|
*
|
||||||
|
* @param string $url
|
||||||
|
* @param string $file
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
function gist($url, $file = null) {
|
||||||
|
return kirbytag(array(
|
||||||
|
'gist' => $url,
|
||||||
|
'file' => $file,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the current url
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
function thisUrl() {
|
||||||
|
return url::current();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Give this any kind of array
|
||||||
|
* to get some kirby style structure
|
||||||
|
*
|
||||||
|
* @param mixed $data
|
||||||
|
* @param mixed $page
|
||||||
|
* @param mixed $key
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
function structure($data, $page = null, $key = null) {
|
||||||
|
|
||||||
|
if(is_null($page)) {
|
||||||
|
$page = page();
|
||||||
|
}
|
||||||
|
|
||||||
|
if(is_array($data)) {
|
||||||
|
$result = new Structure();
|
||||||
|
$result->page = $page;
|
||||||
|
foreach($data as $key => $value) {
|
||||||
|
$result->append($key, structure($value, $page, $key));
|
||||||
|
}
|
||||||
|
return $result;
|
||||||
|
} else if(is_a($data, 'Field')) {
|
||||||
|
return $data;
|
||||||
|
} else {
|
||||||
|
return new Field($page, $key, $data);
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return an image from any page
|
||||||
|
* specified by the path
|
||||||
|
*
|
||||||
|
* Example:
|
||||||
|
* <?= image('some/page/myimage.jpg') ?>
|
||||||
|
*
|
||||||
|
* @param string $path
|
||||||
|
* @return File|null
|
||||||
|
*/
|
||||||
|
function image($path = null) {
|
||||||
|
|
||||||
|
if($path === null) {
|
||||||
|
return page()->image();
|
||||||
|
}
|
||||||
|
|
||||||
|
$uri = dirname($path);
|
||||||
|
$filename = basename($path);
|
||||||
|
|
||||||
|
if($uri == '.') {
|
||||||
|
$uri = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$page = $uri == '/' ? site() : page($uri);
|
||||||
|
|
||||||
|
if($page) {
|
||||||
|
return $page->image($filename);
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shortcut to create a new thumb object
|
||||||
|
*
|
||||||
|
* @param mixed Either a file path or a Media object
|
||||||
|
* @param array An array of additional params for the thumb
|
||||||
|
* @return object Thumb
|
||||||
|
*/
|
||||||
|
function thumb($image, $params = array(), $obj = true) {
|
||||||
|
if(is_a($image, 'File') || is_a($image, 'Asset')) {
|
||||||
|
return $obj ? $image->thumb($params) : $image->thumb($params)->url();
|
||||||
|
} else {
|
||||||
|
$class = new Thumb($image, $params);
|
||||||
|
return $obj ? $class : $class->url();
|
||||||
|
}
|
||||||
|
}
|
785
kirby/kirby.php
Normal file
785
kirby/kirby.php
Normal file
|
@ -0,0 +1,785 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
use Kirby\Component;
|
||||||
|
use Kirby\Pipe;
|
||||||
|
use Kirby\Registry;
|
||||||
|
use Kirby\Request;
|
||||||
|
use Kirby\Roots;
|
||||||
|
use Kirby\Urls;
|
||||||
|
|
||||||
|
class Kirby {
|
||||||
|
|
||||||
|
static public $version = '2.3.0';
|
||||||
|
static public $instance;
|
||||||
|
static public $hooks = array();
|
||||||
|
static public $triggered = array();
|
||||||
|
|
||||||
|
public $roots;
|
||||||
|
public $urls;
|
||||||
|
public $cache;
|
||||||
|
public $path;
|
||||||
|
public $options = array();
|
||||||
|
public $routes;
|
||||||
|
public $router;
|
||||||
|
public $route;
|
||||||
|
public $site;
|
||||||
|
public $page;
|
||||||
|
public $plugins;
|
||||||
|
public $response;
|
||||||
|
public $request;
|
||||||
|
public $components = [];
|
||||||
|
public $registry;
|
||||||
|
|
||||||
|
static public function instance($class = null) {
|
||||||
|
if(!is_null(static::$instance)) return static::$instance;
|
||||||
|
return static::$instance = $class ? new $class : new static;
|
||||||
|
}
|
||||||
|
|
||||||
|
static public function version() {
|
||||||
|
return static::$version;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __construct($options = array()) {
|
||||||
|
$this->roots = new Roots(dirname(__DIR__));
|
||||||
|
$this->urls = new Urls();
|
||||||
|
$this->registry = new Registry($this);
|
||||||
|
$this->options = array_merge($this->defaults(), $options);
|
||||||
|
$this->path = implode('/', (array)url::fragments(detect::path()));
|
||||||
|
|
||||||
|
// make sure the instance is stored / overwritten
|
||||||
|
static::$instance = $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function defaults() {
|
||||||
|
|
||||||
|
$defaults = array(
|
||||||
|
'url' => false,
|
||||||
|
'timezone' => 'UTC',
|
||||||
|
'license' => null,
|
||||||
|
'rewrite' => true,
|
||||||
|
'error' => 'error',
|
||||||
|
'home' => 'home',
|
||||||
|
'locale' => 'en_US.UTF8',
|
||||||
|
'routes' => array(),
|
||||||
|
'headers' => array(),
|
||||||
|
'languages' => array(),
|
||||||
|
'roles' => array(),
|
||||||
|
'cache' => false,
|
||||||
|
'debug' => 'env',
|
||||||
|
'ssl' => false,
|
||||||
|
'cache.driver' => 'file',
|
||||||
|
'cache.options' => array(),
|
||||||
|
'cache.ignore' => array(),
|
||||||
|
'cache.autoupdate' => true,
|
||||||
|
'date.handler' => 'date',
|
||||||
|
'kirbytext.video.class' => 'video',
|
||||||
|
'kirbytext.video.width' => false,
|
||||||
|
'kirbytext.video.height' => false,
|
||||||
|
'kirbytext.video.youtube.options' => array(),
|
||||||
|
'kirbytext.video.vimeo.options' => array(),
|
||||||
|
'kirbytext.image.figure' => true,
|
||||||
|
'content.file.extension' => 'txt',
|
||||||
|
'content.file.ignore' => array(),
|
||||||
|
'content.file.normalize' => false,
|
||||||
|
'email.service' => 'mail',
|
||||||
|
'email.to' => null,
|
||||||
|
'email.replyTo' => null,
|
||||||
|
'email.subject' => null,
|
||||||
|
'email.body' => null,
|
||||||
|
'email.options' => array(),
|
||||||
|
);
|
||||||
|
|
||||||
|
return $defaults;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public function roots() {
|
||||||
|
return $this->roots;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function urls() {
|
||||||
|
return $this->urls;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function registry() {
|
||||||
|
return $this->registry;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function url() {
|
||||||
|
return $this->urls->index();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function options() {
|
||||||
|
return $this->options;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function option($key, $default = null) {
|
||||||
|
return a::get($this->options, $key, $default);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function path() {
|
||||||
|
return $this->path;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function page() {
|
||||||
|
return $this->page;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function response() {
|
||||||
|
return $this->response;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Install a new entry in the registry
|
||||||
|
*/
|
||||||
|
public function set() {
|
||||||
|
return call_user_func_array([$this->registry, 'set'], func_get_args());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve an entry from the registry
|
||||||
|
*/
|
||||||
|
public function get() {
|
||||||
|
return call_user_func_array([$this->registry, 'get'], func_get_args());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function configure() {
|
||||||
|
|
||||||
|
// load all available config files
|
||||||
|
$root = $this->roots()->config();
|
||||||
|
$configs = array(
|
||||||
|
'main' => 'config.php',
|
||||||
|
'host' => 'config.' . server::get('SERVER_NAME') . '.php',
|
||||||
|
'addr' => 'config.' . server::get('SERVER_ADDR') . '.php',
|
||||||
|
);
|
||||||
|
|
||||||
|
$allowed = array_filter(dir::read($root), function($file) {
|
||||||
|
return substr($file, 0, 7) === 'config.' and substr($file, -4) === '.php';
|
||||||
|
});
|
||||||
|
|
||||||
|
foreach($configs as $config) {
|
||||||
|
$file = $root . DS . $config;
|
||||||
|
if(in_array($config, $allowed, true) and file_exists($file)) include_once($file);
|
||||||
|
}
|
||||||
|
|
||||||
|
// apply the options
|
||||||
|
$this->options = array_merge($this->options, c::$data);
|
||||||
|
|
||||||
|
// overwrite the autodetected url
|
||||||
|
if($this->options['url']) {
|
||||||
|
$this->urls->index = $this->options['url'];
|
||||||
|
}
|
||||||
|
|
||||||
|
// connect the url class with its handlers
|
||||||
|
url::$home = $this->urls()->index();
|
||||||
|
url::$to = $this->option('url.to', function($url = '', $lang = null) {
|
||||||
|
|
||||||
|
if(url::isAbsolute($url)) return $url;
|
||||||
|
|
||||||
|
$start = substr($url, 0, 1);
|
||||||
|
switch($start) {
|
||||||
|
case '#':
|
||||||
|
return $url;
|
||||||
|
break;
|
||||||
|
case '.':
|
||||||
|
return page()->url() . '/' . $url;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
if($page = page($url)) {
|
||||||
|
// use the "official" page url
|
||||||
|
return $page->url($lang);
|
||||||
|
} else {
|
||||||
|
// don't convert absolute urls
|
||||||
|
return url::makeAbsolute($url);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
// setup the pagination redirect to the error page
|
||||||
|
pagination::$defaults['redirect'] = $this->option('error');
|
||||||
|
|
||||||
|
// setting up the email class
|
||||||
|
email::$defaults['service'] = $this->option('email.service');
|
||||||
|
email::$defaults['from'] = $this->option('email.from');
|
||||||
|
email::$defaults['to'] = $this->option('email.to');
|
||||||
|
email::$defaults['replyTo'] = $this->option('email.replyTo');
|
||||||
|
email::$defaults['subject'] = $this->option('email.subject');
|
||||||
|
email::$defaults['body'] = $this->option('email.body');
|
||||||
|
email::$defaults['options'] = $this->option('email.options');
|
||||||
|
|
||||||
|
// simple error handling
|
||||||
|
if($this->options['debug'] === true) {
|
||||||
|
error_reporting(E_ALL);
|
||||||
|
ini_set('display_errors', 1);
|
||||||
|
} else if($this->options['debug'] === false) {
|
||||||
|
error_reporting(0);
|
||||||
|
ini_set('display_errors', 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers all routes
|
||||||
|
*
|
||||||
|
* @param array $routes New routes
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function routes($routes = array()) {
|
||||||
|
|
||||||
|
// extend the existing routes
|
||||||
|
if(!empty($routes) and is_array($routes)) {
|
||||||
|
return $this->options['routes'] = array_merge($this->options['routes'], $routes);
|
||||||
|
}
|
||||||
|
|
||||||
|
$routes = $this->options['routes'];
|
||||||
|
$kirby = $this;
|
||||||
|
$site = $this->site();
|
||||||
|
|
||||||
|
if($site->multilang()) {
|
||||||
|
|
||||||
|
foreach($site->languages() as $lang) {
|
||||||
|
|
||||||
|
$routes[] = array(
|
||||||
|
'pattern' => ltrim($lang->url . '/(:all?)', '/'),
|
||||||
|
'method' => 'ALL',
|
||||||
|
'lang' => $lang,
|
||||||
|
'action' => function($path = null) use($kirby, $site) {
|
||||||
|
return $site->visit($path, $kirby->route->lang->code());
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// fallback for the homepage
|
||||||
|
$routes[] = array(
|
||||||
|
'pattern' => '/',
|
||||||
|
'method' => 'ALL',
|
||||||
|
'action' => function() use($kirby, $site) {
|
||||||
|
|
||||||
|
// check if the language detector is activated
|
||||||
|
if($kirby->option('language.detect')) {
|
||||||
|
|
||||||
|
if(s::get('language') and $language = $kirby->site()->sessionLanguage()) {
|
||||||
|
// $language is already set but the user wants to
|
||||||
|
// select the default language
|
||||||
|
$referer = r::referer();
|
||||||
|
if(!empty($referer) && str::startsWith($referer, $this->urls()->index())) {
|
||||||
|
$language = $kirby->site()->defaultLanguage();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// detect the user language
|
||||||
|
$language = $kirby->site()->detectedLanguage();
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// always use the default language if the detector is disabled
|
||||||
|
$language = $kirby->site()->defaultLanguage();
|
||||||
|
}
|
||||||
|
|
||||||
|
// redirect to the language homepage if necessary
|
||||||
|
if($language->url != '/' and $language->url != '') {
|
||||||
|
go($language->url());
|
||||||
|
}
|
||||||
|
|
||||||
|
// plain home pages
|
||||||
|
return $site->visit('/', $language->code());
|
||||||
|
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// tinyurl handling
|
||||||
|
$routes['tinyurl'] = $this->component('tinyurl')->route();
|
||||||
|
|
||||||
|
// home redirect
|
||||||
|
$routes['homeRedirect'] = array(
|
||||||
|
'pattern' => $this->options['home'],
|
||||||
|
'action' => function() {
|
||||||
|
redirect::send(page('home')->url(), 307);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
// plugin assets
|
||||||
|
$routes['pluginAssets'] = array(
|
||||||
|
'pattern' => 'assets/plugins/(:any)/(:all)',
|
||||||
|
'method' => 'GET',
|
||||||
|
'action' => function($plugin, $path) use($kirby) {
|
||||||
|
$root = $kirby->roots()->plugins() . DS . $plugin . DS . 'assets' . DS . $path;
|
||||||
|
$file = new Media($root);
|
||||||
|
|
||||||
|
if($file->exists()) {
|
||||||
|
return new Response(f::read($root), f::extension($root));
|
||||||
|
} else {
|
||||||
|
return new Response('The file could not be found', f::extension($path), 404);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
// all other urls
|
||||||
|
$routes['others'] = array(
|
||||||
|
'pattern' => '(:all)',
|
||||||
|
'method' => 'ALL',
|
||||||
|
'action' => function($path = null) use($site, $kirby) {
|
||||||
|
|
||||||
|
// visit the currently active page
|
||||||
|
$page = $site->visit($path);
|
||||||
|
|
||||||
|
// react on errors for invalid URLs
|
||||||
|
if($page->isErrorPage() and $page->uri() != $path) {
|
||||||
|
|
||||||
|
// get the filename
|
||||||
|
$filename = rawurldecode(basename($path));
|
||||||
|
$pagepath = dirname($path);
|
||||||
|
|
||||||
|
// check if there's a page for the parent path
|
||||||
|
if($page = $site->find($pagepath)) {
|
||||||
|
// check if there's a file for the last element of the path
|
||||||
|
if($file = $page->file($filename)) {
|
||||||
|
go($file->url());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// return the error page if there's no such page
|
||||||
|
return $site->errorPage();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return $page;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
);
|
||||||
|
|
||||||
|
return $routes;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads all available plugins for the site
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function plugins() {
|
||||||
|
|
||||||
|
// check for a cached plugins array
|
||||||
|
if(!is_null($this->plugins)) return $this->plugins;
|
||||||
|
|
||||||
|
// get the plugins root
|
||||||
|
$root = $this->roots->plugins();
|
||||||
|
|
||||||
|
// start the plugin registry
|
||||||
|
$this->plugins = array();
|
||||||
|
|
||||||
|
// check for an existing plugins dir
|
||||||
|
if(!is_dir($root)) return $this->plugins;
|
||||||
|
|
||||||
|
foreach(array_diff(scandir($root), array('.', '..')) as $file) {
|
||||||
|
if(is_dir($root . DS . $file)) {
|
||||||
|
$this->plugin($file, 'dir');
|
||||||
|
} else if(f::extension($file) == 'php') {
|
||||||
|
$this->plugin(f::name($file), 'file');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->plugins;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads a single plugin
|
||||||
|
*
|
||||||
|
* @param string $name
|
||||||
|
* @param string $mode
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function plugin($name, $mode = 'dir') {
|
||||||
|
|
||||||
|
if(isset($this->plugins[$name])) return $this->plugins[$name];
|
||||||
|
|
||||||
|
if($mode == 'dir') {
|
||||||
|
$file = $this->roots->plugins() . DS . $name . DS . $name . '.php';
|
||||||
|
} else {
|
||||||
|
$file = $this->roots->plugins() . DS . $name . '.php';
|
||||||
|
}
|
||||||
|
|
||||||
|
// make the kirby variable available in plugin files
|
||||||
|
$kirby = $this;
|
||||||
|
|
||||||
|
if(file_exists($file)) return $this->plugins[$name] = include_once($file);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load all default extensions
|
||||||
|
*/
|
||||||
|
public function extensions() {
|
||||||
|
|
||||||
|
// load all kirby tags and field methods
|
||||||
|
include_once(__DIR__ . DS . 'extensions' . DS . 'tags.php');
|
||||||
|
include_once(__DIR__ . DS . 'extensions' . DS . 'methods.php');
|
||||||
|
|
||||||
|
// install additional kirby tags
|
||||||
|
kirbytext::install($this->roots->tags());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Autoloads all page models
|
||||||
|
*/
|
||||||
|
public function models() {
|
||||||
|
|
||||||
|
if(!is_dir($this->roots()->models())) return false;
|
||||||
|
|
||||||
|
$root = $this->roots()->models();
|
||||||
|
$files = dir::read($root);
|
||||||
|
$load = array();
|
||||||
|
|
||||||
|
foreach($files as $file) {
|
||||||
|
if(f::extension($file) != 'php') continue;
|
||||||
|
$name = f::name($file);
|
||||||
|
$classname = str_replace(array('.', '-', '_'), '', $name . 'page');
|
||||||
|
$load[$classname] = $root . DS . $file;
|
||||||
|
|
||||||
|
// register the model
|
||||||
|
page::$models[$name] = $classname;
|
||||||
|
}
|
||||||
|
|
||||||
|
// start the autoloader
|
||||||
|
if(!empty($load)) {
|
||||||
|
load($load);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public function localize() {
|
||||||
|
|
||||||
|
$site = $this->site();
|
||||||
|
|
||||||
|
if($site->multilang() and !$site->language()) {
|
||||||
|
$site->language = $site->languages()->findDefault();
|
||||||
|
}
|
||||||
|
|
||||||
|
// set the local for the specific language
|
||||||
|
if(is_array($site->locale())) {
|
||||||
|
foreach($site->locale() as $key => $value) {
|
||||||
|
setlocale($key, $value);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
setlocale(LC_ALL, $site->locale());
|
||||||
|
}
|
||||||
|
|
||||||
|
// additional language variables for multilang sites
|
||||||
|
if($site->multilang()) {
|
||||||
|
// path for the language file
|
||||||
|
$file = $this->roots()->languages() . DS . $site->language()->code() . '.php';
|
||||||
|
// load the file if it exists
|
||||||
|
if(file_exists($file)) include_once($file);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the branch file
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function branch() {
|
||||||
|
|
||||||
|
// which branch?
|
||||||
|
$branch = count($this->options['languages']) > 0 ? 'multilang' : 'default';
|
||||||
|
|
||||||
|
// build the path for the branch file
|
||||||
|
return __DIR__ . DS . 'branches' . DS . $branch . '.php';
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes and returns the site object
|
||||||
|
* depending on the appropriate branch
|
||||||
|
*
|
||||||
|
* @return Site
|
||||||
|
*/
|
||||||
|
public function site() {
|
||||||
|
|
||||||
|
// check for a cached version of the site object
|
||||||
|
if(!is_null($this->site)) return $this->site;
|
||||||
|
|
||||||
|
// load all options
|
||||||
|
$this->configure();
|
||||||
|
|
||||||
|
// setup the cache
|
||||||
|
$this->cache();
|
||||||
|
|
||||||
|
// load the main branch file
|
||||||
|
include_once($this->branch());
|
||||||
|
|
||||||
|
// create the site object
|
||||||
|
return $this->site = new Site($this);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cache setup
|
||||||
|
*
|
||||||
|
* @return Cache
|
||||||
|
*/
|
||||||
|
public function cache() {
|
||||||
|
|
||||||
|
if(!is_null($this->cache)) return $this->cache;
|
||||||
|
|
||||||
|
// cache setup
|
||||||
|
if($this->options['cache']) {
|
||||||
|
if($this->options['cache.driver'] == 'file' and empty($this->options['cache.options'])) {
|
||||||
|
$this->options['cache.options'] = array(
|
||||||
|
'root' => $this->roots()->cache()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return $this->cache = cache::setup($this->options['cache.driver'], $this->options['cache.options']);
|
||||||
|
} else {
|
||||||
|
return $this->cache = cache::setup('mock');
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Renders the HTML for the page or fetches it from the cache
|
||||||
|
*
|
||||||
|
* @param Page $page
|
||||||
|
* @param boolean $headers
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function render(Page $page, $data = array(), $headers = true) {
|
||||||
|
|
||||||
|
// register the currently rendered page
|
||||||
|
$this->page = $page;
|
||||||
|
|
||||||
|
// send all headers for the page
|
||||||
|
if($headers) $page->headers();
|
||||||
|
|
||||||
|
// configure pagination urls
|
||||||
|
$query = (string)$this->request()->query();
|
||||||
|
$params = (string)$this->request()->params() . r($query, '?') . $query;
|
||||||
|
|
||||||
|
pagination::$defaults['url'] = $page->url() . r($params, '/') . $params;
|
||||||
|
|
||||||
|
// cache the result if possible
|
||||||
|
if($this->options['cache'] and $page->isCachable()) {
|
||||||
|
|
||||||
|
// try to read the cache by cid (cache id)
|
||||||
|
$cacheId = md5(url::current());
|
||||||
|
|
||||||
|
// check for modified content within the content folder
|
||||||
|
// and auto-expire the page cache in such a case
|
||||||
|
if($this->options['cache.autoupdate'] and $this->cache()->exists($cacheId)) {
|
||||||
|
|
||||||
|
// get the creation date of the cache file
|
||||||
|
$created = $this->cache()->created($cacheId);
|
||||||
|
|
||||||
|
// make sure to kill the cache if the site has been modified
|
||||||
|
if($this->site->wasModifiedAfter($created)) {
|
||||||
|
$this->cache()->remove($cacheId);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// try to fetch the template from cache
|
||||||
|
$template = $this->cache()->get($cacheId);
|
||||||
|
|
||||||
|
// fetch fresh content if the cache is empty
|
||||||
|
if(empty($template)) {
|
||||||
|
$template = $this->template($page, $data);
|
||||||
|
// store the result for the next round
|
||||||
|
$this->cache()->set($cacheId, $template);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $template;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// return a fresh template
|
||||||
|
return $this->template($page, $data);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Template configuration
|
||||||
|
*
|
||||||
|
* @param Page $page
|
||||||
|
* @param array $data
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function template(Page $page, $data = array()) {
|
||||||
|
return $this->component('template')->render($page, $data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function request() {
|
||||||
|
if(!is_null($this->request)) return $this->request;
|
||||||
|
return $this->request = new Request($this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function router() {
|
||||||
|
return $this->router;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function route() {
|
||||||
|
return $this->route;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Starts the router, renders the page and returns the response
|
||||||
|
*
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function launch() {
|
||||||
|
|
||||||
|
// this will trigger the configuration
|
||||||
|
$site = $this->site();
|
||||||
|
|
||||||
|
// force secure connections if enabled
|
||||||
|
if($this->option('ssl') and !r::secure()) {
|
||||||
|
// rebuild the current url with https
|
||||||
|
go(url::build(array('scheme' => 'https')));
|
||||||
|
}
|
||||||
|
|
||||||
|
// set the timezone for all date functions
|
||||||
|
date_default_timezone_set($this->options['timezone']);
|
||||||
|
|
||||||
|
// load all extensions
|
||||||
|
$this->extensions();
|
||||||
|
|
||||||
|
// load all plugins
|
||||||
|
$this->plugins();
|
||||||
|
|
||||||
|
// load all models
|
||||||
|
$this->models();
|
||||||
|
|
||||||
|
// start the router
|
||||||
|
$this->router = new Router($this->routes());
|
||||||
|
$this->route = $this->router->run($this->path());
|
||||||
|
|
||||||
|
// check for a valid route
|
||||||
|
if(is_null($this->route)) {
|
||||||
|
header::status('500');
|
||||||
|
header::type('json');
|
||||||
|
die(json_encode(array(
|
||||||
|
'status' => 'error',
|
||||||
|
'message' => 'Invalid route or request method'
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
|
||||||
|
// call the router action with all arguments from the pattern
|
||||||
|
$response = call($this->route->action(), $this->route->arguments());
|
||||||
|
|
||||||
|
// load all language variables
|
||||||
|
// this can only be loaded once the router action has been called
|
||||||
|
// otherwise the current language is not yet available
|
||||||
|
$this->localize();
|
||||||
|
|
||||||
|
// build the response
|
||||||
|
$this->response = $this->component('response')->make($response);
|
||||||
|
|
||||||
|
// store the current language in the session
|
||||||
|
if($this->site()->multilang() && $language = $this->site()->language()) {
|
||||||
|
s::set('language', $language->code());
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->response;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register a new hook
|
||||||
|
*
|
||||||
|
* @param string $hook The name of the hook
|
||||||
|
* @param closure $callback
|
||||||
|
*/
|
||||||
|
public function hook($hook, $callback) {
|
||||||
|
|
||||||
|
if(isset(static::$hooks[$hook]) and is_array(static::$hooks[$hook])) {
|
||||||
|
static::$hooks[$hook][] = $callback;
|
||||||
|
} else {
|
||||||
|
static::$hooks[$hook] = array($callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Trigger a hook
|
||||||
|
*
|
||||||
|
* @param string $hook The name of the hook
|
||||||
|
* @param mixed $args Additional arguments for the hook
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function trigger($hook, $args = null) {
|
||||||
|
|
||||||
|
if(isset(static::$hooks[$hook]) and is_array(static::$hooks[$hook])) {
|
||||||
|
foreach(static::$hooks[$hook] as $key => $callback) {
|
||||||
|
|
||||||
|
if(array_key_exists($hook, static::$triggered) && in_array($key, static::$triggered[$hook])) continue;
|
||||||
|
|
||||||
|
static::$triggered[$hook] = $key;
|
||||||
|
|
||||||
|
try {
|
||||||
|
call($callback, $args);
|
||||||
|
} catch(Exception $e) {
|
||||||
|
// caught callback error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static public function start() {
|
||||||
|
return kirby()->launch();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register and fetch core components
|
||||||
|
*/
|
||||||
|
public function component($name, $component = null) {
|
||||||
|
if(is_null($component)) {
|
||||||
|
if(!isset($this->components[$name])) {
|
||||||
|
// load the default component if it exists
|
||||||
|
if(file_exists(__DIR__ . DS . 'kirby' . DS . 'component' . DS . strtolower($name) . '.php')) {
|
||||||
|
$this->component($name, 'Kirby\\Component\\' . $name);
|
||||||
|
} else {
|
||||||
|
throw new Exception('The component "' . $name . '" does not exist');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $this->components[$name];
|
||||||
|
} else {
|
||||||
|
|
||||||
|
if(!is_string($component)) {
|
||||||
|
throw new Exception('Please provide a valid component name');
|
||||||
|
}
|
||||||
|
|
||||||
|
// init the component
|
||||||
|
$object = new $component($this);
|
||||||
|
|
||||||
|
if(!is_a($object, 'Kirby\\Component')) {
|
||||||
|
throw new Exception('The component "' . $name . '" must be an instance of the Kirby\\Component class');
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!is_a($object, 'Kirby\\Component\\' . $name)) {
|
||||||
|
throw new Exception('The component "' . $name . '" must be an instance of the Kirby\\Component\\' . ucfirst($name) . ' class');
|
||||||
|
}
|
||||||
|
|
||||||
|
// add the component defaults
|
||||||
|
$this->options = array_merge($object->defaults(), $this->options);
|
||||||
|
|
||||||
|
// configure the component
|
||||||
|
$object->configure();
|
||||||
|
|
||||||
|
// register the component
|
||||||
|
$this->components[$name] = $object;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
27
kirby/kirby/component.php
Normal file
27
kirby/kirby/component.php
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Kirby;
|
||||||
|
|
||||||
|
use Kirby;
|
||||||
|
|
||||||
|
class Component {
|
||||||
|
|
||||||
|
protected $kirby;
|
||||||
|
|
||||||
|
public function __construct(Kirby $kirby) {
|
||||||
|
$this->kirby = $kirby;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function defaults() {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function configure() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public function kirby() {
|
||||||
|
return $this->kirby;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
52
kirby/kirby/component/css.php
Normal file
52
kirby/kirby/component/css.php
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Kirby\Component;
|
||||||
|
|
||||||
|
use HTML;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Kirby CSS Tag Component
|
||||||
|
*
|
||||||
|
* @package Kirby CMS
|
||||||
|
* @author Bastian Allgeier <bastian@getkirby.com>
|
||||||
|
* @link http://getkirby.com
|
||||||
|
* @copyright Bastian Allgeier
|
||||||
|
* @license http://getkirby.com/license
|
||||||
|
*/
|
||||||
|
class CSS extends \Kirby\Component {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builds the html link tag for the given css file
|
||||||
|
*
|
||||||
|
* @param string $url
|
||||||
|
* @param null|string $media
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function tag($url, $media = null) {
|
||||||
|
|
||||||
|
if(is_array($url)) {
|
||||||
|
$css = array();
|
||||||
|
foreach($url as $u) $css[] = $this->tag($u, $media);
|
||||||
|
return implode(PHP_EOL, $css) . PHP_EOL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// auto template css files
|
||||||
|
if($url == '@auto') {
|
||||||
|
|
||||||
|
$file = $this->kirby->site()->page()->template() . '.css';
|
||||||
|
$root = $this->kirby->roots()->autocss() . DS . $file;
|
||||||
|
$url = $this->kirby->urls()->autocss() . '/' . $file;
|
||||||
|
|
||||||
|
if(!file_exists($root)) return false;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return html::tag('link', null, array(
|
||||||
|
'rel' => 'stylesheet',
|
||||||
|
'href' => url($url),
|
||||||
|
'media' => $media
|
||||||
|
));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
51
kirby/kirby/component/js.php
Normal file
51
kirby/kirby/component/js.php
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Kirby\Component;
|
||||||
|
|
||||||
|
use HTML;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Kirby Script Tag Component
|
||||||
|
*
|
||||||
|
* @package Kirby CMS
|
||||||
|
* @author Bastian Allgeier <bastian@getkirby.com>
|
||||||
|
* @link http://getkirby.com
|
||||||
|
* @copyright Bastian Allgeier
|
||||||
|
* @license http://getkirby.com/license
|
||||||
|
*/
|
||||||
|
class JS extends \Kirby\Component {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builds the html script tag for the given javascript file
|
||||||
|
*
|
||||||
|
* @param string $src
|
||||||
|
* @param boolean async
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function tag($src, $async = false) {
|
||||||
|
|
||||||
|
if(is_array($src)) {
|
||||||
|
$js = array();
|
||||||
|
foreach($src as $s) $js[] = $this->tag($s, $async);
|
||||||
|
return implode(PHP_EOL, $js) . PHP_EOL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// auto template css files
|
||||||
|
if($src == '@auto') {
|
||||||
|
|
||||||
|
$file = $this->kirby->site()->page()->template() . '.js';
|
||||||
|
$root = $this->kirby->roots()->autojs() . DS . $file;
|
||||||
|
$src = $this->kirby->urls()->autojs() . '/' . $file;
|
||||||
|
|
||||||
|
if(!file_exists($root)) return false;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return html::tag('script', '', array(
|
||||||
|
'src' => url($src),
|
||||||
|
'async' => $async
|
||||||
|
));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
56
kirby/kirby/component/markdown.php
Normal file
56
kirby/kirby/component/markdown.php
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Kirby\Component;
|
||||||
|
|
||||||
|
use Parsedown;
|
||||||
|
use ParsedownExtra;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Kirby Markdown Parser Component
|
||||||
|
*
|
||||||
|
* @package Kirby CMS
|
||||||
|
* @author Bastian Allgeier <bastian@getkirby.com>
|
||||||
|
* @link http://getkirby.com
|
||||||
|
* @copyright Bastian Allgeier
|
||||||
|
* @license http://getkirby.com/license
|
||||||
|
*/
|
||||||
|
class Markdown extends \Kirby\Component {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the default options for the component
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function defaults() {
|
||||||
|
return [
|
||||||
|
'markdown' => true,
|
||||||
|
'markdown.extra' => false,
|
||||||
|
'markdown.breaks' => true,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes the Parsedown parser and
|
||||||
|
* transforms the given markdown to HTML
|
||||||
|
*
|
||||||
|
* @param string $markdown
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function parse($markdown) {
|
||||||
|
|
||||||
|
if(!$this->kirby->options['markdown']) {
|
||||||
|
return $markdown;
|
||||||
|
} else {
|
||||||
|
// initialize the right markdown class
|
||||||
|
$parsedown = $this->kirby->options['markdown.extra'] ? new ParsedownExtra() : new Parsedown();
|
||||||
|
|
||||||
|
// set markdown auto-breaks
|
||||||
|
$parsedown->setBreaksEnabled($this->kirby->options['markdown.breaks']);
|
||||||
|
|
||||||
|
// parse it!
|
||||||
|
return $parsedown->text($markdown);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
38
kirby/kirby/component/response.php
Normal file
38
kirby/kirby/component/response.php
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Kirby\Component;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Kirby Response Builder Component
|
||||||
|
*
|
||||||
|
* @package Kirby CMS
|
||||||
|
* @author Bastian Allgeier <bastian@getkirby.com>
|
||||||
|
* @link http://getkirby.com
|
||||||
|
* @copyright Bastian Allgeier
|
||||||
|
* @license http://getkirby.com/license
|
||||||
|
*/
|
||||||
|
class Response extends \Kirby\Component {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builds and return the response by various input
|
||||||
|
*
|
||||||
|
* @param mixed $response
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function make($response) {
|
||||||
|
|
||||||
|
if(is_string($response)) {
|
||||||
|
return $this->kirby->render(page($response));
|
||||||
|
} else if(is_array($response)) {
|
||||||
|
return $this->kirby->render(page($response[0]), $response[1]);
|
||||||
|
} else if(is_a($response, 'Page')) {
|
||||||
|
return $this->kirby->render($response);
|
||||||
|
} else if(is_a($response, 'Response')) {
|
||||||
|
return $response;
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
61
kirby/kirby/component/smartypants.php
Normal file
61
kirby/kirby/component/smartypants.php
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Kirby\Component;
|
||||||
|
|
||||||
|
use SmartyPantsTypographer_Parser;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Kirby Smartypants Parser Component
|
||||||
|
*
|
||||||
|
* @package Kirby CMS
|
||||||
|
* @author Bastian Allgeier <bastian@getkirby.com>
|
||||||
|
* @link http://getkirby.com
|
||||||
|
* @copyright Bastian Allgeier
|
||||||
|
* @license http://getkirby.com/license
|
||||||
|
*/
|
||||||
|
class Smartypants extends \Kirby\Component {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the default options for
|
||||||
|
* the smartypants parser
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function defaults() {
|
||||||
|
return [
|
||||||
|
'smartypants' => false,
|
||||||
|
'smartypants.attr' => 1,
|
||||||
|
'smartypants.doublequote.open' => '“',
|
||||||
|
'smartypants.doublequote.close' => '”',
|
||||||
|
'smartypants.space.emdash' => ' ',
|
||||||
|
'smartypants.space.endash' => ' ',
|
||||||
|
'smartypants.space.colon' => ' ',
|
||||||
|
'smartypants.space.semicolon' => ' ',
|
||||||
|
'smartypants.space.marks' => ' ',
|
||||||
|
'smartypants.space.frenchquote' => ' ',
|
||||||
|
'smartypants.space.thousand' => ' ',
|
||||||
|
'smartypants.space.unit' => ' ',
|
||||||
|
'smartypants.skip' => 'pre|code|kbd|script|style|math',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes the parser and transforms
|
||||||
|
* the given text.
|
||||||
|
*
|
||||||
|
* @param string $text
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function parse($text) {
|
||||||
|
if(!$this->kirby->options['smartypants']) {
|
||||||
|
return $text;
|
||||||
|
} else {
|
||||||
|
// prepare the text
|
||||||
|
$text = str_replace('"', '"', $text);
|
||||||
|
// run the parser
|
||||||
|
$parser = new SmartyPantsTypographer_Parser($this->kirby->options['smartypants.attr']);
|
||||||
|
return $parser->transform($text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
41
kirby/kirby/component/snippet.php
Normal file
41
kirby/kirby/component/snippet.php
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Kirby\Component;
|
||||||
|
|
||||||
|
use Tpl;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Kirby Snippet Builder Component
|
||||||
|
*
|
||||||
|
* @package Kirby CMS
|
||||||
|
* @author Bastian Allgeier <bastian@getkirby.com>
|
||||||
|
* @link http://getkirby.com
|
||||||
|
* @copyright Bastian Allgeier
|
||||||
|
* @license http://getkirby.com/license
|
||||||
|
*/
|
||||||
|
class Snippet extends \Kirby\Component {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a snippet file path by name
|
||||||
|
*
|
||||||
|
* @param string $name
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function file($name) {
|
||||||
|
return $this->kirby->roots()->snippets() . DS . str_replace('/', DS, $name) . '.php';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Renders the snippet with the given data
|
||||||
|
*
|
||||||
|
* @param string $name
|
||||||
|
* @param array $data
|
||||||
|
* @param boolean $return
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function render($name, $data = [], $return = false) {
|
||||||
|
if(is_object($data)) $data = ['item' => $data];
|
||||||
|
return tpl::load($this->kirby->registry->get('snippet', $name), $data, $return);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
89
kirby/kirby/component/template.php
Normal file
89
kirby/kirby/component/template.php
Normal file
|
@ -0,0 +1,89 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Kirby\Component;
|
||||||
|
|
||||||
|
use Exception;
|
||||||
|
use Page;
|
||||||
|
use Tpl;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Kirby Template Builder Component
|
||||||
|
*
|
||||||
|
* @package Kirby CMS
|
||||||
|
* @author Bastian Allgeier <bastian@getkirby.com>
|
||||||
|
* @link http://getkirby.com
|
||||||
|
* @copyright Bastian Allgeier
|
||||||
|
* @license http://getkirby.com/license
|
||||||
|
*/
|
||||||
|
class Template extends \Kirby\Component {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Collects all template data by page
|
||||||
|
*
|
||||||
|
* @param mixed $page
|
||||||
|
* @param array $data
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function data($page, $data = []) {
|
||||||
|
|
||||||
|
if($page instanceof Page) {
|
||||||
|
$data = array_merge(
|
||||||
|
$page->templateData(),
|
||||||
|
$data,
|
||||||
|
$page->controller($data)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// apply the basic template vars
|
||||||
|
return array_merge(array(
|
||||||
|
'kirby' => $this->kirby,
|
||||||
|
'site' => $this->kirby->site(),
|
||||||
|
'pages' => $this->kirby->site()->children(),
|
||||||
|
'page' => $page
|
||||||
|
), $data);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a template file path by name
|
||||||
|
*
|
||||||
|
* @param string $name
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function file($name) {
|
||||||
|
return $this->kirby->roots()->templates() . DS . str_replace('/', DS, $name) . '.php';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Renders the template by page with the additional data
|
||||||
|
*
|
||||||
|
* @param Page|string $template
|
||||||
|
* @param array $data
|
||||||
|
* @param boolean $return
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function render($template, $data = [], $return = true) {
|
||||||
|
|
||||||
|
if($template instanceof Page) {
|
||||||
|
$page = $template;
|
||||||
|
$file = $page->templateFile();
|
||||||
|
$data = $this->data($page, $data);
|
||||||
|
} else {
|
||||||
|
$file = $template;
|
||||||
|
$data = $this->data(null, $data);
|
||||||
|
}
|
||||||
|
|
||||||
|
// check for an existing template
|
||||||
|
if(!file_exists($file)) {
|
||||||
|
throw new Exception('The template could not be found');
|
||||||
|
}
|
||||||
|
|
||||||
|
// merge and register the template data globally
|
||||||
|
tpl::$data = array_merge(tpl::$data, $data);
|
||||||
|
|
||||||
|
// load the template
|
||||||
|
return tpl::load($file, null, $return);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
206
kirby/kirby/component/thumb.php
Normal file
206
kirby/kirby/component/thumb.php
Normal file
|
@ -0,0 +1,206 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Kirby\Component;
|
||||||
|
|
||||||
|
use A;
|
||||||
|
use Asset;
|
||||||
|
use F;
|
||||||
|
use File;
|
||||||
|
use Media;
|
||||||
|
use Obj;
|
||||||
|
use R;
|
||||||
|
use Redirect;
|
||||||
|
use Str;
|
||||||
|
use Thumb as Generator;
|
||||||
|
use Kirby\Component;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Kirby Thumb Render and API Component
|
||||||
|
*
|
||||||
|
* @package Kirby CMS
|
||||||
|
* @author Bastian Allgeier <bastian@getkirby.com>
|
||||||
|
* @link http://getkirby.com
|
||||||
|
* @copyright Bastian Allgeier
|
||||||
|
* @license http://getkirby.com/license
|
||||||
|
*/
|
||||||
|
class Thumb extends Component {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the default options for the thumb component
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function defaults() {
|
||||||
|
|
||||||
|
$self = $this;
|
||||||
|
|
||||||
|
return [
|
||||||
|
'thumbs.driver' => 'gd',
|
||||||
|
'thumbs.bin' => 'convert',
|
||||||
|
'thumbs.interlace' => false,
|
||||||
|
'thumbs.quality' => 90,
|
||||||
|
'thumbs.memory' => '128M',
|
||||||
|
'thumbs.filename' => false,
|
||||||
|
'thumbs.destination' => function($thumb) use($self) {
|
||||||
|
|
||||||
|
$path = $self->path($thumb);
|
||||||
|
|
||||||
|
return new Obj([
|
||||||
|
'root' => $self->kirby->roots()->thumbs() . DS . str_replace('/', DS, $path),
|
||||||
|
'url' => $self->kirby->urls()->thumbs() . '/' . $path,
|
||||||
|
]);
|
||||||
|
|
||||||
|
}
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configures the thumb driver
|
||||||
|
*/
|
||||||
|
public function configure() {
|
||||||
|
|
||||||
|
$self = $this;
|
||||||
|
|
||||||
|
// setup the thumbnail location
|
||||||
|
generator::$defaults['root'] = $this->kirby->roots->thumbs();
|
||||||
|
generator::$defaults['url'] = $this->kirby->urls->thumbs();
|
||||||
|
|
||||||
|
// setup the default thumbnail options
|
||||||
|
generator::$defaults['driver'] = $this->kirby->option('thumbs.driver');
|
||||||
|
generator::$defaults['bin'] = $this->kirby->option('thumbs.bin');
|
||||||
|
generator::$defaults['quality'] = $this->kirby->option('thumbs.quality');
|
||||||
|
generator::$defaults['interlace'] = $this->kirby->option('thumbs.interlace');
|
||||||
|
generator::$defaults['memory'] = $this->kirby->option('thumbs.memory');
|
||||||
|
generator::$defaults['destination'] = $this->kirby->option('thumbs.destination');
|
||||||
|
generator::$defaults['filename'] = $this->kirby->option('thumbs.filename');
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public function create($file, $params) {
|
||||||
|
|
||||||
|
if(!$file->isWebsafe()) {
|
||||||
|
return $file;
|
||||||
|
}
|
||||||
|
|
||||||
|
$thumb = new Generator($file, $params);
|
||||||
|
$asset = new Asset($thumb->result);
|
||||||
|
|
||||||
|
// store a reference to the original file
|
||||||
|
$asset->original($file);
|
||||||
|
|
||||||
|
return $thumb->exists() ? $asset : $file;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the clean path for a thumbnail
|
||||||
|
*
|
||||||
|
* @param Generator $thumb
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
protected function path(Generator $thumb) {
|
||||||
|
return ltrim($this->dir($thumb) . '/' . $this->filename($thumb), '/');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Generator $thumb
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
protected function dir(Generator $thumb) {
|
||||||
|
if(is_a($thumb->source, 'File')) {
|
||||||
|
return $thumb->source->page()->id();
|
||||||
|
} else {
|
||||||
|
return str_replace($this->kirby->urls()->index(), '', dirname($thumb->source->url()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the filename for a thumb including the
|
||||||
|
* identifying option hash
|
||||||
|
*
|
||||||
|
* @param Generator $thumb
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
protected function filename(Generator $thumb) {
|
||||||
|
|
||||||
|
$dimensions = $this->dimensions($thumb);
|
||||||
|
$wh = $dimensions->width() . 'x' . $dimensions->height();
|
||||||
|
$safeName = f::safeName($thumb->source->name());
|
||||||
|
$options = $this->options($thumb);
|
||||||
|
$extension = $thumb->source->extension();
|
||||||
|
|
||||||
|
if($thumb->options['filename'] === false) {
|
||||||
|
return $safeName . '-' . $wh . r($options, '-' . $options) . '.' . $extension;
|
||||||
|
} else {
|
||||||
|
return str::template($thumb->options['filename'], [
|
||||||
|
'extension' => $extension,
|
||||||
|
'name' => $thumb->source->name(),
|
||||||
|
'filename' => $thumb->source->filename(),
|
||||||
|
'safeName' => $safeName,
|
||||||
|
'safeFilename' => $safeName . '.' . $extension,
|
||||||
|
'width' => $dimensions->width(),
|
||||||
|
'height' => $dimensions->height(),
|
||||||
|
'dimensions' => $wh,
|
||||||
|
'options' => $options,
|
||||||
|
'hash' => md5($thumb->source->root() . $thumb->settingsIdentifier()),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an identifying option hash for thumb filenames
|
||||||
|
*
|
||||||
|
* @param Generator $thumb
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
protected function options(Generator $thumb) {
|
||||||
|
|
||||||
|
$keys = [
|
||||||
|
'blur' => 'blur',
|
||||||
|
'grayscale' => 'bw',
|
||||||
|
'quality' => 'q',
|
||||||
|
];
|
||||||
|
|
||||||
|
$string = [];
|
||||||
|
|
||||||
|
foreach($keys as $long => $key) {
|
||||||
|
|
||||||
|
$value = a::get($thumb->options, $long);
|
||||||
|
|
||||||
|
if($value === true) {
|
||||||
|
$string[] = $key;
|
||||||
|
} else if($value === false) {
|
||||||
|
continue;
|
||||||
|
} else if($key === 'q' && $value == generator::$defaults['quality']) {
|
||||||
|
// ignore the default quality setting
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
$string[] = $key . $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return implode('-', array_filter($string));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Generator $thumb
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
protected function dimensions(Generator $thumb) {
|
||||||
|
|
||||||
|
$dimensions = clone $thumb->source->dimensions();
|
||||||
|
|
||||||
|
if(isset($thumb->options['crop']) && $thumb->options['crop']) {
|
||||||
|
$dimensions->crop(a::get($thumb->options, 'width'), a::get($thumb->options, 'height'));
|
||||||
|
} else {
|
||||||
|
$dimensions->resize(a::get($thumb->options, 'width'), a::get($thumb->options, 'height'), a::get($thumb->options, 'upscale'));
|
||||||
|
}
|
||||||
|
|
||||||
|
return $dimensions;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
54
kirby/kirby/component/tinyurl.php
Normal file
54
kirby/kirby/component/tinyurl.php
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Kirby\Component;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Kirby TinyUrl Component
|
||||||
|
*
|
||||||
|
* @package Kirby CMS
|
||||||
|
* @author Bastian Allgeier <bastian@getkirby.com>
|
||||||
|
* @link http://getkirby.com
|
||||||
|
* @copyright Bastian Allgeier
|
||||||
|
* @license http://getkirby.com/license
|
||||||
|
*/
|
||||||
|
class TinyUrl extends \Kirby\Component {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the default options for the tinyurl component
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function defaults() {
|
||||||
|
return [
|
||||||
|
'tinyurl.enabled' => true,
|
||||||
|
'tinyurl.folder' => 'x',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the tinyurl fetching route
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function route() {
|
||||||
|
if(!$this->kirby->options['tinyurl.enabled']) {
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
return [
|
||||||
|
'pattern' => $this->kirby->options['tinyurl.folder'] . '/(:any)/(:any?)',
|
||||||
|
'action' => function($hash, $lang = null) {
|
||||||
|
// get the site object
|
||||||
|
$site = site();
|
||||||
|
// make sure the language is set
|
||||||
|
$site->visit('/', $lang);
|
||||||
|
// find the page by it's tiny hash
|
||||||
|
if($page = $site->index()->findBy('hash', $hash)) {
|
||||||
|
go($page->url($lang));
|
||||||
|
} else {
|
||||||
|
return $site->errorPage();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
118
kirby/kirby/registry.php
Normal file
118
kirby/kirby/registry.php
Normal file
|
@ -0,0 +1,118 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Kirby;
|
||||||
|
|
||||||
|
use Exception;
|
||||||
|
use Kirby;
|
||||||
|
use Str;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registry
|
||||||
|
*
|
||||||
|
* @package Kirby CMS
|
||||||
|
* @author Bastian Allgeier <bastian@getkirby.com>
|
||||||
|
* @link http://getkirby.com
|
||||||
|
* @copyright Bastian Allgeier
|
||||||
|
* @license http://getkirby.com/license
|
||||||
|
*/
|
||||||
|
class Registry {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Kirby Instance
|
||||||
|
*
|
||||||
|
* @var Kirby
|
||||||
|
*/
|
||||||
|
protected $kirby;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Kirby $kirby
|
||||||
|
*/
|
||||||
|
public function __construct(Kirby $kirby) {
|
||||||
|
|
||||||
|
$this->kirby = $kirby;
|
||||||
|
|
||||||
|
// start the registry entry autoloader
|
||||||
|
load([
|
||||||
|
'kirby\\registry\\entry' => __DIR__ . DS . 'registry' . DS . 'entry.php',
|
||||||
|
'kirby\\registry\\blueprint' => __DIR__ . DS . 'registry' . DS . 'blueprint.php',
|
||||||
|
'kirby\\registry\\component' => __DIR__ . DS . 'registry' . DS . 'component.php',
|
||||||
|
'kirby\\registry\\controller' => __DIR__ . DS . 'registry' . DS . 'controller.php',
|
||||||
|
'kirby\\registry\\hook' => __DIR__ . DS . 'registry' . DS . 'hook.php',
|
||||||
|
'kirby\\registry\\field' => __DIR__ . DS . 'registry' . DS . 'field.php',
|
||||||
|
'kirby\\registry\\method' => __DIR__ . DS . 'registry' . DS . 'method.php',
|
||||||
|
'kirby\\registry\\model' => __DIR__ . DS . 'registry' . DS . 'model.php',
|
||||||
|
'kirby\\registry\\option' => __DIR__ . DS . 'registry' . DS . 'option.php',
|
||||||
|
'kirby\\registry\\route' => __DIR__ . DS . 'registry' . DS . 'route.php',
|
||||||
|
'kirby\\registry\\snippet' => __DIR__ . DS . 'registry' . DS . 'snippet.php',
|
||||||
|
'kirby\\registry\\template' => __DIR__ . DS . 'registry' . DS . 'template.php',
|
||||||
|
'kirby\\registry\\tag' => __DIR__ . DS . 'registry' . DS . 'tag.php',
|
||||||
|
'kirby\\registry\\widget' => __DIR__ . DS . 'registry' . DS . 'widget.php',
|
||||||
|
]);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the Kirby instance
|
||||||
|
*
|
||||||
|
* @return Kirby
|
||||||
|
*/
|
||||||
|
public function kirby() {
|
||||||
|
return $this->kirby;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a registry entry object by type
|
||||||
|
*
|
||||||
|
* @param string $type
|
||||||
|
* @param string $subtype
|
||||||
|
* @return Kirby\Registry\Entry
|
||||||
|
*/
|
||||||
|
public function entry($type, $subtype = null) {
|
||||||
|
|
||||||
|
$class = 'kirby\\registry\\' . $type;
|
||||||
|
|
||||||
|
if(!class_exists('kirby\\registry\\' . $type)) {
|
||||||
|
|
||||||
|
if(str::contains($type, '::')) {
|
||||||
|
$parts = str::split($type, '::');
|
||||||
|
$subtype = $parts[0];
|
||||||
|
$type = $parts[1];
|
||||||
|
return $this->entry($type, $subtype);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Exception('Unsupported registry entry type: ' . $type);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return new $class($this, $subtype);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a new entry to the registry
|
||||||
|
* This will initialize a registry object
|
||||||
|
* and call the set method of it
|
||||||
|
* with the passed arguments
|
||||||
|
*/
|
||||||
|
public function set() {
|
||||||
|
$args = func_get_args();
|
||||||
|
$type = strtolower(array_shift($args));
|
||||||
|
return $this->entry($type)->call('set', $args);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves an entry from the registry
|
||||||
|
*
|
||||||
|
* This will initialize a registry object
|
||||||
|
* and call the get method of it
|
||||||
|
* with the passed arguments
|
||||||
|
*
|
||||||
|
* @return Entry
|
||||||
|
*/
|
||||||
|
public function get() {
|
||||||
|
$args = func_get_args();
|
||||||
|
$type = array_shift($args);
|
||||||
|
return $this->entry($type)->call('get', $args);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
69
kirby/kirby/registry/blueprint.php
Normal file
69
kirby/kirby/registry/blueprint.php
Normal file
|
@ -0,0 +1,69 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Kirby\Registry;
|
||||||
|
|
||||||
|
use A;
|
||||||
|
use Exception;
|
||||||
|
use F;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Blueprint Registy Entry
|
||||||
|
*
|
||||||
|
* @package Kirby CMS
|
||||||
|
* @author Bastian Allgeier <bastian@getkirby.com>
|
||||||
|
* @link http://getkirby.com
|
||||||
|
* @copyright Bastian Allgeier
|
||||||
|
* @license http://getkirby.com/license
|
||||||
|
*/
|
||||||
|
class Blueprint extends Entry {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Blueprint store
|
||||||
|
*
|
||||||
|
* @var array $blueprints
|
||||||
|
*/
|
||||||
|
protected static $blueprints = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a new blueprint entry
|
||||||
|
*
|
||||||
|
* Pass a path to an existing blueprint file
|
||||||
|
* to add it to the registry
|
||||||
|
*
|
||||||
|
* @param string $name
|
||||||
|
* @param string $path
|
||||||
|
* @return $path
|
||||||
|
*/
|
||||||
|
public function set($name, $path) {
|
||||||
|
|
||||||
|
if(!$this->kirby->option('debug') || file_exists($path)) {
|
||||||
|
return static::$blueprints[$name] = $path;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Exception('The blueprint does not exist at the specified path: ' . $path);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retreives a registered blueprint file path
|
||||||
|
*
|
||||||
|
* @param string $name
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function get($name = null) {
|
||||||
|
|
||||||
|
if(is_null($name)) {
|
||||||
|
return static::$blueprints;
|
||||||
|
}
|
||||||
|
|
||||||
|
$file = f::resolve($this->kirby->roots()->blueprints() . DS . str_replace('/', DS, $name), ['php', 'yml', 'yaml']);
|
||||||
|
|
||||||
|
if(file_exists($file)) {
|
||||||
|
return $file;
|
||||||
|
} else {
|
||||||
|
return a::get(static::$blueprints, $name);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
39
kirby/kirby/registry/component.php
Normal file
39
kirby/kirby/registry/component.php
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Kirby\Registry;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Component Registy Entry
|
||||||
|
*
|
||||||
|
* @package Kirby CMS
|
||||||
|
* @author Bastian Allgeier <bastian@getkirby.com>
|
||||||
|
* @link http://getkirby.com
|
||||||
|
* @copyright Bastian Allgeier
|
||||||
|
* @license http://getkirby.com/license
|
||||||
|
*/
|
||||||
|
class Component extends Entry {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a new core component to Kirby
|
||||||
|
*
|
||||||
|
* This will directly call the component method of the
|
||||||
|
* Kirby instance to register the component
|
||||||
|
*
|
||||||
|
* @param string $name The name of the component
|
||||||
|
* @param string $class A valid component classname. Must be extend the according Kirby component type class
|
||||||
|
*/
|
||||||
|
public function set($name, $class) {
|
||||||
|
return $this->kirby->component($name, $class);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retreives a component from the Kirby component registry
|
||||||
|
*
|
||||||
|
* @param string $name
|
||||||
|
* @return Kirby\Component
|
||||||
|
*/
|
||||||
|
public function get($name) {
|
||||||
|
return $this->kirby->component($name);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
78
kirby/kirby/registry/controller.php
Normal file
78
kirby/kirby/registry/controller.php
Normal file
|
@ -0,0 +1,78 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Kirby\Registry;
|
||||||
|
|
||||||
|
use A;
|
||||||
|
use Exception;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Controller Registy Entry
|
||||||
|
*
|
||||||
|
* @package Kirby CMS
|
||||||
|
* @author Bastian Allgeier <bastian@getkirby.com>
|
||||||
|
* @link http://getkirby.com
|
||||||
|
* @copyright Bastian Allgeier
|
||||||
|
* @license http://getkirby.com/license
|
||||||
|
*/
|
||||||
|
class Controller extends Entry {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Store of registered controllers
|
||||||
|
*
|
||||||
|
* @var array $controllers
|
||||||
|
*/
|
||||||
|
protected static $controllers = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a new controller to the registry
|
||||||
|
*
|
||||||
|
* @param string $name
|
||||||
|
* @param Closure $callback Must be a valid controller callback
|
||||||
|
* @return Closure
|
||||||
|
*/
|
||||||
|
public function set($name, $callback) {
|
||||||
|
|
||||||
|
$name = strtolower($name);
|
||||||
|
|
||||||
|
if($name === 'site') {
|
||||||
|
throw new Exception('You are not allowed to set the site controller');
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!$this->kirby->option('debug') || is_a($callback, 'Closure') || file_exists($callback)) {
|
||||||
|
return static::$controllers[$name] = $callback;
|
||||||
|
} else {
|
||||||
|
throw new Exception('Invalid controller. You must pass a closure or an existing file');
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retreives a controller from the registry
|
||||||
|
*
|
||||||
|
* @param string $name
|
||||||
|
* @return Closure
|
||||||
|
*/
|
||||||
|
public function get($name) {
|
||||||
|
|
||||||
|
$name = strtolower($name);
|
||||||
|
$file = $this->kirby->roots()->controllers() . DS . $name . '.php';
|
||||||
|
|
||||||
|
if(file_exists($file)) {
|
||||||
|
return include_once $file;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(isset(static::$controllers[$name])) {
|
||||||
|
if(is_a(static::$controllers[$name], 'Closure')) {
|
||||||
|
return static::$controllers[$name];
|
||||||
|
} else if(file_exists(static::$controllers[$name])) {
|
||||||
|
return include_once static::$controllers[$name];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(file_exists($this->kirby->roots()->controllers() . DS . 'site.php')) {
|
||||||
|
return include_once $this->kirby->roots()->controllers() . DS . 'site.php';
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
98
kirby/kirby/registry/entry.php
Normal file
98
kirby/kirby/registry/entry.php
Normal file
|
@ -0,0 +1,98 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Kirby\Registry;
|
||||||
|
|
||||||
|
use Kirby;
|
||||||
|
use Kirby\Registry;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registy Entry
|
||||||
|
*
|
||||||
|
* Base Entry Class. All other registry entries
|
||||||
|
* must extend this class to inherit basic
|
||||||
|
* functionalities of registry entries and to
|
||||||
|
* be accepted by the registry
|
||||||
|
*
|
||||||
|
* @package Kirby CMS
|
||||||
|
* @author Bastian Allgeier <bastian@getkirby.com>
|
||||||
|
* @link http://getkirby.com
|
||||||
|
* @copyright Bastian Allgeier
|
||||||
|
* @license http://getkirby.com/license
|
||||||
|
*/
|
||||||
|
class Entry {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Kirby instance
|
||||||
|
*
|
||||||
|
* @var Kirby
|
||||||
|
*/
|
||||||
|
protected $kirby;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Kirby Registry instance
|
||||||
|
*
|
||||||
|
* @var Kirby\Registry
|
||||||
|
*/
|
||||||
|
protected $registry;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Optional subtype for something
|
||||||
|
* like $kirby->set('field::method', '…')
|
||||||
|
* where `field` is the subtype of type `method`.
|
||||||
|
*
|
||||||
|
* @param string $subtype
|
||||||
|
*/
|
||||||
|
protected $subtype;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Kirby $kirby
|
||||||
|
* @param Kirby\Registry $registry
|
||||||
|
* @param string $subtype
|
||||||
|
*/
|
||||||
|
public function __construct(Registry $registry, $subtype = null) {
|
||||||
|
$this->registry = $registry;
|
||||||
|
$this->kirby = $registry->kirby();
|
||||||
|
$this->subtype = $subtype;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface to call any registry entry method
|
||||||
|
*
|
||||||
|
* Mostly used for set() and get()
|
||||||
|
*
|
||||||
|
* @param string $method
|
||||||
|
* @param array $args
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function call($method, $args) {
|
||||||
|
return call([$this, $method], $args);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the Kirby instance
|
||||||
|
*
|
||||||
|
* @return Kirby
|
||||||
|
*/
|
||||||
|
public function kirby() {
|
||||||
|
return $this->kirby;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the Registry instance
|
||||||
|
*
|
||||||
|
* @return Kirby\Registry
|
||||||
|
*/
|
||||||
|
public function registry() {
|
||||||
|
return $this->registry;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the optional subtype
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function subtype() {
|
||||||
|
return $this->subtype;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
68
kirby/kirby/registry/field.php
Normal file
68
kirby/kirby/registry/field.php
Normal file
|
@ -0,0 +1,68 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Kirby\Registry;
|
||||||
|
|
||||||
|
use A;
|
||||||
|
use Exception;
|
||||||
|
use Obj;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Field Registy Entry
|
||||||
|
*
|
||||||
|
* @package Kirby CMS
|
||||||
|
* @author Bastian Allgeier <bastian@getkirby.com>
|
||||||
|
* @link http://getkirby.com
|
||||||
|
* @copyright Bastian Allgeier
|
||||||
|
* @license http://getkirby.com/license
|
||||||
|
*/
|
||||||
|
class Field extends Entry {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Store for registered fields
|
||||||
|
*
|
||||||
|
* @var array $fields
|
||||||
|
*/
|
||||||
|
protected static $fields = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a new field to the registry
|
||||||
|
*
|
||||||
|
* @param string $name
|
||||||
|
* @param string $root valid field directory path
|
||||||
|
* @return Obj generic Kirby object with info about the field
|
||||||
|
*/
|
||||||
|
public function set($name, $root) {
|
||||||
|
|
||||||
|
$name = strtolower($name);
|
||||||
|
$file = $root . DS . $name . '.php';
|
||||||
|
|
||||||
|
if(!$this->kirby->option('debug') || (is_dir($root) && is_file($file))) {
|
||||||
|
return static::$fields[$name] = new Obj([
|
||||||
|
'root' => $root,
|
||||||
|
'file' => $file,
|
||||||
|
'name' => $name,
|
||||||
|
'class' => $name . 'field',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Exception('The field does not exist at the specified path: ' . $root);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retreives a field info object from the registry
|
||||||
|
*
|
||||||
|
* @param string|null $name If null, all registered fields will be returned as array
|
||||||
|
* @param Obj|null|array
|
||||||
|
*/
|
||||||
|
public function get($name = null) {
|
||||||
|
|
||||||
|
if(is_null($name)) {
|
||||||
|
return static::$fields;
|
||||||
|
}
|
||||||
|
|
||||||
|
return a::get(static::$fields, $name);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
30
kirby/kirby/registry/hook.php
Normal file
30
kirby/kirby/registry/hook.php
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Kirby\Registry;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hook Registy Entry
|
||||||
|
*
|
||||||
|
* @package Kirby CMS
|
||||||
|
* @author Bastian Allgeier <bastian@getkirby.com>
|
||||||
|
* @link http://getkirby.com
|
||||||
|
* @copyright Bastian Allgeier
|
||||||
|
* @license http://getkirby.com/license
|
||||||
|
*/
|
||||||
|
class Hook extends Entry {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers a new hook
|
||||||
|
*
|
||||||
|
* This will directly call the $kirby->hook() method
|
||||||
|
* A hook has to be a valid closure
|
||||||
|
*
|
||||||
|
* @param string $name
|
||||||
|
* @param Closure $callback
|
||||||
|
* @return Closure
|
||||||
|
*/
|
||||||
|
public function set($name, $callback) {
|
||||||
|
return $this->kirby->hook($name, $callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
71
kirby/kirby/registry/method.php
Normal file
71
kirby/kirby/registry/method.php
Normal file
|
@ -0,0 +1,71 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Kirby\Registry;
|
||||||
|
|
||||||
|
use A;
|
||||||
|
use Exception;
|
||||||
|
use Kirby;
|
||||||
|
use Kirby\Registry;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Method Registy Entry
|
||||||
|
*
|
||||||
|
* @package Kirby CMS
|
||||||
|
* @author Bastian Allgeier <bastian@getkirby.com>
|
||||||
|
* @link http://getkirby.com
|
||||||
|
* @copyright Bastian Allgeier
|
||||||
|
* @license http://getkirby.com/license
|
||||||
|
*/
|
||||||
|
class Method extends Entry {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* List of allowed subtypes
|
||||||
|
*
|
||||||
|
* @var array $subtypes
|
||||||
|
*/
|
||||||
|
protected $subtypes = ['site', 'page', 'pages', 'file', 'files', 'field'];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Kirby\Registry $registry
|
||||||
|
* @param string $subtype
|
||||||
|
*/
|
||||||
|
public function __construct(Registry $registry, $subtype) {
|
||||||
|
parent::__construct($registry, $subtype);
|
||||||
|
if(!in_array($this->subtype, $this->subtypes)) {
|
||||||
|
throw new Exception('Invalid method type: ' . $this->subtype . '::method');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a new method to the registry
|
||||||
|
*
|
||||||
|
* A method can be registered for any of the allowed
|
||||||
|
* subtypes, by using the static method syntax:
|
||||||
|
* $kirby->set('page::method')
|
||||||
|
* $kirby->set('field::method')
|
||||||
|
* etc.
|
||||||
|
*
|
||||||
|
* The first part of the name is the subtype.
|
||||||
|
* The second part of the name is the main type (`method` in this case)
|
||||||
|
*
|
||||||
|
* @param string $name
|
||||||
|
* @param Closure $callback
|
||||||
|
* @return Closure
|
||||||
|
*/
|
||||||
|
public function set($name, $callback) {
|
||||||
|
$class = $this->subtype;
|
||||||
|
return $class::$methods[$name] = $callback;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves a registered method
|
||||||
|
*
|
||||||
|
* @param string $name
|
||||||
|
* @return Closure
|
||||||
|
*/
|
||||||
|
public function get($name) {
|
||||||
|
$class = $this->subtype;
|
||||||
|
return a::get($class::$methods, $name);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
77
kirby/kirby/registry/model.php
Normal file
77
kirby/kirby/registry/model.php
Normal file
|
@ -0,0 +1,77 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Kirby\Registry;
|
||||||
|
|
||||||
|
use A;
|
||||||
|
use Exception;
|
||||||
|
use Kirby;
|
||||||
|
use Kirby\Registry;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Model Registy Entry
|
||||||
|
*
|
||||||
|
* @package Kirby CMS
|
||||||
|
* @author Bastian Allgeier <bastian@getkirby.com>
|
||||||
|
* @link http://getkirby.com
|
||||||
|
* @copyright Bastian Allgeier
|
||||||
|
* @license http://getkirby.com/license
|
||||||
|
*/
|
||||||
|
class Model extends Entry {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* List of allowed subtypes
|
||||||
|
*
|
||||||
|
* @var array $subtypes
|
||||||
|
*/
|
||||||
|
protected $subtypes = ['page'];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Kirby\Registry $registry
|
||||||
|
* @param string $subtype
|
||||||
|
*/
|
||||||
|
public function __construct(Registry $registry, $subtype) {
|
||||||
|
parent::__construct($registry, $subtype);
|
||||||
|
if(!in_array($this->subtype, $this->subtypes)) {
|
||||||
|
throw new Exception('Invalid model type: ' . $this->subtype . '::model');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a new model to the registry
|
||||||
|
*
|
||||||
|
* A model can be registered for any of the allowed
|
||||||
|
* subtypes, by using the static method syntax:
|
||||||
|
*
|
||||||
|
* $kirby->set('page::model')
|
||||||
|
*
|
||||||
|
* The first part of the name is the subtype.
|
||||||
|
* The second part of the name is the main type (`model` in this case)
|
||||||
|
*
|
||||||
|
* @param string $name
|
||||||
|
* @param string $classname Must be a valid classname of a loaded/auto-loaded class
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function set($name, $classname) {
|
||||||
|
|
||||||
|
$class = $this->subtype;
|
||||||
|
|
||||||
|
if(!class_exists($classname)) {
|
||||||
|
throw new Exception('The model class does not exist: ' . $classname);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $class::$models[$name] = $classname;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves a registered model
|
||||||
|
*
|
||||||
|
* @param string $name
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function get($name) {
|
||||||
|
$class = $this->subtype;
|
||||||
|
return a::get($class::$models, $name);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
42
kirby/kirby/registry/option.php
Normal file
42
kirby/kirby/registry/option.php
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Kirby\Registry;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Option Registy Entry
|
||||||
|
*
|
||||||
|
* @package Kirby CMS
|
||||||
|
* @author Bastian Allgeier <bastian@getkirby.com>
|
||||||
|
* @link http://getkirby.com
|
||||||
|
* @copyright Bastian Allgeier
|
||||||
|
* @license http://getkirby.com/license
|
||||||
|
*/
|
||||||
|
class Option extends Entry {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets a Kirby option
|
||||||
|
*
|
||||||
|
* This directly adds passed options to the
|
||||||
|
* $kirby->options array and is just a convenient
|
||||||
|
* way to do this through the registry
|
||||||
|
*
|
||||||
|
* @param string $key
|
||||||
|
* @param mixed $value
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function set($key, $value) {
|
||||||
|
return $this->kirby->options[$key] = $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retreives an option from the $kirby->$options array
|
||||||
|
*
|
||||||
|
* @param string $key
|
||||||
|
* @param mixed $default
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function get($key, $default = null) {
|
||||||
|
return $this->kirby->option($key, $default);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
28
kirby/kirby/registry/route.php
Normal file
28
kirby/kirby/registry/route.php
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Kirby\Registry;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Route Registy Entry
|
||||||
|
*
|
||||||
|
* @package Kirby CMS
|
||||||
|
* @author Bastian Allgeier <bastian@getkirby.com>
|
||||||
|
* @link http://getkirby.com
|
||||||
|
* @copyright Bastian Allgeier
|
||||||
|
* @license http://getkirby.com/license
|
||||||
|
*/
|
||||||
|
class Route extends Entry {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers a new route
|
||||||
|
*
|
||||||
|
* This will directly add a route to
|
||||||
|
* Kirby's route system, by calling $kirby->routes()
|
||||||
|
*
|
||||||
|
* @param string $attr
|
||||||
|
*/
|
||||||
|
public function set($attr) {
|
||||||
|
$this->kirby->routes([$attr]);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
64
kirby/kirby/registry/snippet.php
Normal file
64
kirby/kirby/registry/snippet.php
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Kirby\Registry;
|
||||||
|
|
||||||
|
use A;
|
||||||
|
use Exception;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Snippet Registy Entry
|
||||||
|
*
|
||||||
|
* @package Kirby CMS
|
||||||
|
* @author Bastian Allgeier <bastian@getkirby.com>
|
||||||
|
* @link http://getkirby.com
|
||||||
|
* @copyright Bastian Allgeier
|
||||||
|
* @license http://getkirby.com/license
|
||||||
|
*/
|
||||||
|
class Snippet extends Entry {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* List of registered snippet files
|
||||||
|
*
|
||||||
|
* @var array $snippets
|
||||||
|
*/
|
||||||
|
protected static $snippets = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers a new snippet file
|
||||||
|
*
|
||||||
|
* You must pass an existing file in order
|
||||||
|
* to register it as a valid snippet
|
||||||
|
*
|
||||||
|
* @param string $name The name of the snippet. Can contain slashes (i.e. form/field)
|
||||||
|
* @param string $path
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function set($name, $path) {
|
||||||
|
|
||||||
|
if(!$this->kirby->option('debug') || file_exists($path)) {
|
||||||
|
return static::$snippets[$name] = $path;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Exception('The snippet does not exist at the specified path: ' . $path);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the file path for a registered snippet
|
||||||
|
*
|
||||||
|
* @param string $name
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function get($name) {
|
||||||
|
|
||||||
|
$file = $this->kirby->component('snippet')->file($name);
|
||||||
|
|
||||||
|
if(file_exists($file)) {
|
||||||
|
return $file;
|
||||||
|
} else {
|
||||||
|
return a::get(static::$snippets, $name);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
42
kirby/kirby/registry/tag.php
Normal file
42
kirby/kirby/registry/tag.php
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Kirby\Registry;
|
||||||
|
|
||||||
|
use A;
|
||||||
|
use Kirbytext;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tag Registy Entry
|
||||||
|
*
|
||||||
|
* @package Kirby CMS
|
||||||
|
* @author Bastian Allgeier <bastian@getkirby.com>
|
||||||
|
* @link http://getkirby.com
|
||||||
|
* @copyright Bastian Allgeier
|
||||||
|
* @license http://getkirby.com/license
|
||||||
|
*/
|
||||||
|
class Tag extends Entry {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers a new kirby tag array
|
||||||
|
*
|
||||||
|
* This will directly add the tag to the
|
||||||
|
* kirbytext::$tags array.
|
||||||
|
*
|
||||||
|
* @param string $name
|
||||||
|
* @param array $tag
|
||||||
|
*/
|
||||||
|
public function set($name, $tag) {
|
||||||
|
kirbytext::$tags[$name] = $tag;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retreives a registered kirby tag
|
||||||
|
*
|
||||||
|
* @param string $name
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function get($name) {
|
||||||
|
return a::get(kirbytext::$tags, $name);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
62
kirby/kirby/registry/template.php
Normal file
62
kirby/kirby/registry/template.php
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Kirby\Registry;
|
||||||
|
|
||||||
|
use A;
|
||||||
|
use Exception;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Template Registy Entry
|
||||||
|
*
|
||||||
|
* @package Kirby CMS
|
||||||
|
* @author Bastian Allgeier <bastian@getkirby.com>
|
||||||
|
* @link http://getkirby.com
|
||||||
|
* @copyright Bastian Allgeier
|
||||||
|
* @license http://getkirby.com/license
|
||||||
|
*/
|
||||||
|
class Template extends Entry {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* List of registered template files
|
||||||
|
*
|
||||||
|
* @var array $templates
|
||||||
|
*/
|
||||||
|
protected static $templates = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers a new template file
|
||||||
|
*
|
||||||
|
* Must be an existing file
|
||||||
|
*
|
||||||
|
* @param string $name
|
||||||
|
* @param string $path
|
||||||
|
*/
|
||||||
|
public function set($name, $path) {
|
||||||
|
|
||||||
|
if(!$this->kirby->option('debug') || file_exists($path)) {
|
||||||
|
return static::$templates[$name] = $path;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Exception('The template does not exist at the specified path: ' . $path);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves a registered template file
|
||||||
|
*
|
||||||
|
* @param string $name
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function get($name) {
|
||||||
|
|
||||||
|
$file = $this->kirby->component('template')->file($name);
|
||||||
|
|
||||||
|
if(file_exists($file)) {
|
||||||
|
return $file;
|
||||||
|
} else {
|
||||||
|
return a::get(static::$templates, $name);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
67
kirby/kirby/registry/widget.php
Normal file
67
kirby/kirby/registry/widget.php
Normal file
|
@ -0,0 +1,67 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Kirby\Registry;
|
||||||
|
|
||||||
|
use A;
|
||||||
|
use Exception;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Widget Registy Entry
|
||||||
|
*
|
||||||
|
* @package Kirby CMS
|
||||||
|
* @author Bastian Allgeier <bastian@getkirby.com>
|
||||||
|
* @link http://getkirby.com
|
||||||
|
* @copyright Bastian Allgeier
|
||||||
|
* @license http://getkirby.com/license
|
||||||
|
*/
|
||||||
|
class Widget extends Entry {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* List of registered widget directories
|
||||||
|
*
|
||||||
|
* @var array $widgets
|
||||||
|
*/
|
||||||
|
protected static $widgets = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers a new widget
|
||||||
|
*
|
||||||
|
* You must pass an existing widget directory
|
||||||
|
*
|
||||||
|
* @param string $name
|
||||||
|
* @param string $path
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function set($name, $path) {
|
||||||
|
|
||||||
|
if(!$this->kirby->option('debug') || is_dir($path)) {
|
||||||
|
return static::$widgets[$name] = $path;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Exception('The widget does not exist at the specified path: ' . $path);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retreives a registered widget directory
|
||||||
|
*
|
||||||
|
* @param string|null $name If null, all registered widgets will be returned as array
|
||||||
|
* @return string|array
|
||||||
|
*/
|
||||||
|
public function get($name = null) {
|
||||||
|
|
||||||
|
if(is_null($name)) {
|
||||||
|
return static::$widgets;
|
||||||
|
}
|
||||||
|
|
||||||
|
$file = $this->kirby->roots()->widgets() . DS . str_replace('/', DS, $name) . '.php';
|
||||||
|
|
||||||
|
if(file_exists($file)) {
|
||||||
|
return $file;
|
||||||
|
} else {
|
||||||
|
return a::get(static::$widgets, $name);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
41
kirby/kirby/request.php
Normal file
41
kirby/kirby/request.php
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Kirby;
|
||||||
|
|
||||||
|
use Exception;
|
||||||
|
use R;
|
||||||
|
use URL;
|
||||||
|
|
||||||
|
class Request {
|
||||||
|
|
||||||
|
protected $kirby;
|
||||||
|
|
||||||
|
public function __construct($kirby) {
|
||||||
|
$this->kirby = $kirby;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function url() {
|
||||||
|
return url::current();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function params() {
|
||||||
|
return new Request\Params(url::params());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function query() {
|
||||||
|
return new Request\Query(url::query());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function path() {
|
||||||
|
return new Request\Path($this->kirby->path());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __call($method, $arguments) {
|
||||||
|
if(method_exists('r', $method)) {
|
||||||
|
return call('r::' . $method, $arguments);
|
||||||
|
} else {
|
||||||
|
throw new Exception('Invalid method: ' . $method);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
22
kirby/kirby/request/params.php
Normal file
22
kirby/kirby/request/params.php
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Kirby\Request;
|
||||||
|
|
||||||
|
use Obj;
|
||||||
|
use Url;
|
||||||
|
|
||||||
|
class Params extends Obj {
|
||||||
|
|
||||||
|
public function __toString() {
|
||||||
|
|
||||||
|
$params = array();
|
||||||
|
|
||||||
|
foreach((array)$this as $key => $value) {
|
||||||
|
$params[] = $key . url::paramSeparator() . $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
return implode('/', $params);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
18
kirby/kirby/request/path.php
Normal file
18
kirby/kirby/request/path.php
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Kirby\Request;
|
||||||
|
|
||||||
|
use Collection;
|
||||||
|
use Str;
|
||||||
|
|
||||||
|
class Path extends Collection {
|
||||||
|
|
||||||
|
public function __construct($path) {
|
||||||
|
parent::__construct(str::split($path, '/'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __toString() {
|
||||||
|
return implode('/', $this->data);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
13
kirby/kirby/request/query.php
Normal file
13
kirby/kirby/request/query.php
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Kirby\Request;
|
||||||
|
|
||||||
|
use Obj;
|
||||||
|
|
||||||
|
class Query extends Obj {
|
||||||
|
|
||||||
|
public function __toString() {
|
||||||
|
return http_build_query((array)$this);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
99
kirby/kirby/roots.php
Normal file
99
kirby/kirby/roots.php
Normal file
|
@ -0,0 +1,99 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Kirby;
|
||||||
|
|
||||||
|
use Obj;
|
||||||
|
|
||||||
|
class Roots extends Obj {
|
||||||
|
|
||||||
|
public $index;
|
||||||
|
|
||||||
|
public function __construct($index) {
|
||||||
|
$this->index = $index;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function content() {
|
||||||
|
return isset($this->content) ? $this->content : $this->index . DS . 'content';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function site() {
|
||||||
|
return isset($this->site) ? $this->site : $this->index . DS . 'site';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function kirby() {
|
||||||
|
return isset($this->kirby) ? $this->kirby : $this->index . DS . 'kirby';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function thumbs() {
|
||||||
|
return isset($this->thumbs) ? $this->thumbs : $this->index . DS . 'thumbs';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function assets() {
|
||||||
|
return isset($this->assets) ? $this->assets : $this->index . DS . 'assets';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function autocss() {
|
||||||
|
return isset($this->autocss) ? $this->autocss : $this->assets() . DS . 'css' . DS . 'templates';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function autojs() {
|
||||||
|
return isset($this->autojs) ? $this->autojs : $this->assets() . DS . 'js' . DS . 'templates';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function avatars() {
|
||||||
|
return isset($this->avatars) ? $this->avatars : $this->assets() . DS . 'avatars';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function config() {
|
||||||
|
return $this->site() . DS . 'config';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function accounts() {
|
||||||
|
return isset($this->accounts) ? $this->accounts : $this->site() . DS . 'accounts';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function blueprints() {
|
||||||
|
return $this->site() . DS . 'blueprints';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function plugins() {
|
||||||
|
return $this->site() . DS . 'plugins';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function cache() {
|
||||||
|
return isset($this->cache) ? $this->cache : $this->site() . DS . 'cache';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function tags() {
|
||||||
|
return $this->site() . DS . 'tags';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function fields() {
|
||||||
|
return $this->site() . DS . 'fields';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function widgets() {
|
||||||
|
return $this->site() . DS . 'widgets';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function controllers() {
|
||||||
|
return $this->site() . DS . 'controllers';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function models() {
|
||||||
|
return $this->site() . DS . 'models';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function templates() {
|
||||||
|
return $this->site() . DS . 'templates';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function snippets() {
|
||||||
|
return $this->site() . DS . 'snippets';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function languages() {
|
||||||
|
return $this->site() . DS . 'languages';
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
207
kirby/kirby/traits/image.php
Normal file
207
kirby/kirby/traits/image.php
Normal file
|
@ -0,0 +1,207 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Kirby\Traits;
|
||||||
|
|
||||||
|
use A;
|
||||||
|
use Exception;
|
||||||
|
use Media;
|
||||||
|
use Str;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
trait Image {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* store for the original image file
|
||||||
|
*
|
||||||
|
* @var Media|Asset|File
|
||||||
|
*/
|
||||||
|
protected $original;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Media $original
|
||||||
|
* @return Media|this
|
||||||
|
*/
|
||||||
|
public function original(Media $original = null) {
|
||||||
|
if($original === null) {
|
||||||
|
return $this->original;
|
||||||
|
} else {
|
||||||
|
$this->original = $original;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a thumbnail for the image
|
||||||
|
*
|
||||||
|
* @param array $params
|
||||||
|
* @return Asset
|
||||||
|
*/
|
||||||
|
public function thumb($params = []) {
|
||||||
|
// don't scale thumbs further down
|
||||||
|
if($this->original()) {
|
||||||
|
throw new Exception('Thumbnails cannot be modified further');
|
||||||
|
} else {
|
||||||
|
return $this->kirby->component('thumb')->create($this, $params);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Scales the image if possible
|
||||||
|
*
|
||||||
|
* @param int $width
|
||||||
|
* @param mixed $height
|
||||||
|
* @param mixed $quality
|
||||||
|
* @return Asset
|
||||||
|
*/
|
||||||
|
public function resize($width, $height = null, $quality = null) {
|
||||||
|
|
||||||
|
$params = ['width' => $width];
|
||||||
|
|
||||||
|
if($height) $params['height'] = $height;
|
||||||
|
if($quality) $params['quality'] = $quality;
|
||||||
|
|
||||||
|
return $this->thumb($params);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Scales and crops the image if possible
|
||||||
|
*
|
||||||
|
* @param int $width
|
||||||
|
* @param mixed $height
|
||||||
|
* @param mixed $quality
|
||||||
|
* @return Asset
|
||||||
|
*/
|
||||||
|
public function crop($width, $height = null, $quality = null) {
|
||||||
|
|
||||||
|
$params = ['width' => $width, 'crop' => true];
|
||||||
|
|
||||||
|
if($height) $params['height'] = $height;
|
||||||
|
if($quality) $params['quality'] = $quality;
|
||||||
|
|
||||||
|
return $this->thumb($params);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Scales the width of the image
|
||||||
|
*
|
||||||
|
* @param int $width
|
||||||
|
* @param mixed $quality
|
||||||
|
* @return Asset
|
||||||
|
*/
|
||||||
|
public function width($width = null, $quality = null) {
|
||||||
|
|
||||||
|
if($width === null) {
|
||||||
|
return parent::width();
|
||||||
|
}
|
||||||
|
|
||||||
|
$params = ['width' => $width];
|
||||||
|
|
||||||
|
if($quality) $params['quality'] = $quality;
|
||||||
|
|
||||||
|
return $this->thumb($params);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Scales the height of the image
|
||||||
|
*
|
||||||
|
* @param int $height
|
||||||
|
* @param mixed $quality
|
||||||
|
* @return Asset
|
||||||
|
*/
|
||||||
|
public function height($height = null, $quality = null) {
|
||||||
|
|
||||||
|
if($height === null) {
|
||||||
|
return parent::height();
|
||||||
|
}
|
||||||
|
|
||||||
|
$params = ['height' => $height];
|
||||||
|
|
||||||
|
if($quality) $params['quality'] = $quality;
|
||||||
|
|
||||||
|
return $this->thumb($params);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public function ratio($ratio = null) {
|
||||||
|
|
||||||
|
if($ratio === null) {
|
||||||
|
return parent::ratio();
|
||||||
|
}
|
||||||
|
|
||||||
|
if($this->isLandscape() || $this->isSquare()) {
|
||||||
|
$width = $this->width();
|
||||||
|
$height = round($width / $ratio);
|
||||||
|
} else {
|
||||||
|
$height = $this->height();
|
||||||
|
$width = round($height * $ratio);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->crop($width, $height);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public function scale($value) {
|
||||||
|
return $this->thumb(['width' => $this->width() * $value, 'upscale' => true]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts the image to grayscale
|
||||||
|
*
|
||||||
|
* @return Asset
|
||||||
|
*/
|
||||||
|
public function bw() {
|
||||||
|
return $this->thumb(['grayscale' => true]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Blurs the image
|
||||||
|
*
|
||||||
|
* @return Asset
|
||||||
|
*/
|
||||||
|
public function blur() {
|
||||||
|
return $this->thumb(['blur' => true]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the asset is a thumbnail
|
||||||
|
*
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
public function isThumb() {
|
||||||
|
return str::startsWith($this->url(), $this->kirby->urls()->thumbs());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the file/image has a websafe format
|
||||||
|
*
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
public function isWebsafe() {
|
||||||
|
return in_array(strtolower($this->extension()), ['jpg', 'jpeg', 'gif', 'png']);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Makes it possible to echo the entire object
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function __toString() {
|
||||||
|
if($this->isWebsafe()) {
|
||||||
|
return (string)$this->html();
|
||||||
|
} else {
|
||||||
|
return (string)$this->root;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
47
kirby/kirby/urls.php
Normal file
47
kirby/kirby/urls.php
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Kirby;
|
||||||
|
|
||||||
|
use R;
|
||||||
|
use Server;
|
||||||
|
use URL;
|
||||||
|
|
||||||
|
class Urls {
|
||||||
|
|
||||||
|
public function index() {
|
||||||
|
|
||||||
|
if(isset($this->index)) return $this->index;
|
||||||
|
|
||||||
|
if(r::cli()) {
|
||||||
|
return $this->index = '/';
|
||||||
|
} else {
|
||||||
|
return $this->index = url::base() . preg_replace('!\/index\.php$!i', '', server::get('SCRIPT_NAME'));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public function content() {
|
||||||
|
return isset($this->content) ? $this->content : url::makeAbsolute('content', $this->index);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function thumbs() {
|
||||||
|
return isset($this->thumbs) ? $this->thumbs : url::makeAbsolute('thumbs', $this->index);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function assets() {
|
||||||
|
return isset($this->assets) ? $this->assets : url::makeAbsolute('assets', $this->index);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function autocss() {
|
||||||
|
return isset($this->autocss) ? $this->autocss : $this->assets() . '/css/templates';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function autojs() {
|
||||||
|
return isset($this->autojs) ? $this->autojs : $this->assets() . '/js/templates';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function avatars() {
|
||||||
|
return isset($this->avatars) ? $this->avatars : $this->assets() . '/avatars';
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
17
kirby/lib/pageextension.php
Normal file
17
kirby/lib/pageextension.php
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper to create extended page objects
|
||||||
|
*
|
||||||
|
* @param mixed $page
|
||||||
|
*/
|
||||||
|
class PageExtension extends Page {
|
||||||
|
public function __construct($page) {
|
||||||
|
$page = is_string($page) ? page($page) : $page;
|
||||||
|
if($page) {
|
||||||
|
parent::__construct($page->parent(), $page->dirname());
|
||||||
|
} else {
|
||||||
|
throw new Exception('The page could not be found');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
42
kirby/lib/structure.php
Normal file
42
kirby/lib/structure.php
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
class Structure extends Collection {
|
||||||
|
|
||||||
|
public $page = null;
|
||||||
|
|
||||||
|
public function get($key, $default = null) {
|
||||||
|
|
||||||
|
if(isset($this->data[$key])) {
|
||||||
|
return $this->data[$key];
|
||||||
|
} else {
|
||||||
|
$lowerkeys = array_change_key_case($this->data, CASE_LOWER);
|
||||||
|
$lowerkey = strtolower($key);
|
||||||
|
if(isset($lowerkeys[$lowerkey])) {
|
||||||
|
return $lowerkeys[$lowerkey];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Field($this->page, $key, null);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get formatted date fields
|
||||||
|
*
|
||||||
|
* @param string $format
|
||||||
|
* @param string $field
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function date($format = null, $field = 'date') {
|
||||||
|
|
||||||
|
if($timestamp = strtotime($this->get($field))) {
|
||||||
|
if(is_null($format)) {
|
||||||
|
return $timestamp;
|
||||||
|
} else {
|
||||||
|
return kirby()->options['date.handler']($format, $timestamp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
18
kirby/readme.md
Normal file
18
kirby/readme.md
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
# Kirby Core
|
||||||
|
|
||||||
|
This is the Kirby Core submodule.
|
||||||
|
|
||||||
|
Please refer to the [Kirby Starterkit](http://github.com/getkirby/starterkit)
|
||||||
|
for a complete installation of Kirby
|
||||||
|
|
||||||
|
![Build Status](https://travis-ci.org/getkirby/kirby.svg?branch=master)
|
||||||
|
|
||||||
|
## Author
|
||||||
|
Bastian Allgeier
|
||||||
|
<bastian@getkirby.com>
|
||||||
|
|
||||||
|
## Website
|
||||||
|
<http://getkirby.com>
|
||||||
|
|
||||||
|
## License
|
||||||
|
<http://getkirby.com>
|
21
kirby/system.php
Normal file
21
kirby/system.php
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is a legacy file, which makes it possible
|
||||||
|
* to keep using the old v1 index.php with v2.
|
||||||
|
* It's recommended to use the new index.php though.
|
||||||
|
*/
|
||||||
|
|
||||||
|
define('DS', DIRECTORY_SEPARATOR);
|
||||||
|
|
||||||
|
// load the bootstrapper
|
||||||
|
include(__DIR__ . DS . 'bootstrap.php');
|
||||||
|
|
||||||
|
// take the old variables to setup roots
|
||||||
|
$kirby = kirby();
|
||||||
|
$kirby->roots->index = $root;
|
||||||
|
$kirby->roots->site = $rootSite;
|
||||||
|
$kirby->roots->content = $rootContent;
|
||||||
|
|
||||||
|
// render
|
||||||
|
echo $kirby->launch();
|
97
kirby/toolkit/bootstrap.php
Normal file
97
kirby/toolkit/bootstrap.php
Normal file
|
@ -0,0 +1,97 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
if(!defined('DS')) define('DS', DIRECTORY_SEPARATOR);
|
||||||
|
if(!defined('MB')) define('MB', (int)function_exists('mb_get_info'));
|
||||||
|
if(!defined('BOM')) define('BOM', "\xEF\xBB\xBF");
|
||||||
|
|
||||||
|
// polyfill for new sort flag
|
||||||
|
if(!defined('SORT_NATURAL')) define('SORT_NATURAL', 'SORT_NATURAL');
|
||||||
|
|
||||||
|
// a super simple autoloader
|
||||||
|
function load($classmap, $base = null) {
|
||||||
|
spl_autoload_register(function($class) use ($classmap, $base) {
|
||||||
|
$class = strtolower($class);
|
||||||
|
if(!isset($classmap[$class])) return false;
|
||||||
|
if($base) {
|
||||||
|
include($base . DS . $classmap[$class]);
|
||||||
|
} else {
|
||||||
|
include($classmap[$class]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// auto-load all toolkit classes
|
||||||
|
load(array(
|
||||||
|
|
||||||
|
// classes
|
||||||
|
'a' => __DIR__ . DS . 'lib' . DS . 'a.php',
|
||||||
|
'bitmask' => __DIR__ . DS . 'lib' . DS . 'bitmask.php',
|
||||||
|
'brick' => __DIR__ . DS . 'lib' . DS . 'brick.php',
|
||||||
|
'c' => __DIR__ . DS . 'lib' . DS . 'c.php',
|
||||||
|
'cookie' => __DIR__ . DS . 'lib' . DS . 'cookie.php',
|
||||||
|
'cache' => __DIR__ . DS . 'lib' . DS . 'cache.php',
|
||||||
|
'cache\\driver' => __DIR__ . DS . 'lib' . DS . 'cache' . DS . 'driver.php',
|
||||||
|
'cache\\driver\\apc' => __DIR__ . DS . 'lib' . DS . 'cache' . DS . 'driver' . DS . 'apc.php',
|
||||||
|
'cache\\driver\\file' => __DIR__ . DS . 'lib' . DS . 'cache' . DS . 'driver' . DS . 'file.php',
|
||||||
|
'cache\\driver\\memcached' => __DIR__ . DS . 'lib' . DS . 'cache' . DS . 'driver' . DS . 'memcached.php',
|
||||||
|
'cache\\driver\\mock' => __DIR__ . DS . 'lib' . DS . 'cache' . DS . 'driver' . DS . 'mock.php',
|
||||||
|
'cache\\driver\\session' => __DIR__ . DS . 'lib' . DS . 'cache' . DS . 'driver' . DS . 'session.php',
|
||||||
|
'cache\\value' => __DIR__ . DS . 'lib' . DS . 'cache' . DS . 'value.php',
|
||||||
|
'collection' => __DIR__ . DS . 'lib' . DS . 'collection.php',
|
||||||
|
'crypt' => __DIR__ . DS . 'lib' . DS . 'crypt.php',
|
||||||
|
'data' => __DIR__ . DS . 'lib' . DS . 'data.php',
|
||||||
|
'database' => __DIR__ . DS . 'lib' . DS . 'database.php',
|
||||||
|
'database\\query' => __DIR__ . DS . 'lib' . DS . 'database' . DS . 'query.php',
|
||||||
|
'db' => __DIR__ . DS . 'lib' . DS . 'db.php',
|
||||||
|
'detect' => __DIR__ . DS . 'lib' . DS . 'detect.php',
|
||||||
|
'dimensions' => __DIR__ . DS . 'lib' . DS . 'dimensions.php',
|
||||||
|
'dir' => __DIR__ . DS . 'lib' . DS . 'dir.php',
|
||||||
|
'email' => __DIR__ . DS . 'lib' . DS . 'email.php',
|
||||||
|
'embed' => __DIR__ . DS . 'lib' . DS . 'embed.php',
|
||||||
|
'error' => __DIR__ . DS . 'lib' . DS . 'error.php',
|
||||||
|
'errorreporting' => __DIR__ . DS . 'lib' . DS . 'errorreporting.php',
|
||||||
|
'escape' => __DIR__ . DS . 'lib' . DS . 'escape.php',
|
||||||
|
'exif' => __DIR__ . DS . 'lib' . DS . 'exif.php',
|
||||||
|
'exif\\camera' => __DIR__ . DS . 'lib' . DS . 'exif' . DS . 'camera.php',
|
||||||
|
'exif\\location' => __DIR__ . DS . 'lib' . DS . 'exif' . DS . 'location.php',
|
||||||
|
'f' => __DIR__ . DS . 'lib' . DS . 'f.php',
|
||||||
|
'folder' => __DIR__ . DS . 'lib' . DS . 'folder.php',
|
||||||
|
'header' => __DIR__ . DS . 'lib' . DS . 'header.php',
|
||||||
|
'html' => __DIR__ . DS . 'lib' . DS . 'html.php',
|
||||||
|
'i' => __DIR__ . DS . 'lib' . DS . 'i.php',
|
||||||
|
'l' => __DIR__ . DS . 'lib' . DS . 'l.php',
|
||||||
|
'media' => __DIR__ . DS . 'lib' . DS . 'media.php',
|
||||||
|
'obj' => __DIR__ . DS . 'lib' . DS . 'obj.php',
|
||||||
|
'pagination' => __DIR__ . DS . 'lib' . DS . 'pagination.php',
|
||||||
|
'password' => __DIR__ . DS . 'lib' . DS . 'password.php',
|
||||||
|
'r' => __DIR__ . DS . 'lib' . DS . 'r.php',
|
||||||
|
'redirect' => __DIR__ . DS . 'lib' . DS . 'redirect.php',
|
||||||
|
'remote' => __DIR__ . DS . 'lib' . DS . 'remote.php',
|
||||||
|
'response' => __DIR__ . DS . 'lib' . DS . 'response.php',
|
||||||
|
'router' => __DIR__ . DS . 'lib' . DS . 'router.php',
|
||||||
|
's' => __DIR__ . DS . 'lib' . DS . 's.php',
|
||||||
|
'server' => __DIR__ . DS . 'lib' . DS . 'server.php',
|
||||||
|
'silo' => __DIR__ . DS . 'lib' . DS . 'silo.php',
|
||||||
|
'sql' => __DIR__ . DS . 'lib' . DS . 'sql.php',
|
||||||
|
'str' => __DIR__ . DS . 'lib' . DS . 'str.php',
|
||||||
|
'system' => __DIR__ . DS . 'lib' . DS . 'system.php',
|
||||||
|
'thumb' => __DIR__ . DS . 'lib' . DS . 'thumb.php',
|
||||||
|
'timer' => __DIR__ . DS . 'lib' . DS . 'timer.php',
|
||||||
|
'toolkit' => __DIR__ . DS . 'lib' . DS . 'toolkit.php',
|
||||||
|
'tpl' => __DIR__ . DS . 'lib' . DS . 'tpl.php',
|
||||||
|
'upload' => __DIR__ . DS . 'lib' . DS . 'upload.php',
|
||||||
|
'url' => __DIR__ . DS . 'lib' . DS . 'url.php',
|
||||||
|
'v' => __DIR__ . DS . 'lib' . DS . 'v.php',
|
||||||
|
'visitor' => __DIR__ . DS . 'lib' . DS . 'visitor.php',
|
||||||
|
'xml' => __DIR__ . DS . 'lib' . DS . 'xml.php',
|
||||||
|
'yaml' => __DIR__ . DS . 'lib' . DS . 'yaml.php',
|
||||||
|
|
||||||
|
// vendors
|
||||||
|
'spyc' => __DIR__ . DS . 'vendors' . DS . 'yaml' . DS . 'yaml.php',
|
||||||
|
'abeautifulsite\\simpleimage' => __DIR__ . DS . 'vendors' . DS . 'abeautifulsite' . DS . 'SimpleImage.php',
|
||||||
|
'mimereader' => __DIR__ . DS . 'vendors' . DS . 'mimereader' . DS . 'mimereader.php',
|
||||||
|
|
||||||
|
));
|
||||||
|
|
||||||
|
// load all helpers
|
||||||
|
include(__DIR__ . DS . 'helpers.php');
|
353
kirby/toolkit/helpers.php
Normal file
353
kirby/toolkit/helpers.php
Normal file
|
@ -0,0 +1,353 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shortcut for url::to()
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
function url() {
|
||||||
|
return call_user_func_array('url::to', func_get_args());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Even shorter shortcut for url::to()
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
function u() {
|
||||||
|
return call_user_func_array('url::to', func_get_args());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Redirects the user to a new URL
|
||||||
|
* This uses the URL::to() method and can be super
|
||||||
|
* smart with the custom url::to() handler. Check out
|
||||||
|
* the URL class for more information
|
||||||
|
*/
|
||||||
|
function go() {
|
||||||
|
call_user_func_array('redirect::to', func_get_args());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shortcut for r::get()
|
||||||
|
*
|
||||||
|
* @param mixed $key The key to look for. Pass false or null to return the entire request array.
|
||||||
|
* @param mixed $default Optional default value, which should be returned if no element has been found
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
function get($key = null, $default = null) {
|
||||||
|
return r::data($key, $default);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns all params from the current url
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
function params() {
|
||||||
|
return url::params();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a parameter from the current URI object
|
||||||
|
*
|
||||||
|
* @param mixed $key The key to look for. Pass false or null to return the entire params array.
|
||||||
|
* @param mixed $default Optional default value, which should be returned if no element has been found
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
function param($key = null, $default = null) {
|
||||||
|
static $params;
|
||||||
|
if(!$params) $params = url::params();
|
||||||
|
return a::get($params, $key, $default);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Smart version of return with an if condition as first argument
|
||||||
|
*
|
||||||
|
* @param mixed $condition
|
||||||
|
* @param mixed $value The string to be returned if the condition is true
|
||||||
|
* @param mixed $alternative An alternative string which should be returned when the condition is false
|
||||||
|
* @return null
|
||||||
|
*/
|
||||||
|
function r($condition, $value, $alternative = null) {
|
||||||
|
return $condition ? $value : $alternative;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Smart version of echo with an if condition as first argument
|
||||||
|
*
|
||||||
|
* @param mixed $condition
|
||||||
|
* @param mixed $value The string to be echoed if the condition is true
|
||||||
|
* @param mixed $alternative An alternative string which should be echoed when the condition is false
|
||||||
|
*/
|
||||||
|
function e($condition, $value, $alternative = null) {
|
||||||
|
echo r($condition, $value, $alternative);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Alternative for e()
|
||||||
|
*
|
||||||
|
* @see e()
|
||||||
|
* @param $condition
|
||||||
|
* @param $value
|
||||||
|
* @param null $alternative
|
||||||
|
*/
|
||||||
|
function ecco($condition, $value, $alternative = null) {
|
||||||
|
e($condition, $value, $alternative);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dumps any array or object in a human readable way
|
||||||
|
*
|
||||||
|
* @param mixed $variable Whatever you like to inspect
|
||||||
|
* @param boolean $echo
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
function dump($variable, $echo = true) {
|
||||||
|
if(r::cli()) {
|
||||||
|
$output = print_r($variable, true) . PHP_EOL;
|
||||||
|
} else {
|
||||||
|
$output = '<pre>' . print_r($variable, true) . '</pre>';
|
||||||
|
}
|
||||||
|
if($echo === true) echo $output;
|
||||||
|
return $output;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates a single attribute or a list of attributes
|
||||||
|
*
|
||||||
|
* @see html::attr();
|
||||||
|
* @param string $name mixed string: a single attribute with that name will be generated. array: a list of attributes will be generated. Don't pass a second argument in that case.
|
||||||
|
* @param string $value if used for a single attribute, pass the content for the attribute here
|
||||||
|
* @return string the generated html
|
||||||
|
*/
|
||||||
|
function attr($name, $value = null) {
|
||||||
|
return html::attr($name, $value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates safe html by encoding special characters
|
||||||
|
*
|
||||||
|
* @param string $text unencoded text
|
||||||
|
* @param bool $keepTags
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
function html($text, $keepTags = true) {
|
||||||
|
return html::encode($text, $keepTags);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shortcut for html()
|
||||||
|
*
|
||||||
|
* @see html()
|
||||||
|
* @param $text
|
||||||
|
* @param bool $keepTags
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
function h($text, $keepTags = true) {
|
||||||
|
return html::encode($text, $keepTags);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shortcut for xml::encode()
|
||||||
|
*
|
||||||
|
* @param $text
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
function xml($text) {
|
||||||
|
return xml::encode($text);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Escape context specific output
|
||||||
|
*
|
||||||
|
* @param string $string Untrusted data
|
||||||
|
* @param string $context Location of output
|
||||||
|
* @param boolean $strict Whether to escape an extended set of characters (HTML attributes only)
|
||||||
|
* @return string Escaped data
|
||||||
|
*/
|
||||||
|
function esc($string, $context = 'html', $strict = false) {
|
||||||
|
if (method_exists('escape', $context)) {
|
||||||
|
return escape::$context($string, $strict);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The widont function makes sure that there are no
|
||||||
|
* typographical widows at the end of a paragraph –
|
||||||
|
* that's a single word in the last line
|
||||||
|
*
|
||||||
|
* @param string $string
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
function widont($string = '') {
|
||||||
|
return str::widont($string);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert a text to multiline text
|
||||||
|
*
|
||||||
|
* @param string $text
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
function multiline($text) {
|
||||||
|
return nl2br(html($text));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the memory usage in a readable format
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
function memory() {
|
||||||
|
return f::niceSize(memory_get_usage());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines the size/length of numbers, strings, arrays and files
|
||||||
|
*
|
||||||
|
* @param mixed $value
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
function size($value) {
|
||||||
|
if(is_numeric($value)) return $value;
|
||||||
|
if(is_string($value)) return str::length(trim($value));
|
||||||
|
if(is_array($value)) return count($value);
|
||||||
|
if(f::exists($value)) return f::size($value) / 1024;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates a gravatar image link
|
||||||
|
*
|
||||||
|
* @param string $email
|
||||||
|
* @param int $size
|
||||||
|
* @param string $default
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
function gravatar($email, $size = 256, $default = 'mm') {
|
||||||
|
return 'https://gravatar.com/avatar/' . md5(strtolower(trim($email))) . '?d=' . urlencode($default) . '&s=' . $size;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks / returns a csrf token
|
||||||
|
*
|
||||||
|
* @param string $check Pass a token here to compare it to the one in the session
|
||||||
|
* @return mixed Either the token or a boolean check result
|
||||||
|
*/
|
||||||
|
function csrf($check = null) {
|
||||||
|
|
||||||
|
// make sure a session is started
|
||||||
|
s::start();
|
||||||
|
|
||||||
|
if(is_null($check)) {
|
||||||
|
$token = str::random(64);
|
||||||
|
s::set('csrf', $token);
|
||||||
|
return $token;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ($check === s::get('csrf')) ? true : false;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Facepalm typo alias
|
||||||
|
* @see csrf()
|
||||||
|
*/
|
||||||
|
function csfr($check = null) {
|
||||||
|
return csrf($check);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shortcut for call_user_func_array with a better handling of arguments
|
||||||
|
*
|
||||||
|
* @param mixed $function
|
||||||
|
* @param mixed $arguments
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
function call($function, $arguments = array()) {
|
||||||
|
if(!is_callable($function)) return false;
|
||||||
|
if(!is_array($arguments)) $arguments = array($arguments);
|
||||||
|
return call_user_func_array($function, $arguments);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses yaml structured text
|
||||||
|
*
|
||||||
|
* @param $string
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
function yaml($string) {
|
||||||
|
return yaml::decode($string);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Simple email sender helper
|
||||||
|
*
|
||||||
|
* @param array $params
|
||||||
|
* @return Email
|
||||||
|
*/
|
||||||
|
function email($params = array()) {
|
||||||
|
return new Email($params);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shortcut for the upload class
|
||||||
|
*
|
||||||
|
* @param $to
|
||||||
|
* @param array $params
|
||||||
|
* @return Upload
|
||||||
|
*/
|
||||||
|
function upload($to, $params = array()) {
|
||||||
|
return new Upload($to, $params);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks for invalid data
|
||||||
|
*
|
||||||
|
* @param array $data
|
||||||
|
* @param array $rules
|
||||||
|
* @param array $messages
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
function invalid($data, $rules, $messages = array()) {
|
||||||
|
$errors = array();
|
||||||
|
foreach($rules as $field => $validations) {
|
||||||
|
foreach($validations as $method => $options) {
|
||||||
|
if(is_numeric($method)) $method = $options;
|
||||||
|
if($method == 'required') {
|
||||||
|
if(!isset($data[$field]) || (empty($data[$field]) && $data[$field] !== 0)) {
|
||||||
|
$errors[$field] = a::get($messages, $field, $field);
|
||||||
|
}
|
||||||
|
} else if(!empty($data[$field]) || $data[$field] === 0) {
|
||||||
|
if(!is_array($options)) $options = array($options);
|
||||||
|
array_unshift($options, a::get($data, $field));
|
||||||
|
if(!call(array('v', $method), $options)) {
|
||||||
|
$errors[$field] = a::get($messages, $field, $field);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return array_unique($errors);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shortcut for the language variable getter
|
||||||
|
*
|
||||||
|
* @param string $key
|
||||||
|
* @param mixed $default
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
function l($key, $default = null) {
|
||||||
|
return l::get($key, $default);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param $tag
|
||||||
|
* @param bool $html
|
||||||
|
* @param array $attr
|
||||||
|
* @return Brick
|
||||||
|
*/
|
||||||
|
function brick($tag, $html = false, $attr = array()) {
|
||||||
|
return new Brick($tag, $html, $attr);
|
||||||
|
}
|
495
kirby/toolkit/lib/a.php
Normal file
495
kirby/toolkit/lib/a.php
Normal file
|
@ -0,0 +1,495 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Array
|
||||||
|
*
|
||||||
|
* This class is supposed to simplify array handling
|
||||||
|
* and make it more consistent.
|
||||||
|
*
|
||||||
|
* @package Kirby Toolkit
|
||||||
|
* @author Bastian Allgeier <bastian@getkirby.com>
|
||||||
|
* @link http://getkirby.com
|
||||||
|
* @copyright Bastian Allgeier
|
||||||
|
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||||
|
*/
|
||||||
|
class A {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets an element of an array by key
|
||||||
|
*
|
||||||
|
* <code>
|
||||||
|
*
|
||||||
|
* $array = array(
|
||||||
|
* 'cat' => 'miao',
|
||||||
|
* 'dog' => 'wuff',
|
||||||
|
* 'bird' => 'tweet'
|
||||||
|
* );
|
||||||
|
*
|
||||||
|
* echo a::get($array, 'cat');
|
||||||
|
* // output: 'miao'
|
||||||
|
*
|
||||||
|
* echo a::get($array, 'elephant', 'shut up');
|
||||||
|
* // output: 'shut up'
|
||||||
|
*
|
||||||
|
* $catAndDog = a::get(array('cat', 'dog'));
|
||||||
|
* // result: array(
|
||||||
|
* // 'cat' => 'miao',
|
||||||
|
* // 'dog' => 'wuff'
|
||||||
|
* // );
|
||||||
|
*
|
||||||
|
* </code>
|
||||||
|
*
|
||||||
|
* @param array $array The source array
|
||||||
|
* @param mixed $key The key to look for
|
||||||
|
* @param mixed $default Optional default value, which should be returned if no element has been found
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public static function get($array, $key, $default = null) {
|
||||||
|
|
||||||
|
// get an array of keys
|
||||||
|
if(is_array($key)) {
|
||||||
|
$result = array();
|
||||||
|
foreach($key as $k) $result[$k] = static::get($array, $k);
|
||||||
|
return $result;
|
||||||
|
|
||||||
|
// get a single
|
||||||
|
} else if(isset($array[$key])) {
|
||||||
|
return $array[$key];
|
||||||
|
|
||||||
|
// return the entire array if the key is null
|
||||||
|
} else if(is_null($key)) {
|
||||||
|
return $array;
|
||||||
|
|
||||||
|
// get the default value if nothing else worked out
|
||||||
|
} else {
|
||||||
|
return $default;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shows an entire array or object in a human readable way
|
||||||
|
* This is perfect for debugging
|
||||||
|
*
|
||||||
|
* <code>
|
||||||
|
*
|
||||||
|
* $array = array(
|
||||||
|
* 'cat' => 'miao',
|
||||||
|
* 'dog' => 'wuff',
|
||||||
|
* 'bird' => 'tweet'
|
||||||
|
* );
|
||||||
|
*
|
||||||
|
* a::show($array);
|
||||||
|
*
|
||||||
|
* // output:
|
||||||
|
* // Array
|
||||||
|
* // (
|
||||||
|
* // [cat] => miao
|
||||||
|
* // [dog] => wuff
|
||||||
|
* // [bird] => tweet
|
||||||
|
* // )
|
||||||
|
*
|
||||||
|
* </code>
|
||||||
|
*
|
||||||
|
* @param array $array The source array
|
||||||
|
* @param boolean $echo By default the result will be echoed instantly. You can switch that off here.
|
||||||
|
* @return mixed If echo is false, this will return the generated array output.
|
||||||
|
*/
|
||||||
|
public static function show($array, $echo = true) {
|
||||||
|
return dump($array, $echo);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts an array to a JSON string
|
||||||
|
* It's basically a shortcut for json_encode()
|
||||||
|
*
|
||||||
|
* <code>
|
||||||
|
*
|
||||||
|
* $array = array(
|
||||||
|
* 'cat' => 'miao',
|
||||||
|
* 'dog' => 'wuff',
|
||||||
|
* 'bird' => 'tweet'
|
||||||
|
* );
|
||||||
|
*
|
||||||
|
* echo a::json($array);
|
||||||
|
* // output: {"cat":"miao","dog":"wuff","bird":"tweet"}
|
||||||
|
*
|
||||||
|
* </code>
|
||||||
|
*
|
||||||
|
* @param array $array The source array
|
||||||
|
* @return string The JSON string
|
||||||
|
*/
|
||||||
|
public static function json($array) {
|
||||||
|
return json_encode((array)$array);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts an array to a XML string
|
||||||
|
*
|
||||||
|
* <code>
|
||||||
|
*
|
||||||
|
* $array = array(
|
||||||
|
* 'cat' => 'miao',
|
||||||
|
* 'dog' => 'wuff',
|
||||||
|
* 'bird' => 'tweet'
|
||||||
|
* );
|
||||||
|
*
|
||||||
|
* echo a::xml($array, 'animals');
|
||||||
|
* // output:
|
||||||
|
* // <animals>
|
||||||
|
* // <cat>miao</cat>
|
||||||
|
* // <dog>wuff</dog>
|
||||||
|
* // <bird>tweet</bird>
|
||||||
|
* // </animals>
|
||||||
|
*
|
||||||
|
* </code>
|
||||||
|
*
|
||||||
|
* @param array $array The source array
|
||||||
|
* @param string $tag The name of the root element
|
||||||
|
* @param boolean $head Include the xml declaration head or not
|
||||||
|
* @param string $charset The charset, which should be used for the header
|
||||||
|
* @param int $level The indendation level
|
||||||
|
* @return string The XML string
|
||||||
|
*/
|
||||||
|
public static function xml($array, $tag = 'root', $head = true, $charset = 'utf-8', $tab = ' ', $level = 0) {
|
||||||
|
return xml::create($array, $tag, $head, $charset, $tab, $level);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extracts a single column from an array
|
||||||
|
*
|
||||||
|
* <code>
|
||||||
|
*
|
||||||
|
* $array[0] = array(
|
||||||
|
* 'id' => 1,
|
||||||
|
* 'username' => 'bastian',
|
||||||
|
* );
|
||||||
|
*
|
||||||
|
* $array[1] = array(
|
||||||
|
* 'id' => 2,
|
||||||
|
* 'username' => 'peter',
|
||||||
|
* );
|
||||||
|
*
|
||||||
|
* $array[3] = array(
|
||||||
|
* 'id' => 3,
|
||||||
|
* 'username' => 'john',
|
||||||
|
* );
|
||||||
|
*
|
||||||
|
* $extract = a::extract($array, 'username');
|
||||||
|
*
|
||||||
|
* // result: array(
|
||||||
|
* // 'bastian',
|
||||||
|
* // 'peter',
|
||||||
|
* // 'john'
|
||||||
|
* // );
|
||||||
|
*
|
||||||
|
* </code>
|
||||||
|
*
|
||||||
|
* @param array $array The source array
|
||||||
|
* @param string $key The key name of the column to extract
|
||||||
|
* @return array The result array with all values from that column.
|
||||||
|
*/
|
||||||
|
public static function extract($array, $key) {
|
||||||
|
$output = array();
|
||||||
|
foreach($array AS $a) if(isset($a[$key])) $output[] = $a[ $key ];
|
||||||
|
return $output;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shuffles an array and keeps the keys
|
||||||
|
*
|
||||||
|
* <code>
|
||||||
|
*
|
||||||
|
* $array = array(
|
||||||
|
* 'cat' => 'miao',
|
||||||
|
* 'dog' => 'wuff',
|
||||||
|
* 'bird' => 'tweet'
|
||||||
|
* );
|
||||||
|
*
|
||||||
|
* $shuffled = a::shuffle($array);
|
||||||
|
* // output: array(
|
||||||
|
* // 'dog' => 'wuff',
|
||||||
|
* // 'cat' => 'miao',
|
||||||
|
* // 'bird' => 'tweet'
|
||||||
|
* // );
|
||||||
|
*
|
||||||
|
* </code>
|
||||||
|
*
|
||||||
|
* @param array $array The source array
|
||||||
|
* @return array The shuffled result array
|
||||||
|
*/
|
||||||
|
public static function shuffle($array) {
|
||||||
|
|
||||||
|
$keys = array_keys($array);
|
||||||
|
$new = array();
|
||||||
|
|
||||||
|
shuffle($keys);
|
||||||
|
|
||||||
|
// resort the array
|
||||||
|
foreach($keys as $key) $new[$key] = $array[$key];
|
||||||
|
return $new;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the first element of an array
|
||||||
|
*
|
||||||
|
* I always have to lookup the names of that function
|
||||||
|
* so I decided to make this shortcut which is
|
||||||
|
* easier to remember.
|
||||||
|
*
|
||||||
|
* <code>
|
||||||
|
*
|
||||||
|
* $array = array(
|
||||||
|
* 'cat',
|
||||||
|
* 'dog',
|
||||||
|
* 'bird',
|
||||||
|
* );
|
||||||
|
*
|
||||||
|
* $first = a::first($array);
|
||||||
|
* // first: 'cat'
|
||||||
|
*
|
||||||
|
* </code>
|
||||||
|
*
|
||||||
|
* @param array $array The source array
|
||||||
|
* @return mixed The first element
|
||||||
|
*/
|
||||||
|
public static function first($array) {
|
||||||
|
return array_shift($array);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the last element of an array
|
||||||
|
*
|
||||||
|
* I always have to lookup the names of that function
|
||||||
|
* so I decided to make this shortcut which is
|
||||||
|
* easier to remember.
|
||||||
|
*
|
||||||
|
* <code>
|
||||||
|
*
|
||||||
|
* $array = array(
|
||||||
|
* 'cat',
|
||||||
|
* 'dog',
|
||||||
|
* 'bird',
|
||||||
|
* );
|
||||||
|
*
|
||||||
|
* $last = a::last($array);
|
||||||
|
* // first: 'bird'
|
||||||
|
*
|
||||||
|
* </code>
|
||||||
|
*
|
||||||
|
* @param array $array The source array
|
||||||
|
* @return mixed The last element
|
||||||
|
*/
|
||||||
|
public static function last($array) {
|
||||||
|
return array_pop($array);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fills an array up with additional elements to certain amount.
|
||||||
|
*
|
||||||
|
* <code>
|
||||||
|
*
|
||||||
|
* $array = array(
|
||||||
|
* 'cat',
|
||||||
|
* 'dog',
|
||||||
|
* 'bird',
|
||||||
|
* );
|
||||||
|
*
|
||||||
|
* $result = a::fill($array, 5, 'elephant');
|
||||||
|
*
|
||||||
|
* // result: array(
|
||||||
|
* // 'cat',
|
||||||
|
* // 'dog',
|
||||||
|
* // 'bird',
|
||||||
|
* // 'elephant',
|
||||||
|
* // 'elephant',
|
||||||
|
* // );
|
||||||
|
*
|
||||||
|
* </code>
|
||||||
|
*
|
||||||
|
* @param array $array The source array
|
||||||
|
* @param int $limit The number of elements the array should contain after filling it up.
|
||||||
|
* @param mixed $fill The element, which should be used to fill the array
|
||||||
|
* @return array The filled-up result array
|
||||||
|
*/
|
||||||
|
public static function fill($array, $limit, $fill='placeholder') {
|
||||||
|
if(count($array) < $limit) {
|
||||||
|
$diff = $limit-count($array);
|
||||||
|
for($x=0; $x<$diff; $x++) $array[] = $fill;
|
||||||
|
}
|
||||||
|
return $array;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks for missing elements in an array
|
||||||
|
*
|
||||||
|
* This is very handy to check for missing
|
||||||
|
* user values in a request for example.
|
||||||
|
*
|
||||||
|
* <code>
|
||||||
|
*
|
||||||
|
* $array = array(
|
||||||
|
* 'cat' => 'miao',
|
||||||
|
* 'dog' => 'wuff',
|
||||||
|
* 'bird' => 'tweet'
|
||||||
|
* );
|
||||||
|
*
|
||||||
|
* $required = array('cat', 'elephant');
|
||||||
|
*
|
||||||
|
* $missng = a::missing($array, $required);
|
||||||
|
* // missing: array(
|
||||||
|
* // 'elephant'
|
||||||
|
* // );
|
||||||
|
*
|
||||||
|
* </code>
|
||||||
|
*
|
||||||
|
* @param array $array The source array
|
||||||
|
* @param array $required An array of required keys
|
||||||
|
* @return array An array of missing fields. If this is empty, nothing is missing.
|
||||||
|
*/
|
||||||
|
public static function missing($array, $required=array()) {
|
||||||
|
$missing = array();
|
||||||
|
foreach($required AS $r) {
|
||||||
|
if(empty($array[$r])) $missing[] = $r;
|
||||||
|
}
|
||||||
|
return $missing;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sorts a multi-dimensional array by a certain column
|
||||||
|
*
|
||||||
|
* <code>
|
||||||
|
*
|
||||||
|
* $array[0] = array(
|
||||||
|
* 'id' => 1,
|
||||||
|
* 'username' => 'bastian',
|
||||||
|
* );
|
||||||
|
*
|
||||||
|
* $array[1] = array(
|
||||||
|
* 'id' => 2,
|
||||||
|
* 'username' => 'peter',
|
||||||
|
* );
|
||||||
|
*
|
||||||
|
* $array[3] = array(
|
||||||
|
* 'id' => 3,
|
||||||
|
* 'username' => 'john',
|
||||||
|
* );
|
||||||
|
*
|
||||||
|
* $sorted = a::sort($array, 'username ASC');
|
||||||
|
* // Array
|
||||||
|
* // (
|
||||||
|
* // [0] => Array
|
||||||
|
* // (
|
||||||
|
* // [id] => 1
|
||||||
|
* // [username] => bastian
|
||||||
|
* // )
|
||||||
|
* // [1] => Array
|
||||||
|
* // (
|
||||||
|
* // [id] => 3
|
||||||
|
* // [username] => john
|
||||||
|
* // )
|
||||||
|
* // [2] => Array
|
||||||
|
* // (
|
||||||
|
* // [id] => 2
|
||||||
|
* // [username] => peter
|
||||||
|
* // )
|
||||||
|
* // )
|
||||||
|
*
|
||||||
|
* </code>
|
||||||
|
*
|
||||||
|
* @param array $array The source array
|
||||||
|
* @param string $field The name of the column
|
||||||
|
* @param string $direction desc (descending) or asc (ascending)
|
||||||
|
* @param const $method A PHP sort method flag or 'natural' for natural sorting, which is not supported in PHP by sort flags
|
||||||
|
* @return array The sorted array
|
||||||
|
*/
|
||||||
|
public static function sort($array, $field, $direction = 'desc', $method = SORT_REGULAR) {
|
||||||
|
|
||||||
|
$direction = strtolower($direction) == 'desc' ? SORT_DESC : SORT_ASC;
|
||||||
|
$helper = array();
|
||||||
|
$result = array();
|
||||||
|
|
||||||
|
// build the helper array
|
||||||
|
foreach($array as $key => $row) $helper[$key] = $row[$field];
|
||||||
|
|
||||||
|
// natural sorting
|
||||||
|
if($method === SORT_NATURAL) {
|
||||||
|
natsort($helper);
|
||||||
|
if($direction === SORT_DESC) $helper = array_reverse($helper);
|
||||||
|
} else if($direction === SORT_DESC) {
|
||||||
|
arsort($helper, $method);
|
||||||
|
} else {
|
||||||
|
asort($helper, $method);
|
||||||
|
}
|
||||||
|
|
||||||
|
// rebuild the original array
|
||||||
|
foreach($helper as $key => $val) $result[$key] = $array[$key];
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks wether an array is associative or not (experimental)
|
||||||
|
*
|
||||||
|
* @param array $array The array to analyze
|
||||||
|
* @return boolean true: The array is associative false: It's not
|
||||||
|
*/
|
||||||
|
public static function isAssociative($array) {
|
||||||
|
return !ctype_digit(implode(NULL, array_keys($array)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the average value of an array
|
||||||
|
*
|
||||||
|
* @param array $array The source array
|
||||||
|
* @param int $decimals The number of decimals to return
|
||||||
|
* @return int The average value
|
||||||
|
*/
|
||||||
|
public static function average($array, $decimals = 0) {
|
||||||
|
return round(array_sum($array), $decimals) / sizeof($array);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Merges arrays recursively
|
||||||
|
*
|
||||||
|
* @param array $array1
|
||||||
|
* @param array $array2
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public static function merge($array1, $array2) {
|
||||||
|
$merged = $array1;
|
||||||
|
foreach($array2 as $key => $value) {
|
||||||
|
if(is_array($value) && isset($merged[$key]) && is_array($merged[$key])) {
|
||||||
|
$merged[$key] = static::merge($merged[$key], $value);
|
||||||
|
} else {
|
||||||
|
$merged[$key] = $value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $merged;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update an array with a second array
|
||||||
|
* The second array can contain callbacks as values,
|
||||||
|
* which will get the original values as argument
|
||||||
|
*
|
||||||
|
* @param array $array
|
||||||
|
* @param array $update
|
||||||
|
*/
|
||||||
|
public static function update($array, $update) {
|
||||||
|
|
||||||
|
foreach($update as $key => $value) {
|
||||||
|
if(is_a($value, 'Closure')) {
|
||||||
|
$array[$key] = call($value, static::get($array, $key));
|
||||||
|
} else {
|
||||||
|
$array[$key] = $value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $array;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
75
kirby/toolkit/lib/bitmask.php
Normal file
75
kirby/toolkit/lib/bitmask.php
Normal file
|
@ -0,0 +1,75 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Bitmask
|
||||||
|
*
|
||||||
|
* Analyzes and sets bitmasks
|
||||||
|
*
|
||||||
|
* @package Kirby Toolkit
|
||||||
|
* @author Lukas Bestle <lukas@getkirby.com>
|
||||||
|
* @link http://getkirby.com
|
||||||
|
* @copyright Lukas Bestle
|
||||||
|
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||||
|
*/
|
||||||
|
class Bitmask {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if a value can be used as bitmask value (checks for a power of two)
|
||||||
|
*
|
||||||
|
* @param mixed $value The value to check for
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
public static function validValue($value) {
|
||||||
|
return is_int($value) && ($value & ($value - 1)) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if a bitmask includes a value
|
||||||
|
*
|
||||||
|
* @param int $value The value to check for
|
||||||
|
* @param int $bitmask The bitmask to check in
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
public static function includes($value, $bitmask) {
|
||||||
|
if(!static::validValue($value)) return false;
|
||||||
|
|
||||||
|
return ($bitmask & $value) !== 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a value to a bitmask
|
||||||
|
*
|
||||||
|
* @param int $value The value to add
|
||||||
|
* @param int $bitmask The bitmask to add the value to
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
public static function add($value, $bitmask) {
|
||||||
|
if(!static::validValue($value)) {
|
||||||
|
throw new Exception('The value "' . $value . '" is not appropriate for usage in bitmasks.');
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if the bitmask already includes the value
|
||||||
|
if(static::includes($value, $bitmask)) return $bitmask;
|
||||||
|
|
||||||
|
return $bitmask | $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes a value from a bitmask
|
||||||
|
*
|
||||||
|
* @param int $value The value to remove
|
||||||
|
* @param int $bitmask The bitmask to remove the value from
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
public static function remove($value, $bitmask) {
|
||||||
|
if(!static::validValue($value)) {
|
||||||
|
throw new Exception('The value "' . $value . '" is not appropriate for usage in bitmasks.');
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if the bitmask even includes the value
|
||||||
|
if(!static::includes($value, $bitmask)) return $bitmask;
|
||||||
|
|
||||||
|
return $bitmask ^ $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
204
kirby/toolkit/lib/brick.php
Normal file
204
kirby/toolkit/lib/brick.php
Normal file
|
@ -0,0 +1,204 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
class Brick {
|
||||||
|
|
||||||
|
public static $bricks = array();
|
||||||
|
|
||||||
|
public $tag = null;
|
||||||
|
public $attr = array();
|
||||||
|
public $html = null;
|
||||||
|
public $events = array();
|
||||||
|
|
||||||
|
public function __construct($tag, $html = false, $attr = array()) {
|
||||||
|
|
||||||
|
if(is_array($html)) {
|
||||||
|
$attr = $html;
|
||||||
|
$html = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->tag($tag);
|
||||||
|
$this->html($html);
|
||||||
|
$this->attr($attr);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __set($attr, $value) {
|
||||||
|
$this->attr($attr, $value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function on($event, $callback) {
|
||||||
|
if(!isset($this->events[$event])) $this->events[$event] = array();
|
||||||
|
$this->events[$event][] = $callback;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function trigger($event, $args = array()) {
|
||||||
|
if(isset($this->events[$event])) {
|
||||||
|
array_unshift($args, $this);
|
||||||
|
foreach($this->events[$event] as $e) {
|
||||||
|
call_user_func_array($e, $args);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function tag($tag = null) {
|
||||||
|
if(is_null($tag)) return $this->tag;
|
||||||
|
$this->tag = $tag;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function attr($key = null, $value = null) {
|
||||||
|
if(is_null($key)) {
|
||||||
|
return $this->attr;
|
||||||
|
} else if(is_array($key)) {
|
||||||
|
foreach($key as $k => $v) {
|
||||||
|
$this->attr($k, $v);
|
||||||
|
}
|
||||||
|
return $this;
|
||||||
|
} else if(is_null($value)) {
|
||||||
|
return a::get($this->attr, $key);
|
||||||
|
} else if($key == 'class') {
|
||||||
|
$this->addClass($value);
|
||||||
|
return $this;
|
||||||
|
} else {
|
||||||
|
$this->attr[$key] = $value;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function data($key = null, $value = null) {
|
||||||
|
if(is_null($key)) {
|
||||||
|
$data = array();
|
||||||
|
foreach($this->attr as $key => $val) {
|
||||||
|
if(str::startsWith($key, 'data-')) {
|
||||||
|
$data[$key] = $val;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $data;
|
||||||
|
} else if(is_array($key)) {
|
||||||
|
foreach($key as $k => $v) {
|
||||||
|
$this->data($k, $v);
|
||||||
|
}
|
||||||
|
return $this;
|
||||||
|
} else if(is_null($value)) {
|
||||||
|
return a::get($this->attr, 'data-' . $key);
|
||||||
|
} else {
|
||||||
|
$this->attr['data-' . $key] = $value;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function removeAttr($key) {
|
||||||
|
unset($this->attr[$key]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function classNames() {
|
||||||
|
|
||||||
|
if(!isset($this->attr['class'])) {
|
||||||
|
$this->attr['class'] = array();
|
||||||
|
} else if(is_string($this->attr['class'])) {
|
||||||
|
$raw = $this->attr['class'];
|
||||||
|
$this->attr['class'] = array();
|
||||||
|
$this->addClass($raw);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->attr['class'];
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public function val($value = null) {
|
||||||
|
return $this->attr('value', $value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function addClass($class) {
|
||||||
|
|
||||||
|
$classNames = $this->classNames();
|
||||||
|
$classIndex = array_map('strtolower', $classNames);
|
||||||
|
|
||||||
|
foreach(str::split($class, ' ') as $c) {
|
||||||
|
if(!in_array(strtolower($c), $classIndex)) {
|
||||||
|
$classNames[] = $c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->attr['class'] = $classNames;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public function removeClass($class) {
|
||||||
|
|
||||||
|
$classNames = $this->classNames();
|
||||||
|
|
||||||
|
foreach(str::split($class, ' ') as $c) {
|
||||||
|
$classNames = array_filter($classNames, function($e) use($c) {
|
||||||
|
return (strtolower($e) !== strtolower($c));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->attr['class'] = $classNames;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public function replaceClass($classA, $classB) {
|
||||||
|
return $this->removeClass($classA)->addClass($classB);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function text($text = null) {
|
||||||
|
if(is_null($text)) return trim(strip_tags($this->html));
|
||||||
|
$this->html = html($text, false);
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function html($html = null) {
|
||||||
|
if(is_null($html)) {
|
||||||
|
return $this->html = $this->isVoid() ? null : $this->html;
|
||||||
|
}
|
||||||
|
$this->html = $html;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function prepend($html) {
|
||||||
|
if(is_callable($html)) $html = $html();
|
||||||
|
$this->html = $html . $this->html;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function append($html) {
|
||||||
|
if(is_callable($html)) $html = $html();
|
||||||
|
$this->html = $this->html . $html;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isVoid() {
|
||||||
|
return html::isVoid($this->tag());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function toString() {
|
||||||
|
$this->attr['class'] = implode(' ', $this->classNames());
|
||||||
|
return html::tag($this->tag(), $this->html(), $this->attr());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __toString() {
|
||||||
|
try {
|
||||||
|
return $this->toString();
|
||||||
|
} catch(Exception $e) {
|
||||||
|
return 'Error: ' . $e->getMessage();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function make($id, $callback) {
|
||||||
|
static::$bricks[$id] = $callback;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function get($id) {
|
||||||
|
if(!isset(static::$bricks[$id])) return false;
|
||||||
|
$args = array_slice(func_get_args(), 1);
|
||||||
|
return call_user_func_array(static::$bricks[$id], $args);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
17
kirby/toolkit/lib/c.php
Normal file
17
kirby/toolkit/lib/c.php
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Config
|
||||||
|
*
|
||||||
|
* This is the core class to handle
|
||||||
|
* configuration values/constants.
|
||||||
|
*
|
||||||
|
* @package Kirby Toolkit
|
||||||
|
* @author Bastian Allgeier <bastian@getkirby.com>
|
||||||
|
* @link http://getkirby.com
|
||||||
|
* @copyright Bastian Allgeier
|
||||||
|
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||||
|
*/
|
||||||
|
class C extends Silo {
|
||||||
|
public static $data = array();
|
||||||
|
}
|
59
kirby/toolkit/lib/cache.php
Normal file
59
kirby/toolkit/lib/cache.php
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cache
|
||||||
|
*
|
||||||
|
* The ultimate cache wrapper for
|
||||||
|
* all available drivers
|
||||||
|
*
|
||||||
|
* @package Kirby Toolkit
|
||||||
|
* @author Bastian Allgeier <bastian@getkirby.com>
|
||||||
|
* @link http://getkirby.com
|
||||||
|
* @copyright Bastian Allgeier
|
||||||
|
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||||
|
*/
|
||||||
|
class Cache {
|
||||||
|
|
||||||
|
const ERROR_INVALID_DRIVER = 0;
|
||||||
|
const ERROR_INVALID_DRIVER_INSTANCE = 1;
|
||||||
|
const ERROR_UNKNOWN_METHOD = 2;
|
||||||
|
|
||||||
|
public static $driver = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Setup simplifier for the current driver
|
||||||
|
*
|
||||||
|
* @param string $driver
|
||||||
|
* @param mixed $args
|
||||||
|
* @return Cache\Driver
|
||||||
|
*/
|
||||||
|
public static function setup($driver, $args = null) {
|
||||||
|
$ref = new ReflectionClass('Cache\\Driver\\' . $driver);
|
||||||
|
return static::$driver = $ref->newInstanceArgs(array($args));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Accessor for all static driver methods
|
||||||
|
*
|
||||||
|
* @param string $method
|
||||||
|
* @param mixed $args
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public static function __callStatic($method, $args) {
|
||||||
|
|
||||||
|
if(is_null(static::$driver)) {
|
||||||
|
throw new Error('Please define a cache driver', static::ERROR_INVALID_DRIVER);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!is_a(static::$driver, 'Cache\\Driver')) {
|
||||||
|
throw new Error('The cache driver must be an instance of the Cache\\Driver class', static::ERROR_INVALID_DRIVER_INSTANCE);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(method_exists(static::$driver, $method)) {
|
||||||
|
return call(array(static::$driver, $method), $args);
|
||||||
|
} else {
|
||||||
|
throw new Error('Invalid cache method: ' . $method, static::ERROR_UNKNOWN_METHOD);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
190
kirby/toolkit/lib/cache/driver.php
vendored
Executable file
190
kirby/toolkit/lib/cache/driver.php
vendored
Executable file
|
@ -0,0 +1,190 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Cache;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cache Driver Abstract
|
||||||
|
*
|
||||||
|
* Template for all cache drivers
|
||||||
|
*
|
||||||
|
* @package Kirby Toolkit
|
||||||
|
* @author Bastian Allgeier <bastian@getkirby.com>
|
||||||
|
* @link http://getkirby.com
|
||||||
|
* @copyright Bastian Allgeier
|
||||||
|
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||||
|
*/
|
||||||
|
abstract class Driver {
|
||||||
|
|
||||||
|
// stores all options for the driver
|
||||||
|
protected $options = array();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set all parameters which are needed to connect to the cache storage
|
||||||
|
*
|
||||||
|
* @param array $params
|
||||||
|
*/
|
||||||
|
public function __construct($params = array()) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write an item to the cache for a given number of minutes.
|
||||||
|
*
|
||||||
|
* <code>
|
||||||
|
* // Put an item in the cache for 15 minutes
|
||||||
|
* Cache::set('value', 'my value', 15);
|
||||||
|
* </code>
|
||||||
|
*
|
||||||
|
* @param string $key
|
||||||
|
* @param mixed $value
|
||||||
|
* @param int $minutes
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public abstract function set($key, $value, $minutes = null);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Private method to retrieve the cache value
|
||||||
|
* This needs to be defined by the driver
|
||||||
|
*
|
||||||
|
* @param string $key
|
||||||
|
* @return object Value
|
||||||
|
*/
|
||||||
|
public abstract function retrieve($key);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get an item from the cache.
|
||||||
|
*
|
||||||
|
* <code>
|
||||||
|
* // Get an item from the cache driver
|
||||||
|
* $value = Cache::get('value');
|
||||||
|
*
|
||||||
|
* // Return a default value if the requested item isn't cached
|
||||||
|
* $value = Cache::get('value', 'default value');
|
||||||
|
* </code>
|
||||||
|
*
|
||||||
|
* @param string $key
|
||||||
|
* @param mixed $default
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function get($key, $default = null) {
|
||||||
|
|
||||||
|
// get the Value
|
||||||
|
$value = $this->retrieve($key);
|
||||||
|
|
||||||
|
// check for a valid cache value
|
||||||
|
if(!is_a($value, 'Cache\\Value')) return $default;
|
||||||
|
|
||||||
|
// remove the item if it is expired
|
||||||
|
if(time() > $value->expires()) {
|
||||||
|
$this->remove($key);
|
||||||
|
return $default;
|
||||||
|
}
|
||||||
|
|
||||||
|
// get the pure value
|
||||||
|
$cache = $value->value();
|
||||||
|
|
||||||
|
// return the cache value or the default
|
||||||
|
return (!is_null($cache)) ? $cache : $default;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculates the expiration timestamp
|
||||||
|
*
|
||||||
|
* @param int $minutes
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
protected function expiration($minutes = null) {
|
||||||
|
// keep forever if minutes are not defined
|
||||||
|
if(is_null($minutes)) $minutes = 2628000;
|
||||||
|
|
||||||
|
// calculate the time
|
||||||
|
return time() + ($minutes * 60);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks when an item in the cache expires
|
||||||
|
*
|
||||||
|
* @param string $key
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function expires($key) {
|
||||||
|
// get the Value object
|
||||||
|
$value = $this->retrieve($key);
|
||||||
|
|
||||||
|
// check for a valid Value object
|
||||||
|
if(!is_a($value, 'Cache\\Value')) return false;
|
||||||
|
|
||||||
|
// return the expires timestamp
|
||||||
|
return $value->expires();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if an item in the cache is expired
|
||||||
|
*
|
||||||
|
* @param string $key
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
public function expired($key) {
|
||||||
|
return $this->expires($key) <= time();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks when the cache has been created
|
||||||
|
*
|
||||||
|
* @param string $key
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function created($key) {
|
||||||
|
// get the Value object
|
||||||
|
$value = $this->retrieve($key);
|
||||||
|
|
||||||
|
// check for a valid Value object
|
||||||
|
if(!is_a($value, 'Cache\\Value')) return false;
|
||||||
|
|
||||||
|
// return the expires timestamp
|
||||||
|
return $value->created();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Alternate version for cache::created($key)
|
||||||
|
*/
|
||||||
|
public function modified($key) {
|
||||||
|
return static::created($key);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An array with value, created timestamp and expires timestamp
|
||||||
|
*
|
||||||
|
* @param mixed $value The value, which should be cached
|
||||||
|
* @param int $minutes The number of minutes before expiration
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
protected function value($value, $minutes) {
|
||||||
|
return new Value($value, $minutes);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine if an item exists in the cache.
|
||||||
|
*
|
||||||
|
* @param string $key
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
public function exists($key) {
|
||||||
|
return !$this->expired($key);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove an item from the cache
|
||||||
|
*
|
||||||
|
* @param string $key
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
public abstract function remove($key);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Flush the entire cache
|
||||||
|
*
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
public abstract function flush();
|
||||||
|
|
||||||
|
}
|
74
kirby/toolkit/lib/cache/driver/apc.php
vendored
Normal file
74
kirby/toolkit/lib/cache/driver/apc.php
vendored
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Cache\Driver;
|
||||||
|
|
||||||
|
use Cache\Driver;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* APC Cache
|
||||||
|
*
|
||||||
|
* @package Kirby Toolkit
|
||||||
|
* @author Bastian Allgeier <bastian@getkirby.com>
|
||||||
|
* @link http://getkirby.com
|
||||||
|
* @copyright Bastian Allgeier
|
||||||
|
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||||
|
*/
|
||||||
|
class Apc extends Driver {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write an item to the cache for a given number of minutes.
|
||||||
|
*
|
||||||
|
* <code>
|
||||||
|
* // Put an item in the cache for 15 minutes
|
||||||
|
* Cache::set('value', 'my value', 15);
|
||||||
|
* </code>
|
||||||
|
*
|
||||||
|
* @param string $key
|
||||||
|
* @param mixed $value
|
||||||
|
* @param int $minutes
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function set($key, $value, $minutes = null) {
|
||||||
|
return apc_store($key, $this->value($value, $minutes), $this->expiration($minutes));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve an item from the cache.
|
||||||
|
*
|
||||||
|
* @param string $key
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function retrieve($key) {
|
||||||
|
return apc_fetch($key);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the current key exists in cache
|
||||||
|
*
|
||||||
|
* @param string $key
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
public function exists($key) {
|
||||||
|
return apc_exists($key);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove an item from the cache
|
||||||
|
*
|
||||||
|
* @param string $key
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
public function remove($key) {
|
||||||
|
return apc_delete($key);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Flush the entire cache directory
|
||||||
|
*
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
public function flush() {
|
||||||
|
return apc_clear_cache('user');
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
121
kirby/toolkit/lib/cache/driver/file.php
vendored
Normal file
121
kirby/toolkit/lib/cache/driver/file.php
vendored
Normal file
|
@ -0,0 +1,121 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Cache\Driver;
|
||||||
|
|
||||||
|
use Cache\Driver;
|
||||||
|
use Error;
|
||||||
|
use F;
|
||||||
|
use Dir;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* File Cache
|
||||||
|
*
|
||||||
|
* @package Kirby Toolkit
|
||||||
|
* @author Bastian Allgeier <bastian@getkirby.com>
|
||||||
|
* @link http://getkirby.com
|
||||||
|
* @copyright Bastian Allgeier
|
||||||
|
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||||
|
*/
|
||||||
|
class File extends Driver {
|
||||||
|
|
||||||
|
const ERROR_MISSING_CACHE_DIRECTORY = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set all parameters which are needed for the file cache
|
||||||
|
* see defaults for available parameters
|
||||||
|
*
|
||||||
|
* @param array $params
|
||||||
|
*/
|
||||||
|
public function __construct($params = array()) {
|
||||||
|
|
||||||
|
if(is_string($params)) {
|
||||||
|
$params = array('root' => $params);
|
||||||
|
}
|
||||||
|
|
||||||
|
$defaults = array(
|
||||||
|
'root' => null,
|
||||||
|
'extension' => null
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->options = array_merge($defaults, $params);
|
||||||
|
|
||||||
|
// check for a valid cache directory
|
||||||
|
if(!is_dir($this->options['root'])) {
|
||||||
|
throw new Error('The cache directory does not exist', static::ERROR_MISSING_CACHE_DIRECTORY);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the full path to a file for a given key
|
||||||
|
*
|
||||||
|
* @param string $key
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
protected function file($key) {
|
||||||
|
return $this->options['root'] . DS . $key . r($this->options['extension'], '.' . $this->options['extension']);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write an item to the cache for a given number of minutes.
|
||||||
|
*
|
||||||
|
* <code>
|
||||||
|
* // Put an item in the cache for 15 minutes
|
||||||
|
* Cache::set('value', 'my value', 15);
|
||||||
|
* </code>
|
||||||
|
*
|
||||||
|
* @param string $key
|
||||||
|
* @param mixed $value
|
||||||
|
* @param int $minutes
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function set($key, $value, $minutes = null) {
|
||||||
|
return f::write($this->file($key), serialize($this->value($value, $minutes)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve an item from the cache.
|
||||||
|
*
|
||||||
|
* @param string $key
|
||||||
|
* @return object CacheValue
|
||||||
|
*/
|
||||||
|
public function retrieve($key) {
|
||||||
|
// unserialized value array (see $this->value())
|
||||||
|
return unserialize(f::read($this->file($key)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks when the cache has been created
|
||||||
|
*
|
||||||
|
* @param string $key
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
public function created($key) {
|
||||||
|
// use the modification timestamp
|
||||||
|
// as indicator when the cache has been created/overwritten
|
||||||
|
clearstatcache();
|
||||||
|
// get the file for this cache key
|
||||||
|
$file = $this->file($key);
|
||||||
|
return file_exists($file) ? filemtime($this->file($key)) : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove an item from the cache
|
||||||
|
*
|
||||||
|
* @param string $key
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
public function remove($key) {
|
||||||
|
return f::remove($this->file($key));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Flush the entire cache directory
|
||||||
|
*
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
public function flush() {
|
||||||
|
return dir::clean($this->options['root']);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
128
kirby/toolkit/lib/cache/driver/memcached.php
vendored
Normal file
128
kirby/toolkit/lib/cache/driver/memcached.php
vendored
Normal file
|
@ -0,0 +1,128 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Cache\Driver;
|
||||||
|
|
||||||
|
use Cache\Driver;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Memcache
|
||||||
|
*
|
||||||
|
* @package Kirby Toolkit
|
||||||
|
* @author Bastian Allgeier <bastian@getkirby.com>
|
||||||
|
* @link http://getkirby.com
|
||||||
|
* @copyright Bastian Allgeier
|
||||||
|
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||||
|
*/
|
||||||
|
class Memcached extends Driver {
|
||||||
|
|
||||||
|
// store for the memache connection
|
||||||
|
protected $connection = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set all parameters which are needed for the memcache client
|
||||||
|
* see defaults for available parameters
|
||||||
|
*
|
||||||
|
* @param array $params
|
||||||
|
*/
|
||||||
|
public function __construct($params = array()) {
|
||||||
|
|
||||||
|
$defaults = array(
|
||||||
|
'host' => 'localhost',
|
||||||
|
'port' => 11211,
|
||||||
|
'prefix' => null,
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->options = array_merge($defaults, (array)$params);
|
||||||
|
$this->connection = new \Memcached();
|
||||||
|
$this->connection->addServer($this->options['host'], $this->options['port']);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write an item to the cache for a given number of minutes.
|
||||||
|
*
|
||||||
|
* <code>
|
||||||
|
* // Put an item in the cache for 15 minutes
|
||||||
|
* Cache::set('value', 'my value', 15);
|
||||||
|
* </code>
|
||||||
|
*
|
||||||
|
* @param string $key
|
||||||
|
* @param mixed $value
|
||||||
|
* @param int $minutes
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function set($key, $value, $minutes = null) {
|
||||||
|
return $this->connection->set($this->key($key), $this->value($value, $minutes), $this->expiration($minutes));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the full keyname
|
||||||
|
* including the prefix (if set)
|
||||||
|
*
|
||||||
|
* @param string $key
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function key($key) {
|
||||||
|
return $this->options['prefix'] . $key;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the CacheValue object from the cache.
|
||||||
|
*
|
||||||
|
* @param string $key
|
||||||
|
* @return object CacheValue
|
||||||
|
*/
|
||||||
|
public function retrieve($key) {
|
||||||
|
return $this->connection->get($this->key($key));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove an item from the cache
|
||||||
|
*
|
||||||
|
* @param string $key
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
public function remove($key) {
|
||||||
|
return $this->connection->delete($this->key($key));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks when an item in the cache expires
|
||||||
|
*
|
||||||
|
* @param string $key
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
public function expires($key) {
|
||||||
|
return parent::expires($this->key($key));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if an item in the cache is expired
|
||||||
|
*
|
||||||
|
* @param string $key
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
public function expired($key) {
|
||||||
|
return parent::expired($this->key($key));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks when the cache has been created
|
||||||
|
*
|
||||||
|
* @param string $key
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
public function created($key) {
|
||||||
|
return parent::created($this->key($key));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Flush the entire cache directory
|
||||||
|
*
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
public function flush() {
|
||||||
|
return $this->connection->flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
74
kirby/toolkit/lib/cache/driver/mock.php
vendored
Normal file
74
kirby/toolkit/lib/cache/driver/mock.php
vendored
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Cache\Driver;
|
||||||
|
|
||||||
|
use Cache\Driver;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mock Cache Driver
|
||||||
|
*
|
||||||
|
* @package Kirby Toolkit
|
||||||
|
* @author Bastian Allgeier <bastian@getkirby.com>
|
||||||
|
* @link http://getkirby.com
|
||||||
|
* @copyright Bastian Allgeier
|
||||||
|
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||||
|
*/
|
||||||
|
class Mock extends Driver {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write an item to the cache for a given number of minutes.
|
||||||
|
*
|
||||||
|
* <code>
|
||||||
|
* // Put an item in the cache for 15 minutes
|
||||||
|
* Cache::set('value', 'my value', 15);
|
||||||
|
* </code>
|
||||||
|
*
|
||||||
|
* @param string $key
|
||||||
|
* @param mixed $value
|
||||||
|
* @param int $minutes
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function set($key, $value, $minutes = null) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve an item from the cache.
|
||||||
|
*
|
||||||
|
* @param string $key
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function retrieve($key) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the current key exists in cache
|
||||||
|
*
|
||||||
|
* @param string $key
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
public function exists($key) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove an item from the cache
|
||||||
|
*
|
||||||
|
* @param string $key
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
public function remove($key) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Flush the entire cache directory
|
||||||
|
*
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
public function flush() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
78
kirby/toolkit/lib/cache/driver/session.php
vendored
Normal file
78
kirby/toolkit/lib/cache/driver/session.php
vendored
Normal file
|
@ -0,0 +1,78 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Cache\Driver;
|
||||||
|
|
||||||
|
use Cache\Driver;
|
||||||
|
use A;
|
||||||
|
use Exception;
|
||||||
|
use S;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Session Cache
|
||||||
|
*
|
||||||
|
* @package Kirby Toolkit
|
||||||
|
* @author Bastian Allgeier <bastian@getkirby.com>
|
||||||
|
* @link http://getkirby.com
|
||||||
|
* @copyright Bastian Allgeier
|
||||||
|
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||||
|
*/
|
||||||
|
class Session extends Driver {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Make sure the session is started within the constructor
|
||||||
|
*/
|
||||||
|
public function __construct() {
|
||||||
|
s::start();
|
||||||
|
if(!isset($_SESSION['_cache'])) {
|
||||||
|
$_SESSION['_cache'] = array();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write an item to the cache for a given number of minutes.
|
||||||
|
*
|
||||||
|
* <code>
|
||||||
|
* // Put an item in the cache for 15 minutes
|
||||||
|
* Cache::set('value', 'my value', 15);
|
||||||
|
* </code>
|
||||||
|
*
|
||||||
|
* @param string $key
|
||||||
|
* @param mixed $value
|
||||||
|
* @param int $minutes
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function set($key, $value, $minutes = null) {
|
||||||
|
return $_SESSION['_cache'][$key] = $this->value($value, $minutes);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve an item from the cache.
|
||||||
|
*
|
||||||
|
* @param string $key
|
||||||
|
* @return object CacheValue
|
||||||
|
*/
|
||||||
|
public function retrieve($key) {
|
||||||
|
return a::get($_SESSION['_cache'], $key);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove an item from the cache
|
||||||
|
*
|
||||||
|
* @param string $key
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
public function remove($key) {
|
||||||
|
unset($_SESSION['_cache'][$key]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Flush the entire cache directory
|
||||||
|
*
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
public function flush() {
|
||||||
|
$_SESSION['_cache'] = array();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
75
kirby/toolkit/lib/cache/value.php
vendored
Normal file
75
kirby/toolkit/lib/cache/value.php
vendored
Normal file
|
@ -0,0 +1,75 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Cache;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cache Value
|
||||||
|
*
|
||||||
|
* Stores the value, creation timestamp and expiration timestamp
|
||||||
|
* and makes it possible to store all three with a single cache key.
|
||||||
|
*
|
||||||
|
* @package Kirby Toolkit
|
||||||
|
* @author Bastian Allgeier <bastian@getkirby.com>
|
||||||
|
* @link http://getkirby.com
|
||||||
|
* @copyright Bastian Allgeier
|
||||||
|
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||||
|
*/
|
||||||
|
class Value {
|
||||||
|
|
||||||
|
// the cached value
|
||||||
|
protected $value;
|
||||||
|
|
||||||
|
// the expiration timestamp
|
||||||
|
protected $expires;
|
||||||
|
|
||||||
|
// the creation timestamp
|
||||||
|
protected $created;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*
|
||||||
|
* @param mixed $value
|
||||||
|
* @param int $minutes the number of minutes until the value expires
|
||||||
|
*/
|
||||||
|
public function __construct($value, $minutes = null) {
|
||||||
|
|
||||||
|
// keep forever if minutes are not defined
|
||||||
|
if(is_null($minutes)) $minutes = 2628000;
|
||||||
|
|
||||||
|
// take the current time
|
||||||
|
$time = time();
|
||||||
|
|
||||||
|
$this->value = $value;
|
||||||
|
$this->expires = $time + ($minutes * 60);
|
||||||
|
$this->created = $time;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the value
|
||||||
|
*
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function value() {
|
||||||
|
return $this->value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the expiration date as UNIX timestamp
|
||||||
|
*
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
public function expires() {
|
||||||
|
return $this->expires;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the creation date as UNIX timestamp
|
||||||
|
*
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
public function created() {
|
||||||
|
return $this->created;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
652
kirby/toolkit/lib/collection.php
Normal file
652
kirby/toolkit/lib/collection.php
Normal file
|
@ -0,0 +1,652 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Collection
|
||||||
|
*
|
||||||
|
* @package Kirby Toolkit
|
||||||
|
* @author Bastian Allgeier <bastian@getkirby.com>
|
||||||
|
* @link http://getkirby.com
|
||||||
|
* @copyright Bastian Allgeier
|
||||||
|
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||||
|
*/
|
||||||
|
class Collection extends I {
|
||||||
|
|
||||||
|
public static $filters = array();
|
||||||
|
|
||||||
|
protected $pagination;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a slice of the collection
|
||||||
|
*
|
||||||
|
* @param int $offset The optional index to start the slice from
|
||||||
|
* @param int $limit The optional number of elements to return
|
||||||
|
* @return Collection
|
||||||
|
*/
|
||||||
|
public function slice($offset = null, $limit = null) {
|
||||||
|
if($offset === null && $limit === null) return $this;
|
||||||
|
$collection = clone $this;
|
||||||
|
$collection->data = array_slice($collection->data, $offset, $limit);
|
||||||
|
return $collection;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a new combined collection
|
||||||
|
*
|
||||||
|
* @return Collection
|
||||||
|
*/
|
||||||
|
|
||||||
|
public function merge($collection2) {
|
||||||
|
$collection = clone $this;
|
||||||
|
$collection->data = a::merge($collection->data, $collection2->data);
|
||||||
|
return $collection;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a new collection with a limited number of elements
|
||||||
|
*
|
||||||
|
* @param int $limit The number of elements to return
|
||||||
|
* @return Collection
|
||||||
|
*/
|
||||||
|
public function limit($limit) {
|
||||||
|
return $this->slice(0, $limit);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a new collection starting from the given offset
|
||||||
|
*
|
||||||
|
* @param int $offset The index to start from
|
||||||
|
* @return Collection
|
||||||
|
*/
|
||||||
|
public function offset($offset) {
|
||||||
|
return $this->slice($offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the array in reverse order
|
||||||
|
*
|
||||||
|
* @return Collection
|
||||||
|
*/
|
||||||
|
public function flip() {
|
||||||
|
$collection = clone $this;
|
||||||
|
$collection->data = array_reverse($collection->data, true);
|
||||||
|
return $collection;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Counts all elements in the array
|
||||||
|
*
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
public function count() {
|
||||||
|
return count($this->data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the first element from the array
|
||||||
|
*
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function first() {
|
||||||
|
$array = $this->data;
|
||||||
|
return array_shift($array);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if an element is in the collection by key.
|
||||||
|
*
|
||||||
|
* @param string $key
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
public function has($key) {
|
||||||
|
return isset($this->data[$key]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the last element from the array
|
||||||
|
*
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function last() {
|
||||||
|
$array = $this->data;
|
||||||
|
return array_pop($array);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the nth element from the array
|
||||||
|
*
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function nth($n) {
|
||||||
|
$array = array_values($this->data);
|
||||||
|
return (isset($array[$n])) ? $array[$n] : false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts the current object into an array
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function toArray($callback = null) {
|
||||||
|
if(is_null($callback)) return $this->data;
|
||||||
|
return array_map($callback, $this->data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts the current object into a json string
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function toJson() {
|
||||||
|
return json_encode($this->data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Appends an element to the data array
|
||||||
|
*
|
||||||
|
* @param string $key
|
||||||
|
* @param mixed $object
|
||||||
|
* @return Collection
|
||||||
|
*/
|
||||||
|
public function append($key, $object) {
|
||||||
|
$this->data = $this->data + array($key => $object);
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prepends an element to the data array
|
||||||
|
*
|
||||||
|
* @param string $key
|
||||||
|
* @param mixed $object
|
||||||
|
* @return Collection
|
||||||
|
*/
|
||||||
|
public function prepend($key, $object) {
|
||||||
|
$this->data = array($key => $object) + $this->data;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a new collection without the given element(s)
|
||||||
|
*
|
||||||
|
* @param args any number of keys, passed as individual arguments
|
||||||
|
* @return Collection
|
||||||
|
*/
|
||||||
|
public function not() {
|
||||||
|
$collection = clone $this;
|
||||||
|
foreach(func_get_args() as $kill) {
|
||||||
|
unset($collection->data[$kill]);
|
||||||
|
}
|
||||||
|
return $collection;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a new collection without the given element(s)
|
||||||
|
*
|
||||||
|
* @param args any number of keys, passed as individual arguments
|
||||||
|
* @return Collection
|
||||||
|
*/
|
||||||
|
public function without() {
|
||||||
|
return call_user_func_array(array($this, 'not'), func_get_args());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shuffle all elements in the array
|
||||||
|
*
|
||||||
|
* @return object a new shuffled collection
|
||||||
|
*/
|
||||||
|
public function shuffle() {
|
||||||
|
$collection = clone $this;
|
||||||
|
$keys = array_keys($collection->data);
|
||||||
|
shuffle($keys);
|
||||||
|
$collection->data = array_merge(array_flip($keys), $collection->data);
|
||||||
|
return $collection;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an array of all keys in the collection
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function keys() {
|
||||||
|
return array_keys($this->data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tries to find the key for the given element
|
||||||
|
*
|
||||||
|
* @param mixed $needle the element to search for
|
||||||
|
* @return mixed the name of the key or false
|
||||||
|
*/
|
||||||
|
public function keyOf($needle) {
|
||||||
|
return array_search($needle, $this->data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tries to find the index number for the given element
|
||||||
|
*
|
||||||
|
* @param mixed $needle the element to search for
|
||||||
|
* @return mixed the name of the key or false
|
||||||
|
*/
|
||||||
|
public function indexOf($needle) {
|
||||||
|
return array_search($needle, array_values($this->data));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Filter the elements in the array by a callback function
|
||||||
|
*
|
||||||
|
* @param func $callback the callback function
|
||||||
|
* @return Collection
|
||||||
|
*/
|
||||||
|
public function filter($callback) {
|
||||||
|
$collection = clone $this;
|
||||||
|
$collection->data = array_filter($collection->data, $callback);
|
||||||
|
return $collection;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find a single item by a key and value pair
|
||||||
|
*
|
||||||
|
* @param string $key
|
||||||
|
* @param mixed $value
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function findBy($key, $value) {
|
||||||
|
foreach($this->data as $item) {
|
||||||
|
if($this->extractValue($item, $key) == $value) return $item;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Filters the current collection by a field, operator and search value
|
||||||
|
*
|
||||||
|
* @return Collection
|
||||||
|
*/
|
||||||
|
public function filterBy() {
|
||||||
|
|
||||||
|
$args = func_get_args();
|
||||||
|
$operator = '==';
|
||||||
|
$field = @$args[0];
|
||||||
|
$value = @$args[1];
|
||||||
|
$split = @$args[2];
|
||||||
|
$collection = clone $this;
|
||||||
|
|
||||||
|
if(is_string($value) && array_key_exists($value, static::$filters)) {
|
||||||
|
$operator = $value;
|
||||||
|
$value = @$args[2];
|
||||||
|
$split = @$args[3];
|
||||||
|
}
|
||||||
|
|
||||||
|
if(is_object($value)) {
|
||||||
|
$value = (string)$value;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(array_key_exists($operator, static::$filters)) {
|
||||||
|
|
||||||
|
$collection = call_user_func_array(static::$filters[$operator], array(
|
||||||
|
$collection,
|
||||||
|
$field,
|
||||||
|
$value,
|
||||||
|
$split
|
||||||
|
));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return $collection;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Makes sure to provide a valid value for each filter method
|
||||||
|
* no matter if an object or an array is given
|
||||||
|
*
|
||||||
|
* @param mixed $item
|
||||||
|
* @param string $field
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
static public function extractValue($item, $field) {
|
||||||
|
if(is_array($item) && isset($item[$field])) {
|
||||||
|
return $item[$field];
|
||||||
|
} else if(is_object($item)) {
|
||||||
|
return $item->$field();
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sorts the collection by any number of fields
|
||||||
|
*
|
||||||
|
* @return Collection
|
||||||
|
*/
|
||||||
|
public function sortBy() {
|
||||||
|
|
||||||
|
$args = func_get_args();
|
||||||
|
$collection = clone $this;
|
||||||
|
$array = $collection->data;
|
||||||
|
$params = array();
|
||||||
|
|
||||||
|
if(empty($array)) return $collection;
|
||||||
|
|
||||||
|
foreach($args as $i => $param) {
|
||||||
|
if(is_string($param)) {
|
||||||
|
if(strtolower($param) === 'desc') {
|
||||||
|
${"param_$i"} = SORT_DESC;
|
||||||
|
} else if(strtolower($param) === 'asc') {
|
||||||
|
${"param_$i"} = SORT_ASC;
|
||||||
|
} else {
|
||||||
|
${"param_$i"} = array();
|
||||||
|
foreach($array as $index => $row) {
|
||||||
|
${"param_$i"}[$index] = is_array($row) ? str::lower($row[$param]) : str::lower($row->$param());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
${"param_$i"} = $args[$i];
|
||||||
|
}
|
||||||
|
$params[] = &${"param_$i"};
|
||||||
|
}
|
||||||
|
|
||||||
|
$params[] = &$array;
|
||||||
|
|
||||||
|
call_user_func_array('array_multisort', $params);
|
||||||
|
|
||||||
|
$collection->data = $array;
|
||||||
|
|
||||||
|
return $collection;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add pagination
|
||||||
|
*
|
||||||
|
* @param int $limit the number of items per page
|
||||||
|
* @param array $options and optional array with options for the pagination class
|
||||||
|
* @return object a sliced set of data
|
||||||
|
*/
|
||||||
|
public function paginate($limit, $options = array()) {
|
||||||
|
|
||||||
|
if(is_a($limit, 'Pagination')) {
|
||||||
|
$this->pagination = $limit;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
$pagination = new Pagination($this->count(), $limit, $options);
|
||||||
|
$pages = $this->slice($pagination->offset(), $pagination->limit());
|
||||||
|
$pages->pagination = $pagination;
|
||||||
|
|
||||||
|
return $pages;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the previously added pagination object
|
||||||
|
*
|
||||||
|
* @return object
|
||||||
|
*/
|
||||||
|
public function pagination() {
|
||||||
|
return $this->pagination;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Map a function to each item in the collection
|
||||||
|
*
|
||||||
|
* @param function $callback
|
||||||
|
* @return Collection
|
||||||
|
*/
|
||||||
|
public function map($callback) {
|
||||||
|
$this->data = array_map($callback, $this->data);
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extracts all values for a single field into
|
||||||
|
* a new array
|
||||||
|
*
|
||||||
|
* @param string $field
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function pluck($field, $split = null, $unique = false) {
|
||||||
|
|
||||||
|
$result = array();
|
||||||
|
|
||||||
|
foreach($this->data as $item) {
|
||||||
|
$row = $this->extractValue($item, $field);
|
||||||
|
|
||||||
|
if($split) {
|
||||||
|
$result = array_merge($result, str::split($row, $split));
|
||||||
|
} else {
|
||||||
|
$result[] = $row;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if($unique) {
|
||||||
|
$result = array_unique($result);
|
||||||
|
}
|
||||||
|
|
||||||
|
return array_values($result);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Groups the collection by a given callback
|
||||||
|
*
|
||||||
|
* @param callable $callback
|
||||||
|
* @return object A new collection with an item for each group and a subcollection in each group
|
||||||
|
*/
|
||||||
|
public function group($callback) {
|
||||||
|
|
||||||
|
if (!is_callable($callback)) throw new Exception($callback . ' is not callable. Did you mean to use groupBy()?');
|
||||||
|
|
||||||
|
$groups = array();
|
||||||
|
|
||||||
|
foreach($this->data as $key => $item) {
|
||||||
|
|
||||||
|
// get the value to group by
|
||||||
|
$value = call_user_func($callback, $item);
|
||||||
|
|
||||||
|
// make sure that there's always a proper value to group by
|
||||||
|
if(!$value) throw new Exception('Invalid grouping value for key: ' . $key);
|
||||||
|
|
||||||
|
// make sure we have a proper key for each group
|
||||||
|
if(is_array($value)) {
|
||||||
|
throw new Exception('You cannot group by arrays or objects');
|
||||||
|
} else if(is_object($value)) {
|
||||||
|
if(!method_exists($value, '__toString')) {
|
||||||
|
throw new Exception('You cannot group by arrays or objects');
|
||||||
|
} else {
|
||||||
|
$value = (string)$value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!isset($groups[$value])) {
|
||||||
|
// create a new entry for the group if it does not exist yet
|
||||||
|
$groups[$value] = new static(array($key => $item));
|
||||||
|
} else {
|
||||||
|
// add the item to an existing group
|
||||||
|
$groups[$value]->set($key, $item);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Collection($groups);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Groups the collection by a given field
|
||||||
|
*
|
||||||
|
* @param string $field
|
||||||
|
* @return object A new collection with an item for each group and a subcollection in each group
|
||||||
|
*/
|
||||||
|
public function groupBy($field, $i = true) {
|
||||||
|
|
||||||
|
if (!is_string($field)) throw new Exception('Cannot group by non-string values. Did you mean to call group()?');
|
||||||
|
|
||||||
|
return $this->group(function($item) use ($field, $i) {
|
||||||
|
|
||||||
|
$value = $this->extractValue($item, $field);
|
||||||
|
|
||||||
|
// ignore upper/lowercase for group names
|
||||||
|
return ($i == true) ? str::lower($value) : $value;
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public function set($key, $value) {
|
||||||
|
if(is_array($key)) {
|
||||||
|
$this->data = array_merge($this->data, $key);
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
$this->data[$key] = $value;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __set($key, $value) {
|
||||||
|
$this->set($key, $value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function get($key, $default = null) {
|
||||||
|
if(isset($this->data[$key])) {
|
||||||
|
return $this->data[$key];
|
||||||
|
} else {
|
||||||
|
$lowerkeys = array_change_key_case($this->data, CASE_LOWER);
|
||||||
|
if(isset($lowerkeys[strtolower($key)])) {
|
||||||
|
return $lowerkeys[$key];
|
||||||
|
} else {
|
||||||
|
return $default;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __get($key) {
|
||||||
|
return $this->get($key);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __call($key, $arguments) {
|
||||||
|
return $this->get($key);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Makes it possible to echo the entire object
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function __toString() {
|
||||||
|
return implode('<br />', array_map(function($item) {
|
||||||
|
return (string)$item;
|
||||||
|
}, $this->data));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add all available collection filters
|
||||||
|
* Those can be extended by creating your own:
|
||||||
|
* collection::$filters['your operator'] = function($collection, $field, $value, $split = false) {
|
||||||
|
* // your filter code
|
||||||
|
* };
|
||||||
|
*/
|
||||||
|
|
||||||
|
// take all matching elements
|
||||||
|
collection::$filters['=='] = function($collection, $field, $value, $split = false) {
|
||||||
|
|
||||||
|
foreach($collection->data as $key => $item) {
|
||||||
|
|
||||||
|
if($split) {
|
||||||
|
$values = str::split((string)collection::extractValue($item, $field), $split);
|
||||||
|
if(!in_array($value, $values)) unset($collection->$key);
|
||||||
|
} else if(collection::extractValue($item, $field) != $value) {
|
||||||
|
unset($collection->$key);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return $collection;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
// take all elements that won't match
|
||||||
|
collection::$filters['!='] = function($collection, $field, $value, $split = false) {
|
||||||
|
|
||||||
|
foreach($collection->data as $key => $item) {
|
||||||
|
if($split) {
|
||||||
|
$values = str::split((string)collection::extractValue($item, $field), $split);
|
||||||
|
if(in_array($value, $values)) unset($collection->$key);
|
||||||
|
} else if(collection::extractValue($item, $field) == $value) {
|
||||||
|
unset($collection->$key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $collection;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
// take all elements that partly match
|
||||||
|
collection::$filters['*='] = function($collection, $field, $value, $split = false) {
|
||||||
|
|
||||||
|
foreach($collection->data as $key => $item) {
|
||||||
|
if($split) {
|
||||||
|
$values = str::split((string)collection::extractValue($item, $field), $split);
|
||||||
|
foreach($values as $val) {
|
||||||
|
if(strpos($val, $value) === false) {
|
||||||
|
unset($collection->$key);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if(strpos(collection::extractValue($item, $field), $value) === false) {
|
||||||
|
unset($collection->$key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $collection;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
// greater than
|
||||||
|
collection::$filters['>'] = function($collection, $field, $value) {
|
||||||
|
|
||||||
|
foreach($collection->data as $key => $item) {
|
||||||
|
if(collection::extractValue($item, $field) > $value) continue;
|
||||||
|
unset($collection->$key);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $collection;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
// greater and equals
|
||||||
|
collection::$filters['>='] = function($collection, $field, $value) {
|
||||||
|
|
||||||
|
foreach($collection->data as $key => $item) {
|
||||||
|
if(collection::extractValue($item, $field) >= $value) continue;
|
||||||
|
unset($collection->$key);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $collection;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
// less than
|
||||||
|
collection::$filters['<'] = function($collection, $field, $value) {
|
||||||
|
|
||||||
|
foreach($collection->data as $key => $item) {
|
||||||
|
if(collection::extractValue($item, $field) < $value) continue;
|
||||||
|
unset($collection->$key);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $collection;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
// less and equals
|
||||||
|
collection::$filters['<='] = function($collection, $field, $value) {
|
||||||
|
|
||||||
|
foreach($collection->data as $key => $item) {
|
||||||
|
if(collection::extractValue($item, $field) <= $value) continue;
|
||||||
|
unset($collection->$key);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $collection;
|
||||||
|
|
||||||
|
};
|
168
kirby/toolkit/lib/cookie.php
Normal file
168
kirby/toolkit/lib/cookie.php
Normal file
|
@ -0,0 +1,168 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cookie
|
||||||
|
*
|
||||||
|
* This class makes cookie handling easy
|
||||||
|
*
|
||||||
|
* @package Kirby Toolkit
|
||||||
|
* @author Bastian Allgeier <bastian@getkirby.com>
|
||||||
|
* @link http://getkirby.com
|
||||||
|
* @copyright Bastian Allgeier
|
||||||
|
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||||
|
*/
|
||||||
|
class Cookie {
|
||||||
|
|
||||||
|
// configuration
|
||||||
|
public static $salt = 'KirbyToolkitCookieSalt';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set a new cookie
|
||||||
|
*
|
||||||
|
* <code>
|
||||||
|
*
|
||||||
|
* cookie::set('mycookie', 'hello', 60);
|
||||||
|
* // expires in 1 hour
|
||||||
|
*
|
||||||
|
* </code>
|
||||||
|
*
|
||||||
|
* @param string $key The name of the cookie
|
||||||
|
* @param string $value The cookie content
|
||||||
|
* @param int $lifetime The number of minutes until the cookie expires
|
||||||
|
* @param string $path The path on the server to set the cookie for
|
||||||
|
* @param string $domain the domain
|
||||||
|
* @param boolean $secure only sets the cookie over https
|
||||||
|
* @param boolean $httpOnly avoids the cookie to be accessed via javascript
|
||||||
|
* @return boolean true: the cookie has been created, false: cookie creation failed
|
||||||
|
*/
|
||||||
|
public static function set($key, $value, $lifetime = 0, $path = '/', $domain = null, $secure = false, $httpOnly = true) {
|
||||||
|
|
||||||
|
// convert array values to json
|
||||||
|
if(is_array($value)) $value = a::json($value);
|
||||||
|
|
||||||
|
// hash the value
|
||||||
|
$value = static::hash($value) . '+' . $value;
|
||||||
|
|
||||||
|
// store that thing in the cookie global
|
||||||
|
$_COOKIE[$key] = $value;
|
||||||
|
|
||||||
|
// store the cookie
|
||||||
|
return setcookie($key, $value, static::lifetime($lifetime), $path, $domain, $secure, $httpOnly);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculates the lifetime for a cookie
|
||||||
|
*
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
public static function lifetime($minutes) {
|
||||||
|
return $minutes > 0 ? (time() + ($minutes * 60)) : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stores a cookie forever
|
||||||
|
*
|
||||||
|
* <code>
|
||||||
|
*
|
||||||
|
* cookie::forever('mycookie', 'hello');
|
||||||
|
* // never expires
|
||||||
|
*
|
||||||
|
* </code>
|
||||||
|
*
|
||||||
|
* @param string $key The name of the cookie
|
||||||
|
* @param string $value The cookie content
|
||||||
|
* @param string $path The path on the server to set the cookie for
|
||||||
|
* @param string $domain the domain
|
||||||
|
* @param boolean $secure only sets the cookie over https
|
||||||
|
* @return boolean true: the cookie has been created, false: cookie creation failed
|
||||||
|
*/
|
||||||
|
public static function forever($key, $value, $path = '/', $domain = null, $secure = false) {
|
||||||
|
return static::set($key, $value, 2628000, $path, $domain, $secure);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a cookie value
|
||||||
|
*
|
||||||
|
* <code>
|
||||||
|
*
|
||||||
|
* cookie::get('mycookie', 'peter');
|
||||||
|
* // sample output: 'hello' or if the cookie is not set 'peter'
|
||||||
|
*
|
||||||
|
* </code>
|
||||||
|
*
|
||||||
|
* @param string $key The name of the cookie
|
||||||
|
* @param string $default The default value, which should be returned if the cookie has not been found
|
||||||
|
* @return mixed The found value
|
||||||
|
*/
|
||||||
|
public static function get($key = null, $default = null) {
|
||||||
|
if(is_null($key)) return $_COOKIE;
|
||||||
|
$value = isset($_COOKIE[$key]) ? $_COOKIE[$key] : null;
|
||||||
|
return empty($value) ? $default : static::parse($value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if a cookie exists
|
||||||
|
*
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
public static function exists($key) {
|
||||||
|
return !is_null(static::get($key));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a hash for the cookie value
|
||||||
|
* salted with the secret cookie salt string from the defaults
|
||||||
|
*
|
||||||
|
* @param string $value
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
protected static function hash($value) {
|
||||||
|
return sha1($value . static::$salt);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses the hashed value from a cookie
|
||||||
|
* and tries to extract the value
|
||||||
|
*
|
||||||
|
* @param string $string
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
protected static function parse($string) {
|
||||||
|
|
||||||
|
// extract hash and value
|
||||||
|
$parts = str::split($string, '+');
|
||||||
|
$hash = a::first($parts);
|
||||||
|
$value = a::last($parts);
|
||||||
|
|
||||||
|
// if the hash or the value is missing at all return null
|
||||||
|
if(empty($hash) || empty($value)) return null;
|
||||||
|
|
||||||
|
// compare the extracted hash with the hashed value
|
||||||
|
if($hash !== static::hash($value)) return null;
|
||||||
|
|
||||||
|
return $value;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove a cookie
|
||||||
|
*
|
||||||
|
* <code>
|
||||||
|
*
|
||||||
|
* cookie::remove('mycookie');
|
||||||
|
* // mycookie is now gone
|
||||||
|
*
|
||||||
|
* </code>
|
||||||
|
*
|
||||||
|
* @param string $key The name of the cookie
|
||||||
|
* @return mixed true: the cookie has been removed, false: the cookie could not be removed
|
||||||
|
*/
|
||||||
|
public static function remove($key) {
|
||||||
|
if(isset($_COOKIE[$key])) {
|
||||||
|
unset($_COOKIE[$key]);
|
||||||
|
return setcookie($key, '', time() - 3600, '/');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
86
kirby/toolkit/lib/crypt.php
Normal file
86
kirby/toolkit/lib/crypt.php
Normal file
|
@ -0,0 +1,86 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Crypt
|
||||||
|
*
|
||||||
|
* Encodes and decodes strings with different encryption methods
|
||||||
|
*
|
||||||
|
* @package Kirby Toolkit
|
||||||
|
* @author Bastian Allgeier <bastian@getkirby.com>, Arno Richter <oelna@oelna.de>
|
||||||
|
* @link http://getkirby.com
|
||||||
|
* @copyright Bastian Allgeier, Arno Richter
|
||||||
|
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||||
|
*/
|
||||||
|
class Crypt {
|
||||||
|
|
||||||
|
// encryption salt - should be changed
|
||||||
|
public static $salt = '-';
|
||||||
|
|
||||||
|
// all available encryption modes
|
||||||
|
public static $encryption = array(
|
||||||
|
'rijndael-128',
|
||||||
|
'rijndael-256',
|
||||||
|
'blowfish',
|
||||||
|
'twofish',
|
||||||
|
'des'
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encodes a string
|
||||||
|
*
|
||||||
|
* @param string $text
|
||||||
|
* @param string $key An optional encryption key
|
||||||
|
* @param string $mode Check out the $encryption array for available modes
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public static function encode($text, $key = null, $mode = 'blowfish') {
|
||||||
|
|
||||||
|
// check for mcrypt support
|
||||||
|
if(!function_exists('mcrypt_get_iv_size')) {
|
||||||
|
throw new Exception('The mcrypt extension is missing');
|
||||||
|
}
|
||||||
|
|
||||||
|
// all modes are lowercase so we try to avoid errors here
|
||||||
|
$mode = strtolower($mode);
|
||||||
|
|
||||||
|
// check for a valid encryption mode
|
||||||
|
if(!in_array($mode, static::$encryption)) throw new Exception('Invalid encryption mode: ' . $mode);
|
||||||
|
|
||||||
|
$size = mcrypt_get_iv_size($mode, MCRYPT_MODE_ECB);
|
||||||
|
$iv = mcrypt_create_iv($size, MCRYPT_RAND);
|
||||||
|
$result = mcrypt_encrypt($mode, static::$salt . $key, $text, MCRYPT_MODE_ECB, $iv);
|
||||||
|
|
||||||
|
return trim($result);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decodes a string
|
||||||
|
*
|
||||||
|
* @param string $text
|
||||||
|
* @param string $key An optional encryption key
|
||||||
|
* @param string $mode Check out the $encryption array for available modes
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public static function decode($text, $key = null, $mode = 'blowfish') {
|
||||||
|
|
||||||
|
// check for mcrypt support
|
||||||
|
if(!function_exists('mcrypt_get_iv_size')) {
|
||||||
|
throw new Exception('The mcrypt extension is missing');
|
||||||
|
}
|
||||||
|
|
||||||
|
// all modes are lowercase so we try to avoid errors here
|
||||||
|
$mode = strtolower($mode);
|
||||||
|
|
||||||
|
// check for a valid encryption mode
|
||||||
|
if(!in_array($mode, static::$encryption)) throw new Exception('Invalid encryption mode: ' . $mode);
|
||||||
|
|
||||||
|
$size = mcrypt_get_iv_size($mode, MCRYPT_MODE_ECB);
|
||||||
|
$iv = mcrypt_create_iv($size, MCRYPT_RAND);
|
||||||
|
$result = mcrypt_decrypt($mode, static::$salt . $key, $text, MCRYPT_MODE_ECB, $iv);
|
||||||
|
|
||||||
|
return trim($result);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
175
kirby/toolkit/lib/data.php
Normal file
175
kirby/toolkit/lib/data.php
Normal file
|
@ -0,0 +1,175 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Data
|
||||||
|
*
|
||||||
|
* Universal data writer/reader/decoder/encoder for
|
||||||
|
* json, yaml and structured kirby content
|
||||||
|
*
|
||||||
|
* @package Kirby Toolkit
|
||||||
|
* @author Bastian Allgeier <bastian@getkirby.com>
|
||||||
|
* @link http://getkirby.com
|
||||||
|
* @copyright Bastian Allgeier
|
||||||
|
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||||
|
*/
|
||||||
|
class Data {
|
||||||
|
|
||||||
|
const ERROR_INVALID_ADAPTER = 0;
|
||||||
|
|
||||||
|
public static $adapters = array();
|
||||||
|
|
||||||
|
public static function adapter($type) {
|
||||||
|
|
||||||
|
if(isset(static::$adapters[$type])) return static::$adapters[$type];
|
||||||
|
|
||||||
|
foreach(static::$adapters as $adapter) {
|
||||||
|
if(is_array($adapter['extension']) && in_array($type, $adapter['extension'])) {
|
||||||
|
return $adapter;
|
||||||
|
} else if($adapter['extension'] == $type) {
|
||||||
|
return $adapter;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Error('Invalid adapter type', static::ERROR_INVALID_ADAPTER);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function encode($data, $type) {
|
||||||
|
$adapter = static::adapter($type);
|
||||||
|
return call_user_func($adapter['encode'], $data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function decode($data, $type) {
|
||||||
|
$adapter = static::adapter($type);
|
||||||
|
return call_user_func($adapter['decode'], $data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function read($file, $type = null) {
|
||||||
|
|
||||||
|
// type autodetection
|
||||||
|
if(is_null($type)) $type = f::extension($file);
|
||||||
|
|
||||||
|
// get the adapter
|
||||||
|
$adapter = static::adapter($type);
|
||||||
|
|
||||||
|
if(isset($adapter['read'])) {
|
||||||
|
return call($adapter['read'], $file);
|
||||||
|
} else {
|
||||||
|
return data::decode(f::read($file), $type);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function write($file, $data, $type = null) {
|
||||||
|
// type autodetection
|
||||||
|
if(is_null($type)) $type = f::extension($file);
|
||||||
|
return f::write($file, data::encode($data, $type));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Json adapter
|
||||||
|
*/
|
||||||
|
data::$adapters['json'] = array(
|
||||||
|
'extension' => 'json',
|
||||||
|
'encode' => function($data) {
|
||||||
|
return json_encode($data);
|
||||||
|
},
|
||||||
|
'decode' => function($string) {
|
||||||
|
return json_decode($string, true);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Kirby data adapter
|
||||||
|
*/
|
||||||
|
data::$adapters['kd'] = array(
|
||||||
|
'extension' => array('md', 'txt'),
|
||||||
|
'encode' => function($data) {
|
||||||
|
|
||||||
|
$result = array();
|
||||||
|
foreach($data AS $key => $value) {
|
||||||
|
$key = str::ucfirst(str::slug($key));
|
||||||
|
|
||||||
|
if(empty($key) || is_null($value)) continue;
|
||||||
|
|
||||||
|
// avoid problems with arrays
|
||||||
|
if(is_array($value)) {
|
||||||
|
$value = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
// escape accidental dividers within a field
|
||||||
|
$value = preg_replace('!(\n|^)----(.*?\R*)!', "$1\\----$2", $value);
|
||||||
|
|
||||||
|
// multi-line content
|
||||||
|
if(preg_match('!\R!', $value, $matches)) {
|
||||||
|
$result[$key] = $key . ": \n\n" . trim($value);
|
||||||
|
// single-line content
|
||||||
|
} else {
|
||||||
|
$result[$key] = $key . ': ' . trim($value);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
return implode("\n\n----\n\n", $result);
|
||||||
|
|
||||||
|
},
|
||||||
|
'decode' => function($string) {
|
||||||
|
|
||||||
|
// remove BOM
|
||||||
|
$string = str_replace(BOM, '', $string);
|
||||||
|
// explode all fields by the line separator
|
||||||
|
$fields = preg_split('!\n----\s*\n*!', $string);
|
||||||
|
// start the data array
|
||||||
|
$data = array();
|
||||||
|
|
||||||
|
// loop through all fields and add them to the content
|
||||||
|
foreach($fields as $field) {
|
||||||
|
$pos = strpos($field, ':');
|
||||||
|
$key = str_replace(array('-', ' '), '_', strtolower(trim(substr($field, 0, $pos))));
|
||||||
|
|
||||||
|
// Don't add fields with empty keys
|
||||||
|
if(empty($key)) continue;
|
||||||
|
$data[$key] = trim(substr($field, $pos+1));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return $data;
|
||||||
|
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PHP serializer adapter
|
||||||
|
*/
|
||||||
|
data::$adapters['php'] = array(
|
||||||
|
'extension' => array('php'),
|
||||||
|
'encode' => function($array) {
|
||||||
|
return '<?php ' . PHP_EOL . PHP_EOL . 'return ' . var_export($array, true) . PHP_EOL . PHP_EOL . '?>';
|
||||||
|
},
|
||||||
|
'decode' => function() {
|
||||||
|
throw new Error('Decoding PHP strings is not supported');
|
||||||
|
},
|
||||||
|
'read' => function($file) {
|
||||||
|
$array = require $file;
|
||||||
|
return $array;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* YAML adapter
|
||||||
|
*/
|
||||||
|
data::$adapters['yaml'] = array(
|
||||||
|
'extension' => array('yaml', 'yml'),
|
||||||
|
'encode' => function($data) {
|
||||||
|
return yaml::encode($data);
|
||||||
|
},
|
||||||
|
'decode' => function($string) {
|
||||||
|
return yaml::decode($string);
|
||||||
|
}
|
||||||
|
);
|
496
kirby/toolkit/lib/database.php
Normal file
496
kirby/toolkit/lib/database.php
Normal file
|
@ -0,0 +1,496 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Database
|
||||||
|
*
|
||||||
|
* The ingenius Kirby Database class
|
||||||
|
*
|
||||||
|
* @package Kirby Toolkit
|
||||||
|
* @author Bastian Allgeier <bastian@getkirby.com>
|
||||||
|
* @link http://getkirby.com
|
||||||
|
* @copyright Bastian Allgeier
|
||||||
|
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||||
|
*/
|
||||||
|
class Database {
|
||||||
|
|
||||||
|
public static $connectors = array();
|
||||||
|
|
||||||
|
// a global array of started connections
|
||||||
|
public static $connections = array();
|
||||||
|
|
||||||
|
// the established connection
|
||||||
|
protected $connection;
|
||||||
|
|
||||||
|
// dsn
|
||||||
|
protected $dsn;
|
||||||
|
|
||||||
|
// the database type (mysql, sqlite)
|
||||||
|
protected $type;
|
||||||
|
|
||||||
|
// the connection id
|
||||||
|
protected $id;
|
||||||
|
|
||||||
|
// the optional prefix for table names
|
||||||
|
protected $prefix;
|
||||||
|
|
||||||
|
// the PDO query statement
|
||||||
|
protected $statement;
|
||||||
|
|
||||||
|
// whitelists for tables and their columns
|
||||||
|
protected $tableWhitelist;
|
||||||
|
protected $columnWhitelist = array();
|
||||||
|
|
||||||
|
// the number of affected rows for the last query
|
||||||
|
protected $affected;
|
||||||
|
|
||||||
|
// the last insert id
|
||||||
|
protected $lastId;
|
||||||
|
|
||||||
|
// the last query
|
||||||
|
protected $lastQuery;
|
||||||
|
|
||||||
|
// the last result set
|
||||||
|
protected $lastResult;
|
||||||
|
|
||||||
|
// the last error
|
||||||
|
protected $lastError;
|
||||||
|
|
||||||
|
// set to true to throw exceptions on failed queries
|
||||||
|
protected $fail = false;
|
||||||
|
|
||||||
|
// an array with all queries which are being made
|
||||||
|
protected $trace = array();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*/
|
||||||
|
public function __construct($params = null) {
|
||||||
|
$this->connect($params);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns one of the started instance
|
||||||
|
*
|
||||||
|
* @param string $id
|
||||||
|
* @return object
|
||||||
|
*/
|
||||||
|
public static function instance($id = null) {
|
||||||
|
return (is_null($id)) ? a::last(static::$connections) : a::get(static::$connections, $id);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns all started instances
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public static function instances() {
|
||||||
|
return static::$connections;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Connects to a database
|
||||||
|
*
|
||||||
|
* @param mixed $params This can either be a config key or an array of parameters for the connection
|
||||||
|
* @return object
|
||||||
|
*/
|
||||||
|
public function connect($params = null) {
|
||||||
|
|
||||||
|
$defaults = array(
|
||||||
|
'database' => null,
|
||||||
|
'type' => 'mysql',
|
||||||
|
'prefix' => null,
|
||||||
|
'user' => null,
|
||||||
|
'password' => null,
|
||||||
|
'id' => uniqid()
|
||||||
|
);
|
||||||
|
|
||||||
|
$options = array_merge($defaults, $params);
|
||||||
|
|
||||||
|
// store the database information
|
||||||
|
$this->database = $options['database'];
|
||||||
|
$this->type = $options['type'];
|
||||||
|
$this->prefix = $options['prefix'];
|
||||||
|
$this->id = $options['id'];
|
||||||
|
|
||||||
|
if(!isset(static::$connectors[$this->type])) {
|
||||||
|
throw new Exception('Invalid database connector: ' . $this->type);
|
||||||
|
}
|
||||||
|
|
||||||
|
// fetch the dsn and store it
|
||||||
|
$this->dsn = call_user_func(static::$connectors[$this->type], $options);
|
||||||
|
|
||||||
|
// try to connect
|
||||||
|
$this->connection = new PDO($this->dsn, $options['user'], $options['password']);
|
||||||
|
$this->connection->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
||||||
|
$this->connection->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
|
||||||
|
|
||||||
|
// store the connection
|
||||||
|
static::$connections[$this->id] = $this;
|
||||||
|
|
||||||
|
// return the connection
|
||||||
|
return $this->connection;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the currently active connection
|
||||||
|
*
|
||||||
|
* @return object
|
||||||
|
*/
|
||||||
|
public function connection() {
|
||||||
|
return $this->connection;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the exception mode for the next query
|
||||||
|
*
|
||||||
|
* @param boolean $fail
|
||||||
|
*/
|
||||||
|
public function fail($fail = true) {
|
||||||
|
$this->fail = $fail;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the used database type
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function type() {
|
||||||
|
return $this->type;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the used table name prefix
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function prefix() {
|
||||||
|
return $this->prefix;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Escapes a value to be used for a safe query
|
||||||
|
* NOTE: Prepared statements using bound parameters are more secure and solid
|
||||||
|
*
|
||||||
|
* @param string $value
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function escape($value) {
|
||||||
|
return substr($this->connection()->quote($value), 1, -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a value to the db trace and also returns the entire trace if nothing is specified
|
||||||
|
*
|
||||||
|
* @param array $data
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function trace($data = null) {
|
||||||
|
if(is_null($data)) return $this->trace;
|
||||||
|
$this->trace[] = $data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the number of affected rows for the last query
|
||||||
|
*
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
public function affected() {
|
||||||
|
return $this->affected;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the last id if available
|
||||||
|
*
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
public function lastId() {
|
||||||
|
return $this->lastId;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the last query
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function lastQuery() {
|
||||||
|
return $this->lastQuery;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the last set of results
|
||||||
|
*
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function lastResult() {
|
||||||
|
return $this->lastResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the last db error (exception object)
|
||||||
|
*
|
||||||
|
* @return object
|
||||||
|
*/
|
||||||
|
public function lastError() {
|
||||||
|
return $this->lastError;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Private method to execute database queries.
|
||||||
|
* This is used by the query() and execute() methods
|
||||||
|
*
|
||||||
|
* @param string $query
|
||||||
|
* @param array $bindings
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
protected function hit($query, $bindings = array()) {
|
||||||
|
|
||||||
|
// try to prepare and execute the sql
|
||||||
|
try {
|
||||||
|
|
||||||
|
$this->statement = $this->connection->prepare($query);
|
||||||
|
$this->statement->execute($bindings);
|
||||||
|
|
||||||
|
$this->affected = $this->statement->rowCount();
|
||||||
|
$this->lastId = $this->connection->lastInsertId();
|
||||||
|
$this->lastError = null;
|
||||||
|
|
||||||
|
// store the final sql to add it to the trace later
|
||||||
|
$this->lastQuery = $this->statement->queryString;
|
||||||
|
|
||||||
|
} catch(\Exception $e) {
|
||||||
|
|
||||||
|
// store the error
|
||||||
|
$this->affected = 0;
|
||||||
|
$this->lastError = $e;
|
||||||
|
$this->lastId = null;
|
||||||
|
$this->lastQuery = $query;
|
||||||
|
|
||||||
|
// only throw the extension if failing is allowed
|
||||||
|
if($this->fail) throw $e;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// add a new entry to the singleton trace array
|
||||||
|
$this->trace(array(
|
||||||
|
'query' => $this->lastQuery,
|
||||||
|
'bindings' => $bindings,
|
||||||
|
'error' => $this->lastError
|
||||||
|
));
|
||||||
|
|
||||||
|
// reset some stuff
|
||||||
|
$this->fail = false;
|
||||||
|
|
||||||
|
// return true or false on success or failure
|
||||||
|
return is_null($this->lastError);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Exectues a sql query, which is expected to return a set of results
|
||||||
|
*
|
||||||
|
* @param string $query
|
||||||
|
* @param array $bindings
|
||||||
|
* @param array $params
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function query($query, $bindings = array(), $params = array()) {
|
||||||
|
|
||||||
|
$defaults = array(
|
||||||
|
'flag' => null,
|
||||||
|
'method' => 'fetchAll',
|
||||||
|
'fetch' => 'Obj',
|
||||||
|
'iterator' => 'Collection',
|
||||||
|
);
|
||||||
|
|
||||||
|
$options = array_merge($defaults, $params);
|
||||||
|
|
||||||
|
if(!$this->hit($query, $bindings)) return false;
|
||||||
|
|
||||||
|
// define the default flag for the fetch method
|
||||||
|
$flags = $options['fetch'] == 'array' ? PDO::FETCH_ASSOC : PDO::FETCH_CLASS|PDO::FETCH_PROPS_LATE;
|
||||||
|
|
||||||
|
// add optional flags
|
||||||
|
if(!empty($options['flag'])) $flags |= $options['flag'];
|
||||||
|
|
||||||
|
// set the fetch mode
|
||||||
|
if($options['fetch'] == 'array') {
|
||||||
|
$this->statement->setFetchMode($flags);
|
||||||
|
} else {
|
||||||
|
$this->statement->setFetchMode($flags, $options['fetch']);
|
||||||
|
}
|
||||||
|
|
||||||
|
// fetch that stuff
|
||||||
|
$results = $this->statement->{$options['method']}();
|
||||||
|
|
||||||
|
if($options['iterator'] == 'array') return $this->lastResult = $results;
|
||||||
|
return $this->lastResult = new $options['iterator']($results);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Executes a sql query, which is expected to not return a set of results
|
||||||
|
*
|
||||||
|
* @param string $query
|
||||||
|
* @param array $bindings
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
public function execute($query, $bindings = array()) {
|
||||||
|
return $this->lastResult = $this->hit($query, $bindings);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the current table, which should be queried
|
||||||
|
*
|
||||||
|
* @param string $table
|
||||||
|
* @return object Returns a Query object, which can be used to build a full query for that table
|
||||||
|
*/
|
||||||
|
public function table($table) {
|
||||||
|
return new Database\Query($this, $this->prefix() . $table);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if a table exists in the current database
|
||||||
|
*
|
||||||
|
* @param string $table
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
public function validateTable($table) {
|
||||||
|
if(!$this->tableWhitelist) {
|
||||||
|
// Get the table whitelist from the database
|
||||||
|
$sql = new SQL($this);
|
||||||
|
$query = $sql->tableList($this->database);
|
||||||
|
$results = $this->query($query, $sql->bindings($query));
|
||||||
|
|
||||||
|
if($results) {
|
||||||
|
$this->tableWhitelist = $results->pluck('name');
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return in_array($table, $this->tableWhitelist);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if a column exists in a specified table
|
||||||
|
*
|
||||||
|
* @param string $table
|
||||||
|
* @param string $column
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
public function validateColumn($table, $column) {
|
||||||
|
if(!isset($this->columnWhitelist[$table])) {
|
||||||
|
if(!$this->validateTable($table)) {
|
||||||
|
$this->columnWhitelist[$table] = array();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the column whitelist from the database
|
||||||
|
$sql = new SQL($this);
|
||||||
|
$query = $sql->columnList($this->database, $table);
|
||||||
|
$results = $this->query($query, $sql->bindings($query));
|
||||||
|
|
||||||
|
if($results) {
|
||||||
|
$this->columnWhitelist[$table] = $results->pluck('name');
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return in_array($column, $this->columnWhitelist[$table]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new table
|
||||||
|
*
|
||||||
|
* @param string $table
|
||||||
|
* @param array $columns
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
public function createTable($table, $columns = array()) {
|
||||||
|
$sql = new SQL($this);
|
||||||
|
$query = $sql->createTable($table, $columns);
|
||||||
|
$queries = str::split($query, ';');
|
||||||
|
|
||||||
|
foreach($queries as $query) {
|
||||||
|
$query = trim($query);
|
||||||
|
|
||||||
|
if(!$this->execute($query, $sql->bindings($query))) return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Drops a table
|
||||||
|
*
|
||||||
|
* @param string $table
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
public function dropTable($table) {
|
||||||
|
$sql = new SQL($this);
|
||||||
|
$query = $sql->dropTable($table);
|
||||||
|
return $this->execute($query, $sql->bindings($query));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Magic way to start queries for tables by
|
||||||
|
* using a method named like the table.
|
||||||
|
* I.e. $db->users()->all()
|
||||||
|
*/
|
||||||
|
public function __call($method, $arguments = null) {
|
||||||
|
return $this->table($method);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* MySQL database connector
|
||||||
|
*/
|
||||||
|
database::$connectors['mysql'] = function($params) {
|
||||||
|
|
||||||
|
if(!isset($params['host']) && !isset($params['socket'])) {
|
||||||
|
throw new Error('The mysql connection requires either a "host" or a "socket" parameter');
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!isset($params['database'])) {
|
||||||
|
throw new Error('The mysql connection requires a "database" parameter');
|
||||||
|
}
|
||||||
|
|
||||||
|
$parts = array();
|
||||||
|
|
||||||
|
if(!empty($params['host'])) {
|
||||||
|
$parts[] = 'host=' . $params['host'];
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!empty($params['port'])) {
|
||||||
|
$parts[] = 'port=' . $params['port'];
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!empty($params['socket'])) {
|
||||||
|
$parts[] = 'unix_socket=' . $params['socket'];
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!empty($params['database'])) {
|
||||||
|
$parts[] = 'dbname=' . $params['database'];
|
||||||
|
}
|
||||||
|
|
||||||
|
$parts[] = 'charset=' . a::get($params, 'charset', 'utf8');
|
||||||
|
|
||||||
|
return 'mysql:' . implode(';', $parts);
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SQLite database connector
|
||||||
|
*/
|
||||||
|
database::$connectors['sqlite'] = function($params) {
|
||||||
|
if(!isset($params['database'])) throw new Error('The sqlite connection requires a "database" parameter');
|
||||||
|
return 'sqlite:' . $params['database'];
|
||||||
|
};
|
923
kirby/toolkit/lib/database/query.php
Normal file
923
kirby/toolkit/lib/database/query.php
Normal file
|
@ -0,0 +1,923 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Database;
|
||||||
|
|
||||||
|
use A;
|
||||||
|
use Error;
|
||||||
|
use Pagination;
|
||||||
|
use Str;
|
||||||
|
use Sql;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Database Query
|
||||||
|
*
|
||||||
|
* The query builder is used by the Database class
|
||||||
|
* to build SQL queries in a fluent, jquery-style way
|
||||||
|
*
|
||||||
|
* @package Kirby Toolkit
|
||||||
|
* @author Bastian Allgeier <bastian@getkirby.com>
|
||||||
|
* @link http://getkirby.com
|
||||||
|
* @copyright Bastian Allgeier
|
||||||
|
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||||
|
*/
|
||||||
|
class Query {
|
||||||
|
|
||||||
|
const ERROR_INVALID_QUERY_METHOD = 0;
|
||||||
|
|
||||||
|
protected $database = null;
|
||||||
|
|
||||||
|
// The object which should be fetched for each row
|
||||||
|
protected $fetch = 'Obj';
|
||||||
|
|
||||||
|
// The iterator class, which should be used for result sets
|
||||||
|
protected $iterator = 'Collection';
|
||||||
|
|
||||||
|
// An array of bindings for the final query
|
||||||
|
protected $bindings = array();
|
||||||
|
|
||||||
|
// The table name
|
||||||
|
protected $table;
|
||||||
|
|
||||||
|
// The name of the primary key column
|
||||||
|
protected $primaryKeyName = 'id';
|
||||||
|
|
||||||
|
// An array with additional join parameters
|
||||||
|
protected $join;
|
||||||
|
|
||||||
|
// A list of columns, which should be selected
|
||||||
|
protected $select;
|
||||||
|
|
||||||
|
// Boolean for distinct select clauses
|
||||||
|
protected $distinct;
|
||||||
|
|
||||||
|
// Boolean for if exceptions should be thrown on failing queries
|
||||||
|
protected $fail = false;
|
||||||
|
|
||||||
|
// A list of values for update and insert clauses
|
||||||
|
protected $values;
|
||||||
|
|
||||||
|
// WHERE clause
|
||||||
|
protected $where;
|
||||||
|
|
||||||
|
// GROUP BY clause
|
||||||
|
protected $group;
|
||||||
|
|
||||||
|
// HAVING clause
|
||||||
|
protected $having;
|
||||||
|
|
||||||
|
// ORDER BY clause
|
||||||
|
protected $order;
|
||||||
|
|
||||||
|
// The offset, which should be applied to the select query
|
||||||
|
protected $offset = 0;
|
||||||
|
|
||||||
|
// The limit, which should be applied to the select query
|
||||||
|
protected $limit;
|
||||||
|
|
||||||
|
// Boolean to enable query debugging
|
||||||
|
protected $debug = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*
|
||||||
|
* @param Database $database Database object
|
||||||
|
* @param string $table Optional name of the table, which should be queried
|
||||||
|
*/
|
||||||
|
public function __construct($database, $table) {
|
||||||
|
$this->database = $database;
|
||||||
|
|
||||||
|
$this->table($table);
|
||||||
|
if(!$this->table) throw new Error('Invalid table ' . $table);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reset the query class after each db hit
|
||||||
|
*/
|
||||||
|
protected function reset() {
|
||||||
|
$this->join = null;
|
||||||
|
$this->select = null;
|
||||||
|
$this->distinct = null;
|
||||||
|
$this->fail = false;
|
||||||
|
$this->values = null;
|
||||||
|
$this->where = null;
|
||||||
|
$this->group = null;
|
||||||
|
$this->having = null;
|
||||||
|
$this->order = null;
|
||||||
|
$this->offset = null;
|
||||||
|
$this->limit = null;
|
||||||
|
$this->debug = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enables query debugging.
|
||||||
|
* If enabled, the query will return an array with all important info about
|
||||||
|
* the query instead of actually executing the query and returning results
|
||||||
|
*
|
||||||
|
* @param boolean $debug
|
||||||
|
* @return object
|
||||||
|
*/
|
||||||
|
public function debug($debug = true) {
|
||||||
|
$this->debug = $debug;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enables distinct select clauses.
|
||||||
|
*
|
||||||
|
* @param boolean $distinct
|
||||||
|
* @return object
|
||||||
|
*/
|
||||||
|
public function distinct($distinct = true) {
|
||||||
|
$this->distinct = $distinct;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enables failing queries.
|
||||||
|
* If enabled queries will no longer fail silently but throw an exception
|
||||||
|
*
|
||||||
|
* @param boolean $fail
|
||||||
|
* @return object
|
||||||
|
*/
|
||||||
|
public function fail($fail = true) {
|
||||||
|
$this->fail = $fail;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the object class, which should be fetched
|
||||||
|
* Set this to array to get a simple array instead of an object
|
||||||
|
*
|
||||||
|
* @param string $fetch
|
||||||
|
* @return object
|
||||||
|
*/
|
||||||
|
public function fetch($fetch) {
|
||||||
|
if(!is_null($fetch)) $this->fetch = $fetch;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the iterator class, which should be used for multiple results
|
||||||
|
* Set this to array to get a simple array instead of an iterator object
|
||||||
|
*
|
||||||
|
* @param string $iterator
|
||||||
|
* @return object
|
||||||
|
*/
|
||||||
|
public function iterator($iterator) {
|
||||||
|
if(!is_null($iterator)) $this->iterator = $iterator;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the name of the table, which should be queried
|
||||||
|
*
|
||||||
|
* @param string $table
|
||||||
|
* @return object
|
||||||
|
*/
|
||||||
|
public function table($table) {
|
||||||
|
if(!is_null($table) && $this->database->validateTable($table)) $this->table = $table;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the name of the primary key column
|
||||||
|
*
|
||||||
|
* @param string $primaryKeyName
|
||||||
|
* @return object
|
||||||
|
*/
|
||||||
|
public function primaryKeyName($primaryKeyName) {
|
||||||
|
$this->primaryKeyName = $primaryKeyName;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the columns, which should be selected from the table
|
||||||
|
* By default all columns will be selected
|
||||||
|
*
|
||||||
|
* @param mixed $select Pass either a string of columns or an array
|
||||||
|
* @return object
|
||||||
|
*/
|
||||||
|
public function select($select) {
|
||||||
|
$this->select = $select;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a new join clause to the query
|
||||||
|
*
|
||||||
|
* @param string $table Name of the table, which should be joined
|
||||||
|
* @param string $on The on clause for this join
|
||||||
|
* @param string $type The join type. Uses an inner join by default
|
||||||
|
* @return object
|
||||||
|
*/
|
||||||
|
public function join($table, $on, $type = '') {
|
||||||
|
|
||||||
|
$join = array(
|
||||||
|
'table' => $table,
|
||||||
|
'on' => $on,
|
||||||
|
'type' => $type
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->join[] = $join;
|
||||||
|
return $this;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shortcut for creating a left join clause
|
||||||
|
*
|
||||||
|
* @param string $table Name of the table, which should be joined
|
||||||
|
* @param string $on The on clause for this join
|
||||||
|
* @return object
|
||||||
|
*/
|
||||||
|
public function leftJoin($table, $on) {
|
||||||
|
return $this->join($table, $on, 'left');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shortcut for creating a right join clause
|
||||||
|
*
|
||||||
|
* @param string $table Name of the table, which should be joined
|
||||||
|
* @param string $on The on clause for this join
|
||||||
|
* @return object
|
||||||
|
*/
|
||||||
|
public function rightJoin($table, $on) {
|
||||||
|
return $this->join($table, $on, 'right');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shortcut for creating an inner join clause
|
||||||
|
*
|
||||||
|
* @param string $table Name of the table, which should be joined
|
||||||
|
* @param string $on The on clause for this join
|
||||||
|
* @return object
|
||||||
|
*/
|
||||||
|
public function innerJoin($table, $on) {
|
||||||
|
return $this->join($table, $on, 'inner');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the values which should be used for the update or insert clause
|
||||||
|
*
|
||||||
|
* @param mixed $values Can either be a string or an array of values
|
||||||
|
* @return object
|
||||||
|
*/
|
||||||
|
public function values($values = array()) {
|
||||||
|
if(!is_null($values)) $this->values = $values;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attaches additional bindings to the query.
|
||||||
|
* Also can be used as getter for all attached bindings by not passing an argument.
|
||||||
|
*
|
||||||
|
* @param mixed $bindings Array of bindings or null to use this method as getter
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function bindings($bindings = null) {
|
||||||
|
|
||||||
|
if(is_array($bindings)) {
|
||||||
|
$this->bindings = array_merge($this->bindings, $bindings);
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->bindings;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attaches an additional where clause
|
||||||
|
*
|
||||||
|
* All available ways to add where clauses
|
||||||
|
*
|
||||||
|
* ->where('username like "myuser"'); (args: 1)
|
||||||
|
* ->where(array('username' => 'myuser')); (args: 1)
|
||||||
|
* ->where(function($where) { $where->where('id', '=', 1) }) (args: 1)
|
||||||
|
* ->where('username like ?', 'myuser') (args: 2)
|
||||||
|
* ->where('username', 'like', 'myuser'); (args: 3)
|
||||||
|
*
|
||||||
|
* @param list
|
||||||
|
* @return object
|
||||||
|
*/
|
||||||
|
public function where() {
|
||||||
|
$this->where = $this->filterQuery(func_get_args(), $this->where);
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shortcut to attach a where clause with an OR operator.
|
||||||
|
* Check out the where() method docs for additional info.
|
||||||
|
*
|
||||||
|
* @param list
|
||||||
|
* @return object
|
||||||
|
*/
|
||||||
|
public function orWhere() {
|
||||||
|
|
||||||
|
$args = func_get_args();
|
||||||
|
$mode = a::last($args);
|
||||||
|
|
||||||
|
// if there's a where clause mode attribute attached…
|
||||||
|
if(in_array($mode, array('AND', 'OR'))) {
|
||||||
|
// remove that from the list of arguments
|
||||||
|
array_pop($args);
|
||||||
|
}
|
||||||
|
|
||||||
|
// make sure to always attach the OR mode indicator
|
||||||
|
$args[] = 'OR';
|
||||||
|
|
||||||
|
call_user_func_array(array($this, 'where'), $args);
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shortcut to attach a where clause with an AND operator.
|
||||||
|
* Check out the where() method docs for additional info.
|
||||||
|
*
|
||||||
|
* @param list
|
||||||
|
* @return object
|
||||||
|
*/
|
||||||
|
public function andWhere() {
|
||||||
|
|
||||||
|
$args = func_get_args();
|
||||||
|
$mode = a::last($args);
|
||||||
|
|
||||||
|
// if there's a where clause mode attribute attached…
|
||||||
|
if(in_array($mode, array('AND', 'OR'))) {
|
||||||
|
// remove that from the list of arguments
|
||||||
|
array_pop($args);
|
||||||
|
}
|
||||||
|
|
||||||
|
// make sure to always attach the AND mode indicator
|
||||||
|
$args[] = 'AND';
|
||||||
|
|
||||||
|
call_user_func_array(array($this, 'where'), func_get_args());
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attaches a group by clause
|
||||||
|
*
|
||||||
|
* @param string $group
|
||||||
|
* @return object
|
||||||
|
*/
|
||||||
|
public function group($group) {
|
||||||
|
$this->group = $group;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attaches an additional having clause
|
||||||
|
*
|
||||||
|
* All available ways to add having clauses
|
||||||
|
*
|
||||||
|
* ->having('username like "myuser"'); (args: 1)
|
||||||
|
* ->having(array('username' => 'myuser')); (args: 1)
|
||||||
|
* ->having(function($having) { $having->having('id', '=', 1) }) (args: 1)
|
||||||
|
* ->having('username like ?', 'myuser') (args: 2)
|
||||||
|
* ->having('username', 'like', 'myuser'); (args: 3)
|
||||||
|
*
|
||||||
|
* @param list
|
||||||
|
* @return object
|
||||||
|
*/
|
||||||
|
public function having() {
|
||||||
|
$this->having = $this->filterQuery(func_get_args(), $this->having);
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attaches an order clause
|
||||||
|
*
|
||||||
|
* @param string $order
|
||||||
|
* @return object
|
||||||
|
*/
|
||||||
|
public function order($order) {
|
||||||
|
$this->order = $order;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the offset for select clauses
|
||||||
|
*
|
||||||
|
* @param int $offset
|
||||||
|
* @return object
|
||||||
|
*/
|
||||||
|
public function offset($offset) {
|
||||||
|
$this->offset = $offset;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the limit for select clauses
|
||||||
|
*
|
||||||
|
* @param int $limit
|
||||||
|
* @return object
|
||||||
|
*/
|
||||||
|
public function limit($limit) {
|
||||||
|
$this->limit = $limit;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builds the different types of SQL queries
|
||||||
|
* This uses the SQL class to build stuff.
|
||||||
|
*
|
||||||
|
* @param string $type (select, update, insert)
|
||||||
|
* @return string The final query
|
||||||
|
*/
|
||||||
|
public function build($type) {
|
||||||
|
|
||||||
|
$sql = new SQL($this->database, $this);
|
||||||
|
|
||||||
|
switch($type) {
|
||||||
|
case 'select':
|
||||||
|
|
||||||
|
return $sql->select(array(
|
||||||
|
'table' => $this->table,
|
||||||
|
'columns' => $this->select,
|
||||||
|
'join' => $this->join,
|
||||||
|
'distinct' => $this->distinct,
|
||||||
|
'where' => $this->where,
|
||||||
|
'group' => $this->group,
|
||||||
|
'having' => $this->having,
|
||||||
|
'order' => $this->order,
|
||||||
|
'offset' => $this->offset,
|
||||||
|
'limit' => $this->limit
|
||||||
|
));
|
||||||
|
|
||||||
|
case 'update':
|
||||||
|
|
||||||
|
return $sql->update(array(
|
||||||
|
'table' => $this->table,
|
||||||
|
'where' => $this->where,
|
||||||
|
'values' => $this->values,
|
||||||
|
));
|
||||||
|
|
||||||
|
case 'insert':
|
||||||
|
|
||||||
|
return $sql->insert(array(
|
||||||
|
'table' => $this->table,
|
||||||
|
'values' => $this->values,
|
||||||
|
));
|
||||||
|
|
||||||
|
case 'delete':
|
||||||
|
|
||||||
|
return $sql->delete(array(
|
||||||
|
'table' => $this->table,
|
||||||
|
'where' => $this->where,
|
||||||
|
));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builds a count query
|
||||||
|
*
|
||||||
|
* @return object
|
||||||
|
*/
|
||||||
|
public function count() {
|
||||||
|
return $this->aggregate('COUNT');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builds a max query
|
||||||
|
*
|
||||||
|
* @param string $column
|
||||||
|
* @return object
|
||||||
|
*/
|
||||||
|
public function max($column) {
|
||||||
|
return $this->aggregate('MAX', $column);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builds a min query
|
||||||
|
*
|
||||||
|
* @param string $column
|
||||||
|
* @return object
|
||||||
|
*/
|
||||||
|
public function min($column) {
|
||||||
|
return $this->aggregate('MIN', $column);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builds a sum query
|
||||||
|
*
|
||||||
|
* @param string $column
|
||||||
|
* @return object
|
||||||
|
*/
|
||||||
|
public function sum($column) {
|
||||||
|
return $this->aggregate('SUM', $column);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builds an average query
|
||||||
|
*
|
||||||
|
* @param string $column
|
||||||
|
* @return object
|
||||||
|
*/
|
||||||
|
public function avg($column) {
|
||||||
|
return $this->aggregate('AVG', $column);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builds an aggregation query.
|
||||||
|
* This is used by all the aggregation methods above
|
||||||
|
*
|
||||||
|
* @param string $method
|
||||||
|
* @param string $column
|
||||||
|
* @param string $default An optional default value, which should be returned if the query fails
|
||||||
|
* @return object
|
||||||
|
*/
|
||||||
|
public function aggregate($method, $column = '*', $default = 0) {
|
||||||
|
|
||||||
|
// reset the sorting to avoid counting issues
|
||||||
|
$this->order = null;
|
||||||
|
|
||||||
|
// validate column
|
||||||
|
if($column !== '*') {
|
||||||
|
$sql = new SQL($this->database, $this);
|
||||||
|
list($table, $columnPart) = $sql->splitIdentifier($this->table, $column);
|
||||||
|
if(!$this->database->validateColumn($table, $columnPart)) {
|
||||||
|
throw new Error('Invalid column ' . $column);
|
||||||
|
}
|
||||||
|
|
||||||
|
$column = $sql->combineIdentifier($table, $columnPart);
|
||||||
|
}
|
||||||
|
|
||||||
|
$fetch = $this->fetch;
|
||||||
|
$row = $this->select($method . '(' . $column . ') as aggregation')->fetch('Obj')->first();
|
||||||
|
$result = $row ? $row->get('aggregation') : $default;
|
||||||
|
$this->fetch($fetch);
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used as an internal shortcut for firing a db query
|
||||||
|
*
|
||||||
|
* @param string $query
|
||||||
|
* @param array $params
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
protected function query($query, $params = array()) {
|
||||||
|
|
||||||
|
if($this->debug) return array(
|
||||||
|
'query' => $query,
|
||||||
|
'bindings' => $this->bindings(),
|
||||||
|
'options' => $params
|
||||||
|
);
|
||||||
|
|
||||||
|
if($this->fail) $this->database->fail();
|
||||||
|
|
||||||
|
$result = $this->database->query($query, $this->bindings(), $params);
|
||||||
|
$this->reset();
|
||||||
|
return $result;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used as an internal shortcut for executing a db query
|
||||||
|
*
|
||||||
|
* @param string $query
|
||||||
|
* @param array $params
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
protected function execute($query, $params = array()) {
|
||||||
|
|
||||||
|
if($this->debug) return array(
|
||||||
|
'query' => $query,
|
||||||
|
'bindings' => $this->bindings(),
|
||||||
|
'options' => $params
|
||||||
|
);
|
||||||
|
|
||||||
|
if($this->fail) $this->database->fail();
|
||||||
|
|
||||||
|
$result = $this->database->execute($query, $this->bindings(), $params);
|
||||||
|
$this->reset();
|
||||||
|
return $result;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Selects only one row from a table
|
||||||
|
*
|
||||||
|
* @return object
|
||||||
|
*/
|
||||||
|
public function first() {
|
||||||
|
return $this->query($this->offset(0)->limit(1)->build('select'), array(
|
||||||
|
'fetch' => $this->fetch,
|
||||||
|
'iterator' => 'array',
|
||||||
|
'method' => 'fetch',
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Selects only one row from a table
|
||||||
|
*
|
||||||
|
* @return object
|
||||||
|
*/
|
||||||
|
public function row() {
|
||||||
|
return $this->first();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Selects only one row from a table
|
||||||
|
*
|
||||||
|
* @return object
|
||||||
|
*/
|
||||||
|
public function one() {
|
||||||
|
return $this->first();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Automatically adds pagination to a query
|
||||||
|
*
|
||||||
|
* @param int $page
|
||||||
|
* @param int $limit The number of rows, which should be returned for each page
|
||||||
|
* @param array $params Optional params for the pagination object
|
||||||
|
* @return object Collection iterator with attached pagination object
|
||||||
|
*/
|
||||||
|
public function page($page, $limit, $params = array()) {
|
||||||
|
|
||||||
|
$defaults = array(
|
||||||
|
'page' => $page
|
||||||
|
);
|
||||||
|
|
||||||
|
$options = array_merge($defaults, $params);
|
||||||
|
|
||||||
|
// clone this to create a counter query
|
||||||
|
$counter = clone $this;
|
||||||
|
|
||||||
|
// count the total number of rows for this query
|
||||||
|
$count = $counter->count();
|
||||||
|
|
||||||
|
// pagination
|
||||||
|
$pagination = new Pagination($count, $limit, $options);
|
||||||
|
|
||||||
|
// apply it to the dataset and retrieve all rows. make sure to use Collection as the iterator to be able to attach the pagination object
|
||||||
|
$collection = $this->offset($pagination->offset())->limit($pagination->limit())->all();
|
||||||
|
|
||||||
|
// store all pagination vars in a separate object
|
||||||
|
if($collection) $collection->paginate($pagination);
|
||||||
|
|
||||||
|
// return the limited collection
|
||||||
|
return $collection;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns all matching rows from a table
|
||||||
|
*
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function all() {
|
||||||
|
|
||||||
|
return $this->query($this->build('select'), array(
|
||||||
|
'fetch' => $this->fetch,
|
||||||
|
'iterator' => $this->iterator,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns only values from a single column
|
||||||
|
*
|
||||||
|
* @param string $column
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function column($column) {
|
||||||
|
|
||||||
|
$sql = new SQL($this->database, $this);
|
||||||
|
$primaryKey = $sql->combineIdentifier($this->table, $this->primaryKeyName);
|
||||||
|
|
||||||
|
$results = $this->query($this->select(array($column))->order($primaryKey . ' ASC')->build('select'), array(
|
||||||
|
'iterator' => 'array',
|
||||||
|
'fetch' => 'array',
|
||||||
|
));
|
||||||
|
|
||||||
|
$results = a::extract($results, $column);
|
||||||
|
|
||||||
|
if($this->iterator == 'array') return $results;
|
||||||
|
|
||||||
|
$iterator = $this->iterator;
|
||||||
|
return new $iterator($results);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find a single row by column and value
|
||||||
|
*
|
||||||
|
* @param string $column
|
||||||
|
* @param mixed $value
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function findBy($column, $value) {
|
||||||
|
return $this->where(array($column => $value))->first();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find a single row by its primary key
|
||||||
|
*
|
||||||
|
* @param mixed $id
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function find($id) {
|
||||||
|
return $this->findBy($this->primaryKeyName, $id);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fires an insert query
|
||||||
|
*
|
||||||
|
* @param array $values You can pass values here or set them with ->values() before
|
||||||
|
* @return mixed Returns the last inserted id on success or false.
|
||||||
|
*/
|
||||||
|
public function insert($values = null) {
|
||||||
|
$query = $this->execute($this->values($values)->build('insert'));
|
||||||
|
return ($query) ? $this->database->lastId() : false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fires an update query
|
||||||
|
*
|
||||||
|
* @param array $values You can pass values here or set them with ->values() before
|
||||||
|
* @param mixed $where You can pass a where clause here or set it with ->where() before
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
public function update($values = null, $where = null) {
|
||||||
|
return $this->execute($this->values($values)->where($where)->build('update'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fires a delete query
|
||||||
|
*
|
||||||
|
* @param mixed $where You can pass a where clause here or set it with ->where() before
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
public function delete($where = null) {
|
||||||
|
return $this->execute($this->where($where)->build('delete'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enables magic queries like findByUsername or findByEmail
|
||||||
|
*
|
||||||
|
* @param string $method
|
||||||
|
* @param array $arguments
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function __call($method, $arguments) {
|
||||||
|
|
||||||
|
if(preg_match('!^findBy([a-z]+)!i', $method, $match)) {
|
||||||
|
$column = str::lower($match[1]);
|
||||||
|
return $this->findBy($column, $arguments[0]);
|
||||||
|
} else {
|
||||||
|
throw new Error('Invalid query method: ' . $method, static::ERROR_INVALID_QUERY_METHOD);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builder for where and having clauses
|
||||||
|
*
|
||||||
|
* @param array $args Arguments, see where() description
|
||||||
|
* @param string $current Current value (like $this->where)
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
protected function filterQuery($args, $current) {
|
||||||
|
|
||||||
|
$mode = a::last($args);
|
||||||
|
$result = '';
|
||||||
|
|
||||||
|
// if there's a where clause mode attribute attached…
|
||||||
|
if(in_array($mode, array('AND', 'OR'))) {
|
||||||
|
// remove that from the list of arguments
|
||||||
|
array_pop($args);
|
||||||
|
} else {
|
||||||
|
$mode = 'AND';
|
||||||
|
}
|
||||||
|
|
||||||
|
switch(count($args)) {
|
||||||
|
case 1:
|
||||||
|
|
||||||
|
if(is_null($args[0])) {
|
||||||
|
|
||||||
|
return $current;
|
||||||
|
|
||||||
|
// ->where('username like "myuser"');
|
||||||
|
} else if(is_string($args[0])) {
|
||||||
|
|
||||||
|
// simply add the entire string to the where clause
|
||||||
|
// escaping or using bindings has to be done before calling this method
|
||||||
|
$result = $args[0];
|
||||||
|
|
||||||
|
// ->where(array('username' => 'myuser'));
|
||||||
|
} else if(is_array($args[0])) {
|
||||||
|
|
||||||
|
$sql = new SQL($this->database, $this);
|
||||||
|
|
||||||
|
// simple array mode (AND operator)
|
||||||
|
$result = $sql->values($this->table, $args[0], ' AND ', true, true);
|
||||||
|
|
||||||
|
} else if(is_callable($args[0])) {
|
||||||
|
|
||||||
|
$query = clone $this;
|
||||||
|
call_user_func($args[0], $query);
|
||||||
|
$result = '(' . $query->where . ')';
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
|
||||||
|
// ->where('username like :username', array('username' => 'myuser'))
|
||||||
|
if(is_string($args[0]) && is_array($args[1])) {
|
||||||
|
|
||||||
|
// prepared where clause
|
||||||
|
$result = $args[0];
|
||||||
|
|
||||||
|
// store the bindings
|
||||||
|
$this->bindings($args[1]);
|
||||||
|
|
||||||
|
// ->where('username like ?', 'myuser')
|
||||||
|
} else if(is_string($args[0]) && is_string($args[1])) {
|
||||||
|
|
||||||
|
// prepared where clause
|
||||||
|
$result = $args[0];
|
||||||
|
|
||||||
|
// store the bindings
|
||||||
|
$this->bindings(array($args[1]));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
|
||||||
|
// ->where('username', 'like', 'myuser');
|
||||||
|
if(is_string($args[0]) && is_string($args[1])) {
|
||||||
|
|
||||||
|
// validate column
|
||||||
|
$sql = new SQL($this->database, $this);
|
||||||
|
list($table, $column) = $sql->splitIdentifier($this->table, $args[0]);
|
||||||
|
if(!$this->database->validateColumn($table, $column)) {
|
||||||
|
throw new Error('Invalid column ' . $args[0]);
|
||||||
|
}
|
||||||
|
$key = $sql->combineIdentifier($table, $column);
|
||||||
|
|
||||||
|
// ->where('username', 'in', array('myuser', 'myotheruser'));
|
||||||
|
if(is_array($args[2])) {
|
||||||
|
|
||||||
|
$predicate = trim(strtoupper($args[1]));
|
||||||
|
if(!in_array($predicate, array(
|
||||||
|
'IN', 'NOT IN'
|
||||||
|
))) throw new Error('Invalid predicate ' . $predicate);
|
||||||
|
|
||||||
|
// build a list of bound values
|
||||||
|
$values = array();
|
||||||
|
$bindings = array();
|
||||||
|
foreach($args[2] as $value) {
|
||||||
|
$valueBinding = sql::generateBindingName('value');
|
||||||
|
$bindings[$valueBinding] = $value;
|
||||||
|
$values[] = $valueBinding;
|
||||||
|
}
|
||||||
|
|
||||||
|
// add that to the where clause in parenthesis
|
||||||
|
$result = $key . ' ' . $predicate . ' (' . implode(', ', $values) . ')';
|
||||||
|
|
||||||
|
$this->bindings($bindings);
|
||||||
|
|
||||||
|
// ->where('username', 'like', 'myuser');
|
||||||
|
} else {
|
||||||
|
|
||||||
|
$predicate = trim(strtoupper($args[1]));
|
||||||
|
if(!in_array($predicate, array(
|
||||||
|
'=', '>=', '>', '<=', '<', '<>', '!=', '<=>',
|
||||||
|
'IS', 'IS NOT',
|
||||||
|
'BETWEEN', 'NOT BETWEEN',
|
||||||
|
'LIKE', 'NOT LIKE',
|
||||||
|
'SOUNDS LIKE',
|
||||||
|
'REGEXP', 'NOT REGEXP'
|
||||||
|
))) throw new Error('Invalid predicate/operator ' . $predicate);
|
||||||
|
|
||||||
|
$valueBinding = sql::generateBindingName('value');
|
||||||
|
$bindings[$valueBinding] = $args[2];
|
||||||
|
|
||||||
|
$result = $key . ' ' . $predicate . ' ' . $valueBinding;
|
||||||
|
|
||||||
|
$this->bindings($bindings);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// attach the where clause
|
||||||
|
if(!empty($current)) {
|
||||||
|
return $current . ' ' . $mode . ' ' . $result;
|
||||||
|
} else {
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
251
kirby/toolkit/lib/db.php
Normal file
251
kirby/toolkit/lib/db.php
Normal file
|
@ -0,0 +1,251 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DB
|
||||||
|
*
|
||||||
|
* Database shortcuts
|
||||||
|
*
|
||||||
|
* @package Kirby Toolkit
|
||||||
|
* @author Bastian Allgeier <bastian@getkirby.com>
|
||||||
|
* @link http://getkirby.com
|
||||||
|
* @copyright Bastian Allgeier
|
||||||
|
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||||
|
*/
|
||||||
|
class DB {
|
||||||
|
|
||||||
|
const ERROR_UNKNOWN_METHOD = 0;
|
||||||
|
|
||||||
|
// query shortcuts
|
||||||
|
public static $queries = array();
|
||||||
|
|
||||||
|
// The singleton Database object
|
||||||
|
public static $connection = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* (Re)connect the database
|
||||||
|
*
|
||||||
|
* @param mixed $params Pass array() to use the default params from the config
|
||||||
|
* @return object
|
||||||
|
*/
|
||||||
|
public static function connect($params = null) {
|
||||||
|
if(is_null($params) && !is_null(static::$connection)) return static::$connection;
|
||||||
|
if(is_null($params)) {
|
||||||
|
|
||||||
|
// try to connect with the default connection settings
|
||||||
|
$params = array(
|
||||||
|
'type' => c::get('db.type', 'mysql'),
|
||||||
|
'host' => c::get('db.host', 'localhost'),
|
||||||
|
'user' => c::get('db.user', 'root'),
|
||||||
|
'password' => c::get('db.password', ''),
|
||||||
|
'database' => c::get('db.name', ''),
|
||||||
|
'prefix' => c::get('db.prefix', ''),
|
||||||
|
);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return static::$connection = new Database($params);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the current database connection
|
||||||
|
*
|
||||||
|
* @return object
|
||||||
|
*/
|
||||||
|
public static function connection() {
|
||||||
|
return static::$connection;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the current table, which should be queried
|
||||||
|
*
|
||||||
|
* @param string $table
|
||||||
|
* @return object Returns a DBQuery object, which can be used to build a full query for that table
|
||||||
|
*/
|
||||||
|
public static function table($table) {
|
||||||
|
$connection = db::connect();
|
||||||
|
return $connection->table($table);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Executes a raw sql query which expects a set of results
|
||||||
|
*
|
||||||
|
* @param string $query
|
||||||
|
* @param array $bindings
|
||||||
|
* @param array $params
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public static function query($query, $bindings = array(), $params = array()) {
|
||||||
|
$connection = db::connect();
|
||||||
|
return $connection->query($query, $bindings, $params);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Executes a raw sql query which expects no set of results (i.e. update, insert, delete)
|
||||||
|
*
|
||||||
|
* @param string $query
|
||||||
|
* @param array $bindings
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public static function execute($query, $bindings = array()) {
|
||||||
|
$connection = db::connect();
|
||||||
|
return $connection->execute($query, $bindings);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Magic calls for other static db methods,
|
||||||
|
* which are redircted to the database class if available
|
||||||
|
*
|
||||||
|
* @param string $method
|
||||||
|
* @param mixed $arguments
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public static function __callStatic($method, $arguments) {
|
||||||
|
|
||||||
|
if(isset(static::$queries[$method])) {
|
||||||
|
return call(static::$queries[$method], $arguments);
|
||||||
|
} else if(!is_callable(array(static::$connection, $method))) {
|
||||||
|
throw new Error('invalid static db method: ' . $method, static::ERROR_UNKNOWN_METHOD);
|
||||||
|
} else {
|
||||||
|
return call(array(static::$connection, $method), $arguments);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shortcut for select clauses
|
||||||
|
*
|
||||||
|
* @param string $table The name of the table, which should be queried
|
||||||
|
* @param mixed $columns Either a string with columns or an array of column names
|
||||||
|
* @param mixed $where The where clause. Can be a string or an array
|
||||||
|
* @param mixed $order
|
||||||
|
* @param int $offset
|
||||||
|
* @param int $limit
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
db::$queries['select'] = function($table, $columns = '*', $where = null, $order = null, $offset = 0, $limit = null) {
|
||||||
|
return db::table($table)->select($columns)->where($where)->order($order)->offset($offset)->limit($limit)->all();
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shortcut for selecting a single row in a table
|
||||||
|
*
|
||||||
|
* @param string $table The name of the table, which should be queried
|
||||||
|
* @param mixed $columns Either a string with columns or an array of column names
|
||||||
|
* @param mixed $where The where clause. Can be a string or an array
|
||||||
|
* @param mixed $order
|
||||||
|
* @param int $offset
|
||||||
|
* @param int $limit
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
db::$queries['first'] = db::$queries['row'] = db::$queries['one'] = function($table, $columns = '*', $where = null, $order = null) {
|
||||||
|
return db::table($table)->select($columns)->where($where)->order($order)->first();
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns only values from a single column
|
||||||
|
*
|
||||||
|
* @param string $table The name of the table, which should be queried
|
||||||
|
* @param mixed $column The name of the column to select from
|
||||||
|
* @param mixed $where The where clause. Can be a string or an array
|
||||||
|
* @param mixed $order
|
||||||
|
* @param int $offset
|
||||||
|
* @param int $limit
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
db::$queries['column'] = function($table, $column, $where = null, $order = null, $offset = 0, $limit = null) {
|
||||||
|
return db::table($table)->where($where)->order($order)->offset($offset)->limit($limit)->column($column);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shortcut for inserting a new row into a table
|
||||||
|
*
|
||||||
|
* @param string $table The name of the table, which should be queried
|
||||||
|
* @param string $values An array of values, which should be inserted
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
db::$queries['insert'] = function($table, $values) {
|
||||||
|
return db::table($table)->insert($values);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shortcut for updating a row in a table
|
||||||
|
*
|
||||||
|
* @param string $table The name of the table, which should be queried
|
||||||
|
* @param string $values An array of values, which should be inserted
|
||||||
|
* @param mixed $where An optional where clause
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
db::$queries['update'] = function($table, $values, $where = null) {
|
||||||
|
return db::table($table)->where($where)->update($values);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shortcut for deleting rows in a table
|
||||||
|
*
|
||||||
|
* @param string $table The name of the table, which should be queried
|
||||||
|
* @param mixed $where An optional where clause
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
db::$queries['delete'] = function($table, $where = null) {
|
||||||
|
return db::table($table)->where($where)->delete();
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shortcut for counting rows in a table
|
||||||
|
*
|
||||||
|
* @param string $table The name of the table, which should be queried
|
||||||
|
* @param string $where An optional where clause
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
db::$queries['count'] = function($table, $where = null) {
|
||||||
|
return db::table($table)->where($where)->count();
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shortcut for calculating the minimum value in a column
|
||||||
|
*
|
||||||
|
* @param string $table The name of the table, which should be queried
|
||||||
|
* @param string $column The name of the column of which the minimum should be calculated
|
||||||
|
* @param string $where An optional where clause
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
db::$queries['min'] = function($table, $column, $where = null) {
|
||||||
|
return db::table($table)->where($where)->min($column);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shortcut for calculating the maximum value in a column
|
||||||
|
*
|
||||||
|
* @param string $table The name of the table, which should be queried
|
||||||
|
* @param string $column The name of the column of which the maximum should be calculated
|
||||||
|
* @param string $where An optional where clause
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
db::$queries['max'] = function($table, $column, $where = null) {
|
||||||
|
return db::table($table)->where($where)->max($column);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shortcut for calculating the average value in a column
|
||||||
|
*
|
||||||
|
* @param string $table The name of the table, which should be queried
|
||||||
|
* @param string $column The name of the column of which the average should be calculated
|
||||||
|
* @param string $where An optional where clause
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
db::$queries['avg'] = function($table, $column, $where = null) {
|
||||||
|
return db::table($table)->where($where)->avg($column);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shortcut for calculating the sum of all values in a column
|
||||||
|
*
|
||||||
|
* @param string $table The name of the table, which should be queried
|
||||||
|
* @param string $column The name of the column of which the sum should be calculated
|
||||||
|
* @param string $where An optional where clause
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
db::$queries['sum'] = function($table, $column, $where = null) {
|
||||||
|
return db::table($table)->where($where)->sum($column);
|
||||||
|
};
|
261
kirby/toolkit/lib/detect.php
Normal file
261
kirby/toolkit/lib/detect.php
Normal file
|
@ -0,0 +1,261 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Detect
|
||||||
|
*
|
||||||
|
* This class is a system feature detection helper
|
||||||
|
* to check for installed packages and software
|
||||||
|
*
|
||||||
|
* @package Kirby Toolkit
|
||||||
|
* @author Bastian Allgeier <bastian@getkirby.com>
|
||||||
|
* @link http://getkirby.com
|
||||||
|
* @copyright Bastian Allgeier
|
||||||
|
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||||
|
*/
|
||||||
|
class Detect {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the mb string extension is installed
|
||||||
|
*
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
public static function mbstring() {
|
||||||
|
return function_exists('mb_split');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the required php version is installed
|
||||||
|
*
|
||||||
|
* @param mixed $min
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
public static function php($min = '5.3') {
|
||||||
|
return version_compare(PHP_VERSION, $min, '>=');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if PHP is running on Apache
|
||||||
|
*
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
public static function apache() {
|
||||||
|
return apache_get_version() ? true : false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the site is running on Windows
|
||||||
|
*
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
public static function windows() {
|
||||||
|
return DS == '/' ? false : true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the site is running on IIS
|
||||||
|
*
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
public static function iis() {
|
||||||
|
return isset($_SERVER['SERVER_SOFTWARE']) && strpos($_SERVER['SERVER_SOFTWARE'],'IIS') !== false ? true : false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if mysql installed with the minimum required version
|
||||||
|
*
|
||||||
|
* @param mixed $min
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
public static function mysql($min = '5') {
|
||||||
|
$extensions = get_loaded_extensions();
|
||||||
|
if(!in_array('mysql', $extensions)) return false;
|
||||||
|
$version = preg_replace('#(^\D*)([0-9.]+).*$#', '\2', mysql_get_client_info());
|
||||||
|
return version_compare($version, $min, '>=');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if SQLite 3 is installed
|
||||||
|
*
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
public static function sqlite() {
|
||||||
|
return in_array('sqlite3', get_loaded_extensions());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if safe mode is enabled
|
||||||
|
*
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
public static function safemode() {
|
||||||
|
return ini_get('safe_mode');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if gdlib is installed
|
||||||
|
*
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
public static function gdlib() {
|
||||||
|
return function_exists('gd_info');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if imageick is installed
|
||||||
|
*
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
public static function imagick() {
|
||||||
|
return class_exists('Imagick');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if CURL is installed
|
||||||
|
*
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
public static function curl() {
|
||||||
|
return in_array('curl', get_loaded_extensions());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if APC cache is installed
|
||||||
|
*
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
public static function apc() {
|
||||||
|
return function_exists('apc_add');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the Memcache extension is installed
|
||||||
|
*
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
public static function memcache() {
|
||||||
|
return class_exists('Memcache');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the Memcached extension is installed
|
||||||
|
*
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
public static function memcached() {
|
||||||
|
return class_exists('Memcached');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the imap extension is installed
|
||||||
|
*
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
public static function imap() {
|
||||||
|
return function_exists('imap_body');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the mcrypt extension is installed
|
||||||
|
*
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
public static function mcrypt() {
|
||||||
|
return function_exists('mcrypt_encrypt');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the exif extension is installed
|
||||||
|
*
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
public static function exif() {
|
||||||
|
return function_exists('read_exif_data');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Detect if the script is installed in a subfolder
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public static function subfolder() {
|
||||||
|
return trim(dirname($_SERVER['SCRIPT_NAME']), '/\\');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Detects the current path
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public static function path() {
|
||||||
|
$uri = explode('/', url::path());
|
||||||
|
$script = explode('/', trim($_SERVER['SCRIPT_NAME'], '/\\'));
|
||||||
|
$parts = array_diff_assoc($uri, $script);
|
||||||
|
if(empty($parts)) return false;
|
||||||
|
return implode('/', $parts);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Detect the document root
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public static function documentRoot() {
|
||||||
|
$local = $_SERVER['SCRIPT_NAME'];
|
||||||
|
$absolute = $_SERVER['SCRIPT_FILENAME'];
|
||||||
|
return substr($absolute, 0, strpos($absolute, $local));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts any ini size value to an integer
|
||||||
|
*
|
||||||
|
* @param string $key
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
public static function iniSize($key) {
|
||||||
|
|
||||||
|
$size = ini_get($key);
|
||||||
|
$size = trim($size);
|
||||||
|
$last = strtolower($size[strlen($size)-1]);
|
||||||
|
switch($last) {
|
||||||
|
case 'g':
|
||||||
|
$size *= 1024;
|
||||||
|
case 'm':
|
||||||
|
$size *= 1024;
|
||||||
|
case 'k':
|
||||||
|
$size *= 1024;
|
||||||
|
}
|
||||||
|
return $size;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the max accepted upload size
|
||||||
|
* defined in the php.ini
|
||||||
|
*
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
public static function maxUploadSize() {
|
||||||
|
return static::iniSize('upload_max_filesize');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the max accepted post size
|
||||||
|
* defined in the php.ini
|
||||||
|
*
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
public static function maxPostSize() {
|
||||||
|
return static::iniSize('post_max_size');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dirty browser sniffing for an ios device
|
||||||
|
*
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
public static function ios() {
|
||||||
|
$ua = visitor::ua();
|
||||||
|
return (str::contains($ua, 'iPod') || str::contains($ua, 'iPhone') || str::contains($ua, 'iPad'));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
316
kirby/toolkit/lib/dimensions.php
Normal file
316
kirby/toolkit/lib/dimensions.php
Normal file
|
@ -0,0 +1,316 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dimensions
|
||||||
|
*
|
||||||
|
* The dimension object is used to provide additional
|
||||||
|
* methods for KirbyImage objects and possibly other
|
||||||
|
* objects with width and height to recalculate the size,
|
||||||
|
* get the ratio or just the width and height.
|
||||||
|
*
|
||||||
|
* @package Kirby Toolkit
|
||||||
|
* @author Bastian Allgeier <bastian@getkirby.com>
|
||||||
|
* @link http://getkirby.com
|
||||||
|
* @copyright Bastian Allgeier
|
||||||
|
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||||
|
*/
|
||||||
|
class Dimensions {
|
||||||
|
|
||||||
|
// the width of the parent object
|
||||||
|
public $width = 0;
|
||||||
|
|
||||||
|
// the height of the parent object
|
||||||
|
public $height = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*
|
||||||
|
* @param int $width
|
||||||
|
* @param int $height
|
||||||
|
*/
|
||||||
|
public function __construct($width, $height) {
|
||||||
|
$this->width = $width;
|
||||||
|
$this->height = $height;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the width
|
||||||
|
*
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
public function width() {
|
||||||
|
return $this->width;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the height
|
||||||
|
*
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
public function height() {
|
||||||
|
return $this->height;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculates and returns the ratio
|
||||||
|
*
|
||||||
|
* <code>
|
||||||
|
*
|
||||||
|
* $dimensions = new Dimensions(1200, 768);
|
||||||
|
* echo $dimensions->ratio();
|
||||||
|
* // output: 1.5625
|
||||||
|
*
|
||||||
|
* </code>
|
||||||
|
*
|
||||||
|
* @return float
|
||||||
|
*/
|
||||||
|
public function ratio() {
|
||||||
|
if($this->width && $this->height) {
|
||||||
|
return ($this->width / $this->height);
|
||||||
|
} else {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Recalculates the width and height
|
||||||
|
* to fit into the given box.
|
||||||
|
*
|
||||||
|
* <code>
|
||||||
|
*
|
||||||
|
* $dimensions = new Dimensions(1200, 768);
|
||||||
|
* $dimensions->fit(500);
|
||||||
|
*
|
||||||
|
* echo $dimensions->width();
|
||||||
|
* // output: 500
|
||||||
|
*
|
||||||
|
* echo $dimensions->height();
|
||||||
|
* // output: 320
|
||||||
|
*
|
||||||
|
* </code>
|
||||||
|
*
|
||||||
|
* @param int $box the max width and/or height
|
||||||
|
* @param boolean $force If true, the dimensions will be upscaled to fit the box if smaller
|
||||||
|
* @return object returns this object with recalculated dimensions
|
||||||
|
*/
|
||||||
|
public function fit($box, $force = false) {
|
||||||
|
|
||||||
|
if($this->width == 0 || $this->height == 0) {
|
||||||
|
$this->width = $box;
|
||||||
|
$this->height = $box;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
$ratio = $this->ratio();
|
||||||
|
|
||||||
|
if($this->width > $this->height) {
|
||||||
|
if($this->width > $box || $force === true) $this->width = $box;
|
||||||
|
$this->height = round($this->width / $ratio);
|
||||||
|
} elseif($this->height > $this->width) {
|
||||||
|
if($this->height > $box || $force === true) $this->height = $box;
|
||||||
|
$this->width = round($this->height * $ratio);
|
||||||
|
} elseif($this->width > $box) {
|
||||||
|
$this->width = $box;
|
||||||
|
$this->height = $box;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Recalculates the width and height
|
||||||
|
* to fit the given width
|
||||||
|
*
|
||||||
|
* <code>
|
||||||
|
*
|
||||||
|
* $dimensions = new Dimensions(1200, 768);
|
||||||
|
* $dimensions->fitWidth(500);
|
||||||
|
*
|
||||||
|
* echo $dimensions->width();
|
||||||
|
* // output: 500
|
||||||
|
*
|
||||||
|
* echo $dimensions->height();
|
||||||
|
* // output: 320
|
||||||
|
*
|
||||||
|
* </code>
|
||||||
|
*
|
||||||
|
* @param int $fit the max width
|
||||||
|
* @param boolean $force If true, the dimensions will be upscaled to fit the width if smaller
|
||||||
|
* @return object returns this object with recalculated dimensions
|
||||||
|
*/
|
||||||
|
public function fitWidth($fit, $force = false) {
|
||||||
|
|
||||||
|
if(!$fit) return $this;
|
||||||
|
|
||||||
|
if($this->width <= $fit && !$force) return $this;
|
||||||
|
|
||||||
|
$ratio = $this->ratio();
|
||||||
|
|
||||||
|
$this->width = $fit;
|
||||||
|
$this->height = round($fit / $ratio);
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Recalculates the width and height
|
||||||
|
* to fit the given height
|
||||||
|
*
|
||||||
|
* <code>
|
||||||
|
*
|
||||||
|
* $dimensions = new Dimensions(1200, 768);
|
||||||
|
* $dimensions->fitHeight(500);
|
||||||
|
*
|
||||||
|
* echo $dimensions->width();
|
||||||
|
* // output: 781
|
||||||
|
*
|
||||||
|
* echo $dimensions->height();
|
||||||
|
* // output: 500
|
||||||
|
*
|
||||||
|
* </code>
|
||||||
|
*
|
||||||
|
* @param int $fit the max height
|
||||||
|
* @param boolean $force If true, the dimensions will be upscaled to fit the height if smaller
|
||||||
|
* @return object returns this object with recalculated dimensions
|
||||||
|
*/
|
||||||
|
public function fitHeight($fit, $force = false) {
|
||||||
|
|
||||||
|
if(!$fit) return $this;
|
||||||
|
|
||||||
|
if($this->height <= $fit && !$force) return $this;
|
||||||
|
|
||||||
|
$ratio = $this->ratio();
|
||||||
|
|
||||||
|
$this->width = round($fit * $ratio);
|
||||||
|
$this->height = $fit;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Recalculates the dimensions by the width and height
|
||||||
|
*
|
||||||
|
* @param int $width the max height
|
||||||
|
* @param int $height the max width
|
||||||
|
* @return object
|
||||||
|
*/
|
||||||
|
public function fitWidthAndHeight($width, $height, $force = false) {
|
||||||
|
|
||||||
|
if($this->width > $this->height) {
|
||||||
|
|
||||||
|
$this->fitWidth($width, $force);
|
||||||
|
|
||||||
|
// do another check for the max height
|
||||||
|
if($this->height > $height) $this->fitHeight($height);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
$this->fitHeight($height, $force);
|
||||||
|
|
||||||
|
// do another check for the max width
|
||||||
|
if($this->width > $width) $this->fitWidth($width);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param int $width
|
||||||
|
* @param int $height
|
||||||
|
* @param boolean $force
|
||||||
|
* @return Dimensions
|
||||||
|
*/
|
||||||
|
public function resize($width, $height, $force = false) {
|
||||||
|
$this->fitWidthAndHeight($width, $height, $force);
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Crops the dimensions by width and height
|
||||||
|
*
|
||||||
|
* @param int $width
|
||||||
|
* @param int $height
|
||||||
|
* @return object
|
||||||
|
*/
|
||||||
|
public function crop($width, $height = null) {
|
||||||
|
|
||||||
|
$this->width = $width;
|
||||||
|
$this->height = $width;
|
||||||
|
|
||||||
|
if($height) {
|
||||||
|
$this->height = $height;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a string representation of the orientation
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function orientation() {
|
||||||
|
if(!$this->ratio()) return false;
|
||||||
|
if($this->portrait()) return 'portrait';
|
||||||
|
if($this->landscape()) return 'landscape';
|
||||||
|
if($this->square()) return 'square';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the dimensions are portrait
|
||||||
|
*
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
public function portrait() {
|
||||||
|
return $this->height > $this->width;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the dimensions are landscape
|
||||||
|
*
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
public function landscape() {
|
||||||
|
return $this->width > $this->height;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the dimensions are square
|
||||||
|
*
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
public function square() {
|
||||||
|
return $this->width == $this->height;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts the dimensions object
|
||||||
|
* to a plain PHP array
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function toArray() {
|
||||||
|
return array(
|
||||||
|
'width' => $this->width(),
|
||||||
|
'height' => $this->height(),
|
||||||
|
'ratio' => $this->ratio(),
|
||||||
|
'orientation' => $this->orientation(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Echos the dimensions as width × height
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function __toString() {
|
||||||
|
return $this->width . ' × ' . $this->height;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
209
kirby/toolkit/lib/dir.php
Normal file
209
kirby/toolkit/lib/dir.php
Normal file
|
@ -0,0 +1,209 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dir
|
||||||
|
*
|
||||||
|
* Low level directory handling utilities
|
||||||
|
*
|
||||||
|
* @package Kirby Toolkit
|
||||||
|
* @author Bastian Allgeier <bastian@getkirby.com>
|
||||||
|
* @link http://getkirby.com
|
||||||
|
* @copyright Bastian Allgeier
|
||||||
|
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||||
|
*/
|
||||||
|
class Dir {
|
||||||
|
|
||||||
|
public static $defaults = array(
|
||||||
|
'permissions' => 0755,
|
||||||
|
'ignore' => array('.', '..', '.DS_Store', '.gitignore', '.git', '.svn', '.htaccess', 'Thumb.db', '@eaDir')
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new directory
|
||||||
|
*
|
||||||
|
* <code>
|
||||||
|
*
|
||||||
|
* $create = dir::make('/app/test/new-directory');
|
||||||
|
*
|
||||||
|
* if($create) echo 'the directory has been created';
|
||||||
|
*
|
||||||
|
* </code>
|
||||||
|
*
|
||||||
|
* @param string $dir The path for the new directory
|
||||||
|
* @return boolean True: the dir has been created, false: creating failed
|
||||||
|
*/
|
||||||
|
public static function make($dir, $recursive = true) {
|
||||||
|
return is_dir($dir) ? true : @mkdir($dir, static::$defaults['permissions'], $recursive);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads all files from a directory and returns them as an array.
|
||||||
|
* It skips unwanted invisible stuff.
|
||||||
|
*
|
||||||
|
* <code>
|
||||||
|
*
|
||||||
|
* $files = dir::read('mydirectory');
|
||||||
|
* // returns array('file-1.txt', 'file-2.txt', 'file-3.txt', etc...);
|
||||||
|
*
|
||||||
|
* </code>
|
||||||
|
*
|
||||||
|
* @param string $dir The path of directory
|
||||||
|
* @param array $ignore Optional array with filenames, which should be ignored
|
||||||
|
* @return mixed An array of filenames or false
|
||||||
|
*/
|
||||||
|
public static function read($dir, $ignore = array()) {
|
||||||
|
if(!is_dir($dir)) return array();
|
||||||
|
$skip = array_merge(static::$defaults['ignore'], $ignore);
|
||||||
|
return (array)array_diff(scandir($dir),$skip);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Moves a directory to a new location
|
||||||
|
*
|
||||||
|
* <code>
|
||||||
|
*
|
||||||
|
* $move = dir::move('mydirectory', 'mynewdirectory');
|
||||||
|
*
|
||||||
|
* if($move) echo 'the directory has been moved to mynewdirectory';
|
||||||
|
*
|
||||||
|
* </code>
|
||||||
|
*
|
||||||
|
* @param string $old The current path of the directory
|
||||||
|
* @param string $new The desired path where the dir should be moved to
|
||||||
|
* @return boolean True: the directory has been moved, false: moving failed
|
||||||
|
*/
|
||||||
|
public static function move($old, $new) {
|
||||||
|
if(!is_dir($old)) return false;
|
||||||
|
return @rename($old, $new);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes a directory
|
||||||
|
*
|
||||||
|
* <code>
|
||||||
|
*
|
||||||
|
* $remove = dir::remove('mydirectory');
|
||||||
|
*
|
||||||
|
* if($remove) echo 'the directory has been removed';
|
||||||
|
*
|
||||||
|
* </code>
|
||||||
|
*
|
||||||
|
* @param string $dir The path of the directory
|
||||||
|
* @param boolean $keep If set to true, the directory will flushed but not removed.
|
||||||
|
* @return boolean True: the directory has been removed, false: removing failed
|
||||||
|
*/
|
||||||
|
public static function remove($dir, $keep = false) {
|
||||||
|
if(!is_dir($dir)) return false;
|
||||||
|
|
||||||
|
// It's easier to handle this with the Folder class
|
||||||
|
$object = new Folder($dir);
|
||||||
|
return $object->remove($keep);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Flushes a directory
|
||||||
|
*
|
||||||
|
* @param string $dir The path of the directory
|
||||||
|
* @return boolean True: the directory has been flushed, false: flushing failed
|
||||||
|
*/
|
||||||
|
public static function clean($dir) {
|
||||||
|
return static::remove($dir, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the size of the directory and all subfolders and files
|
||||||
|
*
|
||||||
|
* @param string $dir The path of the directory
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public static function size($dir) {
|
||||||
|
|
||||||
|
if(!file_exists($dir)) return false;
|
||||||
|
|
||||||
|
// It's easier to handle this with the Folder class
|
||||||
|
$object = new Folder($dir);
|
||||||
|
return $object->size();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a nicely formatted size of all the contents of the folder
|
||||||
|
*
|
||||||
|
* @param string $dir The path of the directory
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public static function niceSize($dir) {
|
||||||
|
return f::niceSize(static::size($dir));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Recursively check when the dir and all
|
||||||
|
* subfolders have been modified for the last time.
|
||||||
|
*
|
||||||
|
* @param string $dir The path of the directory
|
||||||
|
* @param string $format
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
public static function modified($dir, $format = null, $handler = 'date') {
|
||||||
|
// It's easier to handle this with the Folder class
|
||||||
|
$object = new Folder($dir);
|
||||||
|
return $object->modified($format, $handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the directory or any subdirectory has been
|
||||||
|
* modified after the given timestamp
|
||||||
|
*
|
||||||
|
* @param string $dir
|
||||||
|
* @param int $time
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
public static function wasModifiedAfter($dir, $time) {
|
||||||
|
|
||||||
|
if(filemtime($dir) > $time) return true;
|
||||||
|
|
||||||
|
$content = dir::read($dir);
|
||||||
|
|
||||||
|
foreach($content as $item) {
|
||||||
|
$subdir = $dir . DS . $item;
|
||||||
|
if(filemtime($subdir) > $time) return true;
|
||||||
|
if(is_dir($subdir) && dir::wasModifiedAfter($subdir, $time)) return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the dir is writable
|
||||||
|
*
|
||||||
|
* @param string $dir
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
public static function writable($dir) {
|
||||||
|
return is_writable($dir);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the dir is readable
|
||||||
|
*
|
||||||
|
* @param string $dir
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
public static function readable($dir) {
|
||||||
|
return is_readable($dir);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copy a file, or recursively copy a folder and its contents
|
||||||
|
*
|
||||||
|
* @param string $dir Source path
|
||||||
|
* @param string $to Destination path
|
||||||
|
*/
|
||||||
|
public static function copy($dir, $to) {
|
||||||
|
// It's easier to handle this with the Folder class
|
||||||
|
$object = new Folder($dir);
|
||||||
|
return $object->copy($to);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
291
kirby/toolkit/lib/email.php
Normal file
291
kirby/toolkit/lib/email.php
Normal file
|
@ -0,0 +1,291 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Email
|
||||||
|
*
|
||||||
|
* A simple email handling class which supports
|
||||||
|
* multiple email services. Check out the email subfolder
|
||||||
|
* for all available services
|
||||||
|
*
|
||||||
|
* @package Kirby Toolkit
|
||||||
|
* @author Bastian Allgeier <bastian@getkirby.com>
|
||||||
|
* @link http://getkirby.com
|
||||||
|
* @copyright Bastian Allgeier
|
||||||
|
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||||
|
*/
|
||||||
|
class Email extends Obj {
|
||||||
|
|
||||||
|
const ERROR_INVALID_RECIPIENT = 0;
|
||||||
|
const ERROR_INVALID_SENDER = 1;
|
||||||
|
const ERROR_INVALID_REPLY_TO = 2;
|
||||||
|
const ERROR_INVALID_SUBJECT = 3;
|
||||||
|
const ERROR_INVALID_BODY = 4;
|
||||||
|
const ERROR_INVALID_SERVICE = 5;
|
||||||
|
const ERROR_DISABLED = 6;
|
||||||
|
|
||||||
|
public static $defaults = array(
|
||||||
|
'service' => 'mail',
|
||||||
|
'options' => array(),
|
||||||
|
'to' => null,
|
||||||
|
'from' => null,
|
||||||
|
'replyTo' => null,
|
||||||
|
'subject' => null,
|
||||||
|
'body' => null
|
||||||
|
);
|
||||||
|
|
||||||
|
public static $services = array();
|
||||||
|
public static $disabled = false;
|
||||||
|
|
||||||
|
public $error;
|
||||||
|
public $service;
|
||||||
|
public $options;
|
||||||
|
public $to;
|
||||||
|
public $from;
|
||||||
|
public $replyTo;
|
||||||
|
public $subject;
|
||||||
|
public $body;
|
||||||
|
|
||||||
|
public function __construct($params = array()) {
|
||||||
|
$options = a::merge(static::$defaults, $params);
|
||||||
|
parent::__construct($options);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __set($key, $value) {
|
||||||
|
$this->$key = $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validates the constructed email
|
||||||
|
* to make sure it can be sent at all
|
||||||
|
*/
|
||||||
|
public function validate() {
|
||||||
|
if(!v::email($this->extractAddress($this->to))) throw new Error('Invalid recipient', static::ERROR_INVALID_RECIPIENT);
|
||||||
|
if(!v::email($this->extractAddress($this->from))) throw new Error('Invalid sender', static::ERROR_INVALID_SENDER);
|
||||||
|
if(!v::email($this->extractAddress($this->replyTo))) throw new Error('Invalid reply address', static::ERROR_INVALID_REPLY_TO);
|
||||||
|
if(empty($this->subject)) throw new Error('Missing subject', static::ERROR_INVALID_SUBJECT);
|
||||||
|
if(empty($this->body)) throw new Error('Missing body', static::ERROR_INVALID_BODY);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Public getter for the error exception
|
||||||
|
*
|
||||||
|
* @return Exception
|
||||||
|
*/
|
||||||
|
public function error() {
|
||||||
|
return $this->error;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extracts the email address from an address string
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
protected function extractAddress($string) {
|
||||||
|
if(v::email($string)) return $string;
|
||||||
|
preg_match('/<(.*?)>/i', $string, $array);
|
||||||
|
return (empty($array[1])) ? $string : $array[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sends the constructed email
|
||||||
|
*
|
||||||
|
* @param array $params Optional way to set values for the email
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
public function send($params = null) {
|
||||||
|
|
||||||
|
try {
|
||||||
|
|
||||||
|
// fail silently if sending emails is disabled
|
||||||
|
if(static::$disabled) throw new Error('Sending emails is disabled', static::ERROR_DISABLED);
|
||||||
|
|
||||||
|
// overwrite already set values
|
||||||
|
if(is_array($params) && !empty($params)) {
|
||||||
|
foreach(a::merge($this->toArray(), $params) as $key => $val) {
|
||||||
|
$this->set($key, $val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// reset all errors
|
||||||
|
$this->error = null;
|
||||||
|
|
||||||
|
// default service
|
||||||
|
if(empty($this->service)) $this->service = 'mail';
|
||||||
|
|
||||||
|
// if there's no dedicated reply to address, use the from address
|
||||||
|
if(empty($this->replyTo)) $this->replyTo = $this->from;
|
||||||
|
|
||||||
|
// validate the email
|
||||||
|
$this->validate();
|
||||||
|
|
||||||
|
// check if the email service is available
|
||||||
|
if(!isset(static::$services[$this->service])) {
|
||||||
|
throw new Error('The email service is not available: ' . $this->service, static::ERROR_INVALID_SERVICE);
|
||||||
|
}
|
||||||
|
|
||||||
|
// run the service
|
||||||
|
call(static::$services[$this->service], $this);
|
||||||
|
|
||||||
|
// reset the error
|
||||||
|
$this->error = null;
|
||||||
|
return true;
|
||||||
|
|
||||||
|
} catch(Exception $e) {
|
||||||
|
$this->error = $e;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default mail driver
|
||||||
|
*/
|
||||||
|
email::$services['mail'] = function($email) {
|
||||||
|
|
||||||
|
$headers = array(
|
||||||
|
'From: ' . $email->from,
|
||||||
|
'Reply-To: ' . $email->replyTo,
|
||||||
|
'Return-Path: ' . $email->replyTo,
|
||||||
|
'Message-ID: <' . time() . '-' . $email->from . '>',
|
||||||
|
'X-Mailer: PHP v' . phpversion(),
|
||||||
|
'Content-Type: text/plain; charset=utf-8',
|
||||||
|
'Content-Transfer-Encoding: 8bit',
|
||||||
|
);
|
||||||
|
|
||||||
|
ini_set('sendmail_from', $email->from);
|
||||||
|
$send = mail($email->to, str::utf8($email->subject), str::utf8($email->body), implode(PHP_EOL, $headers));
|
||||||
|
ini_restore('sendmail_from');
|
||||||
|
|
||||||
|
if(!$send) {
|
||||||
|
throw new Error('The email could not be sent');
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Amazon mail driver
|
||||||
|
*/
|
||||||
|
email::$services['amazon'] = function($email) {
|
||||||
|
|
||||||
|
if(empty($email->options['key'])) throw new Error('Missing Amazon API key');
|
||||||
|
if(empty($email->options['secret'])) throw new Error('Missing Amazon API secret');
|
||||||
|
|
||||||
|
$setup = array(
|
||||||
|
'Action' => 'SendEmail',
|
||||||
|
'Destination.ToAddresses.member.1' => $email->to,
|
||||||
|
'ReplyToAddresses.member.1' => $email->replyTo,
|
||||||
|
'ReturnPath' => $email->replyTo,
|
||||||
|
'Source' => $email->from,
|
||||||
|
'Message.Subject.Data' => $email->subject,
|
||||||
|
'Message.Body.Text.Data' => $email->body
|
||||||
|
);
|
||||||
|
|
||||||
|
$params = array();
|
||||||
|
|
||||||
|
foreach($setup as $key => $value) {
|
||||||
|
$params[] = $key . '=' . str_replace('%7E', '~', rawurlencode($value));
|
||||||
|
}
|
||||||
|
|
||||||
|
sort($params, SORT_STRING);
|
||||||
|
|
||||||
|
$host = a::get($email->options, 'host', 'email.us-east-1.amazonaws.com');
|
||||||
|
$url = 'https://' . $host . '/';
|
||||||
|
$date = gmdate('D, d M Y H:i:s e');
|
||||||
|
$signature = base64_encode(hash_hmac('sha256', $date, $email->options['secret'], true));
|
||||||
|
$query = implode('&', $params);
|
||||||
|
$headers = array();
|
||||||
|
$auth = 'AWS3-HTTPS AWSAccessKeyId=' . $email->options['key'];
|
||||||
|
$auth .= ',Algorithm=HmacSHA256,Signature=' . $signature;
|
||||||
|
|
||||||
|
$headers[] = 'Date: ' . $date;
|
||||||
|
$headers[] = 'Host: ' . $host;
|
||||||
|
$headers[] = 'X-Amzn-Authorization: '. $auth;
|
||||||
|
$headers[] = 'Content-Type: application/x-www-form-urlencoded';
|
||||||
|
|
||||||
|
$email->response = remote::post($url, array(
|
||||||
|
'data' => $query,
|
||||||
|
'headers' => $headers
|
||||||
|
));
|
||||||
|
|
||||||
|
if(!in_array($email->response->code(), array(200, 201, 202, 204))) {
|
||||||
|
throw new Error('The mail could not be sent!', $email->response->code());
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mailgun mail driver
|
||||||
|
*/
|
||||||
|
email::$services['mailgun'] = function($email) {
|
||||||
|
|
||||||
|
if(empty($email->options['key'])) throw new Error('Missing Mailgun API key');
|
||||||
|
if(empty($email->options['domain'])) throw new Error('Missing Mailgun API domain');
|
||||||
|
|
||||||
|
$url = 'https://api.mailgun.net/v2/' . $email->options['domain'] . '/messages';
|
||||||
|
$auth = base64_encode('api:' . $email->options['key']);
|
||||||
|
|
||||||
|
$headers = array(
|
||||||
|
'Accept: application/json',
|
||||||
|
'Authorization: Basic ' . $auth
|
||||||
|
);
|
||||||
|
|
||||||
|
$data = array(
|
||||||
|
'from' => $email->from,
|
||||||
|
'to' => $email->to,
|
||||||
|
'subject' => $email->subject,
|
||||||
|
'text' => $email->body,
|
||||||
|
'h:Reply-To' => $email->replyTo,
|
||||||
|
);
|
||||||
|
|
||||||
|
$email->response = remote::post($url, array(
|
||||||
|
'data' => $data,
|
||||||
|
'headers' => $headers
|
||||||
|
));
|
||||||
|
|
||||||
|
if($email->response->code() != 200) {
|
||||||
|
throw new Error('The mail could not be sent!');
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Postmark mail driver
|
||||||
|
*/
|
||||||
|
email::$services['postmark'] = function($email) {
|
||||||
|
|
||||||
|
if(empty($email->options['key'])) throw new Error('Invalid Postmark API Key');
|
||||||
|
|
||||||
|
// reset the api key if we are in test mode
|
||||||
|
if(a::get($email->options, 'test')) $email->options['key'] = 'POSTMARK_API_TEST';
|
||||||
|
|
||||||
|
// the url for postmarks api
|
||||||
|
$url = 'https://api.postmarkapp.com/email';
|
||||||
|
|
||||||
|
$headers = array(
|
||||||
|
'Accept: application/json',
|
||||||
|
'Content-Type: application/json',
|
||||||
|
'X-Postmark-Server-Token: ' . $email->options['key']
|
||||||
|
);
|
||||||
|
|
||||||
|
$data = array(
|
||||||
|
'From' => $email->from,
|
||||||
|
'To' => $email->to,
|
||||||
|
'ReplyTo' => $email->replyTo,
|
||||||
|
'Subject' => $email->subject,
|
||||||
|
'TextBody' => $email->body
|
||||||
|
);
|
||||||
|
|
||||||
|
// fetch the response
|
||||||
|
$email->response = remote::post($url, array(
|
||||||
|
'data' => json_encode($data),
|
||||||
|
'headers' => $headers
|
||||||
|
));
|
||||||
|
|
||||||
|
if($email->response->code() != 200) {
|
||||||
|
throw new Error('The mail could not be sent');
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
124
kirby/toolkit/lib/embed.php
Normal file
124
kirby/toolkit/lib/embed.php
Normal file
|
@ -0,0 +1,124 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Embed
|
||||||
|
*
|
||||||
|
* Simple embedding of stuff like
|
||||||
|
* flash, youtube videos, vimeo videos or gists
|
||||||
|
*
|
||||||
|
* @package Kirby Toolkit
|
||||||
|
* @author Bastian Allgeier <bastian@getkirby.com>
|
||||||
|
* @link http://getkirby.com
|
||||||
|
* @copyright Bastian Allgeier
|
||||||
|
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||||
|
*/
|
||||||
|
class Embed {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Embeds a youtube video by passing the Youtube url
|
||||||
|
*
|
||||||
|
* @param string $url Youtube url i.e. http://www.youtube.com/watch?v=d9NF2edxy-M
|
||||||
|
* @param array $attr Additional attributes for the iframe
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public static function youtube($url, $attr = array()) {
|
||||||
|
|
||||||
|
// http://www.youtube.com/embed/d9NF2edxy-M
|
||||||
|
if(preg_match('!youtube.com\/embed\/([a-z0-9_-]+)!i', $url, $array)) {
|
||||||
|
$id = $array[1];
|
||||||
|
// http://www.youtube.com/watch?feature=player_embedded&v=d9NF2edxy-M#!
|
||||||
|
} elseif(preg_match('!v=([a-z0-9_-]+)!i', $url, $array)) {
|
||||||
|
$id = $array[1];
|
||||||
|
// http://youtu.be/d9NF2edxy-M
|
||||||
|
} elseif(preg_match('!youtu.be\/([a-z0-9_-]+)!i', $url, $array)) {
|
||||||
|
$id = $array[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
// no id no result!
|
||||||
|
if(empty($id)) return false;
|
||||||
|
|
||||||
|
// default options
|
||||||
|
if(!empty($attr['options'])) {
|
||||||
|
$options = '?' . http_build_query($attr['options']);
|
||||||
|
// options should not propagate to the attr list
|
||||||
|
unset($attr['options']);
|
||||||
|
} else {
|
||||||
|
$options = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
// default attributes
|
||||||
|
$attr = array_merge(array(
|
||||||
|
'src' => '//youtube.com/embed/' . $id . $options,
|
||||||
|
'frameborder' => '0',
|
||||||
|
'webkitAllowFullScreen' => 'true',
|
||||||
|
'mozAllowFullScreen' => 'true',
|
||||||
|
'allowFullScreen' => 'true',
|
||||||
|
'width' => '100%',
|
||||||
|
'height' => '100%',
|
||||||
|
), $attr);
|
||||||
|
|
||||||
|
return html::tag('iframe', '', $attr);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Embeds a vimeo video by passing the vimeo url
|
||||||
|
*
|
||||||
|
* @param string $url vimeo url i.e. http://vimeo.com/52345557
|
||||||
|
* @param array $attr Additional attributes for the iframe
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public static function vimeo($url, $attr = array()) {
|
||||||
|
|
||||||
|
// get the uid from the url
|
||||||
|
if(preg_match('!vimeo.com\/([0-9]+)!i', $url, $array)) {
|
||||||
|
$id = $array[1];
|
||||||
|
} else {
|
||||||
|
$id = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// no id no result!
|
||||||
|
if(empty($id)) return false;
|
||||||
|
|
||||||
|
// default options
|
||||||
|
if(!empty($attr['options'])) {
|
||||||
|
$options = '?' . http_build_query($attr['options']);
|
||||||
|
// options should not propagate to the attr list
|
||||||
|
unset($attr['options']);
|
||||||
|
} else {
|
||||||
|
$options = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
// default attributes
|
||||||
|
$attr = array_merge(array(
|
||||||
|
'src' => '//player.vimeo.com/video/' . $id . $options,
|
||||||
|
'frameborder' => '0',
|
||||||
|
'webkitAllowFullScreen' => 'true',
|
||||||
|
'mozAllowFullScreen' => 'true',
|
||||||
|
'allowFullScreen' => 'true',
|
||||||
|
'width' => '100%',
|
||||||
|
'height' => '100%',
|
||||||
|
), $attr);
|
||||||
|
|
||||||
|
return html::tag('iframe', '', $attr);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Embeds a github gist
|
||||||
|
*
|
||||||
|
* @param string $url Gist url: i.e. https://gist.github.com/2924148
|
||||||
|
* @param string $file The name of a particular file from the gist, which should displayed only.
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public static function gist($url, $file = null) {
|
||||||
|
|
||||||
|
// url for the script file
|
||||||
|
$url = $url . '.js' . r(!is_null($file), '?file=' . $file);
|
||||||
|
|
||||||
|
// load the gist
|
||||||
|
return html::tag('script', '', array('src' => $url));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
26
kirby/toolkit/lib/error.php
Normal file
26
kirby/toolkit/lib/error.php
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Error
|
||||||
|
*
|
||||||
|
* @package Kirby Toolkit
|
||||||
|
* @author Bastian Allgeier <bastian@getkirby.com>
|
||||||
|
* @link http://getkirby.com
|
||||||
|
* @copyright Bastian Allgeier
|
||||||
|
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||||
|
*/
|
||||||
|
class Error extends Exception {
|
||||||
|
|
||||||
|
public function message() {
|
||||||
|
return $this->message;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function code() {
|
||||||
|
return $this->code;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __toString() {
|
||||||
|
return $this->message;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
94
kirby/toolkit/lib/errorreporting.php
Normal file
94
kirby/toolkit/lib/errorreporting.php
Normal file
|
@ -0,0 +1,94 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Error Reporting
|
||||||
|
*
|
||||||
|
* Changes values of the PHP error reporting
|
||||||
|
*
|
||||||
|
* @package Kirby Toolkit
|
||||||
|
* @author Lukas Bestle <lukas@getkirby.com>
|
||||||
|
* @link http://getkirby.com
|
||||||
|
* @copyright Lukas Bestle
|
||||||
|
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||||
|
*/
|
||||||
|
class ErrorReporting {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the current raw value
|
||||||
|
*
|
||||||
|
* @return int The current value
|
||||||
|
*/
|
||||||
|
public static function get() {
|
||||||
|
return error_reporting();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets a new raw error reporting value
|
||||||
|
*
|
||||||
|
* @param int $level The new level to set
|
||||||
|
* @return int The new value
|
||||||
|
*/
|
||||||
|
public static function set($level) {
|
||||||
|
if(static::get() !== error_reporting($level)) {
|
||||||
|
throw new Exception('Internal error: error_reporting() did not return the old value.');
|
||||||
|
}
|
||||||
|
return static::get();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the current error reporting includes an error level
|
||||||
|
*
|
||||||
|
* @param mixed $level The level to check for
|
||||||
|
* @param int $current A custom current level
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
public static function includes($level, $current = null) {
|
||||||
|
// also allow strings
|
||||||
|
if(is_string($level)) {
|
||||||
|
if(defined($level)) {
|
||||||
|
$level = constant($level);
|
||||||
|
} else if(defined('E_' . strtoupper($level))) {
|
||||||
|
$level = constant('E_' . strtoupper($level));
|
||||||
|
} else {
|
||||||
|
throw new Exception('The level "' . $level . '" does not exist.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$value = ($current)? $current : static::get();
|
||||||
|
return bitmask::includes($level, $value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a level to the current error reporting
|
||||||
|
*
|
||||||
|
* @param int $level The level to add
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
public static function add($level) {
|
||||||
|
// check if it is already added
|
||||||
|
if(static::includes($level)) return false;
|
||||||
|
|
||||||
|
$old = static::get();
|
||||||
|
$newExpected = bitmask::add($level, $old);
|
||||||
|
$newActual = static::set($newExpected);
|
||||||
|
|
||||||
|
return $newActual === $newExpected;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes a level from the current error reporting
|
||||||
|
*
|
||||||
|
* @param int $level The level to remove
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
public static function remove($level) {
|
||||||
|
// check if it is already removed
|
||||||
|
if(!static::includes($level)) return false;
|
||||||
|
|
||||||
|
$old = static::get();
|
||||||
|
$newExpected = bitmask::remove($level, $old);
|
||||||
|
$newActual = static::set($newExpected);
|
||||||
|
|
||||||
|
return $newActual === $newExpected;
|
||||||
|
}
|
||||||
|
}
|
266
kirby/toolkit/lib/escape.php
Normal file
266
kirby/toolkit/lib/escape.php
Normal file
|
@ -0,0 +1,266 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Escape
|
||||||
|
*
|
||||||
|
* Class to handle context specific output escaping per OWASP recommendations.
|
||||||
|
*
|
||||||
|
* Most of this class is based on methods from Zend\Escaper, but modified for Kirby.
|
||||||
|
* Copyrighted (c) 2005-2014 Zend Technologies USA Inc. (http://www.zend.com)
|
||||||
|
* under the New BSD License (http://framework.zend.com/license/new-bsd).
|
||||||
|
*
|
||||||
|
* @link https://github.com/zendframework/zf2/blob/master/library/Zend/Escaper/Escaper.php
|
||||||
|
* @link https://www.owasp.org/index.php/XSS_(Cross_Site_Scripting)_Prevention_Cheat_Sheet
|
||||||
|
*
|
||||||
|
* @package Kirby Toolkit
|
||||||
|
* @author Ezra Verheijen <ezra.verheijen@gmail.com>
|
||||||
|
* @link https://github.com/ezraverheijen/escape
|
||||||
|
* @copyright Ezra Verheijen
|
||||||
|
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||||
|
*/
|
||||||
|
class Escape {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if a string needs to be escaped or not
|
||||||
|
*
|
||||||
|
* @param string $string
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
public static function noNeedToEscape($string) {
|
||||||
|
return $string === '' || ctype_digit($string);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert a character from UTF-8 to UTF-16BE
|
||||||
|
*
|
||||||
|
* @param string $char
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public static function convertEncoding($char) {
|
||||||
|
return str::convert($char, 'UTF-16BE', 'UTF-8');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if a character is undefined in HTML
|
||||||
|
*
|
||||||
|
* @param string $char
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
public static function charIsUndefined($char) {
|
||||||
|
$ascii = ord($char);
|
||||||
|
return ($ascii <= 0x1f && $char != "\t" && $char != "\n" && $char != "\r")
|
||||||
|
|| ($ascii >= 0x7f && $ascii <= 0x9f);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Escape HTML element content
|
||||||
|
*
|
||||||
|
* This can be used to put untrusted data directly into the HTML body somewhere.
|
||||||
|
* This includes inside normal tags like div, p, b, td, etc.
|
||||||
|
*
|
||||||
|
* Escapes &, <, >, ", and ' with HTML entity encoding to prevent switching
|
||||||
|
* into any execution context, such as script, style, or event handlers.
|
||||||
|
*
|
||||||
|
* <body>...ESCAPE UNTRUSTED DATA BEFORE PUTTING HERE...</body>
|
||||||
|
* <div>...ESCAPE UNTRUSTED DATA BEFORE PUTTING HERE...</div>
|
||||||
|
*
|
||||||
|
* @uses ENT_SUBSTITUE if available (PHP >= 5.4)
|
||||||
|
*
|
||||||
|
* @param string $string
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public static function html($string) {
|
||||||
|
$flags = ENT_QUOTES;
|
||||||
|
if(defined('ENT_SUBSTITUTE')) {
|
||||||
|
$flags |= ENT_SUBSTITUTE;
|
||||||
|
}
|
||||||
|
return htmlspecialchars($string, $flags, 'UTF-8');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Escape XML element content
|
||||||
|
*
|
||||||
|
* Removes offending characters that could be wrongfully interpreted as XML markup.
|
||||||
|
*
|
||||||
|
* The following characters are reserved in XML and will be replaced with their
|
||||||
|
* corresponding XML entities:
|
||||||
|
*
|
||||||
|
* ' is replaced with '
|
||||||
|
* " is replaced with "
|
||||||
|
* & is replaced with &
|
||||||
|
* < is replaced with <
|
||||||
|
* > is replaced with >
|
||||||
|
*
|
||||||
|
* @uses ENT_XML1 if available (PHP >= 5.4)
|
||||||
|
*
|
||||||
|
* @param string $string
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public static function xml($string) {
|
||||||
|
if (defined('ENT_XML1')) {
|
||||||
|
return htmlspecialchars($string, ENT_QUOTES | ENT_XML1, 'UTF-8');
|
||||||
|
} else {
|
||||||
|
return str_replace(''', ''', htmlspecialchars($string, ENT_QUOTES, 'UTF-8'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Escape common HTML attributes data
|
||||||
|
*
|
||||||
|
* This can be used to put untrusted data into typical attribute values
|
||||||
|
* like width, name, value, etc.
|
||||||
|
*
|
||||||
|
* This should not be used for complex attributes like href, src, style,
|
||||||
|
* or any of the event handlers like onmouseover.
|
||||||
|
* Use esc($string, 'js') for event handler attributes, esc($string, 'url')
|
||||||
|
* for src attributes and esc($string, 'css') for style attributes.
|
||||||
|
*
|
||||||
|
* <div attr=...ESCAPE UNTRUSTED DATA BEFORE PUTTING HERE...>content</div>
|
||||||
|
* <div attr='...ESCAPE UNTRUSTED DATA BEFORE PUTTING HERE...'>content</div>
|
||||||
|
* <div attr="...ESCAPE UNTRUSTED DATA BEFORE PUTTING HERE...">content</div>
|
||||||
|
*
|
||||||
|
* @param string $string
|
||||||
|
* @param string $strict Whether to escape characters like [space] % * + , - / ; < = > ^ and |
|
||||||
|
* which is necessary in case of unquoted HTML attributes.
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public static function attr($string, $strict = false) {
|
||||||
|
if(static::noNeedToEscape($string)) return $string;
|
||||||
|
if($strict !== true) {
|
||||||
|
return preg_replace_callback('/[^a-z0-9,\.\-_]/iSu', 'static::escapeAttrChar', $string);
|
||||||
|
}
|
||||||
|
return static::html($string);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Escape JavaScript data values
|
||||||
|
*
|
||||||
|
* This can be used to put dynamically generated JavaScript code
|
||||||
|
* into both script blocks and event-handler attributes.
|
||||||
|
*
|
||||||
|
* <script>alert('...ESCAPE UNTRUSTED DATA BEFORE PUTTING HERE...')</script>
|
||||||
|
* <script>x='...ESCAPE UNTRUSTED DATA BEFORE PUTTING HERE...'</script>
|
||||||
|
* <div onmouseover="x='...ESCAPE UNTRUSTED DATA BEFORE PUTTING HERE...'"</div>
|
||||||
|
*
|
||||||
|
* @param string $string
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public static function js($string) {
|
||||||
|
if(static::noNeedToEscape($string)) return $string;
|
||||||
|
return preg_replace_callback('/[^a-z0-9,\._]/iSu', 'static::escapeJSChar', $string);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Escape HTML style property values
|
||||||
|
*
|
||||||
|
* This can be used to put untrusted data into a stylesheet or a style tag.
|
||||||
|
*
|
||||||
|
* Stay away from putting untrusted data into complex properties like url,
|
||||||
|
* behavior, and custom (-moz-binding). You should also not put untrusted data
|
||||||
|
* into IE’s expression property value which allows JavaScript.
|
||||||
|
*
|
||||||
|
* <style>selector { property : ...ESCAPE UNTRUSTED DATA BEFORE PUTTING HERE...; } </style>
|
||||||
|
* <style>selector { property : "...ESCAPE UNTRUSTED DATA BEFORE PUTTING HERE..."; } </style>
|
||||||
|
* <span style="property : ...ESCAPE UNTRUSTED DATA BEFORE PUTTING HERE...">text</span>
|
||||||
|
*
|
||||||
|
* @param string $string
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public static function css($string) {
|
||||||
|
if(static::noNeedToEscape($string)) return $string;
|
||||||
|
return preg_replace_callback('/[^a-z0-9]/iSu', 'static::escapeCSSChar', $string);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Escape URL parameter values
|
||||||
|
*
|
||||||
|
* This can be used to put untrusted data into HTTP GET parameter values.
|
||||||
|
* This should not be used to escape an entire URI.
|
||||||
|
*
|
||||||
|
* <a href="http://www.somesite.com?test=...ESCAPE UNTRUSTED DATA BEFORE PUTTING HERE...">link</a>
|
||||||
|
*
|
||||||
|
* @param string $string
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public static function url($string) {
|
||||||
|
return rawurlencode($string);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Escape character for HTML attribute
|
||||||
|
*
|
||||||
|
* Callback function for preg_replace_callback() that applies HTML attribute
|
||||||
|
* escaping to all matches.
|
||||||
|
*
|
||||||
|
* @param array $matches
|
||||||
|
* @return mixed Unicode replacement if character is undefined in HTML,
|
||||||
|
* named HTML entity if available (only those that XML supports),
|
||||||
|
* upper hex entity if a named entity does not exist or
|
||||||
|
* entity with the &#xHH; format if ASCII value is less than 256.
|
||||||
|
*/
|
||||||
|
protected static function escapeAttrChar($matches) {
|
||||||
|
$char = $matches[0];
|
||||||
|
|
||||||
|
if(static::charIsUndefined($char)) {
|
||||||
|
return '�';
|
||||||
|
}
|
||||||
|
|
||||||
|
$dec = hexdec(bin2hex($char));
|
||||||
|
|
||||||
|
$namedEntities = array(
|
||||||
|
34 => '"', // "
|
||||||
|
38 => '&', // &
|
||||||
|
60 => '<', // <
|
||||||
|
62 => '>' // >
|
||||||
|
);
|
||||||
|
|
||||||
|
if(isset($namedEntities[$dec])) {
|
||||||
|
return $namedEntities[$dec];
|
||||||
|
}
|
||||||
|
|
||||||
|
if($dec > 255) {
|
||||||
|
return sprintf('&#x%04X;', $dec);
|
||||||
|
}
|
||||||
|
|
||||||
|
return sprintf('&#x%02X;', $dec);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Escape character for JavaScript
|
||||||
|
*
|
||||||
|
* Callback function for preg_replace_callback() that applies Javascript
|
||||||
|
* escaping to all matches.
|
||||||
|
*
|
||||||
|
* @param array $matches
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
protected static function escapeJSChar($matches) {
|
||||||
|
$char = $matches[0];
|
||||||
|
if(str::length($char) == 1) {
|
||||||
|
return sprintf('\\x%02X', ord($char));
|
||||||
|
}
|
||||||
|
$char = static::convertEncoding($char);
|
||||||
|
return sprintf('\\u%04s', str::upper(bin2hex($char)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Escape character for CSS
|
||||||
|
*
|
||||||
|
* Callback function for preg_replace_callback() that applies CSS
|
||||||
|
* escaping to all matches.
|
||||||
|
*
|
||||||
|
* @param array $matches
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
protected static function escapeCSSChar($matches) {
|
||||||
|
$char = $matches[0];
|
||||||
|
if(str::length($char) == 1) {
|
||||||
|
$ord = ord($char);
|
||||||
|
} else {
|
||||||
|
$char = static::convertEncoding($char);
|
||||||
|
$ord = hexdec(bin2hex($char));
|
||||||
|
}
|
||||||
|
return sprintf('\\%X ', $ord);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
221
kirby/toolkit/lib/exif.php
Normal file
221
kirby/toolkit/lib/exif.php
Normal file
|
@ -0,0 +1,221 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Exif
|
||||||
|
*
|
||||||
|
* Reads exif data from a given media object
|
||||||
|
*
|
||||||
|
* @package Kirby Toolkit
|
||||||
|
* @author Bastian Allgeier <bastian@getkirby.com>
|
||||||
|
* @link http://getkirby.com
|
||||||
|
* @copyright Bastian Allgeier
|
||||||
|
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||||
|
*/
|
||||||
|
class Exif {
|
||||||
|
|
||||||
|
// the parent media object
|
||||||
|
protected $media = null;
|
||||||
|
|
||||||
|
// the raw exif array
|
||||||
|
protected $data = null;
|
||||||
|
|
||||||
|
// the camera object with model and make
|
||||||
|
protected $camera = null;
|
||||||
|
|
||||||
|
// the location object
|
||||||
|
protected $location = null;
|
||||||
|
|
||||||
|
// the timestamp
|
||||||
|
protected $timestamp = null;
|
||||||
|
|
||||||
|
// the exposure value
|
||||||
|
protected $exposure = null;
|
||||||
|
|
||||||
|
// the aperture value
|
||||||
|
protected $aperture = null;
|
||||||
|
|
||||||
|
// iso value
|
||||||
|
protected $iso = null;
|
||||||
|
|
||||||
|
// focal length
|
||||||
|
protected $focalLength = null;
|
||||||
|
|
||||||
|
// color or black/white
|
||||||
|
protected $isColor = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*
|
||||||
|
* @param Media $media
|
||||||
|
*/
|
||||||
|
public function __construct(Media $media) {
|
||||||
|
$this->media = $media;
|
||||||
|
$this->parse();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the raw data array from the parser
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function data() {
|
||||||
|
return $this->data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the Camera object
|
||||||
|
*
|
||||||
|
* @return object KirbyExifCamera
|
||||||
|
*/
|
||||||
|
public function camera() {
|
||||||
|
|
||||||
|
if(!is_null($this->camera)) return $this->camera;
|
||||||
|
|
||||||
|
// check for valid exif data
|
||||||
|
if(!is_array($this->data)) return null;
|
||||||
|
|
||||||
|
// initialize and return it
|
||||||
|
return $this->camera = new Exif\Camera($this->data);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the location object
|
||||||
|
*
|
||||||
|
* @return object ExifLocation
|
||||||
|
*/
|
||||||
|
public function location() {
|
||||||
|
|
||||||
|
if(!is_null($this->location)) return $this->location;
|
||||||
|
|
||||||
|
// check for valid exif data
|
||||||
|
if(!is_array($this->data)) return null;
|
||||||
|
|
||||||
|
// initialize and return it
|
||||||
|
return $this->location = new Exif\Location($this->data);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the timestamp
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function timestamp() {
|
||||||
|
return $this->timestamp;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the exposure
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function exposure() {
|
||||||
|
return $this->exposure;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the aperture
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function aperture() {
|
||||||
|
return $this->aperture;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the iso value
|
||||||
|
*
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
public function iso() {
|
||||||
|
return $this->iso;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if this is a color picture
|
||||||
|
*
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
public function isColor() {
|
||||||
|
return $this->isColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if this is a bw picture
|
||||||
|
*
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
public function isBW() {
|
||||||
|
return !$this->isColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the focal length
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function focalLength() {
|
||||||
|
return $this->focalLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pareses and stores all relevant exif data
|
||||||
|
*/
|
||||||
|
protected function parse() {
|
||||||
|
|
||||||
|
// read the exif data of the media object if possible
|
||||||
|
$this->data = @read_exif_data($this->media->root());
|
||||||
|
|
||||||
|
// stop on invalid exif data
|
||||||
|
if(!is_array($this->data)) return false;
|
||||||
|
|
||||||
|
// store the timestamp when the picture has been taken
|
||||||
|
if(isset($this->data['DateTimeOriginal'])) {
|
||||||
|
$this->timestamp = strtotime($this->data['DateTimeOriginal']);
|
||||||
|
} else {
|
||||||
|
$this->timestamp = a::get($this->data, 'FileDateTime', $this->media->modified());
|
||||||
|
}
|
||||||
|
|
||||||
|
// exposure
|
||||||
|
$this->exposure = a::get($this->data, 'ExposureTime');
|
||||||
|
|
||||||
|
// iso
|
||||||
|
$this->iso = a::get($this->data, 'ISOSpeedRatings');
|
||||||
|
|
||||||
|
// focal length
|
||||||
|
if(isset($this->data['FocalLength'])) {
|
||||||
|
$this->focalLength = $this->data['FocalLength'];
|
||||||
|
} else if(isset($this->data['FocalLengthIn35mmFilm'])) {
|
||||||
|
$this->focalLength = $this->data['FocalLengthIn35mmFilm'];
|
||||||
|
}
|
||||||
|
|
||||||
|
// aperture
|
||||||
|
$this->aperture = @$this->data['COMPUTED']['ApertureFNumber'];
|
||||||
|
|
||||||
|
// color or bw
|
||||||
|
$this->isColor = @$this->data['COMPUTED']['IsColor'] == true;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts the object into a nicely readable array
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function toArray() {
|
||||||
|
|
||||||
|
return array(
|
||||||
|
'camera' => $this->camera()->toArray(),
|
||||||
|
'location' => $this->location()->toArray(),
|
||||||
|
'timestamp' => $this->timestamp(),
|
||||||
|
'exposure' => $this->exposure(),
|
||||||
|
'aperture' => $this->aperture(),
|
||||||
|
'iso' => $this->iso(),
|
||||||
|
'focalLength' => $this->focalLength(),
|
||||||
|
'isColor' => $this->isColor()
|
||||||
|
);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue