Merge branch 'feature/blueprint-extend' into develop

Conflicts:
	system/blueprints/config/site.yaml
	system/blueprints/config/system.yaml
This commit is contained in:
Andy Miller
2015-07-28 18:36:02 -06:00
11 changed files with 497 additions and 67 deletions

View File

@@ -0,0 +1,76 @@
title: Blog
@extends:
type: default
context: blueprints://pages
child_type: item
title: Default
rules:
slug:
pattern: "[a-z][a-z0-9_\-]+"
min: 2
max: 80
form:
fields:
tabs:
type: tabs
active: 1
fields:
advanced:
fields:
overrides:
fields:
header.child_type:
default: item
blog:
type: tab
title: Blog List
fields:
header.content.items:
type: select
label: Items
default: @self.children
options:
@self.children: Children
header.content.limit:
type: text
label: Max Item Count
default: 5
validate:
required: true
type: int
min: 1
header.content.order.by:
type: select
label: Order By
default: date
options:
folder: Folder
title: Title
date: Date
default: Default
header.content.order.dir:
type: select
label: Order
default: desc
options:
asc: Ascending
desc: Descending
header.content.pagination:
type: toggle
label: Pagination
highlight: 1
default: 1
options:
1: Enabled
0: Disabled

View File

@@ -0,0 +1,186 @@
title: Default
rules:
slug:
pattern: "[a-z][a-z0-9_\-]+"
min: 2
max: 80
form:
fields:
type:
type: hidden
label: Page Type
default: default
tabs:
type: tabs
active: 1
fields:
content:
type: tab
title: Content
fields:
header.title:
type: text
label: Title
validate:
required: true
header.process:
type: checkboxes
label: Process
toggleable: true
@config-default: system.pages.process
default:
markdown: true
twig: false
options:
markdown: Markdown
twig: Twig
use: keys
content:
type: markdown
label: Content
validate:
type: textarea
uploads:
type: uploads
label: Page Media
meta:
type: tab
title: Meta
fields:
header.description:
type: textarea
label: Description
validate:
max: 120
header.keywords:
type: text
label: Keywords
validate:
max: 120
header.robots:
type: checkboxes
label: Robots
options:
noindex: No index
nofollow: No follow
use: keys
taxonomies:
type: section
title: Taxonomies
underline: true
fields:
header.taxonomy:
type: taxonomy
label: Taxonomy
validate:
type: array
advanced:
type: tab
title: Advanced
fields:
columns:
type: columns
fields:
column1:
type: column
fields:
folder:
type: text
label: Folder Name
validate:
type: slug
route:
type: select
label: Parent
classes: fancy
@data-options: '\Grav\Common\Page\Pages::parents'
@data-default: '\Grav\Plugin\admin::route'
options:
'/': '- Root -'
type:
type: select
classes: fancy
label: Display Template
default: default
@data-options: '\Grav\Common\Page\Pages::types'
column2:
type: column
fields:
order:
type: order
label: Ordering
sitemap:
overrides:
type: section
title: Overrides
underline: true
fields:
header.menu:
type: text
label: Menu
header.slug:
type: text
label: Alias
validate:
rule: slug
header.cache_enable:
type: toggle
label: Caching
highlight: 1
options:
'': Global
1: Enabled
0: Disabled
validate:
type: bool
header.routable:
type: toggle
label: Access by URL
highlight: 1
default: ''
options:
'': Global
1: Enabled
0: Disabled
validate:
type: bool
header.child_type:
type: select
label: Default Child Type
default: default
@data-options: '\Grav\Common\Page\Pages::types'
header.order_by:
type: hidden
header.order_manual:
type: hidden
validate:
type: commalist

View File

@@ -0,0 +1,83 @@
title: Item
@extends:
type: default
context: blueprints://pages
form:
fields:
tabs:
fields:
blog:
type: tab
title: Blog Item
fields:
publishing:
type: section
title: Publishing
underline: true
fields:
header.date:
type: datetime
label: Date
header.publish_date:
type: datetime
label: Publish post on this date
header.unpublish_date:
type: datetime
label: Unpublish post on this date
header.published:
type: toggle
label: Published
highlight: 1
default: 1
options:
1: Yes
0: No
summary:
type: section
title: Summary
underline: true
fields:
header.summary.enabled:
type: toggle
label: Summary
highlight: 1
default: 1
options:
1: Enabled
0: Disabled
header.summary.format:
type: select
label: Format
classes: fancy
default: short
options:
'short': 'Use the first occurence of delimter or size'
'long': 'Summary delimiter will be ignored'
header.summary.size:
type: text
label: Size
classes: large
default: 300
validate:
type: int
min: 1
header.summary.delimiter:
type: text
label: Summary delimiter
classes: large
default: ===
placeholder: ===

