From 0ab980a479850855fbc84a67bac1d8a560721196 Mon Sep 17 00:00:00 2001 From: Pyry Pennanen Date: Fri, 20 May 2016 12:21:23 +0300 Subject: [PATCH 1/7] Allow BlockAreaName translations --- code/BlockManager.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/BlockManager.php b/code/BlockManager.php index 9b58e6b..5a36ce4 100644 --- a/code/BlockManager.php +++ b/code/BlockManager.php @@ -107,7 +107,7 @@ public function getAreasForPageType($class) if (count($areas)) { foreach ($areas as $k => $v) { - $areas[$k] = FormField::name_to_label($k); + $areas[$k] = _t('Block.BlockAreaName.'.$k, FormField::name_to_label($k)); } return $areas; From 8c4b047f3d6009f359ac9e513f2c210517296b09 Mon Sep 17 00:00:00 2001 From: Shea Dawson Date: Mon, 23 May 2016 13:50:59 +1200 Subject: [PATCH 2/7] Add scrutinizer badge --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 42ec8f1..39dd5e8 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,7 @@ # SilverStripe Blocks +[![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/sheadawson/silverstripe-blocks/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/sheadawson/silverstripe-blocks/?branch=master) + The Blocks modules aims to provide developers with a flexible foundation for defining reusable blocks of content or widgets that can be managed in the CMS. ## Features From 4abf00a715ade200d10e82c420b08058e390f124 Mon Sep 17 00:00:00 2001 From: Shea Dawson Date: Wed, 25 May 2016 09:49:02 +1200 Subject: [PATCH 3/7] Remove validation on Block Title field As mentioned in #95 --- code/dataobjects/Block.php | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/code/dataobjects/Block.php b/code/dataobjects/Block.php index 58ca441..8c12079 100644 --- a/code/dataobjects/Block.php +++ b/code/dataobjects/Block.php @@ -202,20 +202,6 @@ public function getCMSFields() return $fields; } - /** - * @return ValidationResult - */ - public function validate() - { - $result = parent::validate(); - - if (!$this->Title) { - $result->error(_t('Block.TitleRequired', 'Block Title is required')); - } - - return $result; - } - /** * Renders this block with appropriate templates * looks for templates that match BlockClassName_AreaName From 816c9843e6e8b93b492e8d4620edc4a3cd28346e Mon Sep 17 00:00:00 2001 From: Shea Dawson Date: Tue, 7 Jun 2016 10:09:55 +1200 Subject: [PATCH 4/7] Add translatbale gist to readme --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 39dd5e8..1b33b76 100644 --- a/README.md +++ b/README.md @@ -191,6 +191,10 @@ public function getTypeForGridfield() } ``` +## Translatable Blocks + +For creating Blocks with translatable content, using the [translatble module](https://github.com/silverstripe/silverstripe-translatable), see [this gist](https://gist.github.com/thezenmonkey/6e6730023af553f12e3ab762ace3b08a) for a kick start. + ## Screenshots ![](docs/images/overview-1.0.png) From 9df84b4d20b1206a1e435d29a0a3cc1905e15f04 Mon Sep 17 00:00:00 2001 From: Shea Date: Fri, 17 Jun 2016 09:46:27 +1200 Subject: [PATCH 5/7] update .editorconfig php use tabs not spaces --- .editorconfig | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/.editorconfig b/.editorconfig index 47ae637..8b8edb1 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,17 +1,18 @@ -# For more information about the properties used in this file, -# please see the EditorConfig documentation: -# http://editorconfig.org +# For more information about the properties used in +# this file, please see the EditorConfig documentation: +# http://editorconfig.org/ [*] charset = utf-8 end_of_line = lf indent_size = 4 -indent_style = space +indent_style = tab insert_final_newline = true trim_trailing_whitespace = true -[{*.yml,package.json}] -indent_size = 2 +[*.md] +trim_trailing_whitespace = false -# The indent size used in the package.json file cannot be changed: -# https://github.com/npm/npm/pull/3180#issuecomment-16336516 +[*.yml] +indent_size = 2 +indent_style = space From 567f5732f87788488bc38e972cf278840eb88293 Mon Sep 17 00:00:00 2001 From: Shea Date: Fri, 17 Jun 2016 10:54:37 +1200 Subject: [PATCH 6/7] implement pagetype_black/whitelist. Fixes #39 --- README.md | 66 +-- code/BlockManager.php | 415 +++++++------- code/extensions/BlocksSiteTreeExtension.php | 598 ++++++++++---------- 3 files changed, 562 insertions(+), 517 deletions(-) diff --git a/README.md b/README.md index 1b33b76..3b1e6d0 100644 --- a/README.md +++ b/README.md @@ -44,25 +44,27 @@ Install via composer, run `dev/build` ``` yml BlockManager: - themes: - simple: - areas: - Sidebar: true # a Sidebar area will be available on all page types in simple theme - BeforeContent: - only: HomePage # a BeforeContent area will be available only on HomePage page types in simple theme - AfterContent: - except: HomePage # a AfterContent area will be available on all page types except HomePage in simple theme - Footer: true # a Footer area will be available on all page types in simple theme - - use_blocksets: false # Whether to use BlockSet functionality (default if undeclared: true) - use_extra_css_classes: true # Whether to allow cms users to add extra css classes to blocks (default if undeclared: false) - prefix_default_css_classes: 'myprefix--' # prefix the automatically generated CSSClasses based on class name (default if undeclared: false) - exclude_from_page_types: # Disable the Blocks tab completely on these pages of these types - - ContactPage - #disabled_blocks: #allows you to disable specific blocks - # - ContentBlock - use_default_blocks: false # Disable/enable the default Block types (ContentBlock) (default if undeclared: true) - block_area_preview: false # Disable block area preview button in CMS (default if undeclared: true) + themes: + simple: + areas: + Sidebar: true # a Sidebar area will be available on all page types in simple theme + BeforeContent: + only: HomePage # a BeforeContent area will be available only on HomePage page types in simple theme + AfterContent: + except: HomePage # a AfterContent area will be available on all page types except HomePage in simple theme + Footer: true # a Footer area will be available on all page types in simple theme + + #use_blocksets: false # Whether to use BlockSet functionality (default if undeclared: true) + #use_extra_css_classes: true # Whether to allow cms users to add extra css classes to blocks (default if undeclared: false) + #prefix_default_css_classes: 'myprefix--' # prefix the automatically generated CSSClasses based on class name (default if undeclared: false) + #pagetype_whitelist: # Enable the Blocks tab only pages of these types (optional) + # - HomePage + #pagetype_blacklist: # Disable the Blocks tab on pages of these types (optional) + # - ContactPage + #disabled_blocks: #allows you to disable specific blocks (optional) + # - ContentBlock + #use_default_blocks: false # Disable/enable the default Block types (ContentBlock) (default if undeclared: true) + #block_area_preview: false # Disable block area preview button in CMS (default if undeclared: true) ``` Remember to run `?flush=1` after modifying your `.yml` config to make sure it gets applied. @@ -153,10 +155,10 @@ If your running your SS instance "themeless", you can configure your blocks usin ``` yml BlockManager: - themes: - default: - areas: - ... + themes: + default: + areas: + ... ``` ### Remove the Blocks button from the main CMS menu @@ -176,18 +178,18 @@ Here's an example that uses font awesome: ```php public function getIcon() { - return ''; + return ''; } public function getTypeForGridfield() { - $icon = $this->getIcon(); - if ($icon) { - $obj = HTMLText::create(); - $obj->setValue($icon); - return $obj; - } else { - return parent::getTypeForGridfield(); - } + $icon = $this->getIcon(); + if ($icon) { + $obj = HTMLText::create(); + $obj->setValue($icon); + return $obj; + } else { + return parent::getTypeForGridfield(); + } } ``` diff --git a/code/BlockManager.php b/code/BlockManager.php index 5a36ce4..9434fde 100644 --- a/code/BlockManager.php +++ b/code/BlockManager.php @@ -6,201 +6,222 @@ */ class BlockManager extends Object { - /** - * Define areas and config on a per theme basis. - * - * @var array - **/ - private static $themes = array(); - - /** - * Use default ContentBlock class. - * - * @var bool - **/ - private static $use_default_blocks = true; - - /** - * Show a block area preview button in CMS - * - * @var bool - **/ - private static $block_area_preview = true; - - public function __construct() - { - parent::__construct(); - } - - /** - * Gets an array of all areas defined for the current theme. - * - * @param string $theme - * @param bool $keyAsValue - * - * @return array $areas - **/ - public function getAreasForTheme($theme = null, $keyAsValue = true) - { - $theme = $theme ? $theme : $this->getTheme(); - if (!$theme) { - return false; - } - $config = $this->config()->get('themes'); - if (!isset($config[$theme]['areas'])) { - return false; - } - $areas = $config[$theme]['areas']; - $areas = $keyAsValue ? ArrayLib::valuekey(array_keys($areas)) : $areas; - if (count($areas)) { - foreach ($areas as $k => $v) { - $areas[$k] = $keyAsValue ? FormField::name_to_label($k) : $v; - } - } - - return $areas; - } - - /** - * Gets an array of all areas defined for the current theme that are compatible - * with pages of type $class. - * - * @param string $class - * - * @return array $areas - **/ - public function getAreasForPageType($class) - { - $areas = $this->getAreasForTheme(null, false); - - if (!$areas) { - return false; - } - - foreach ($areas as $area => $config) { - if (!is_array($config)) { - continue; - } - - if (isset($config['except'])) { - $except = $config['except']; - if (is_array($except) - ? in_array($class, $except) - : $except == $class - ) { - unset($areas[$area]); - continue; - } - } - - if (isset($config['only'])) { - $only = $config['only']; - if (is_array($only) - ? !in_array($class, $only) - : $only != $class - ) { - unset($areas[$area]); - continue; - } - } - } - - if (count($areas)) { - foreach ($areas as $k => $v) { - $areas[$k] = _t('Block.BlockAreaName.'.$k, FormField::name_to_label($k)); - } - - return $areas; - } else { - return $areas; - } - } - - public function getBlockClasses() - { - $classes = ArrayLib::valuekey(ClassInfo::subclassesFor('Block')); - array_shift($classes); - foreach ($classes as $k => $v) { - $classes[$k] = singleton($k)->singular_name(); - } - - if (!Config::inst()->get('BlockManager', 'use_default_blocks')) { - unset($classes['ContentBlock']); - } - - if ($disabledArr = Config::inst()->get('BlockManager', 'disabled_blocks')) { - foreach ($disabledArr as $k => $v) { - unset($classes[$v]); - } - } - - return $classes; - } - - /* - * Get the current/active theme or 'default' to support theme-less sites - */ - private function getTheme() - { - $currentTheme = Config::inst()->get('SSViewer', 'theme'); - - // check directly on SiteConfig incase ContentController hasn't set - // the theme yet in ContentController->init() - if (!$currentTheme && class_exists('SiteConfig')) { - $currentTheme = SiteConfig::current_site_config()->Theme; - } - - return $currentTheme ? $currentTheme : 'default'; - } - - /* - * Get the block config for the current theme - */ - private function getThemeConfig() - { - $theme = $this->getTheme(); - $config = $this->config()->get('themes'); - - return $theme && isset($config[$theme]) ? $config[$theme] : null; - } - - /* - * Usage of BlockSets configurable from yaml - */ - public function getUseBlockSets() - { - $config = $this->getThemeConfig(); - - return isset($config['use_blocksets']) ? $config['use_blocksets'] : true; - } - - /* - * Exclusion of blocks from page types defined in yaml - */ - public function getExcludeFromPageTypes() - { - $config = $this->getThemeConfig(); - - return isset($config['exclude_from_page_types']) ? $config['exclude_from_page_types'] : array(); - } - - /* - * Usage of extra css classes configurable from yaml - */ - public function getUseExtraCSSClasses() - { - $config = $this->getThemeConfig(); - - return isset($config['use_extra_css_classes']) ? $config['use_extra_css_classes'] : false; - } - - /* - * Prefix for the default CSSClasses - */ - public function getPrefixDefaultCSSClasses() - { - $config = $this->getThemeConfig(); - - return isset($config['prefix_default_css_classes']) ? $config['prefix_default_css_classes'] : false; - } + /** + * Define areas and config on a per theme basis. + * + * @var array + **/ + private static $themes = array(); + + /** + * Use default ContentBlock class. + * + * @var bool + **/ + private static $use_default_blocks = true; + + /** + * Show a block area preview button in CMS + * + * @var bool + **/ + private static $block_area_preview = true; + + public function __construct() + { + parent::__construct(); + } + + /** + * Gets an array of all areas defined for the current theme. + * + * @param string $theme + * @param bool $keyAsValue + * + * @return array $areas + **/ + public function getAreasForTheme($theme = null, $keyAsValue = true) + { + $theme = $theme ? $theme : $this->getTheme(); + if (!$theme) { + return false; + } + $config = $this->config()->get('themes'); + if (!isset($config[$theme]['areas'])) { + return false; + } + $areas = $config[$theme]['areas']; + $areas = $keyAsValue ? ArrayLib::valuekey(array_keys($areas)) : $areas; + if (count($areas)) { + foreach ($areas as $k => $v) { + $areas[$k] = $keyAsValue ? FormField::name_to_label($k) : $v; + } + } + + return $areas; + } + + /** + * Gets an array of all areas defined for the current theme that are compatible + * with pages of type $class. + * + * @param string $class + * + * @return array $areas + **/ + public function getAreasForPageType($class) + { + $areas = $this->getAreasForTheme(null, false); + + if (!$areas) { + return false; + } + + foreach ($areas as $area => $config) { + if (!is_array($config)) { + continue; + } + + if (isset($config['except'])) { + $except = $config['except']; + if (is_array($except) + ? in_array($class, $except) + : $except == $class + ) { + unset($areas[$area]); + continue; + } + } + + if (isset($config['only'])) { + $only = $config['only']; + if (is_array($only) + ? !in_array($class, $only) + : $only != $class + ) { + unset($areas[$area]); + continue; + } + } + } + + if (count($areas)) { + foreach ($areas as $k => $v) { + $areas[$k] = _t('Block.BlockAreaName.'.$k, FormField::name_to_label($k)); + } + + return $areas; + } else { + return $areas; + } + } + + public function getBlockClasses() + { + $classes = ArrayLib::valuekey(ClassInfo::subclassesFor('Block')); + array_shift($classes); + foreach ($classes as $k => $v) { + $classes[$k] = singleton($k)->singular_name(); + } + + if (!Config::inst()->get('BlockManager', 'use_default_blocks')) { + unset($classes['ContentBlock']); + } + + if ($disabledArr = Config::inst()->get('BlockManager', 'disabled_blocks')) { + foreach ($disabledArr as $k => $v) { + unset($classes[$v]); + } + } + + return $classes; + } + + /* + * Get the current/active theme or 'default' to support theme-less sites + */ + private function getTheme() + { + $currentTheme = Config::inst()->get('SSViewer', 'theme'); + + // check directly on SiteConfig incase ContentController hasn't set + // the theme yet in ContentController->init() + if (!$currentTheme && class_exists('SiteConfig')) { + $currentTheme = SiteConfig::current_site_config()->Theme; + } + + return $currentTheme ? $currentTheme : 'default'; + } + + /* + * Get the block config for the current theme + */ + private function getThemeConfig() + { + $theme = $this->getTheme(); + $config = $this->config()->get('themes'); + + return $theme && isset($config[$theme]) ? $config[$theme] : null; + } + + /* + * Usage of BlockSets configurable from yaml + */ + public function getUseBlockSets() + { + $config = $this->getThemeConfig(); + + return isset($config['use_blocksets']) ? $config['use_blocksets'] : true; + } + + /* + * Exclusion of blocks from page types defined in yaml + */ + public function getExcludeFromPageTypes() + { + $config = $this->getThemeConfig(); + + return isset($config['exclude_from_page_types']) ? $config['exclude_from_page_types'] : array(); + } + + /* + * getWhiteListedPageTypes optionally configured by the developer + */ + public function getWhiteListedPageTypes() + { + $config = $this->getThemeConfig(); + return isset($config['pagetype_whitelist']) ? $config['pagetype_whitelist'] : array(); + } + + /* + * getBlackListedPageTypes optionally configured by the developer + * Includes blacklisted page types defined in the old exclude_from_page_types array + */ + public function getBlackListedPageTypes() + { + $config = $this->getThemeConfig(); + $legacy = isset($config['exclude_from_page_types']) ? $config['exclude_from_page_types'] : array(); + $current = isset($config['pagetype_blacklist']) ? $config['pagetype_blacklist'] : array(); + return array_merge($legacy, $current); + } + + /* + * Usage of extra css classes configurable from yaml + */ + public function getUseExtraCSSClasses() + { + $config = $this->getThemeConfig(); + + return isset($config['use_extra_css_classes']) ? $config['use_extra_css_classes'] : false; + } + + /* + * Prefix for the default CSSClasses + */ + public function getPrefixDefaultCSSClasses() + { + $config = $this->getThemeConfig(); + + return isset($config['prefix_default_css_classes']) ? $config['prefix_default_css_classes'] : false; + } } diff --git a/code/extensions/BlocksSiteTreeExtension.php b/code/extensions/BlocksSiteTreeExtension.php index cf5bf8f..86e4b47 100644 --- a/code/extensions/BlocksSiteTreeExtension.php +++ b/code/extensions/BlocksSiteTreeExtension.php @@ -6,292 +6,314 @@ */ class BlocksSiteTreeExtension extends SiteTreeExtension { - private static $db = array( - 'InheritBlockSets' => 'Boolean', - ); - private static $many_many = array( - 'Blocks' => 'Block', - 'DisabledBlocks' => 'Block', - ); - public static $many_many_extraFields = array( - 'Blocks' => array( - 'Sort' => 'Int', - 'BlockArea' => 'Varchar', - ), - ); - private static $defaults = array( - 'InheritBlockSets' => 1, - ); - private static $dependencies = array( - 'blockManager' => '%$blockManager', - ); - public $blockManager; - - /** - * Block manager for Pages. - * */ - public function updateCMSFields(FieldList $fields) - { - if ($fields->fieldByName('Root.Blocks') || in_array($this->owner->ClassName, $this->blockManager->getExcludeFromPageTypes()) || !$this->owner->exists()) { - return; - } - - if (!Permission::check('BLOCK_EDIT')) { - return; - } - - $areas = $this->blockManager->getAreasForPageType($this->owner->ClassName); - - if ($areas && count($areas)) { - $fields->addFieldToTab('Root', new Tab('Blocks', _t('Block.PLURALNAME'))); - if (BlockManager::config()->get('block_area_preview')) { - $fields->addFieldToTab('Root.Blocks', - LiteralField::create('PreviewLink', $this->areasPreviewButton())); - } - - // Blocks related directly to this Page - $gridConfig = GridFieldConfig_BlockManager::create(true, true, true, true) - ->addExisting($this->owner->class) - //->addBulkEditing() - ->addComponent(new GridFieldOrderableRows()) - ; - - // TODO it seems this sort is not being applied... - $gridSource = $this->owner->Blocks(); - // ->sort(array( - // "FIELD(SiteTree_Blocks.BlockArea, '" . implode("','", array_keys($areas)) . "')" => '', - // 'SiteTree_Blocks.Sort' => 'ASC', - // 'Name' => 'ASC' - // )); - - $fields->addFieldToTab('Root.Blocks', GridField::create('Blocks', _t('Block.PLURALNAME', 'Blocks'), $gridSource, $gridConfig)); - - // Blocks inherited from BlockSets - if ($this->blockManager->getUseBlockSets()) { - $inheritedBlocks = $this->getBlocksFromAppliedBlockSets(null, true); - - if ($inheritedBlocks->count()) { - $activeInherited = $this->getBlocksFromAppliedBlockSets(null, false); - - if ($activeInherited->count()) { - $fields->addFieldsToTab('Root.Blocks', array( - GridField::create('InheritedBlockList', _t('BlocksSiteTreeExtension.BlocksInheritedFromBlockSets', 'Blocks Inherited from Block Sets'), $activeInherited, - GridFieldConfig_BlockManager::create(false, false, false)), - LiteralField::create('InheritedBlockListTip', "

"._t('BlocksSiteTreeExtension.InheritedBlocksEditLink', 'Tip: Inherited blocks can be edited in the {link_start}Block Admin area{link_end}', '', array('link_start' => '', 'link_end' => '')).'

'), - )); - } - - $fields->addFieldToTab('Root.Blocks', - ListBoxField::create('DisabledBlocks', _t('BlocksSiteTreeExtension.DisableInheritedBlocks', 'Disable Inherited Blocks'), - $inheritedBlocks->map('ID', 'Title'), null, null, true) - ->setDescription(_t('BlocksSiteTreeExtension.DisableInheritedBlocksDescription', 'Select any inherited blocks that you would not like displayed on this page.')) - ); - } else { - $fields->addFieldToTab('Root.Blocks', - ReadonlyField::create('DisabledBlocksReadOnly', _t('BlocksSiteTreeExtension.DisableInheritedBlocks', 'Disable Inherited Blocks'), - _t('BlocksSiteTreeExtension.NoInheritedBlocksToDisable','This page has no inherited blocks to disable.'))); - } - - $fields->addFieldToTab('Root.Blocks', - CheckboxField::create('InheritBlockSets', _t('BlocksSiteTreeExtension.InheritBlocksFromBlockSets', 'Inherit Blocks from Block Sets'))); - } - } - } - - /** - * Called from templates to get rendered blocks for the given area. - * - * @param string $area - * @param int $limit Limit the items to this number, or null for no limit - */ - public function BlockArea($area, $limit = null) - { - if ($this->owner->ID <= 0) { - return; - } // blocks break on fake pages ie Security/login - - $list = $this->getBlockList($area); - - foreach ($list as $block) { - if (!$block->canView()) { - $list->remove($block); - } - } - - if ($limit !== null) { - $list = $list->limit($limit); - } - - $data = array(); - $data['HasBlockArea'] = ($this->owner->canEdit() && isset($_REQUEST['block_preview']) && $_REQUEST['block_preview']) || $list->Count() > 0; - $data['BlockArea'] = $list; - $data['AreaID'] = $area; - - $data = $this->owner->customise($data); - - $template = array('BlockArea_'.$area); - - if (SSViewer::hasTemplate($template)) { - return $data->renderWith($template); - } else { - return $data->renderWith('BlockArea'); - } - } - - public function HasBlockArea($area) - { - if ($this->owner->canEdit() && isset($_REQUEST['block_preview']) && $_REQUEST['block_preview']) { - return true; - } - - $list = $this->getBlockList($area); - - foreach ($list as $block) { - if (!$block->canView()) { - $list->remove($block); - } - } - - return $list->Count() > 0; - } - - /** - * Get a merged list of all blocks on this page and ones inherited from BlockSets. - * - * @param string|null $area filter by block area - * @param bool $includeDisabled Include blocks that have been explicitly excluded from this page - * i.e. blocks from block sets added to the "disable inherited blocks" list - * - * @return ArrayList - * */ - public function getBlockList($area = null, $includeDisabled = false) - { - $includeSets = $this->blockManager->getUseBlockSets() && $this->owner->InheritBlockSets; - $blocks = ArrayList::create(); - - // get blocks directly linked to this page - $nativeBlocks = $this->owner->Blocks()->sort('Sort'); - if ($area) { - $nativeBlocks = $nativeBlocks->filter('BlockArea', $area); - } - - if ($nativeBlocks->count()) { - foreach ($nativeBlocks as $block) { - $blocks->add($block); - } - } - - // get blocks from BlockSets - if ($includeSets) { - $blocksFromSets = $this->getBlocksFromAppliedBlockSets($area, $includeDisabled); - if ($blocksFromSets->count()) { - // merge set sources - foreach ($blocksFromSets as $block) { - if (!$blocks->find('ID', $block->ID)) { - if ($block->AboveOrBelow == 'Above') { - $blocks->unshift($block); - } else { - $blocks->push($block); - } - } - } - } - } - - return $blocks; - } - - /** - * Get Any BlockSets that apply to this page. - * - * @return ArrayList - * */ - public function getAppliedSets() - { - $list = ArrayList::create(); - if (!$this->owner->InheritBlockSets) { - return $list; - } - - $sets = BlockSet::get()->where("(PageTypesValue IS NULL) OR (PageTypesValue LIKE '%:\"{$this->owner->ClassName}%')"); - $ancestors = $this->owner->getAncestors()->column('ID'); - - foreach ($sets as $set) { - $restrictedToParerentIDs = $set->PageParents()->column('ID'); - if (count($restrictedToParerentIDs)) { - // check whether the set should include selected parent, in which case check whether - // it was in the restricted parents list. If it's not, or if include parentpage - // wasn't selected, we check the ancestors of this page. - if ($set->IncludePageParent && in_array($this->owner->ID, $restrictedToParerentIDs)) { - $list->add($set); - } else { - if (count($ancestors)) { - foreach ($ancestors as $ancestor) { - if (in_array($ancestor, $restrictedToParerentIDs)) { - $list->add($set); - continue; - } - } - } - } - } else { - $list->add($set); - } - } - - return $list; - } - - /** - * Get all Blocks from BlockSets that apply to this page. - * - * @return ArrayList - * */ - public function getBlocksFromAppliedBlockSets($area = null, $includeDisabled = false) - { - $blocks = ArrayList::create(); - $sets = $this->getAppliedSets(); - - if (!$sets->count()) { - return $blocks; - } - - foreach ($sets as $set) { - $setBlocks = $set->Blocks()->sort('Sort DESC'); - - if (!$includeDisabled) { - $setBlocks = $setBlocks->exclude('ID', $this->owner->DisabledBlocks()->column('ID')); - } - - if ($area) { - $setBlocks = $setBlocks->filter('BlockArea', $area); - } - - $blocks->merge($setBlocks); - } - - $blocks->removeDuplicates(); - - return $blocks; - } - - /** - * Get's the link for a block area preview button. - * - * @return string - * */ - public function areasPreviewLink() - { - return Controller::join_links($this->owner->Link(), '?block_preview=1'); - } - - /** - * Get's html for a block area preview button. - * - * @return string - * */ - public function areasPreviewButton() - { - return ""._t('BlocksSiteTreeExtension.PreviewBlockAreasLink', 'Preview Block Areas for this page').''; - } + private static $db = array( + 'InheritBlockSets' => 'Boolean', + ); + private static $many_many = array( + 'Blocks' => 'Block', + 'DisabledBlocks' => 'Block', + ); + public static $many_many_extraFields = array( + 'Blocks' => array( + 'Sort' => 'Int', + 'BlockArea' => 'Varchar', + ), + ); + private static $defaults = array( + 'InheritBlockSets' => 1, + ); + private static $dependencies = array( + 'blockManager' => '%$blockManager', + ); + public $blockManager; + + + /** + * Check if the Blocks CMSFields should be displayed for this Page + * + * @return boolean + **/ + public function showBlocksFields() + { + $whiteList = $this->blockManager->getWhiteListedPageTypes(); + $blackList = $this->blockManager->getBlackListedPageTypes(); + + if (in_array($this->owner->ClassName, $blackList)) { + return false; + } + + if (count($whiteList) && !in_array($this->owner->ClassName, $whiteList)) { + return false; + } + + if (!Permission::check('BLOCK_EDIT')) { + return false; + } + + return true; + } + + /** + * Block manager for Pages. + * */ + public function updateCMSFields(FieldList $fields) + { + if ($fields->fieldByName('Root.Blocks') || !$this->showBlocksFields()) { + return; + } + + $areas = $this->blockManager->getAreasForPageType($this->owner->ClassName); + + if ($areas && count($areas)) { + $fields->addFieldToTab('Root', new Tab('Blocks', _t('Block.PLURALNAME'))); + if (BlockManager::config()->get('block_area_preview')) { + $fields->addFieldToTab('Root.Blocks', + LiteralField::create('PreviewLink', $this->areasPreviewButton())); + } + + // Blocks related directly to this Page + $gridConfig = GridFieldConfig_BlockManager::create(true, true, true, true) + ->addExisting($this->owner->class) + //->addBulkEditing() + ->addComponent(new GridFieldOrderableRows()) + ; + + // TODO it seems this sort is not being applied... + $gridSource = $this->owner->Blocks(); + // ->sort(array( + // "FIELD(SiteTree_Blocks.BlockArea, '" . implode("','", array_keys($areas)) . "')" => '', + // 'SiteTree_Blocks.Sort' => 'ASC', + // 'Name' => 'ASC' + // )); + + $fields->addFieldToTab('Root.Blocks', GridField::create('Blocks', _t('Block.PLURALNAME', 'Blocks'), $gridSource, $gridConfig)); + + // Blocks inherited from BlockSets + if ($this->blockManager->getUseBlockSets()) { + $inheritedBlocks = $this->getBlocksFromAppliedBlockSets(null, true); + + if ($inheritedBlocks->count()) { + $activeInherited = $this->getBlocksFromAppliedBlockSets(null, false); + + if ($activeInherited->count()) { + $fields->addFieldsToTab('Root.Blocks', array( + GridField::create('InheritedBlockList', _t('BlocksSiteTreeExtension.BlocksInheritedFromBlockSets', 'Blocks Inherited from Block Sets'), $activeInherited, + GridFieldConfig_BlockManager::create(false, false, false)), + LiteralField::create('InheritedBlockListTip', "

"._t('BlocksSiteTreeExtension.InheritedBlocksEditLink', 'Tip: Inherited blocks can be edited in the {link_start}Block Admin area{link_end}', '', array('link_start' => '', 'link_end' => '')).'

'), + )); + } + + $fields->addFieldToTab('Root.Blocks', + ListBoxField::create('DisabledBlocks', _t('BlocksSiteTreeExtension.DisableInheritedBlocks', 'Disable Inherited Blocks'), + $inheritedBlocks->map('ID', 'Title'), null, null, true) + ->setDescription(_t('BlocksSiteTreeExtension.DisableInheritedBlocksDescription', 'Select any inherited blocks that you would not like displayed on this page.')) + ); + } else { + $fields->addFieldToTab('Root.Blocks', + ReadonlyField::create('DisabledBlocksReadOnly', _t('BlocksSiteTreeExtension.DisableInheritedBlocks', 'Disable Inherited Blocks'), + _t('BlocksSiteTreeExtension.NoInheritedBlocksToDisable','This page has no inherited blocks to disable.'))); + } + + $fields->addFieldToTab('Root.Blocks', + CheckboxField::create('InheritBlockSets', _t('BlocksSiteTreeExtension.InheritBlocksFromBlockSets', 'Inherit Blocks from Block Sets'))); + } + } + } + + /** + * Called from templates to get rendered blocks for the given area. + * + * @param string $area + * @param int $limit Limit the items to this number, or null for no limit + */ + public function BlockArea($area, $limit = null) + { + if ($this->owner->ID <= 0) { + return; + } // blocks break on fake pages ie Security/login + + $list = $this->getBlockList($area); + + foreach ($list as $block) { + if (!$block->canView()) { + $list->remove($block); + } + } + + if ($limit !== null) { + $list = $list->limit($limit); + } + + $data = array(); + $data['HasBlockArea'] = ($this->owner->canEdit() && isset($_REQUEST['block_preview']) && $_REQUEST['block_preview']) || $list->Count() > 0; + $data['BlockArea'] = $list; + $data['AreaID'] = $area; + + $data = $this->owner->customise($data); + + $template = array('BlockArea_'.$area); + + if (SSViewer::hasTemplate($template)) { + return $data->renderWith($template); + } else { + return $data->renderWith('BlockArea'); + } + } + + public function HasBlockArea($area) + { + if ($this->owner->canEdit() && isset($_REQUEST['block_preview']) && $_REQUEST['block_preview']) { + return true; + } + + $list = $this->getBlockList($area); + + foreach ($list as $block) { + if (!$block->canView()) { + $list->remove($block); + } + } + + return $list->Count() > 0; + } + + /** + * Get a merged list of all blocks on this page and ones inherited from BlockSets. + * + * @param string|null $area filter by block area + * @param bool $includeDisabled Include blocks that have been explicitly excluded from this page + * i.e. blocks from block sets added to the "disable inherited blocks" list + * + * @return ArrayList + * */ + public function getBlockList($area = null, $includeDisabled = false) + { + $includeSets = $this->blockManager->getUseBlockSets() && $this->owner->InheritBlockSets; + $blocks = ArrayList::create(); + + // get blocks directly linked to this page + $nativeBlocks = $this->owner->Blocks()->sort('Sort'); + if ($area) { + $nativeBlocks = $nativeBlocks->filter('BlockArea', $area); + } + + if ($nativeBlocks->count()) { + foreach ($nativeBlocks as $block) { + $blocks->add($block); + } + } + + // get blocks from BlockSets + if ($includeSets) { + $blocksFromSets = $this->getBlocksFromAppliedBlockSets($area, $includeDisabled); + if ($blocksFromSets->count()) { + // merge set sources + foreach ($blocksFromSets as $block) { + if (!$blocks->find('ID', $block->ID)) { + if ($block->AboveOrBelow == 'Above') { + $blocks->unshift($block); + } else { + $blocks->push($block); + } + } + } + } + } + + return $blocks; + } + + /** + * Get Any BlockSets that apply to this page. + * + * @return ArrayList + * */ + public function getAppliedSets() + { + $list = ArrayList::create(); + if (!$this->owner->InheritBlockSets) { + return $list; + } + + $sets = BlockSet::get()->where("(PageTypesValue IS NULL) OR (PageTypesValue LIKE '%:\"{$this->owner->ClassName}%')"); + $ancestors = $this->owner->getAncestors()->column('ID'); + + foreach ($sets as $set) { + $restrictedToParerentIDs = $set->PageParents()->column('ID'); + if (count($restrictedToParerentIDs)) { + // check whether the set should include selected parent, in which case check whether + // it was in the restricted parents list. If it's not, or if include parentpage + // wasn't selected, we check the ancestors of this page. + if ($set->IncludePageParent && in_array($this->owner->ID, $restrictedToParerentIDs)) { + $list->add($set); + } else { + if (count($ancestors)) { + foreach ($ancestors as $ancestor) { + if (in_array($ancestor, $restrictedToParerentIDs)) { + $list->add($set); + continue; + } + } + } + } + } else { + $list->add($set); + } + } + + return $list; + } + + /** + * Get all Blocks from BlockSets that apply to this page. + * + * @return ArrayList + * */ + public function getBlocksFromAppliedBlockSets($area = null, $includeDisabled = false) + { + $blocks = ArrayList::create(); + $sets = $this->getAppliedSets(); + + if (!$sets->count()) { + return $blocks; + } + + foreach ($sets as $set) { + $setBlocks = $set->Blocks()->sort('Sort DESC'); + + if (!$includeDisabled) { + $setBlocks = $setBlocks->exclude('ID', $this->owner->DisabledBlocks()->column('ID')); + } + + if ($area) { + $setBlocks = $setBlocks->filter('BlockArea', $area); + } + + $blocks->merge($setBlocks); + } + + $blocks->removeDuplicates(); + + return $blocks; + } + + /** + * Get's the link for a block area preview button. + * + * @return string + * */ + public function areasPreviewLink() + { + return Controller::join_links($this->owner->Link(), '?block_preview=1'); + } + + /** + * Get's html for a block area preview button. + * + * @return string + * */ + public function areasPreviewButton() + { + return ""._t('BlocksSiteTreeExtension.PreviewBlockAreasLink', 'Preview Block Areas for this page').''; + } } From 623a947387b0e93f671d9813b360d27a3d442d42 Mon Sep 17 00:00:00 2001 From: Shea Date: Wed, 22 Jun 2016 11:18:24 +1200 Subject: [PATCH 7/7] fix blocks tab not showing in some cases. Fixes #98 --- code/extensions/BlocksSiteTreeExtension.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/extensions/BlocksSiteTreeExtension.php b/code/extensions/BlocksSiteTreeExtension.php index 86e4b47..9b79d26 100644 --- a/code/extensions/BlocksSiteTreeExtension.php +++ b/code/extensions/BlocksSiteTreeExtension.php @@ -65,7 +65,7 @@ public function updateCMSFields(FieldList $fields) $areas = $this->blockManager->getAreasForPageType($this->owner->ClassName); if ($areas && count($areas)) { - $fields->addFieldToTab('Root', new Tab('Blocks', _t('Block.PLURALNAME'))); + $fields->addFieldToTab('Root', new Tab('Blocks', _t('Block.PLURALNAME', 'Blocks'))); if (BlockManager::config()->get('block_area_preview')) { $fields->addFieldToTab('Root.Blocks', LiteralField::create('PreviewLink', $this->areasPreviewButton()));