diff --git a/system/blueprints/pages/blog.yaml b/system/blueprints/pages/blog.yaml new file mode 100644 index 000000000..b5937f959 --- /dev/null +++ b/system/blueprints/pages/blog.yaml @@ -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 diff --git a/system/blueprints/pages/default.yaml b/system/blueprints/pages/default.yaml new file mode 100644 index 000000000..e907e80c6 --- /dev/null +++ b/system/blueprints/pages/default.yaml @@ -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 diff --git a/system/blueprints/pages/item.yaml b/system/blueprints/pages/item.yaml new file mode 100644 index 000000000..f31e9c9a9 --- /dev/null +++ b/system/blueprints/pages/item.yaml @@ -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: === diff --git a/system/blueprints/pages/modular.yaml b/system/blueprints/pages/modular.yaml new file mode 100644 index 000000000..84236be45 --- /dev/null +++ b/system/blueprints/pages/modular.yaml @@ -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 diff --git a/system/blueprints/pages/page.yaml b/system/blueprints/pages/page.yaml deleted file mode 100644 index 81948231b..000000000 --- a/system/blueprints/pages/page.yaml +++ /dev/null @@ -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 diff --git a/system/src/Grav/Common/Data/Blueprint.php b/system/src/Grav/Common/Data/Blueprint.php index 768d8e41d..d92976ddd 100644 --- a/system/src/Grav/Common/Data/Blueprint.php +++ b/system/src/Grav/Common/Data/Blueprint.php @@ -1,6 +1,7 @@ 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']); } } diff --git a/system/src/Grav/Common/Data/Blueprints.php b/system/src/Grav/Common/Data/Blueprints.php index a92f235f2..a024c87f7 100644 --- a/system/src/Grav/Common/Data/Blueprints.php +++ b/system/src/Grav/Common/Data/Blueprints.php @@ -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)); } } diff --git a/system/src/Grav/Common/Data/Validation.php b/system/src/Grav/Common/Data/Validation.php index fd400fc51..d2b7066db 100644 --- a/system/src/Grav/Common/Data/Validation.php +++ b/system/src/Grav/Common/Data/Validation.php @@ -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) diff --git a/system/src/Grav/Common/Page/Page.php b/system/src/Grav/Common/Page/Page.php index 7bf743fd3..2e4529d37 100644 --- a/system/src/Grav/Common/Page/Page.php +++ b/system/src/Grav/Common/Page/Page.php @@ -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.'); } /** diff --git a/system/src/Grav/Common/Page/Pages.php b/system/src/Grav/Common/Page/Pages.php index c8872c483..d8cad6e07 100644 --- a/system/src/Grav/Common/Page/Pages.php +++ b/system/src/Grav/Common/Page/Pages.php @@ -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; diff --git a/system/src/Grav/Common/Page/Types.php b/system/src/Grav/Common/Page/Types.php index 418faba9c..f6407cadd 100644 --- a/system/src/Grav/Common/Page/Types.php +++ b/system/src/Grav/Common/Page/Types.php @@ -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); + } }