View File

@@ -0,0 +1,46 @@
title: Modular
@extends:
type: default
context: blueprints://pages
form:
fields:
tabs:
type: tabs
active: 1
fields:
content:
fields:
header.content.items:
type: select
label: Items
default: @self.modular
options:
@self.modular: Children
header.content.order.by:
type: select
label: Order By
default: date
options:
folder: Folder
title: Title
date: Date
default: Default
header.content.order.dir:
type: select
label: Order
default: desc
options:
asc: Ascending
desc: Descending
header.process:
type: ignore
content:
type: ignore
uploads:
type: ignore

View File

@@ -1,42 +0,0 @@
rules:
slug:
pattern: "[a-z][a-z0-9_\-]+"
min: 2
max: 80
form:
validation: loose
fields:
title:
type: text
label: Title
validate:
required: true
folder:
type: text
label: Folder
validate:
type: slug
required: true
route:
type: select
label: Parent
classes: fancy
@data-options: '\Grav\Common\Page\Pages::parents'
@data-default: '\Grav\Plugin\admin::route'
options:
'/': '- Root -'
validate:
required: true
type:
type: select
classes: fancy
label: Display Template
default: default
@data-options: '\Grav\Common\Page\Pages::types'
validate:
required: true

View File

@@ -1,6 +1,7 @@
<?php
namespace Grav\Common\Data;
use Grav\Common\GravTrait;
use RocketTheme\Toolbox\ArrayTraits\Export;
/**
@@ -11,7 +12,7 @@ use RocketTheme\Toolbox\ArrayTraits\Export;
*/
class Blueprint
{
use Export, DataMutatorTrait;
use Export, DataMutatorTrait, GravTrait;
public $name;
@@ -117,7 +118,17 @@ class Blueprint
{
// Initialize data
$this->fields();
return $this->extraArray($data, $this->nested, $prefix);
$rules = $this->nested;
// Drill down to prefix level
if (!empty($prefix)) {
$parts = explode('.', trim($prefix, '.'));
foreach ($parts as $part) {
$rules = isset($rules[$part]) ? $rules[$part] : [];
}
}
return $this->extraArray($data, $rules, $prefix);
}
/**
@@ -286,7 +297,7 @@ class Blueprint
// Item has been defined in blueprints.
} elseif (is_array($field) && is_array($val)) {
// Array has been defined in blueprints.
$array += $this->ExtraArray($field, $val, $prefix);
$array += $this->ExtraArray($field, $val, $prefix . $key . '.');
} else {
// Undefined/extra item.
$array[$prefix.$key] = $field;
@@ -313,11 +324,11 @@ class Blueprint
$field['name'] = $prefix . $key;
$field += $params;
if (isset($field['fields'])) {
if (isset($field['fields']) && $field['type'] !== 'list') {
// Recursively get all the nested fields.
$newParams = array_intersect_key($this->filter, $field);
$this->parseFormFields($field['fields'], $newParams, $prefix, $current[$key]['fields']);
} else {
} else if ($field['type'] !== 'ignore') {
// Add rule.
$this->rules[$prefix . $key] = &$field;
$this->addProperty($prefix . $key);
@@ -362,10 +373,20 @@ class Blueprint
}
}
}
elseif (substr($name, 0, 8) == '@config-') {
$property = substr($name, 8);
$default = isset($field[$property]) ? $field[$property] : null;
$config = self::getGrav()['config']->get($value, $default);
if (!is_null($config)) {
$field[$property] = $config;
}
}
}
// Initialize predefined validation rule.
if (isset($field['validate']['rule'])) {
if (isset($field['validate']['rule']) && $field['type'] !== 'ignore') {
$field['validate'] += $this->getRule($field['validate']['rule']);
}
}

View File

@@ -2,6 +2,7 @@
namespace Grav\Common\Data;
use Grav\Common\File\CompiledYamlFile;
use Grav\Common\GravTrait;
/**
* Blueprints class keeps track on blueprint instances.
@@ -11,6 +12,8 @@ use Grav\Common\File\CompiledYamlFile;
*/
class Blueprints
{
use GravTrait;
protected $search;
protected $types;
protected $instances = array();
@@ -55,8 +58,20 @@ class Blueprints
if (isset($blueprints['@extends'])) {
// Extend blueprint by other blueprints.
$extends = (array) $blueprints['@extends'];
foreach ($extends as $extendType) {
$blueprint->extend($this->get($extendType));
if (is_string(key($extends))) {
$extends = [ $extends ];
}
foreach ($extends as $extendConfig) {
$extendType = !is_string($extendConfig) ? empty($extendConfig['type']) ? false : $extendConfig['type'] : $extendConfig;
if (!$extendType) {
continue;
}
$context = is_string($extendConfig) || empty($extendConfig['context']) ? $this : new self(self::getGrav()['locator']->findResource($extendConfig['context']));
$blueprint->extend($context->get($extendType));
}
}

View File

@@ -503,6 +503,30 @@ class Validation
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;
}
/**
* Custom input: ignore (will not validate)
*
@@ -516,6 +540,11 @@ class Validation
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)

View File

@@ -539,7 +539,7 @@ class Page
return preg_replace($regex, '', $this->folder);
}
if ($name == 'type') {
return basename($this->name(), '.md');
return $this->template();
}
if ($name == 'media') {
return $this->media()->all();
@@ -725,7 +725,7 @@ class Page
public function extra()
{
$blueprints = $this->blueprints();
return $blueprints->extra($this->toArray(), 'header.');
return $blueprints->extra($this->toArray()['header'], 'header.');
}
/**

View File

@@ -404,8 +404,8 @@ class Pages
{
if (!self::$types) {
self::$types = new Types();
self::$types->scanBlueprints('theme://blueprints/');
self::$types->scanTemplates('theme://templates/');
file_exists('theme://blueprints/') && self::$types->scanBlueprints('theme://blueprints/');
file_exists('theme://templates/') && self::$types->scanTemplates('theme://templates/');
$event = new Event();
$event->types = self::$types;

View File

@@ -13,27 +13,24 @@ class Types implements \ArrayAccess, \Iterator, \Countable
use ArrayAccess, Constructor, Iterator, Countable, Export;
protected $items;
protected $systemBlueprints;
public function register($type, $blueprint = null)
{
if (!$blueprint && $this->systemBlueprints && isset($this->systemBlueprints[$type])) {
$useBlueprint = $this->systemBlueprints[$type];
} else {
$useBlueprint = $blueprint;
}
if ($blueprint || empty($this->items[$type])) {
$this->items[$type] = $blueprint;
$this->items[$type] = $useBlueprint;
}
}
public function scanBlueprints($path)
{
$options = [
'compare' => 'Filename',
'pattern' => '|\.yaml$|',
'filters' => [
'key' => '|\.yaml$|'
],
'key' => 'SubPathName',
'value' => 'PathName',
];
$this->items = Folder::all($path, $options) + $this->items;
$this->items = $this->findBlueprints($path) + $this->items;
}
public function scanTemplates($path)
@@ -48,6 +45,10 @@ class Types implements \ArrayAccess, \Iterator, \Countable
'recursive' => false
];
if (!$this->systemBlueprints) {
$this->systemBlueprints = $this->findBlueprints('blueprints://pages');
}
foreach (Folder::all($path, $options) as $type) {
$this->register($type);
}
@@ -78,9 +79,24 @@ class Types implements \ArrayAccess, \Iterator, \Countable
if (strpos($name, 'modular/') !== 0) {
continue;
}
$list[basename($name)] = trim(ucfirst(strtr(basename($name), '_', ' ')));
$list[$name] = trim(ucfirst(strtr(basename($name), '_', ' ')));
}
ksort($list);
return $list;
}
private function findBlueprints($path)
{
$options = [
'compare' => 'Filename',
'pattern' => '|\.yaml$|',
'filters' => [
'key' => '|\.yaml$|'
],
'key' => 'SubPathName',
'value' => 'PathName',
];
return Folder::all($path, $options);
}
}