first commit
This commit is contained in:
254
system/src/Grav/Common/Data/Blueprint.php
Normal file
254
system/src/Grav/Common/Data/Blueprint.php
Normal file
@@ -0,0 +1,254 @@
|
||||
<?php
|
||||
/**
|
||||
* @package Grav.Common.Data
|
||||
*
|
||||
* @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved.
|
||||
* @license MIT License; see LICENSE file for details.
|
||||
*/
|
||||
|
||||
namespace Grav\Common\Data;
|
||||
|
||||
use Grav\Common\File\CompiledYamlFile;
|
||||
use Grav\Common\Grav;
|
||||
use RocketTheme\Toolbox\Blueprints\BlueprintForm;
|
||||
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;
|
||||
|
||||
class Blueprint extends BlueprintForm
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $context = 'blueprints://';
|
||||
|
||||
/**
|
||||
* @var BlueprintSchema
|
||||
*/
|
||||
protected $blueprintSchema;
|
||||
|
||||
/**
|
||||
* Set default values for field types.
|
||||
*
|
||||
* @param array $types
|
||||
* @return $this
|
||||
*/
|
||||
public function setTypes(array $types)
|
||||
{
|
||||
$this->initInternals();
|
||||
|
||||
$this->blueprintSchema->setTypes($types);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get nested structure containing default values defined in the blueprints.
|
||||
*
|
||||
* Fields without default value are ignored in the list.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getDefaults()
|
||||
{
|
||||
$this->initInternals();
|
||||
|
||||
return $this->blueprintSchema->getDefaults();
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge two arrays by using blueprints.
|
||||
*
|
||||
* @param array $data1
|
||||
* @param array $data2
|
||||
* @param string $name Optional
|
||||
* @param string $separator Optional
|
||||
* @return array
|
||||
*/
|
||||
public function mergeData(array $data1, array $data2, $name = null, $separator = '.')
|
||||
{
|
||||
$this->initInternals();
|
||||
|
||||
return $this->blueprintSchema->mergeData($data1, $data2, $name, $separator);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return data fields that do not exist in blueprints.
|
||||
*
|
||||
* @param array $data
|
||||
* @param string $prefix
|
||||
* @return array
|
||||
*/
|
||||
public function extra(array $data, $prefix = '')
|
||||
{
|
||||
$this->initInternals();
|
||||
|
||||
return $this->blueprintSchema->extra($data, $prefix);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate data against blueprints.
|
||||
*
|
||||
* @param array $data
|
||||
* @throws \RuntimeException
|
||||
*/
|
||||
public function validate(array $data)
|
||||
{
|
||||
$this->initInternals();
|
||||
|
||||
$this->blueprintSchema->validate($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter data by using blueprints.
|
||||
*
|
||||
* @param array $data
|
||||
* @return array
|
||||
*/
|
||||
public function filter(array $data)
|
||||
{
|
||||
$this->initInternals();
|
||||
|
||||
return $this->blueprintSchema->filter($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return blueprint data schema.
|
||||
*
|
||||
* @return BlueprintSchema
|
||||
*/
|
||||
public function schema()
|
||||
{
|
||||
$this->initInternals();
|
||||
|
||||
return $this->blueprintSchema;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize validator.
|
||||
*/
|
||||
protected function initInternals()
|
||||
{
|
||||
if (!isset($this->blueprintSchema)) {
|
||||
$types = Grav::instance()['plugins']->formFieldTypes;
|
||||
|
||||
$this->blueprintSchema = new BlueprintSchema;
|
||||
if ($types) {
|
||||
$this->blueprintSchema->setTypes($types);
|
||||
}
|
||||
$this->blueprintSchema->embed('', $this->items);
|
||||
$this->blueprintSchema->init();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $filename
|
||||
* @return string
|
||||
*/
|
||||
protected function loadFile($filename)
|
||||
{
|
||||
$file = CompiledYamlFile::instance($filename);
|
||||
$content = $file->content();
|
||||
$file->free();
|
||||
|
||||
return $content;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string|array $path
|
||||
* @param string $context
|
||||
* @return array
|
||||
*/
|
||||
protected function getFiles($path, $context = null)
|
||||
{
|
||||
/** @var UniformResourceLocator $locator */
|
||||
$locator = Grav::instance()['locator'];
|
||||
|
||||
if (is_string($path) && !$locator->isStream($path)) {
|
||||
// Find path overrides.
|
||||
$paths = isset($this->overrides[$path]) ? (array) $this->overrides[$path] : [];
|
||||
|
||||
// Add path pointing to default context.
|
||||
if ($context === null) {
|
||||
$context = $this->context;
|
||||
}
|
||||
if ($context && $context[strlen($context)-1] !== '/') {
|
||||
$context .= '/';
|
||||
}
|
||||
$path = $context . $path;
|
||||
|
||||
if (!preg_match('/\.yaml$/', $path)) {
|
||||
$path .= '.yaml';
|
||||
}
|
||||
|
||||
$paths[] = $path;
|
||||
} else {
|
||||
$paths = (array) $path;
|
||||
}
|
||||
|
||||
$files = [];
|
||||
foreach ($paths as $lookup) {
|
||||
if (is_string($lookup) && strpos($lookup, '://')) {
|
||||
$files = array_merge($files, $locator->findResources($lookup));
|
||||
} else {
|
||||
$files[] = $lookup;
|
||||
}
|
||||
}
|
||||
|
||||
return array_values(array_unique($files));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $field
|
||||
* @param string $property
|
||||
* @param array $call
|
||||
*/
|
||||
protected function dynamicData(array &$field, $property, array &$call)
|
||||
{
|
||||
$params = $call['params'];
|
||||
|
||||
if (is_array($params)) {
|
||||
$function = array_shift($params);
|
||||
} else {
|
||||
$function = $params;
|
||||
$params = [];
|
||||
}
|
||||
|
||||
list($o, $f) = preg_split('/::/', $function, 2);
|
||||
if (!$f) {
|
||||
if (function_exists($o)) {
|
||||
$data = call_user_func_array($o, $params);
|
||||
}
|
||||
} else {
|
||||
if (method_exists($o, $f)) {
|
||||
$data = call_user_func_array(array($o, $f), $params);
|
||||
}
|
||||
}
|
||||
|
||||
// If function returns a value,
|
||||
if (isset($data)) {
|
||||
if (isset($field[$property]) && is_array($field[$property]) && is_array($data)) {
|
||||
// Combine field and @data-field together.
|
||||
$field[$property] += $data;
|
||||
} else {
|
||||
// Or create/replace field with @data-field.
|
||||
$field[$property] = $data;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $field
|
||||
* @param string $property
|
||||
* @param array $call
|
||||
*/
|
||||
protected function dynamicConfig(array &$field, $property, array &$call)
|
||||
{
|
||||
$value = $call['params'];
|
||||
|
||||
$default = isset($field[$property]) ? $field[$property] : null;
|
||||
$config = Grav::instance()['config']->get($value, $default);
|
||||
|
||||
if (!is_null($config)) {
|
||||
$field[$property] = $config;
|
||||
}
|
||||
}
|
||||
}
|
||||
171
system/src/Grav/Common/Data/BlueprintSchema.php
Normal file
171
system/src/Grav/Common/Data/BlueprintSchema.php
Normal file
@@ -0,0 +1,171 @@
|
||||
<?php
|
||||
/**
|
||||
* @package Grav.Common.Data
|
||||
*
|
||||
* @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved.
|
||||
* @license MIT License; see LICENSE file for details.
|
||||
*/
|
||||
|
||||
namespace Grav\Common\Data;
|
||||
|
||||
use Grav\Common\Grav;
|
||||
use RocketTheme\Toolbox\ArrayTraits\Export;
|
||||
use RocketTheme\Toolbox\ArrayTraits\ExportInterface;
|
||||
use RocketTheme\Toolbox\Blueprints\BlueprintSchema as BlueprintSchemaBase;
|
||||
|
||||
class BlueprintSchema extends BlueprintSchemaBase implements ExportInterface
|
||||
{
|
||||
use Export;
|
||||
|
||||
protected $ignoreFormKeys = [
|
||||
'title' => true,
|
||||
'help' => true,
|
||||
'placeholder' => true,
|
||||
'placeholder_key' => true,
|
||||
'placeholder_value' => true,
|
||||
'fields' => true
|
||||
];
|
||||
|
||||
/**
|
||||
* Validate data against blueprints.
|
||||
*
|
||||
* @param array $data
|
||||
* @throws \RuntimeException
|
||||
*/
|
||||
public function validate(array $data)
|
||||
{
|
||||
try {
|
||||
$messages = $this->validateArray($data, $this->nested);
|
||||
|
||||
} catch (\RuntimeException $e) {
|
||||
throw (new ValidationException($e->getMessage(), $e->getCode(), $e))->setMessages();
|
||||
}
|
||||
|
||||
if (!empty($messages)) {
|
||||
throw (new ValidationException())->setMessages($messages);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter data by using blueprints.
|
||||
*
|
||||
* @param array $data
|
||||
* @return array
|
||||
*/
|
||||
public function filter(array $data)
|
||||
{
|
||||
return $this->filterArray($data, $this->nested);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $data
|
||||
* @param array $rules
|
||||
* @returns array
|
||||
* @throws \RuntimeException
|
||||
* @internal
|
||||
*/
|
||||
protected function validateArray(array $data, array $rules)
|
||||
{
|
||||
$messages = $this->checkRequired($data, $rules);
|
||||
|
||||
foreach ($data as $key => $field) {
|
||||
$val = isset($rules[$key]) ? $rules[$key] : (isset($rules['*']) ? $rules['*'] : null);
|
||||
$rule = is_string($val) ? $this->items[$val] : null;
|
||||
|
||||
if ($rule) {
|
||||
// Item has been defined in blueprints.
|
||||
$messages += Validation::validate($field, $rule);
|
||||
} elseif (is_array($field) && is_array($val)) {
|
||||
// Array has been defined in blueprints.
|
||||
$messages += $this->validateArray($field, $val);
|
||||
} elseif (isset($rules['validation']) && $rules['validation'] === 'strict') {
|
||||
// Undefined/extra item.
|
||||
throw new \RuntimeException(sprintf('%s is not defined in blueprints', $key));
|
||||
}
|
||||
}
|
||||
|
||||
return $messages;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $data
|
||||
* @param array $rules
|
||||
* @return array
|
||||
* @internal
|
||||
*/
|
||||
protected function filterArray(array $data, array $rules)
|
||||
{
|
||||
$results = array();
|
||||
foreach ($data as $key => $field) {
|
||||
$val = isset($rules[$key]) ? $rules[$key] : (isset($rules['*']) ? $rules['*'] : null);
|
||||
$rule = is_string($val) ? $this->items[$val] : null;
|
||||
|
||||
if ($rule) {
|
||||
// Item has been defined in blueprints.
|
||||
$field = Validation::filter($field, $rule);
|
||||
} elseif (is_array($field) && is_array($val)) {
|
||||
// Array has been defined in blueprints.
|
||||
$field = $this->filterArray($field, $val);
|
||||
} elseif (isset($rules['validation']) && $rules['validation'] === 'strict') {
|
||||
$field = null;
|
||||
}
|
||||
|
||||
if (isset($field) && (!is_array($field) || !empty($field))) {
|
||||
$results[$key] = $field;
|
||||
}
|
||||
}
|
||||
|
||||
return $results;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $data
|
||||
* @param array $fields
|
||||
* @return array
|
||||
*/
|
||||
protected function checkRequired(array $data, array $fields)
|
||||
{
|
||||
$messages = [];
|
||||
|
||||
foreach ($fields as $name => $field) {
|
||||
if (!is_string($field)) {
|
||||
continue;
|
||||
}
|
||||
$field = $this->items[$field];
|
||||
if (isset($field['validate']['required'])
|
||||
&& $field['validate']['required'] === true) {
|
||||
|
||||
if (isset($data[$name])) {
|
||||
continue;
|
||||
}
|
||||
if ($field['type'] === 'file' && isset($data['data']['name'][$name])) { //handle case of file input fields required
|
||||
continue;
|
||||
}
|
||||
|
||||
$value = isset($field['label']) ? $field['label'] : $field['name'];
|
||||
$language = Grav::instance()['language'];
|
||||
$message = sprintf($language->translate('FORM.MISSING_REQUIRED_FIELD', null, true) . ' %s', $language->translate($value));
|
||||
$messages[$field['name']][] = $message;
|
||||
}
|
||||
}
|
||||
|
||||
return $messages;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $field
|
||||
* @param string $property
|
||||
* @param array $call
|
||||
*/
|
||||
protected function dynamicConfig(array &$field, $property, array &$call)
|
||||
{
|
||||
$value = $call['params'];
|
||||
|
||||
$default = isset($field[$property]) ? $field[$property] : null;
|
||||
$config = Grav::instance()['config']->get($value, $default);
|
||||
|
||||
if (null !== $config) {
|
||||
$field[$property] = $config;
|
||||
}
|
||||
}
|
||||
}
|
||||
100
system/src/Grav/Common/Data/Blueprints.php
Normal file
100
system/src/Grav/Common/Data/Blueprints.php
Normal file
@@ -0,0 +1,100 @@
|
||||
<?php
|
||||
/**
|
||||
* @package Grav.Common.Data
|
||||
*
|
||||
* @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved.
|
||||
* @license MIT License; see LICENSE file for details.
|
||||
*/
|
||||
|
||||
namespace Grav\Common\Data;
|
||||
|
||||
use Grav\Common\Grav;
|
||||
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;
|
||||
|
||||
class Blueprints
|
||||
{
|
||||
protected $search;
|
||||
protected $types;
|
||||
protected $instances = [];
|
||||
|
||||
/**
|
||||
* @param string|array $search Search path.
|
||||
*/
|
||||
public function __construct($search = 'blueprints://')
|
||||
{
|
||||
$this->search = $search;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get blueprint.
|
||||
*
|
||||
* @param string $type Blueprint type.
|
||||
* @return Blueprint
|
||||
* @throws \RuntimeException
|
||||
*/
|
||||
public function get($type)
|
||||
{
|
||||
if (!isset($this->instances[$type])) {
|
||||
$this->instances[$type] = $this->loadFile($type);
|
||||
}
|
||||
|
||||
return $this->instances[$type];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all available blueprint types.
|
||||
*
|
||||
* @return array List of type=>name
|
||||
*/
|
||||
public function types()
|
||||
{
|
||||
if ($this->types === null) {
|
||||
$this->types = array();
|
||||
|
||||
$grav = Grav::instance();
|
||||
|
||||
/** @var UniformResourceLocator $locator */
|
||||
$locator = $grav['locator'];
|
||||
|
||||
// Get stream / directory iterator.
|
||||
if ($locator->isStream($this->search)) {
|
||||
$iterator = $locator->getIterator($this->search);
|
||||
} else {
|
||||
$iterator = new \DirectoryIterator($this->search);
|
||||
}
|
||||
|
||||
/** @var \DirectoryIterator $file */
|
||||
foreach ($iterator as $file) {
|
||||
if (!$file->isFile() || '.' . $file->getExtension() !== YAML_EXT) {
|
||||
continue;
|
||||
}
|
||||
$name = $file->getBasename(YAML_EXT);
|
||||
$this->types[$name] = ucfirst(str_replace('_', ' ', $name));
|
||||
}
|
||||
}
|
||||
|
||||
return $this->types;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Load blueprint file.
|
||||
*
|
||||
* @param string $name Name of the blueprint.
|
||||
* @return Blueprint
|
||||
*/
|
||||
protected function loadFile($name)
|
||||
{
|
||||
$blueprint = new Blueprint($name);
|
||||
|
||||
if (is_array($this->search) || is_object($this->search)) {
|
||||
// Page types.
|
||||
$blueprint->setOverrides($this->search);
|
||||
$blueprint->setContext('blueprints://pages');
|
||||
} else {
|
||||
$blueprint->setContext($this->search);
|
||||
}
|
||||
|
||||
return $blueprint->load()->init();
|
||||
}
|
||||
}
|
||||
287
system/src/Grav/Common/Data/Data.php
Normal file
287
system/src/Grav/Common/Data/Data.php
Normal file
@@ -0,0 +1,287 @@
|
||||
<?php
|
||||
/**
|
||||
* @package Grav.Common.Data
|
||||
*
|
||||
* @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved.
|
||||
* @license MIT License; see LICENSE file for details.
|
||||
*/
|
||||
|
||||
namespace Grav\Common\Data;
|
||||
|
||||
use RocketTheme\Toolbox\ArrayTraits\Countable;
|
||||
use RocketTheme\Toolbox\ArrayTraits\Export;
|
||||
use RocketTheme\Toolbox\ArrayTraits\ExportInterface;
|
||||
use RocketTheme\Toolbox\ArrayTraits\NestedArrayAccessWithGetters;
|
||||
use RocketTheme\Toolbox\File\File;
|
||||
use RocketTheme\Toolbox\File\FileInterface;
|
||||
|
||||
class Data implements DataInterface, \ArrayAccess, \Countable, ExportInterface
|
||||
{
|
||||
use NestedArrayAccessWithGetters, Countable, Export;
|
||||
|
||||
protected $gettersVariable = 'items';
|
||||
protected $items;
|
||||
|
||||
/**
|
||||
* @var Blueprints
|
||||
*/
|
||||
protected $blueprints;
|
||||
|
||||
/**
|
||||
* @var File
|
||||
*/
|
||||
protected $storage;
|
||||
|
||||
/**
|
||||
* @param array $items
|
||||
* @param Blueprint|callable $blueprints
|
||||
*/
|
||||
public function __construct(array $items = array(), $blueprints = null)
|
||||
{
|
||||
$this->items = $items;
|
||||
$this->blueprints = $blueprints;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get value by using dot notation for nested arrays/objects.
|
||||
*
|
||||
* @example $value = $data->value('this.is.my.nested.variable');
|
||||
*
|
||||
* @param string $name Dot separated path to the requested value.
|
||||
* @param mixed $default Default value (or null).
|
||||
* @param string $separator Separator, defaults to '.'
|
||||
* @return mixed Value.
|
||||
*/
|
||||
public function value($name, $default = null, $separator = '.')
|
||||
{
|
||||
return $this->get($name, $default, $separator);
|
||||
}
|
||||
|
||||
/**
|
||||
* Join nested values together by using blueprints.
|
||||
*
|
||||
* @param string $name Dot separated path to the requested value.
|
||||
* @param mixed $value Value to be joined.
|
||||
* @param string $separator Separator, defaults to '.'
|
||||
* @return $this
|
||||
* @throws \RuntimeException
|
||||
*/
|
||||
public function join($name, $value, $separator = '.')
|
||||
{
|
||||
$old = $this->get($name, null, $separator);
|
||||
if ($old !== null) {
|
||||
if (!is_array($old)) {
|
||||
throw new \RuntimeException('Value ' . $old);
|
||||
}
|
||||
if (is_object($value)) {
|
||||
$value = (array) $value;
|
||||
} elseif (!is_array($value)) {
|
||||
throw new \RuntimeException('Value ' . $value);
|
||||
}
|
||||
$value = $this->blueprints()->mergeData($old, $value, $name, $separator);
|
||||
}
|
||||
|
||||
$this->set($name, $value, $separator);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get nested structure containing default values defined in the blueprints.
|
||||
*
|
||||
* Fields without default value are ignored in the list.
|
||||
|
||||
* @return array
|
||||
*/
|
||||
public function getDefaults()
|
||||
{
|
||||
return $this->blueprints()->getDefaults();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set default values by using blueprints.
|
||||
*
|
||||
* @param string $name Dot separated path to the requested value.
|
||||
* @param mixed $value Value to be joined.
|
||||
* @param string $separator Separator, defaults to '.'
|
||||
* @return $this
|
||||
*/
|
||||
public function joinDefaults($name, $value, $separator = '.')
|
||||
{
|
||||
if (is_object($value)) {
|
||||
$value = (array) $value;
|
||||
}
|
||||
$old = $this->get($name, null, $separator);
|
||||
if ($old !== null) {
|
||||
$value = $this->blueprints()->mergeData($value, $old, $name, $separator);
|
||||
}
|
||||
|
||||
$this->set($name, $value, $separator);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get value from the configuration and join it with given data.
|
||||
*
|
||||
* @param string $name Dot separated path to the requested value.
|
||||
* @param array $value Value to be joined.
|
||||
* @param string $separator Separator, defaults to '.'
|
||||
* @return array
|
||||
* @throws \RuntimeException
|
||||
*/
|
||||
public function getJoined($name, $value, $separator = '.')
|
||||
{
|
||||
if (is_object($value)) {
|
||||
$value = (array) $value;
|
||||
} elseif (!is_array($value)) {
|
||||
throw new \RuntimeException('Value ' . $value);
|
||||
}
|
||||
|
||||
$old = $this->get($name, null, $separator);
|
||||
|
||||
if ($old === null) {
|
||||
// No value set; no need to join data.
|
||||
return $value;
|
||||
}
|
||||
|
||||
if (!is_array($old)) {
|
||||
throw new \RuntimeException('Value ' . $old);
|
||||
}
|
||||
|
||||
// Return joined data.
|
||||
return $this->blueprints()->mergeData($old, $value, $name, $separator);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Merge two configurations together.
|
||||
*
|
||||
* @param array $data
|
||||
* @return $this
|
||||
*/
|
||||
public function merge(array $data)
|
||||
{
|
||||
$this->items = $this->blueprints()->mergeData($this->items, $data);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set default values to the configuration if variables were not set.
|
||||
*
|
||||
* @param array $data
|
||||
* @return $this
|
||||
*/
|
||||
public function setDefaults(array $data)
|
||||
{
|
||||
$this->items = $this->blueprints()->mergeData($data, $this->items);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate by blueprints.
|
||||
*
|
||||
* @return $this
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function validate()
|
||||
{
|
||||
$this->blueprints()->validate($this->items);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return $this
|
||||
* Filter all items by using blueprints.
|
||||
*/
|
||||
public function filter()
|
||||
{
|
||||
$this->items = $this->blueprints()->filter($this->items);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get extra items which haven't been defined in blueprints.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function extra()
|
||||
{
|
||||
return $this->blueprints()->extra($this->items);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return blueprints.
|
||||
*
|
||||
* @return Blueprint
|
||||
*/
|
||||
public function blueprints()
|
||||
{
|
||||
if (!$this->blueprints){
|
||||
$this->blueprints = new Blueprint;
|
||||
} elseif (is_callable($this->blueprints)) {
|
||||
// Lazy load blueprints.
|
||||
$blueprints = $this->blueprints;
|
||||
$this->blueprints = $blueprints();
|
||||
}
|
||||
return $this->blueprints;
|
||||
}
|
||||
|
||||
/**
|
||||
* Save data if storage has been defined.
|
||||
* @throws \RuntimeException
|
||||
*/
|
||||
public function save()
|
||||
{
|
||||
$file = $this->file();
|
||||
if ($file) {
|
||||
$file->save($this->items);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the data already exists in the storage.
|
||||
*
|
||||
* NOTE: This method does not check if the data is current.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function exists()
|
||||
{
|
||||
$file = $this->file();
|
||||
|
||||
return $file && $file->exists();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return unmodified data as raw string.
|
||||
*
|
||||
* NOTE: This function only returns data which has been saved to the storage.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function raw()
|
||||
{
|
||||
$file = $this->file();
|
||||
|
||||
return $file ? $file->raw() : '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Set or get the data storage.
|
||||
*
|
||||
* @param FileInterface $storage Optionally enter a new storage.
|
||||
* @return FileInterface
|
||||
*/
|
||||
public function file(FileInterface $storage = null)
|
||||
{
|
||||
if ($storage) {
|
||||
$this->storage = $storage;
|
||||
}
|
||||
return $this->storage;
|
||||
}
|
||||
}
|
||||
69
system/src/Grav/Common/Data/DataInterface.php
Normal file
69
system/src/Grav/Common/Data/DataInterface.php
Normal file
@@ -0,0 +1,69 @@
|
||||
<?php
|
||||
/**
|
||||
* @package Grav.Common.Data
|
||||
*
|
||||
* @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved.
|
||||
* @license MIT License; see LICENSE file for details.
|
||||
*/
|
||||
|
||||
namespace Grav\Common\Data;
|
||||
|
||||
use RocketTheme\Toolbox\File\FileInterface;
|
||||
|
||||
interface DataInterface
|
||||
{
|
||||
/**
|
||||
* Get value by using dot notation for nested arrays/objects.
|
||||
*
|
||||
* @example $value = $data->value('this.is.my.nested.variable');
|
||||
*
|
||||
* @param string $name Dot separated path to the requested value.
|
||||
* @param mixed $default Default value (or null).
|
||||
* @param string $separator Separator, defaults to '.'
|
||||
* @return mixed Value.
|
||||
*/
|
||||
public function value($name, $default = null, $separator = '.');
|
||||
|
||||
/**
|
||||
* Merge external data.
|
||||
*
|
||||
* @param array $data
|
||||
* @return mixed
|
||||
*/
|
||||
public function merge(array $data);
|
||||
|
||||
/**
|
||||
* Return blueprints.
|
||||
*/
|
||||
public function blueprints();
|
||||
|
||||
/**
|
||||
* Validate by blueprints.
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function validate();
|
||||
|
||||
/**
|
||||
* Filter all items by using blueprints.
|
||||
*/
|
||||
public function filter();
|
||||
|
||||
/**
|
||||
* Get extra items which haven't been defined in blueprints.
|
||||
*/
|
||||
public function extra();
|
||||
|
||||
/**
|
||||
* Save data into the file.
|
||||
*/
|
||||
public function save();
|
||||
|
||||
/**
|
||||
* Set or get the data storage.
|
||||
*
|
||||
* @param FileInterface $storage Optionally enter a new storage.
|
||||
* @return FileInterface
|
||||
*/
|
||||
public function file(FileInterface $storage = null);
|
||||
}
|
||||
765
system/src/Grav/Common/Data/Validation.php
Normal file
765
system/src/Grav/Common/Data/Validation.php
Normal file
@@ -0,0 +1,765 @@
|
||||
<?php
|
||||
/**
|
||||
* @package Grav.Common.Data
|
||||
*
|
||||
* @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved.
|
||||
* @license MIT License; see LICENSE file for details.
|
||||
*/
|
||||
|
||||
namespace Grav\Common\Data;
|
||||
|
||||
use Grav\Common\Grav;
|
||||
use Grav\Common\Utils;
|
||||
use Grav\Common\Yaml;
|
||||
use RocketTheme\Toolbox\Compat\Yaml\Yaml as FallbackYaml;
|
||||
|
||||
class Validation
|
||||
{
|
||||
/**
|
||||
* Validate value against a blueprint field definition.
|
||||
*
|
||||
* @param $value
|
||||
* @param array $field
|
||||
* @return array
|
||||
*/
|
||||
public static function validate($value, array $field)
|
||||
{
|
||||
$messages = [];
|
||||
|
||||
$validate = isset($field['validate']) ? (array) $field['validate'] : [];
|
||||
// Validate type with fallback type text.
|
||||
$type = (string) isset($validate['type']) ? $validate['type'] : $field['type'];
|
||||
$method = 'type'.strtr($type, '-', '_');
|
||||
|
||||
// If value isn't required, we will stop validation if empty value is given.
|
||||
if ((empty($validate['required']) || (isset($validate['required']) && $validate['required'] !== true)) && ($value === null || $value === '' || (($field['type'] === 'checkbox' || $field['type'] === 'switch') && $value == false))) {
|
||||
return $messages;
|
||||
}
|
||||
|
||||
if (!isset($field['type'])) {
|
||||
$field['type'] = 'text';
|
||||
}
|
||||
|
||||
// Get language class.
|
||||
$language = Grav::instance()['language'];
|
||||
|
||||
$name = ucfirst(isset($field['label']) ? $field['label'] : $field['name']);
|
||||
$message = (string) isset($field['validate']['message'])
|
||||
? $language->translate($field['validate']['message'])
|
||||
: $language->translate('FORM.INVALID_INPUT', null, true) . ' "' . $language->translate($name) . '"';
|
||||
|
||||
|
||||
// If this is a YAML field validate/filter as such
|
||||
if ($type != 'yaml' && isset($field['yaml']) && $field['yaml'] === true) {
|
||||
$method = 'typeYaml';
|
||||
}
|
||||
|
||||
if (method_exists(__CLASS__, $method)) {
|
||||
$success = self::$method($value, $validate, $field);
|
||||
} else {
|
||||
$success = true;
|
||||
}
|
||||
|
||||
if (!$success) {
|
||||
$messages[$field['name']][] = $message;
|
||||
}
|
||||
|
||||
// Check individual rules.
|
||||
foreach ($validate as $rule => $params) {
|
||||
$method = 'validate' . ucfirst(strtr($rule, '-', '_'));
|
||||
|
||||
if (method_exists(__CLASS__, $method)) {
|
||||
$success = self::$method($value, $params);
|
||||
|
||||
if (!$success) {
|
||||
$messages[$field['name']][] = $message;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $messages;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter value against a blueprint field definition.
|
||||
*
|
||||
* @param mixed $value
|
||||
* @param array $field
|
||||
* @return mixed Filtered value.
|
||||
*/
|
||||
public static function filter($value, array $field)
|
||||
{
|
||||
$validate = isset($field['validate']) ? (array) $field['validate'] : [];
|
||||
|
||||
// If value isn't required, we will return null if empty value is given.
|
||||
if (empty($validate['required']) && ($value === null || $value === '')) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!isset($field['type'])) {
|
||||
$field['type'] = 'text';
|
||||
}
|
||||
|
||||
|
||||
// Validate type with fallback type text.
|
||||
$type = (string) isset($field['validate']['type']) ? $field['validate']['type'] : $field['type'];
|
||||
$method = 'filter' . ucfirst(strtr($type, '-', '_'));
|
||||
|
||||
// If this is a YAML field validate/filter as such
|
||||
if ($type !== 'yaml' && isset($field['yaml']) && $field['yaml'] === true) {
|
||||
$method = 'filterYaml';
|
||||
}
|
||||
|
||||
if (!method_exists(__CLASS__, $method)) {
|
||||
$method = 'filterText';
|
||||
}
|
||||
|
||||
return self::$method($value, $validate, $field);
|
||||
}
|
||||
|
||||
/**
|
||||
* HTML5 input: text
|
||||
*
|
||||
* @param mixed $value Value to be validated.
|
||||
* @param array $params Validation parameters.
|
||||
* @param array $field Blueprint for the field.
|
||||
* @return bool True if validation succeeded.
|
||||
*/
|
||||
public static function typeText($value, array $params, array $field)
|
||||
{
|
||||
if (!is_string($value) && !is_numeric($value)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$value = (string)$value;
|
||||
|
||||
if (isset($params['min']) && strlen($value) < $params['min']) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (isset($params['max']) && strlen($value) > $params['max']) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$min = isset($params['min']) ? $params['min'] : 0;
|
||||
if (isset($params['step']) && (strlen($value) - $min) % $params['step'] == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((!isset($params['multiline']) || !$params['multiline']) && preg_match('/\R/um', $value)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
protected static function filterText($value, array $params, array $field)
|
||||
{
|
||||
return (string) $value;
|
||||
}
|
||||
|
||||
protected static function filterCommaList($value, array $params, array $field)
|
||||
{
|
||||
return is_array($value) ? $value : preg_split('/\s*,\s*/', $value, -1, PREG_SPLIT_NO_EMPTY);
|
||||
}
|
||||
|
||||
public static function typeCommaList($value, array $params, array $field)
|
||||
{
|
||||
return is_array($value) ? true : self::typeText($value, $params, $field);
|
||||
}
|
||||
|
||||
protected static function filterLower($value, array $params)
|
||||
{
|
||||
return strtolower($value);
|
||||
}
|
||||
|
||||
protected static function filterUpper($value, array $params)
|
||||
{
|
||||
return strtoupper($value);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* HTML5 input: textarea
|
||||
*
|
||||
* @param mixed $value Value to be validated.
|
||||
* @param array $params Validation parameters.
|
||||
* @param array $field Blueprint for the field.
|
||||
* @return bool True if validation succeeded.
|
||||
*/
|
||||
public static function typeTextarea($value, array $params, array $field)
|
||||
{
|
||||
if (!isset($params['multiline'])) {
|
||||
$params['multiline'] = true;
|
||||
}
|
||||
|
||||
return self::typeText($value, $params, $field);
|
||||
}
|
||||
|
||||
/**
|
||||
* HTML5 input: password
|
||||
*
|
||||
* @param mixed $value Value to be validated.
|
||||
* @param array $params Validation parameters.
|
||||
* @param array $field Blueprint for the field.
|
||||
* @return bool True if validation succeeded.
|
||||
*/
|
||||
public static function typePassword($value, array $params, array $field)
|
||||
{
|
||||
return self::typeText($value, $params, $field);
|
||||
}
|
||||
|
||||
/**
|
||||
* HTML5 input: hidden
|
||||
*
|
||||
* @param mixed $value Value to be validated.
|
||||
* @param array $params Validation parameters.
|
||||
* @param array $field Blueprint for the field.
|
||||
* @return bool True if validation succeeded.
|
||||
*/
|
||||
public static function typeHidden($value, array $params, array $field)
|
||||
{
|
||||
return self::typeText($value, $params, $field);
|
||||
}
|
||||
|
||||
/**
|
||||
* Custom input: checkbox list
|
||||
*
|
||||
* @param mixed $value Value to be validated.
|
||||
* @param array $params Validation parameters.
|
||||
* @param array $field Blueprint for the field.
|
||||
* @return bool True if validation succeeded.
|
||||
*/
|
||||
public static function typeCheckboxes($value, array $params, array $field)
|
||||
{
|
||||
// Set multiple: true so checkboxes can easily use min/max counts to control number of options required
|
||||
$field['multiple'] = true;
|
||||
return self::typeArray((array) $value, $params, $field);
|
||||
}
|
||||
|
||||
protected static function filterCheckboxes($value, array $params, array $field)
|
||||
{
|
||||
return self::filterArray($value, $params, $field);
|
||||
}
|
||||
|
||||
/**
|
||||
* HTML5 input: checkbox
|
||||
*
|
||||
* @param mixed $value Value to be validated.
|
||||
* @param array $params Validation parameters.
|
||||
* @param array $field Blueprint for the field.
|
||||
* @return bool True if validation succeeded.
|
||||
*/
|
||||
public static function typeCheckbox($value, array $params, array $field)
|
||||
{
|
||||
$value = (string) $value;
|
||||
|
||||
if (!isset($field['value'])) {
|
||||
$field['value'] = 1;
|
||||
}
|
||||
if (isset($value) && $value != $field['value']) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* HTML5 input: radio
|
||||
*
|
||||
* @param mixed $value Value to be validated.
|
||||
* @param array $params Validation parameters.
|
||||
* @param array $field Blueprint for the field.
|
||||
* @return bool True if validation succeeded.
|
||||
*/
|
||||
public static function typeRadio($value, array $params, array $field)
|
||||
{
|
||||
return self::typeArray((array) $value, $params, $field);
|
||||
}
|
||||
|
||||
/**
|
||||
* Custom input: toggle
|
||||
*
|
||||
* @param mixed $value Value to be validated.
|
||||
* @param array $params Validation parameters.
|
||||
* @param array $field Blueprint for the field.
|
||||
* @return bool True if validation succeeded.
|
||||
*/
|
||||
public static function typeToggle($value, array $params, array $field)
|
||||
{
|
||||
return self::typeArray((array) $value, $params, $field);
|
||||
}
|
||||
|
||||
/**
|
||||
* Custom input: file
|
||||
*
|
||||
* @param mixed $value Value to be validated.
|
||||
* @param array $params Validation parameters.
|
||||
* @param array $field Blueprint for the field.
|
||||
* @return bool True if validation succeeded.
|
||||
*/
|
||||
public static function typeFile($value, array $params, array $field)
|
||||
{
|
||||
return self::typeArray((array) $value, $params, $field);
|
||||
}
|
||||
|
||||
protected static function filterFile($value, array $params, array $field)
|
||||
{
|
||||
return (array) $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* HTML5 input: select
|
||||
*
|
||||
* @param mixed $value Value to be validated.
|
||||
* @param array $params Validation parameters.
|
||||
* @param array $field Blueprint for the field.
|
||||
* @return bool True if validation succeeded.
|
||||
*/
|
||||
public static function typeSelect($value, array $params, array $field)
|
||||
{
|
||||
return self::typeArray((array) $value, $params, $field);
|
||||
}
|
||||
|
||||
/**
|
||||
* HTML5 input: number
|
||||
*
|
||||
* @param mixed $value Value to be validated.
|
||||
* @param array $params Validation parameters.
|
||||
* @param array $field Blueprint for the field.
|
||||
* @return bool True if validation succeeded.
|
||||
*/
|
||||
public static function typeNumber($value, array $params, array $field)
|
||||
{
|
||||
if (!is_numeric($value)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (isset($params['min']) && $value < $params['min']) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (isset($params['max']) && $value > $params['max']) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$min = isset($params['min']) ? $params['min'] : 0;
|
||||
if (isset($params['step']) && fmod($value - $min, $params['step']) == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
protected static function filterNumber($value, array $params, array $field)
|
||||
{
|
||||
return (string)(int)$value !== (string)(float)$value ? (float) $value : (int) $value;
|
||||
}
|
||||
|
||||
protected static function filterDateTime($value, array $params, array $field)
|
||||
{
|
||||
$format = Grav::instance()['config']->get('system.pages.dateformat.default');
|
||||
if ($format) {
|
||||
$converted = new \DateTime($value);
|
||||
return $converted->format($format);
|
||||
}
|
||||
return $value;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* HTML5 input: range
|
||||
*
|
||||
* @param mixed $value Value to be validated.
|
||||
* @param array $params Validation parameters.
|
||||
* @param array $field Blueprint for the field.
|
||||
* @return bool True if validation succeeded.
|
||||
*/
|
||||
public static function typeRange($value, array $params, array $field)
|
||||
{
|
||||
return self::typeNumber($value, $params, $field);
|
||||
}
|
||||
|
||||
protected static function filterRange($value, array $params, array $field)
|
||||
{
|
||||
return self::filterNumber($value, $params, $field);
|
||||
}
|
||||
|
||||
/**
|
||||
* HTML5 input: color
|
||||
*
|
||||
* @param mixed $value Value to be validated.
|
||||
* @param array $params Validation parameters.
|
||||
* @param array $field Blueprint for the field.
|
||||
* @return bool True if validation succeeded.
|
||||
*/
|
||||
public static function typeColor($value, array $params, array $field)
|
||||
{
|
||||
return preg_match('/^\#[0-9a-fA-F]{3}[0-9a-fA-F]{3}?$/u', $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* HTML5 input: email
|
||||
*
|
||||
* @param mixed $value Value to be validated.
|
||||
* @param array $params Validation parameters.
|
||||
* @param array $field Blueprint for the field.
|
||||
* @return bool True if validation succeeded.
|
||||
*/
|
||||
public static function typeEmail($value, array $params, array $field)
|
||||
{
|
||||
$values = !is_array($value) ? explode(',', preg_replace('/\s+/', '', $value)) : $value;
|
||||
|
||||
foreach ($values as $value) {
|
||||
if (!(self::typeText($value, $params, $field) && filter_var($value, FILTER_VALIDATE_EMAIL))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* HTML5 input: url
|
||||
*
|
||||
* @param mixed $value Value to be validated.
|
||||
* @param array $params Validation parameters.
|
||||
* @param array $field Blueprint for the field.
|
||||
* @return bool True if validation succeeded.
|
||||
*/
|
||||
|
||||
public static function typeUrl($value, array $params, array $field)
|
||||
{
|
||||
return self::typeText($value, $params, $field) && filter_var($value, FILTER_VALIDATE_URL);
|
||||
}
|
||||
|
||||
/**
|
||||
* HTML5 input: datetime
|
||||
*
|
||||
* @param mixed $value Value to be validated.
|
||||
* @param array $params Validation parameters.
|
||||
* @param array $field Blueprint for the field.
|
||||
* @return bool True if validation succeeded.
|
||||
*/
|
||||
public static function typeDatetime($value, array $params, array $field)
|
||||
{
|
||||
if ($value instanceof \DateTime) {
|
||||
return true;
|
||||
} elseif (!is_string($value)) {
|
||||
return false;
|
||||
} elseif (!isset($params['format'])) {
|
||||
return false !== strtotime($value);
|
||||
}
|
||||
|
||||
$dateFromFormat = \DateTime::createFromFormat($params['format'], $value);
|
||||
|
||||
return $dateFromFormat && $value === date($params['format'], $dateFromFormat->getTimestamp());
|
||||
}
|
||||
|
||||
/**
|
||||
* HTML5 input: datetime-local
|
||||
*
|
||||
* @param mixed $value Value to be validated.
|
||||
* @param array $params Validation parameters.
|
||||
* @param array $field Blueprint for the field.
|
||||
* @return bool True if validation succeeded.
|
||||
*/
|
||||
public static function typeDatetimeLocal($value, array $params, array $field)
|
||||
{
|
||||
return self::typeDatetime($value, $params, $field);
|
||||
}
|
||||
|
||||
/**
|
||||
* HTML5 input: date
|
||||
*
|
||||
* @param mixed $value Value to be validated.
|
||||
* @param array $params Validation parameters.
|
||||
* @param array $field Blueprint for the field.
|
||||
* @return bool True if validation succeeded.
|
||||
*/
|
||||
public static function typeDate($value, array $params, array $field)
|
||||
{
|
||||
$params = array($params);
|
||||
if (!isset($params['format'])) {
|
||||
$params['format'] = 'Y-m-d';
|
||||
}
|
||||
return self::typeDatetime($value, $params, $field);
|
||||
}
|
||||
|
||||
/**
|
||||
* HTML5 input: time
|
||||
*
|
||||
* @param mixed $value Value to be validated.
|
||||
* @param array $params Validation parameters.
|
||||
* @param array $field Blueprint for the field.
|
||||
* @return bool True if validation succeeded.
|
||||
*/
|
||||
public static function typeTime($value, array $params, array $field)
|
||||
{
|
||||
$params = array($params);
|
||||
if (!isset($params['format'])) {
|
||||
$params['format'] = 'H:i';
|
||||
}
|
||||
return self::typeDatetime($value, $params, $field);
|
||||
}
|
||||
|
||||
/**
|
||||
* HTML5 input: month
|
||||
*
|
||||
* @param mixed $value Value to be validated.
|
||||
* @param array $params Validation parameters.
|
||||
* @param array $field Blueprint for the field.
|
||||
* @return bool True if validation succeeded.
|
||||
*/
|
||||
public static function typeMonth($value, array $params, array $field)
|
||||
{
|
||||
$params = array($params);
|
||||
if (!isset($params['format'])) {
|
||||
$params['format'] = 'Y-m';
|
||||
}
|
||||
return self::typeDatetime($value, $params, $field);
|
||||
}
|
||||
|
||||
/**
|
||||
* HTML5 input: week
|
||||
*
|
||||
* @param mixed $value Value to be validated.
|
||||
* @param array $params Validation parameters.
|
||||
* @param array $field Blueprint for the field.
|
||||
* @return bool True if validation succeeded.
|
||||
*/
|
||||
public static function typeWeek($value, array $params, array $field)
|
||||
{
|
||||
if (!isset($params['format']) && !preg_match('/^\d{4}-W\d{2}$/u', $value)) {
|
||||
return false;
|
||||
}
|
||||
return self::typeDatetime($value, $params, $field);
|
||||
}
|
||||
|
||||
/**
|
||||
* Custom input: array
|
||||
*
|
||||
* @param mixed $value Value to be validated.
|
||||
* @param array $params Validation parameters.
|
||||
* @param array $field Blueprint for the field.
|
||||
* @return bool True if validation succeeded.
|
||||
*/
|
||||
public static function typeArray($value, array $params, array $field)
|
||||
{
|
||||
if (!is_array($value)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (isset($field['multiple'])) {
|
||||
if (isset($params['min']) && count($value) < $params['min']) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (isset($params['max']) && count($value) > $params['max']) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$min = isset($params['min']) ? $params['min'] : 0;
|
||||
if (isset($params['step']) && (count($value) - $min) % $params['step'] == 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
$options = isset($field['options']) ? array_keys($field['options']) : array();
|
||||
$values = isset($field['use']) && $field['use'] == 'keys' ? array_keys($value) : $value;
|
||||
if ($options && array_diff($values, $options)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
protected static function filterArray($value, $params, $field)
|
||||
{
|
||||
$values = (array) $value;
|
||||
$options = isset($field['options']) ? array_keys($field['options']) : array();
|
||||
$multi = isset($field['multiple']) ? $field['multiple'] : false;
|
||||
|
||||
if (count($values) == 1 && isset($values[0]) && $values[0] == '') {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
if ($options) {
|
||||
$useKey = isset($field['use']) && $field['use'] == 'keys';
|
||||
foreach ($values as $key => $value) {
|
||||
$values[$key] = $useKey ? (bool) $value : $value;
|
||||
}
|
||||
}
|
||||
|
||||
if ($multi) {
|
||||
foreach ($values as $key => $value) {
|
||||
if (is_array($value)) {
|
||||
$value = implode(',', $value);
|
||||
$values[$key] = array_map('trim', explode(',', $value));
|
||||
} else {
|
||||
$values[$key] = trim($value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($field['ignore_empty']) && Utils::isPositive($field['ignore_empty'])) {
|
||||
foreach ($values as $key => $value) {
|
||||
foreach ($value as $inner_key => $inner_value) {
|
||||
if ($inner_value == '') {
|
||||
unset($value[$inner_key]);
|
||||
}
|
||||
}
|
||||
|
||||
$values[$key] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
return $values;
|
||||
}
|
||||
|
||||
public static function typeList($value, array $params, array $field)
|
||||
{
|
||||
if (!is_array($value)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (isset($field['fields'])) {
|
||||
foreach ($value as $key => $item) {
|
||||
foreach ($field['fields'] as $subKey => $subField) {
|
||||
$subKey = trim($subKey, '.');
|
||||
$subValue = isset($item[$subKey]) ? $item[$subKey] : null;
|
||||
self::validate($subValue, $subField);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
protected static function filterList($value, array $params, array $field)
|
||||
{
|
||||
return (array) $value;
|
||||
}
|
||||
|
||||
public static function filterYaml($value, $params)
|
||||
{
|
||||
if (!is_string($value)) {
|
||||
return $value;
|
||||
}
|
||||
|
||||
return (array) Yaml::parse($value);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Custom input: ignore (will not validate)
|
||||
*
|
||||
* @param mixed $value Value to be validated.
|
||||
* @param array $params Validation parameters.
|
||||
* @param array $field Blueprint for the field.
|
||||
* @return bool True if validation succeeded.
|
||||
*/
|
||||
public static function typeIgnore($value, array $params, array $field)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public static function filterIgnore($value, array $params, array $field)
|
||||
{
|
||||
return $value;
|
||||
}
|
||||
|
||||
|
||||
// HTML5 attributes (min, max and range are handled inside the types)
|
||||
|
||||
public static function validateRequired($value, $params)
|
||||
{
|
||||
if (is_scalar($value)) {
|
||||
return (bool) $params !== true || $value !== '';
|
||||
} else {
|
||||
return (bool) $params !== true || !empty($value);
|
||||
}
|
||||
}
|
||||
|
||||
public static function validatePattern($value, $params)
|
||||
{
|
||||
return (bool) preg_match("`^{$params}$`u", $value);
|
||||
}
|
||||
|
||||
|
||||
// Internal types
|
||||
|
||||
public static function validateAlpha($value, $params)
|
||||
{
|
||||
return ctype_alpha($value);
|
||||
}
|
||||
|
||||
public static function validateAlnum($value, $params)
|
||||
{
|
||||
return ctype_alnum($value);
|
||||
}
|
||||
|
||||
public static function typeBool($value, $params)
|
||||
{
|
||||
return is_bool($value) || $value == 1 || $value == 0;
|
||||
}
|
||||
|
||||
public static function validateBool($value, $params)
|
||||
{
|
||||
return is_bool($value) || $value == 1 || $value == 0;
|
||||
}
|
||||
|
||||
protected static function filterBool($value, $params)
|
||||
{
|
||||
return (bool) $value;
|
||||
}
|
||||
|
||||
public static function validateDigit($value, $params)
|
||||
{
|
||||
return ctype_digit($value);
|
||||
}
|
||||
|
||||
public static function validateFloat($value, $params)
|
||||
{
|
||||
return is_float(filter_var($value, FILTER_VALIDATE_FLOAT));
|
||||
}
|
||||
|
||||
protected static function filterFloat($value, $params)
|
||||
{
|
||||
return (float) $value;
|
||||
}
|
||||
|
||||
public static function validateHex($value, $params)
|
||||
{
|
||||
return ctype_xdigit($value);
|
||||
}
|
||||
|
||||
public static function validateInt($value, $params)
|
||||
{
|
||||
return is_numeric($value) && (int) $value == $value;
|
||||
}
|
||||
|
||||
protected static function filterInt($value, $params)
|
||||
{
|
||||
return (int) $value;
|
||||
}
|
||||
|
||||
public static function validateArray($value, $params)
|
||||
{
|
||||
return is_array($value)
|
||||
|| ($value instanceof \ArrayAccess
|
||||
&& $value instanceof \Traversable
|
||||
&& $value instanceof \Countable);
|
||||
}
|
||||
|
||||
public static function filterItem_List($value, $params)
|
||||
{
|
||||
return array_values(array_filter($value, function($v) { return !empty($v); } ));
|
||||
}
|
||||
|
||||
public static function validateJson($value, $params)
|
||||
{
|
||||
return (bool) (@json_decode($value));
|
||||
}
|
||||
}
|
||||
37
system/src/Grav/Common/Data/ValidationException.php
Normal file
37
system/src/Grav/Common/Data/ValidationException.php
Normal file
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
/**
|
||||
* @package Grav.Common.Data
|
||||
*
|
||||
* @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved.
|
||||
* @license MIT License; see LICENSE file for details.
|
||||
*/
|
||||
|
||||
namespace Grav\Common\Data;
|
||||
|
||||
use Grav\Common\Grav;
|
||||
|
||||
class ValidationException extends \RuntimeException
|
||||
{
|
||||
protected $messages = [];
|
||||
|
||||
public function setMessages(array $messages = []) {
|
||||
$this->messages = $messages;
|
||||
|
||||
$language = Grav::instance()['language'];
|
||||
$this->message = $language->translate('FORM.VALIDATION_FAIL', null, true) . ' ' . $this->message;
|
||||
|
||||
foreach ($messages as $variable => &$list) {
|
||||
$list = array_unique($list);
|
||||
foreach ($list as $message) {
|
||||
$this->message .= "<br/>$message";
|
||||
}
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getMessages()
|
||||
{
|
||||
return $this->messages;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user