.
29 | *
30 | * @return RequestHandler
31 | */
32 | public function handleBlock()
33 | {
34 | if ($id = $this->owner->getRequest()->param('ID')) {
35 | $blocks = $this->owner->data()->getBlockList(null, true, true, true);
36 | if ($block = $blocks->find('ID', $id)) {
37 | return $block->getController();
38 | }
39 | }
40 |
41 | return $block->getController();
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | Copyright (c) 2015, LiveSource All rights reserved.
2 |
3 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
4 |
5 | Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. Neither the name LiveSource nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
6 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 |
2 | language: php
3 |
4 | sudo: false
5 |
6 | addons:
7 | apt:
8 | packages:
9 | - tidy
10 |
11 | before_install:
12 | - pip install --user codecov
13 |
14 | env:
15 | global:
16 | - DB=MYSQL CORE_RELEASE=4.0
17 | - MODULE_PATH=blocks
18 | - COVERAGE=0
19 |
20 | matrix:
21 | include:
22 | - php: 7.0
23 | env: DB=SQLITE
24 | - php: 7.0
25 | env: DB=PGSQL
26 | - php: 7.0
27 | env: COVERAGE=1
28 | - php: 5.6
29 | allow_failures:
30 | - php: 7.0
31 | env: DB=SQLITE
32 |
33 |
34 | before_script:
35 | - phpenv rehash
36 | - composer self-update || true
37 | - git clone git://github.com/silverstripe-labs/silverstripe-travis-support.git ~/travis-support
38 | - php ~/travis-support/travis_setup.php --source `pwd` --target ~/builds/ss
39 | - cd ~/builds/ss
40 | - mv "$MODULE_PATH/phpunit.xml.dist" .
41 |
42 | script:
43 | # Execute tests with no coverage. This is the fastest option
44 | - "if [ \"$COVERAGE\" = \"0\" ]; then vendor/bin/phpunit $MODULE_PATH/tests/; fi"
45 |
46 | # Execute tests with coverage. Do this for a small
47 | - "if [ \"$COVERAGE\" = \"1\" ]; then vendor/bin/phpunit --coverage-clover=coverage.clover $MODULE_PATH/tests/; fi"
48 |
49 | after_script:
50 | - "if [ \"$COVERAGE\" = \"1\" ]; then mv coverage.clover ~/build/$TRAVIS_REPO_SLUG/; fi"
51 | - cd ~/build/$TRAVIS_REPO_SLUG
52 | - wget https://scrutinizer-ci.com/ocular.phar
53 | - "if [ \"$COVERAGE\" = \"1\" ]; then travis_retry codecov && travis_retry php ocular.phar code-coverage:upload --format=php-clover coverage.clover; fi"
54 |
55 |
--------------------------------------------------------------------------------
/src/controllers/BlockController.php:
--------------------------------------------------------------------------------
1 |
12 | */
13 | class BlockController extends Controller
14 | {
15 | /**
16 | * @var Block
17 | */
18 | protected $block;
19 |
20 | /**
21 | * @param Block $block
22 | */
23 | public function __construct($block = null)
24 | {
25 | if ($block) {
26 | $this->block = $block;
27 | $this->failover = $block;
28 | }
29 |
30 | parent::__construct();
31 | }
32 |
33 | public function index()
34 | {
35 | return;
36 | }
37 |
38 | /**
39 | * @param string $action
40 | *
41 | * @return string
42 | */
43 | public function Link($action = null)
44 | {
45 | $id = ($this->block) ? $this->block->ID : null;
46 | $segment = Controller::join_links('block', $id, $action);
47 |
48 | if ($page = Director::get_current_page()) {
49 | return $page->Link($segment);
50 | }
51 |
52 | return Controller::curr()->Link($segment);
53 | }
54 |
55 | /**
56 | * @return string - link to page this block is on
57 | */
58 | public function pageLink()
59 | {
60 | $parts = explode('/block/', $this->Link());
61 |
62 | return isset($parts[0]) ? $parts[0] : null;
63 | }
64 |
65 | /**
66 | * @return Block
67 | */
68 | public function getBlock()
69 | {
70 | return $this->block;
71 | }
72 |
73 | /**
74 | * CSS Classes to apply to block element in template.
75 | *
76 | * @return string $classes
77 | */
78 | public function CSSClasses($stopAtClass = 'DataObject')
79 | {
80 | return $this->getBlock()->CSSClasses($stopAtClass);
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/tasks/BlockUpgradeTask.php:
--------------------------------------------------------------------------------
1 |
16 | */
17 | class BlockUpgradeTask extends BuildTask
18 | {
19 | public function run($request)
20 | {
21 |
22 | // update block/set titles
23 | // Name field has been reverted back to Title
24 | // DB::query("update Block set Name = Title");
25 | // DB::query("update BlockSet set Name = Title");
26 |
27 | // update block areas
28 |
29 | DB::query('
30 | update SiteTree_Blocks
31 | left join Block on SiteTree_Blocks.BlockID = Block.ID
32 | set BlockArea = Block.Area
33 | where BlockID = Block.ID
34 | ');
35 |
36 | // update block sort
37 |
38 | DB::query('
39 | update SiteTree_Blocks
40 | left join Block on SiteTree_Blocks.BlockID = Block.ID
41 | set Sort = Block.Weight
42 | where BlockID = Block.ID
43 | ');
44 |
45 | echo 'BlockAreas, Sort updated
';
46 |
47 | // migrate global blocks
48 |
49 | $sc = SiteConfig::current_site_config();
50 | if ($sc->Blocks()->Count()) {
51 | $set = BlockSet::get()->filter('Title', 'Global')->first();
52 | if (!$set) {
53 | $set = BlockSet::create([
54 | 'Title' => 'Global',
55 | ]);
56 | $set->write();
57 | }
58 | foreach ($sc->Blocks() as $block) {
59 | if (!$set->Blocks()->find('ID', $block->ID)) {
60 | $set->Blocks()->add($block, [
61 | 'Sort' => $block->Weight,
62 | 'BlockArea' => $block->Area,
63 | ]);
64 | echo "Block #$block->ID added to Global block set
";
65 | }
66 | }
67 | }
68 |
69 | // publish blocks
70 | $blocks = Block::get()->filter('Published', 1);
71 | foreach ($blocks as $block) {
72 | $block->publish('Stage', 'Live');
73 | echo "Published Block #$block->ID
";
74 | }
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/lang/en.yml:
--------------------------------------------------------------------------------
1 | en:
2 | Block:
3 | Any: (any)
4 | BlockArea: 'Block Area'
5 | BlockSetsAsString: 'Block Sets: {sets}'
6 | BlockType: 'Block Type'
7 | Content: Content
8 | CreateBlock: 'Create a Block'
9 | DeleteBlock: 'Delete a Block'
10 | EditBlock: 'Edit a Block'
11 | ExtraCSSClasses: 'Extra CSS Classes'
12 | IsPublishedField: Published
13 | PLURALNAME: Blocks
14 | PagesAsString: 'Pages: {pages}'
15 | PermissionCategory: Blocks
16 | PublishBlock: 'Publish a Block'
17 | SINGULARNAME: Block
18 | Title: Title
19 | TitleRequired: 'Block Title is required'
20 | UsageListAsString: 'Used on'
21 | ViewerGroups: 'Viewer Groups'
22 | BlockAdmin:
23 | MENUTITLE: Blocks
24 | BlockSet:
25 | ApplyBlockSetToSelectedPageParentsAsWellAsChildren: 'Apply block set to selected page parents as well as children'
26 | CreateBlockSet: 'Create a Block Set'
27 | DeleteBlockSet: 'Delete a Block Set'
28 | EditBlockSet: 'Edit a Block Set'
29 | OnlyApplyToChildrenOfThesePages: 'Only apply to children of these Pages:'
30 | OnlyApplyToThesePageTypes: 'Only apply to these Page Types:'
31 | OnlyApplyToThesePageTypesDescription: 'Selected Page Types will inherit this Block Set automatically. Leave all unchecked to apply to all page types.'
32 | PLURALNAME: 'Block Sets'
33 | SINGULARNAME: 'Block Set'
34 | Settings: Settings
35 | YouCanAddBlocksToThisSetOnceYouHaveSavedIt: 'You can add Blocks to this set once you have saved it for the first time'
36 | BlocksSiteTreeExtension:
37 | BlocksInheritedFromBlockSets: 'Blocks Inherited from Block Sets'
38 | DisableInheritedBlocks: 'Disable Inherited Blocks'
39 | DisableInheritedBlocksDescription: 'Select any inherited blocks that you would not like displayed on this page.'
40 | InheritBlocksFromBlockSets: 'Inherit Blocks from Block Sets'
41 | InheritedBlocksEditLink: 'Tip: Inherited blocks can be edited in the {link_start}Block Admin area{link_end}'
42 | NoBlockAreasConfigured: 'This page type has no Block Areas configured.'
43 | NoInheritedBlocksToDisable: 'This page has no inherited blocks to disable.'
44 | PreviewBlockAreasLink: 'Preview Block Areas for this page'
45 | ContentBlock:
46 | PLURALNAME: 'Content Blocks'
47 | SINGULARNAME: 'Content Block'
48 | GridFieldConfigBlockManager:
49 | AboveOrBelow: 'Above or Below'
50 |
--------------------------------------------------------------------------------
/lang/hr.yml:
--------------------------------------------------------------------------------
1 | hr:
2 | Block:
3 | Any: (bilo koji)
4 | BlockArea: 'Područje bloka'
5 | BlockSetsAsString: 'Blok setovi: {sets}'
6 | BlockType: 'Vrsta bloka'
7 | Content: Sadržaj
8 | CreateBlock: 'Kreiraj blok'
9 | DeleteBlock: 'Obriši blok'
10 | EditBlock: 'Uredi block'
11 | ExtraCSSClasses: 'Dodatne CSS klase'
12 | IsPublishedField: Objavljen
13 | PLURALNAME: Blokovi
14 | PagesAsString: 'Stranice: {pages}'
15 | PermissionCategory: Blokovi
16 | PublishBlock: 'Objavi blok'
17 | SINGULARNAME: Blok
18 | Title: Naslov
19 | TitleRequired: 'Naslov bloka je obavezan'
20 | UsageListAsString: 'Korišten na'
21 | ViewerGroups: 'Grupe za pregled'
22 | BlockAdmin:
23 | MENUTITLE: Blokovi
24 | BlockSet:
25 | ApplyBlockSetToSelectedPageParentsAsWellAsChildren: 'Primjeni blok set na označene stranice roditelja kao i djece'
26 | CreateBlockSet: 'Kreiraj blok set'
27 | DeleteBlockSet: 'Obriši blok set'
28 | EditBlockSet: 'Uredi blok set'
29 | OnlyApplyToChildrenOfThesePages: 'Primjeni samo na djecu ovih stranica:'
30 | OnlyApplyToThesePageTypes: 'Primjeni samo na ove tipove stranica:'
31 | OnlyApplyToThesePageTypesDescription: 'Odabrani tipovi stranica će naslijediti ovaj blok set automatski. Ostavite sve neoznačeno kako bi primjenili na sve tipove stranica.'
32 | PLURALNAME: 'Blok setovi'
33 | SINGULARNAME: 'Blok set'
34 | Settings: Postavke
35 | YouCanAddBlocksToThisSetOnceYouHaveSavedIt: 'Možete dodati blokove na ovaj set nakon što ga spremite po prvi puta'
36 | BlocksSiteTreeExtension:
37 | BlocksInheritedFromBlockSets: 'Blokovi nasljeđeni iz blok setova'
38 | DisableInheritedBlocks: 'Onemogući nasljeđivanje blokova'
39 | DisableInheritedBlocksDescription: 'Označi samo nasljeđene blokove koje ne želite prikazati na ovoj stranici.'
40 | InheritBlocksFromBlockSets: 'Nasljeđeni blokovi iz blok setova'
41 | InheritedBlocksEditLink: 'Savjet: Nasljeđeni blokovi mogu se uređivati {link_start}Blok admin sekciji{link_end}'
42 | NoBlockAreasConfigured: 'Ovaj tip stranice nema konfiguriranih blok područja.'
43 | NoInheritedBlocksToDisable: 'Ova stranica nema nasljećenih blokova za onemogućavanje.'
44 | PreviewBlockAreasLink: 'Pregled blok područja za ovu stranicu'
45 | ContentBlock:
46 | PLURALNAME: 'Sadržajni blokovi'
47 | SINGULARNAME: 'Sadržajni blok'
48 | GridFieldConfigBlockManager:
49 | AboveOrBelow: 'Iznad ili ispod'
50 |
--------------------------------------------------------------------------------
/lang/lt.yml:
--------------------------------------------------------------------------------
1 | lt:
2 | Block:
3 | Any: '(bet koks)'
4 | BlockArea: 'Bloko vieta'
5 | BlockSetsAsString: 'Bloko rinkinys: {sets}'
6 | BlockType: 'Bloko tipas'
7 | Content: 'Turinys'
8 | CreateBlock: 'Kurti bloką'
9 | DeleteBlock: 'Trinti bloką'
10 | EditBlock: 'Redaguoti bloką'
11 | ExtraCSSClasses: 'Papildomos CSS klasės'
12 | IsPublishedField: 'Publikuojamas'
13 | PLURALNAME: 'Blokai'
14 | PagesAsString: 'Puslapiai: {pages}'
15 | PermissionCategory: 'Blokai'
16 | PublishBlock: 'Publikuoti bloką'
17 | SINGULARNAME: 'Blokas'
18 | Title: 'Antraštė'
19 | TitleRequired: 'Bloko antraštė yra privaloma'
20 | UsageListAsString: 'Naudojamas'
21 | ViewerGroups: 'Peržiūros grupės'
22 | BlockAdmin:
23 | MENUTITLE: 'Blokai'
24 | BlockSet:
25 | ApplyBlockSetToSelectedPageParentsAsWellAsChildren: 'Taikyti bloko rinkinį pasirinktiems tėvyniams ir jų vaikiniams puslapiams'
26 | CreateBlockSet: 'Kurti bloko rinkinį'
27 | DeleteBlockSet: 'Trinti bloko rinkinį'
28 | EditBlockSet: 'Redaguoti bloko rinkinį'
29 | OnlyApplyToChildrenOfThesePages: 'Taikyti tik vaikiniams šiems puslapiams:'
30 | OnlyApplyToThesePageTypes: 'Taikyti tik šiems puslapių tipams:'
31 | OnlyApplyToThesePageTypesDescription: 'Pasirinkti puslapių tipai automatiškai paveldės šį bloko rinkinį. Palikite visus nepažymėtus, kad priskirtu visiems puslapių tipams.'
32 | PLURALNAME: 'Blokų rinkiniai'
33 | SINGULARNAME: 'Blokų rinkinys'
34 | Settings: 'Nustatymai'
35 | YouCanAddBlocksToThisSetOnceYouHaveSavedIt: 'Jūs galite pridėti bloką į šį rinkinį vieną kartą išsaugojus pirmą kartą'
36 | BlocksSiteTreeExtension:
37 | BlocksInheritedFromBlockSets: 'Paveldėti blokai iš rinkinio.'
38 | DisableInheritedBlocks: 'Išjungti blokų paveldėjimą'
39 | DisableInheritedBlocksDescription: 'Pasirinkite paveldėtus blokus, kuriuos nenorite rodyti šiame puslapyje.'
40 | InheritBlocksFromBlockSets: 'Paveldėti blokus iš rinkinio.'
41 | InheritedBlocksEditLink: 'Patarimas: paveldėtus blokus galimą redaguoti {link_start}Bloko administravimo dalyje{link_end}'
42 | NoBlockAreasConfigured: 'Šis puslapio tipas neturi sukonfiguruotų bloko sričių.'
43 | NoInheritedBlocksToDisable: 'Šis puslapis neturi paveldėtu blokų, kuriuos galėtu išjungti.'
44 | PreviewBlockAreasLink: 'Peržiūrėti bloko sritis šiame puslapyje.'
45 | ContentBlock:
46 | PLURALNAME: 'Turinio blokai'
47 | SINGULARNAME: 'Turinio blokas'
48 | GridFieldConfigBlockManager:
49 | AboveOrBelow: 'Virš arba po'
50 |
--------------------------------------------------------------------------------
/lang/fi.yml:
--------------------------------------------------------------------------------
1 | fi:
2 | Block:
3 | Any: '(mikä tahansa)'
4 | BlockArea: 'Sijaintialue'
5 | BlockSetsAsString: 'Laatikkoryhmät: {sets}'
6 | BlockType: 'Laatikon tyyppi'
7 | Content: Sisältö
8 | CreateBlock: 'Luo laatikko'
9 | DeleteBlock: 'Poista laatikko'
10 | EditBlock: 'Muokkaa laatikkoa'
11 | ExtraCSSClasses: 'Lisä-CSS-luokat'
12 | IsPublishedField: Julkaistu
13 | PLURALNAME: Laatikot
14 | PagesAsString: 'Sivut: {pages}'
15 | PermissionCategory: Laatikot
16 | PublishBlock: 'Julkaise laatikko'
17 | SINGULARNAME: Laatikko
18 | Title: Otsikko
19 | TitleRequired: 'Laatikolle tulee syöttää otsikko'
20 | UsageListAsString: 'Käytössä'
21 | ViewerGroups: 'Oikeudet'
22 | BlockAdmin:
23 | MENUTITLE: Laatikot
24 | BlockSet:
25 | ApplyBlockSetToSelectedPageParentsAsWellAsChildren: 'Apply block set to selected page parents as well as children'
26 | CreateBlockSet: 'Luo laatikkoryhmä'
27 | DeleteBlockSet: 'Poista laatikkoryhmä'
28 | EditBlockSet: 'Muokkaa laatikkoryhmää'
29 | OnlyApplyToChildrenOfThesePages: 'Käytä vain näiden sivujen alasivuilla:'
30 | OnlyApplyToThesePageTypes: 'Käytä vain näissä sivutyypeissä:'
31 | OnlyApplyToThesePageTypesDescription: 'Valitut sivutyypit tulevat käyttämään tätä laatikkoryhmää automaattisesti. Jätä kaikki valitsematta käyttääksesi kaikilla sivutyypeillä.'
32 | PLURALNAME: 'Laatikkoryhmät'
33 | SINGULARNAME: 'Laatikkoryhmä'
34 | Settings: Asetukset
35 | YouCanAddBlocksToThisSetOnceYouHaveSavedIt: 'Voit lisätä laatikoita tähän ryhmään sitten kun olet tallentanut ryhmän ensimmäisen kerran.'
36 | BlocksSiteTreeExtension:
37 | BlocksInheritedFromBlockSets: 'Laatikkoryhmistä perityt laatikot'
38 | DisableInheritedBlocks: 'Jätä pois nämä perityt laatikot'
39 | DisableInheritedBlocksDescription: 'Voit valita mitä tahansa perittyjä laatikoita, joita et halua käyttää tällä sivulla.'
40 | InheritBlocksFromBlockSets: 'Laatikkoryhmistä perittävät laatikot'
41 | InheritedBlocksEditLink: 'Perittyjä laatikoita voi muokata {link_start}Laatikot-osiossa{link_end}'
42 | NoBlockAreasConfigured: 'Tälle sivutyypille ei ole määritetty lainkaan laatikoiden sijaintialueita asetustiedostossa.'
43 | NoInheritedBlocksToDisable: 'Tämä sivu ei peri mitään laatikoita.'
44 | PreviewBlockAreasLink: 'Näytä laatikoiden sijaintialueet tällä sivulla'
45 | ContentBlock:
46 | PLURALNAME: 'Sisältölaatikot'
47 | SINGULARNAME: 'Sisältölaatikko'
48 | GridFieldConfigBlockManager:
49 | AboveOrBelow: 'Ylä- tai alapuolella'
50 |
--------------------------------------------------------------------------------
/lang/ny.yml:
--------------------------------------------------------------------------------
1 | nl:
2 | Block:
3 | Any: '(alle)'
4 | BlockArea: 'Blokgebied'
5 | BlockSetsAsString: 'Blokreeks: {sets}'
6 | BlockType: 'Bloktype'
7 | Content: 'Inhoud'
8 | CreateBlock: 'Creër een Blok'
9 | DeleteBlock: 'Verwijder een Blok'
10 | EditBlock: 'Bewerk een Blok'
11 | ExtraCSSClasses: 'Extra CSS Classes'
12 | IsPublishedField: 'Gepubliceerd'
13 | PLURALNAME: 'Blokken'
14 | PagesAsString: "Pagina's: {Pagina's}"
15 | PermissionCategory: 'Blokken'
16 | PublishBlock: 'Publiceer een Blok'
17 | SINGULARNAME: 'Blok'
18 | Title: 'Titel'
19 | TitleRequired: 'Blok Titel is verplicht'
20 | UsageListAsString: 'Gebruikt op'
21 | ViewerGroups: 'Viewer Groepen'
22 | BlockAdmin:
23 | MENUTITLE: 'Blokken'
24 | BlockSet:
25 | ApplyBlockSetToSelectedPageParentsAsWellAsChildren: "Blokreeks toepassen op geselecteerde pagina's én onderliggende pagina's"
26 | CreateBlockSet: 'Creër een Blokreeks'
27 | DeleteBlockSet: 'Verwijder een Blokreeks'
28 | EditBlockSet: 'Bewerk een Blokreeks'
29 | OnlyApplyToChildrenOfThesePages: "Alleen van toepassing op onderliggende pagina's van deze pagina's:"
30 | OnlyApplyToThesePageTypes: 'Alleen van toepassing op deze paginatypen:'
31 | OnlyApplyToThesePageTypesDescription: 'Geselecteerde paginatypen zullen deze Blokreeks automatisch overnemen. Laat alle uitgeschakeld om toe te passen op alle paginatypen.'
32 | PLURALNAME: 'Blokreeks'
33 | SINGULARNAME: 'Blokreeksen'
34 | Settings: 'Instellingen'
35 | YouCanAddBlocksToThisSetOnceYouHaveSavedIt: 'U kunt blokken toevoegen aan deze reeks zodra u het voor de eerste keer heeft opgeslagen'
36 | BlocksSiteTreeExtension:
37 | BlocksInheritedFromBlockSets: 'Blokken overgenomen van Blokreeksen'
38 | DisableInheritedBlocks: 'Overgenomen Blokken uitschakelen'
39 | DisableInheritedBlocksDescription: 'Selecteer alle overgenomen blokken welke u niet wilt tonen op deze pagina.'
40 | InheritBlocksFromBlockSets: 'Neem Blokken over van Blokreeksen'
41 | InheritedBlocksEditLink: 'Tip: Overgenomen blokken kunnen worden bewerkt in de {link_start}Blokadminstrator{link_end}'
42 | NoBlockAreasConfigured: 'Dit paginatype heeft geen Blokgebieden geconfigureerd.'
43 | NoInheritedBlocksToDisable: 'Deze pagina heeft geen overgenomen Blokken om uit te schakelen.'
44 | PreviewBlockAreasLink: 'Bekijk Blokgebieden voor deze pagina'
45 | ContentBlock:
46 | PLURALNAME: 'Inhoudblokken'
47 | SINGULARNAME: 'Inhoudblok'
48 | GridFieldConfigBlockManager:
49 | AboveOrBelow: 'Boven of onder'
50 |
--------------------------------------------------------------------------------
/src/controllers/BlockAdmin.php:
--------------------------------------------------------------------------------
1 |
15 | */
16 | class BlockAdmin extends ModelAdmin
17 | {
18 | private static $managed_models = [
19 | Block::class,
20 | Blockset::class,
21 | ];
22 |
23 | private static $url_segment = "block-admin";
24 |
25 | private static $menu_title = "Blocks";
26 |
27 | public $showImportForm = false;
28 |
29 | private static $dependencies = [
30 | "blockManager" => '%$blockManager',
31 | ];
32 |
33 | public $blockManager;
34 |
35 | /**
36 | * @return array
37 | **/
38 | public function getManagedModels()
39 | {
40 | $models = parent::getManagedModels();
41 |
42 | // remove blocksets if not in use (set in config):
43 | if (!$this->blockManager->getUseBlockSets()) {
44 | unset($models['BlockSet']);
45 | }
46 |
47 | return $models;
48 | }
49 |
50 | /**
51 | * @return Form
52 | **/
53 | public function getEditForm($id = null, $fields = null)
54 | {
55 | Versioned::set_stage('Stage');
56 | $form = parent::getEditForm($id, $fields);
57 |
58 | if ($blockGridField = $form->Fields()->fieldByName('Block')) {
59 | $blockGridField->setConfig(GridFieldConfigBlockManager::create(true, true, false));
60 | $config = $blockGridField->getConfig();
61 | $dcols = $config->getComponentByType('GridFieldDataColumns');
62 | $dfields = $dcols->getDisplayFields($blockGridField);
63 | unset($dfields['BlockArea']);
64 | $dcols->setDisplayFields($dfields);
65 | }
66 |
67 | return $form;
68 | }
69 |
70 | public function getSearchContext()
71 | {
72 | $context = parent::getSearchContext();
73 | $fields = $context->getFields();
74 | $subclasses = $this->blockManager->getBlockClasses();
75 | if ($fields->dataFieldByName('q[ClassName]') && sizeof($subclasses) > 1) {
76 | $fields->dataFieldByName('q[ClassName]')->setSource($subclasses);
77 | $fields->dataFieldByName('q[ClassName]')->setEmptyString('(any)');
78 | } else {
79 | $fields->removeByName('q[ClassName]');
80 | }
81 | return $context;
82 | }
83 | }
84 |
--------------------------------------------------------------------------------
/tests/BlockSetTest.php:
--------------------------------------------------------------------------------
1 | objFromFixture(BlockSet::class, 'default');
23 | $fields = $object->getCMSFields();
24 | $this->assertInstanceOf(FieldList::class, $fields);
25 | }
26 |
27 | /**
28 | *
29 | */
30 | public function testCanView()
31 | {
32 | $object = $this->objFromFixture(BlockSet::class, 'default');
33 | $admin = $this->objFromFixture(Member::class, 'admin');
34 | $this->assertTrue($object->canView($admin));
35 | $member = $this->objFromFixture(Member::class, 'default');
36 | $this->assertTrue($object->canView($member));
37 | }
38 |
39 | /**
40 | *
41 | */
42 | public function testCanEdit()
43 | {
44 | $object = $this->objFromFixture(BlockSet::class, 'default');
45 | $admin = $this->objFromFixture(Member::class, 'admin');
46 | $this->assertTrue($object->canEdit($admin));
47 | $member = $this->objFromFixture(Member::class, 'default');
48 | $this->assertFalse($object->canEdit($member));
49 | }
50 |
51 | /**
52 | *
53 | */
54 | public function testCanDelete()
55 | {
56 | $object = $this->objFromFixture(BlockSet::class, 'default');
57 | $admin = $this->objFromFixture(Member::class, 'admin');
58 | $this->assertTrue($object->canDelete($admin));
59 | $member = $this->objFromFixture(Member::class, 'default');
60 | $this->assertFalse($object->canDelete($member));
61 | }
62 |
63 | /**
64 | *
65 | */
66 | public function testCanCreate()
67 | {
68 | $object = $this->objFromFixture(BlockSet::class, 'default');
69 | $admin = $this->objFromFixture(Member::class, 'admin');
70 | $this->assertTrue($object->canCreate($admin));
71 | $member = $this->objFromFixture(Member::class, 'default');
72 | $this->assertFalse($object->canCreate($member));
73 | }
74 |
75 | /**
76 | *
77 | */
78 | public function testProvidePermissions()
79 | {
80 | $object = singleton(BlockSet::class);
81 | $expected = [
82 | 'BLOCKSET_EDIT' => [
83 | 'name' => _t('BlockSet.EditBlockSet','Edit a Block Set'),
84 | 'category' => _t('Block.PermissionCategory', 'Blocks'),
85 | ],
86 | 'BLOCKSET_DELETE' => [
87 | 'name' => _t('BlockSet.DeleteBlockSet','Delete a Block Set'),
88 | 'category' => _t('Block.PermissionCategory', 'Blocks'),
89 | ],
90 | 'BLOCKSET_CREATE' => [
91 | 'name' => _t('BlockSet.CreateBlockSet','Create a Block Set'),
92 | 'category' => _t('Block.PermissionCategory', 'Blocks'),
93 | ],
94 | ];
95 |
96 | $this->assertEquals($expected, $object->providePermissions());
97 | }
98 | }
--------------------------------------------------------------------------------
/tests/BlockTest.php:
--------------------------------------------------------------------------------
1 | objFromFixture(Block::class, 'default');
23 | $this->assertEquals($object->getTypeForGridfield(), $object->singular_name());
24 | }
25 |
26 | /**
27 | *
28 | */
29 | public function testGetCMSFields()
30 | {
31 | $object = $this->objFromFixture(Block::class, 'default');
32 | $fields = $object->getCMSFields();
33 | $this->assertInstanceOf(FieldList::class, $fields);
34 | }
35 |
36 | /**
37 | *
38 | */
39 | public function testCanView()
40 | {
41 | $object = $this->objFromFixture(Block::class, 'default');
42 | $admin = $this->objFromFixture(Member::class, 'admin');
43 | $this->assertTrue($object->canView($admin));
44 | $member = $this->objFromFixture(Member::class, 'default');
45 | $this->assertTrue($object->canView($member));
46 | }
47 |
48 | /**
49 | *
50 | */
51 | public function testCanEdit()
52 | {
53 | $object = $this->objFromFixture(Block::class, 'default');
54 | $admin = $this->objFromFixture(Member::class, 'admin');
55 | $this->assertTrue($object->canEdit($admin));
56 | $member = $this->objFromFixture(Member::class, 'default');
57 | $this->assertFalse($object->canEdit($member));
58 | }
59 |
60 | /**
61 | *
62 | */
63 | public function testCanDelete()
64 | {
65 | $object = $this->objFromFixture(Block::class, 'default');
66 | $admin = $this->objFromFixture(Member::class, 'admin');
67 | $this->assertTrue($object->canDelete($admin));
68 | $member = $this->objFromFixture(Member::class, 'default');
69 | $this->assertFalse($object->canDelete($member));
70 | }
71 |
72 | /**
73 | *
74 | */
75 | public function testCanCreate()
76 | {
77 | $object = $this->objFromFixture(Block::class, 'default');
78 | $admin = $this->objFromFixture(Member::class, 'admin');
79 | $this->assertTrue($object->canCreate($admin));
80 | $member = $this->objFromFixture(Member::class, 'default');
81 | $this->assertFalse($object->canCreate($member));
82 | }
83 |
84 | /**
85 | *
86 | */
87 | public function testProvidePermissions()
88 | {
89 | $object = singleton(Block::class);
90 | $expected = [
91 | 'BLOCK_EDIT' => [
92 | 'name' => _t('Block.EditBlock', 'Edit a Block'),
93 | 'category' => _t('Block.PermissionCategory', 'Blocks'),
94 | ],
95 | 'BLOCK_DELETE' => [
96 | 'name' => _t('Block.DeleteBlock', 'Delete a Block'),
97 | 'category' => _t('Block.PermissionCategory', 'Blocks'),
98 | ],
99 | 'BLOCK_CREATE' => [
100 | 'name' => _t('Block.CreateBlock', 'Create a Block'),
101 | 'category' => _t('Block.PermissionCategory', 'Blocks'),
102 | ],
103 | 'BLOCK_PUBLISH' => [
104 | 'name' => _t('Block.PublishBlock', 'Publish a Block'),
105 | 'category' => _t('Block.PermissionCategory', 'Blocks'),
106 | ],
107 | ];
108 |
109 | $this->assertEquals($expected, $object->providePermissions());
110 | }
111 | }
--------------------------------------------------------------------------------
/src/BlockManager.php:
--------------------------------------------------------------------------------
1 |
17 | */
18 | class BlockManager extends ViewableData
19 | {
20 | /**
21 | * Use default ContentBlock class.
22 | *
23 | * @var bool
24 | **/
25 | private static $use_default_blocks = true;
26 |
27 | /**
28 | * Show a block area preview button in CMS
29 | *
30 | * @var bool
31 | **/
32 | private static $block_area_preview = true;
33 |
34 | public function __construct()
35 | {
36 | parent::__construct();
37 | }
38 |
39 | /**
40 | * Gets an array of all areas defined for blocks.
41 | *
42 | * @param bool $keyAsValue
43 | *
44 | * @return array $areas
45 | **/
46 | public function getAreas($keyAsValue = true)
47 | {
48 | $areas = $this->config()->get('areas');
49 |
50 | $areas = $keyAsValue ? ArrayLib::valuekey(array_keys($areas)) : $areas;
51 | if (count($areas)) {
52 | foreach ($areas as $k => $v) {
53 | $areas[$k] = $keyAsValue ? FormField::name_to_label($k) : $v;
54 | }
55 | }
56 |
57 | return $areas;
58 | }
59 |
60 | /**
61 | * Gets an array of all areas defined that are compatible with pages of type $class.
62 | *
63 | * @param string $class
64 | *
65 | * @return array $areas
66 | **/
67 | public function getAreasForPageType($class)
68 | {
69 | $areas = $this->getAreas(false);
70 |
71 | if (!$areas) {
72 | return false;
73 | }
74 |
75 | foreach ($areas as $area => $config) {
76 | if (!is_array($config)) {
77 | continue;
78 | }
79 |
80 | if (isset($config['except'])) {
81 | $except = $config['except'];
82 | if (is_array($except)
83 | ? in_array($class, $except)
84 | : $except == $class
85 | ) {
86 | unset($areas[$area]);
87 | continue;
88 | }
89 | }
90 |
91 | if (isset($config['only'])) {
92 | $only = $config['only'];
93 | if (is_array($only)
94 | ? !in_array($class, $only)
95 | : $only != $class
96 | ) {
97 | unset($areas[$area]);
98 | continue;
99 | }
100 | }
101 | }
102 |
103 | if (count($areas)) {
104 | foreach ($areas as $k => $v) {
105 | $areas[$k] = _t('Block.BlockAreaName.'.$k, FormField::name_to_label($k));
106 | }
107 |
108 | return $areas;
109 | } else {
110 | return $areas;
111 | }
112 | }
113 |
114 | public function getBlockClasses()
115 | {
116 | $classes = ArrayLib::valuekey(ClassInfo::subclassesFor("SheaDawson\Blocks\model\Block"));
117 | array_shift($classes);
118 | foreach ($classes as $k => $v) {
119 | $classes[$k] = singleton($k)->singular_name();
120 | }
121 |
122 | $config = $this->config()->get('options');
123 |
124 | if (isset($config['use_default_blocks']) && !$config['use_default_blocks']) {
125 | unset($classes['ContentBlock']);
126 | } else if (!$config['use_default_blocks']) {
127 | unset($classes['ContentBlock']);
128 | }
129 |
130 | $disabledArr = Config::inst()->get("BlockManager", 'disabled_blocks') ? Config::inst()->get("BlockManager", 'disabled_blocks') : [];
131 | if (isset($config['disabled_blocks'])) {
132 | $disabledArr = array_merge($disabledArr, $config['disabled_blocks']);
133 | }
134 | if (count($disabledArr)) {
135 | foreach ($disabledArr as $k => $v) {
136 | unset($classes[$v]);
137 | }
138 | }
139 |
140 | return $classes;
141 | }
142 |
143 | /*
144 | * Usage of BlockSets configurable from yaml
145 | */
146 | public function getUseBlockSets()
147 | {
148 | $config = $this->config()->get('options');
149 |
150 | return isset($config['use_blocksets']) ? $config['use_blocksets'] : true;
151 | }
152 |
153 | /*
154 | * Exclusion of blocks from page types defined in yaml
155 | */
156 | public function getExcludeFromPageTypes()
157 | {
158 | $config = $this->config()->get('options');
159 |
160 | return isset($config['exclude_from_page_types']) ? $config['exclude_from_page_types'] : [];
161 | }
162 |
163 | /*
164 | * getWhiteListedPageTypes optionally configured by the developer
165 | */
166 | public function getWhiteListedPageTypes()
167 | {
168 | $config = $this->config()->get('options');
169 | return isset($config['pagetype_whitelist']) ? $config['pagetype_whitelist'] : [];
170 | }
171 |
172 | /*
173 | * getBlackListedPageTypes optionally configured by the developer
174 | * Includes blacklisted page types defined in the old exclude_from_page_types array
175 | */
176 | public function getBlackListedPageTypes()
177 | {
178 | $config = $this->config()->get('options');
179 | $legacy = isset($config['exclude_from_page_types']) ? $config['exclude_from_page_types'] : [];
180 | $current = isset($config['pagetype_blacklist']) ? $config['pagetype_blacklist'] : [];
181 | return array_merge($legacy, $current);
182 | }
183 |
184 | /*
185 | * Usage of extra css classes configurable from yaml
186 | */
187 | public function getUseExtraCSSClasses()
188 | {
189 | $config = $this->config()->get('options');
190 |
191 | return isset($config['use_extra_css_classes']) ? $config['use_extra_css_classes'] : false;
192 | }
193 |
194 | /*
195 | * Prefix for the default CSSClasses
196 | */
197 | public function getPrefixDefaultCSSClasses()
198 | {
199 | $config = $this->config()->get('options');
200 |
201 | return isset($config['prefix_default_css_classes']) ? $config['prefix_default_css_classes'] : false;
202 | }
203 | }
204 |
--------------------------------------------------------------------------------
/src/forms/GridfieldConfigBlockManager.php:
--------------------------------------------------------------------------------
1 |
29 | */
30 | class GridFieldConfigBlockManager extends GridFieldConfig
31 | {
32 | public $blockManager;
33 |
34 | public function __construct($canAdd = true, $canEdit = true, $canDelete = true, $editableRows = false, $aboveOrBelow = false)
35 | {
36 | parent::__construct();
37 |
38 | $this->blockManager = Injector::inst()->get('SheaDawson\\Blocks\\BlockManager');
39 | $controllerClass = Controller::curr()->class;
40 | // Get available Areas (for page) or all in case of ModelAdmin
41 | if ($controllerClass == 'CMSPageEditController') {
42 | $currentPage = Controller::curr()->currentPage();
43 | $areasFieldSource = $this->blockManager->getAreasForPageType($currentPage->ClassName);
44 | } else {
45 | $areasFieldSource = $this->blockManager->getAreas();
46 | }
47 |
48 | // EditableColumns only makes sense on Saveable parenst (eg Page), or inline changes won't be saved
49 | if ($editableRows) {
50 | $this->addComponent($editable = new GridFieldEditableColumns());
51 | $displayfields = array(
52 | 'TypeForGridfield' => array('title' => _t('Block.BlockType', 'Block Type'), 'field' => 'SilverStripe\\Forms\\LiteralField'),
53 | 'Title' => array('title' => _t('Block.Title', 'Title'), 'field' => 'Silverstripe\\Forms\\ReadonlyField'),
54 | 'BlockArea' => array(
55 | 'title' => _t('Block.BlockArea', 'Block Area'),
56 | 'callback' => function () use ($areasFieldSource) {
57 | $areasField = DropdownField::create('BlockArea', 'Block Area', $areasFieldSource);
58 | if (count($areasFieldSource) > 1) {
59 | $areasField->setHasEmptyDefault(true);
60 | }
61 | return $areasField;
62 | },
63 | ),
64 | 'isPublishedIcon' => array('title' => _t('Block.IsPublishedField', 'Published'), 'field' => 'SilverStripe\\Forms\\LiteralField'),
65 | 'UsageListAsString' => array('title' => _t('Block.UsageListAsString', 'Used on'), 'field' => 'SilverStripe\\Forms\\LiteralField'),
66 | );
67 |
68 | if ($aboveOrBelow) {
69 | $displayfields['AboveOrBelow'] = array(
70 | 'title' => _t('GridFieldConfigBlockManager.AboveOrBelow', 'Above or Below'),
71 | 'callback' => function () {
72 | return DropdownField::create('AboveOrBelow', _t('GridFieldConfigBlockManager.AboveOrBelow', 'Above or Below'), BlockSet::config()->get('above_or_below_options'));
73 | },
74 | );
75 | }
76 | $editable->setDisplayFields($displayfields);
77 | } else {
78 | $this->addComponent($dcols = new GridFieldDataColumns());
79 |
80 | $displayfields = array(
81 | 'TypeForGridfield' => array('title' => _t('Block.BlockType', 'Block Type'), 'field' => 'SilverStripe\\Forms\\LiteralField'),
82 | 'Title' => _t('Block.Title', 'Title'),
83 | 'BlockArea' => _t('Block.BlockArea', 'Block Area'),
84 | 'isPublishedIcon' => array('title' => _t('Block.IsPublishedField', 'Published'), 'field' => 'SilverStripe\\Forms\\LiteralField'),
85 | 'UsageListAsString' => _t('Block.UsageListAsString', 'Used on'),
86 | );
87 | $dcols->setDisplayFields($displayfields);
88 | $dcols->setFieldCasting(array('UsageListAsString' => 'HTMLText->Raw'));
89 | }
90 |
91 |
92 | $this->addComponent(new GridFieldButtonRow('before'));
93 | $this->addComponent(new GridFieldToolbarHeader());
94 | $this->addComponent(new GridFieldDetailForm());
95 | $this->addComponent($sort = new GridFieldSortableHeader());
96 | $this->addComponent($filter = new GridFieldFilterHeader());
97 | $this->addComponent(new GridFieldDetailForm());
98 | if ($controllerClass == 'BlockAdmin' && class_exists('GridFieldCopyButton')) {
99 | $this->addComponent(new GridFieldCopyButton());
100 | }
101 |
102 | $filter->setThrowExceptionOnBadDataType(false);
103 | $sort->setThrowExceptionOnBadDataType(false);
104 |
105 | if ($canAdd) {
106 | $multiClass = new GridFieldAddNewMultiClass();
107 | $classes = $this->blockManager->getBlockClasses();
108 | $multiClass->setClasses($classes);
109 | $this->addComponent($multiClass);
110 | //$this->addComponent(new GridFieldAddNewButton());
111 | }
112 |
113 | if ($canEdit) {
114 | $this->addComponent(new GridFieldEditButton());
115 | }
116 |
117 | if ($canDelete) {
118 | $this->addComponent(new GridFieldDeleteAction(true));
119 | }
120 |
121 | return $this;
122 | }
123 |
124 | /**
125 | * Add the GridFieldAddExistingSearchButton component to this grid config.
126 | *
127 | * @return $this
128 | **/
129 | public function addExisting()
130 | {
131 | $classes = $this->blockManager->getBlockClasses();
132 |
133 | $this->addComponent($add = new GridFieldAddExistingSearchButton());
134 | $add->setSearchList(Block::get()->filter(array(
135 | 'ClassName' => array_keys($classes),
136 | )));
137 |
138 | return $this;
139 | }
140 |
141 | /**
142 | * Add the GridFieldBulkManager component to this grid config.
143 | *
144 | * @return $this
145 | **/
146 | public function addBulkEditing()
147 | {
148 | if (class_exists('GridFieldBulkManager')) {
149 | $this->addComponent(new GridFieldBulkManager());
150 | }
151 |
152 | return $this;
153 | }
154 | }
155 |
--------------------------------------------------------------------------------
/src/model/BlockSet.php:
--------------------------------------------------------------------------------
1 |
27 | */
28 | class BlockSet extends DataObject implements PermissionProvider
29 | {
30 | /**
31 | * @var array
32 | **/
33 | private static $table_name = "BlockSet";
34 |
35 | /**
36 | * @var array
37 | **/
38 | private static $db = [
39 | 'Title' => 'Varchar(255)',
40 | 'PageTypes' => MultiValueField::class,
41 | 'IncludePageParent' => 'Boolean',
42 | ];
43 |
44 | /**
45 | * @var array
46 | **/
47 | private static $many_many = [
48 | "Blocks" => Block::class,
49 | "PageParents" => SiteTree::class,
50 | ];
51 |
52 | /**
53 | * @var array
54 | **/
55 | private static $many_many_extraFields = [
56 | 'Blocks' => [
57 | 'Sort' => 'Int',
58 | 'BlockArea' => 'Varchar',
59 | 'AboveOrBelow' => 'Varchar',
60 | ],
61 | ];
62 |
63 | /**
64 | * @var array
65 | **/
66 | private static $above_or_below_options = [
67 | 'Above' => 'Above Page Blocks',
68 | 'Below' => 'Below Page Blocks',
69 | ];
70 |
71 | public function getCMSFields()
72 | {
73 | $fields = parent::getCMSFields();
74 |
75 | $fields->removeFieldFromTab('Root', 'PageParents');
76 |
77 | $fields->addFieldToTab('Root.Main', HeaderField::create('SettingsHeading', _t('BlockSet.Settings', 'Settings')), 'Title');
78 | $fields->addFieldToTab('Root.Main', MultiValueCheckboxField::create('PageTypes', _t('BlockSet.OnlyApplyToThesePageTypes', 'Only apply to these Page Types:'), $this->pageTypeOptions())
79 | ->setDescription(_t('BlockSet.OnlyApplyToThesePageTypesDescription', 'Selected Page Types will inherit this Block Set automatically. Leave all unchecked to apply to all page types.')));
80 | $fields->addFieldToTab('Root.Main', TreeMultiselectField::create('PageParents', _t('BlockSet.OnlyApplyToChildrenOfThesePages', 'Only apply to children of these Pages:'), 'SilverStripe\\CMS\\Model\\SiteTree'));
81 | $fields->addFieldToTab('Root.Main', CheckboxField::create('IncludePageParent', _t('BlockSet.ApplyBlockSetToSelectedPageParentsAsWellAsChildren','Apply block set to selected page parents as well as children')));
82 |
83 | if (!$this->ID) {
84 | $fields->addFieldToTab('Root.Main', LiteralField::create('NotSaved', ""._t('BlockSet.YouCanAddBlocksToThisSetOnceYouHaveSavedIt', 'You can add Blocks to this set once you have saved it for the first time').'
'));
85 |
86 | return $fields;
87 | }
88 |
89 | $fields->removeFieldFromTab('Root', 'Blocks');
90 |
91 | /**
92 | * @todo - change relation editor back to the custom block manager config and fix issues when 'creating' Blocks from a BlockSet.
93 | */
94 | $gridConfig = GridFieldConfig_RelationEditor::create();
95 | $gridConfig->addComponent(new GridFieldOrderableRows('Sort'));
96 | $gridConfig->addComponent(new GridFieldDeleteAction());
97 |
98 | $gridSource = $this->Blocks()->Sort('Sort');
99 |
100 | $fields->addFieldToTab('Root.Blocks', HeaderField::create('BlocksHeading', _t('Block.PLURALNAME', 'Blocks')));
101 | $fields->addFieldToTab('Root.Blocks', GridField::create('Blocks', _t('Block.PLURALNAME', 'Blocks'), $gridSource, $gridConfig));
102 |
103 | return $fields;
104 | }
105 |
106 | /**
107 | * Returns a sorted array suitable for a dropdown with pagetypes and their translated name.
108 | *
109 | * @return array
110 | */
111 | protected function pageTypeOptions()
112 | {
113 | $pageTypes = [];
114 | $classes = ArrayLib::valueKey(SiteTree::page_type_classes());
115 | unset($classes['VirtualPage']);
116 | unset($classes['ErrorPage']);
117 | unset($classes['RedirectorPage']);
118 | foreach ($classes as $pageTypeClass) {
119 | $pageTypes[$pageTypeClass] = singleton($pageTypeClass)->i18n_singular_name();
120 | }
121 | asort($pageTypes);
122 |
123 | return $pageTypes;
124 | }
125 |
126 | /**
127 | * Returns a list of pages this BlockSet features on.
128 | *
129 | * @return DataList
130 | */
131 | public function Pages()
132 | {
133 | $pages = SiteTree::get();
134 | $types = $this->PageTypes->getValue();
135 | if (count($types)) {
136 | $pages = $pages->filter('ClassName', $types);
137 | }
138 |
139 | $parents = $this->PageParents()->column('ID');
140 | if (count($parents)) {
141 | $pages = $pages->filter('ParentID', $parents);
142 | }
143 |
144 | return $pages;
145 | }
146 |
147 | public function canView($member = null)
148 | {
149 | return true;
150 | }
151 |
152 | public function canEdit($member = null)
153 | {
154 | return Permission::check('ADMIN', 'any', $member) || Permission::check('BLOCK_EDIT', 'any', $member);
155 | }
156 |
157 | public function canDelete($member = null)
158 | {
159 | return Permission::check('ADMIN', 'any', $member) || Permission::check('BLOCK_DELETE', 'any', $member);
160 | }
161 |
162 | public function canCreate($member = null, $context = [])
163 | {
164 | return Permission::check('ADMIN', 'any', $member) || Permission::check('BLOCK_CREATE', 'any', $member);
165 | }
166 |
167 | public function providePermissions()
168 | {
169 | return [
170 | 'BLOCKSET_EDIT' => [
171 | 'name' => _t('BlockSet.EditBlockSet','Edit a Block Set'),
172 | 'category' => _t('Block.PermissionCategory', 'Blocks'),
173 | ],
174 | 'BLOCKSET_DELETE' => [
175 | 'name' => _t('BlockSet.DeleteBlockSet','Delete a Block Set'),
176 | 'category' => _t('Block.PermissionCategory', 'Blocks'),
177 | ],
178 | 'BLOCKSET_CREATE' => [
179 | 'name' => _t('BlockSet.CreateBlockSet','Create a Block Set'),
180 | 'category' => _t('Block.PermissionCategory', 'Blocks'),
181 | ],
182 | ];
183 | }
184 | }
185 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # SilverStripe Blocks
2 |
3 | [](https://travis-ci.org/sheadawson/silverstripe-blocks)
4 | [](https://scrutinizer-ci.com/g/sheadawson/silverstripe-blocks/?branch=master)
5 | [](https://codecov.io/gh/sheadawson/silverstripe-blocks)
6 |
7 | 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.
8 |
9 | ## Notice
10 |
11 | This module is no longer maintained. If you would like to adopt it and give it a good home please submit your interest and I will be happy to discuss.
12 |
13 | ## Features
14 |
15 | * Blocks are Versioned
16 | * Blocks with Forms possible (through `BlockController`)
17 | * Drag and Drop re-ordering of Blocks
18 | * Duplicate Blocks
19 | * BlockSets for global blocks
20 | * Allow exclusion of any page types from using Blocks
21 | * Allow disabling of default/example block type - ContentBlock
22 | * Allow disabling of specific blocks
23 |
24 |
25 | ## Upgrading from 0.x
26 |
27 | See [the upgrade guide](docs/upgrading.md)
28 |
29 | ## Requirements
30 |
31 | * SilverStripe CMS ^4.0
32 | * [GridFieldExtensions](https://github.com/silverstripe-australia/silverstripe-gridfieldextensions)
33 | * [MultivalueField](https://github.com/nyeholt/silverstripe-multivaluefield)
34 | * [GridField BetterButtons](https://github.com/unclecheese/silverstripe-gridfield-betterbuttons)
35 |
36 | ## Recommended
37 | * [GridField Copybutton](https://github.com/unisolutions/silverstripe-copybutton) (duplication of blocks, from BlockAdmin)
38 | * [GridField Sitetree Buttons](https://github.com/micschk/silverstripe-gridfieldsitetreebuttons) (Edit pages directly from a Block's 'used on page' list)
39 |
40 | ## Installation
41 |
42 | ```sh
43 | composer require sheadawson/silverstripe-blocks
44 | ```
45 |
46 | Install via composer, run `dev/build`
47 |
48 | ## Quickstart
49 |
50 | ### 1. Define Block Areas and Settings for your project in `mysite/_config/config.yml`
51 |
52 | ``` yml
53 | SheaDawson\Blocks\BlockManager:
54 | areas:
55 | Sidebar: true # a Sidebar area will be available on all page types
56 | BeforeContent:
57 | only: HomePage # a BeforeContent area will be available only on HomePage page types
58 | AfterContent:
59 | except: HomePage # a AfterContent area will be available on all page types except HomePage
60 | Footer: true # a Footer area will be available on all page types
61 | options:
62 | #use_blocksets: false # Whether to use BlockSet functionality (default if undeclared: true)
63 | #use_extra_css_classes: true # Whether to allow cms users to add extra css classes to blocks (default if undeclared: false)
64 | #prefix_default_css_classes: 'myprefix--' # prefix the automatically generated CSSClasses based on class name (default if undeclared: false)
65 | #pagetype_whitelist: # Enable the Blocks tab only pages of these types (optional)
66 | # - HomePage
67 | #pagetype_blacklist: # Disable the Blocks tab on pages of these types (optional)
68 | # - ContactPage
69 | #disabled_blocks: #allows you to disable specific blocks (optional)
70 | # - ContentBlock
71 | #use_default_blocks: false # Disable/enable the default Block types (ContentBlock) (default if undeclared: true)
72 | #block_area_preview: false # Disable block area preview button in CMS (default if undeclared: true)
73 | ```
74 |
75 | Remember to run `?flush=1` after modifying your `.yml` config to make sure it gets applied.
76 |
77 | ### 2. Add Block Areas to your templates
78 |
79 | Adding the `BeforeContent` and `AfterContent` blocks would look something like
80 |
81 | ```html
82 |
83 | $Title
84 | $BlockArea(BeforeContent)
85 | $Content
86 | $BlockArea(AfterContent)
87 |
88 | ```
89 |
90 | `$BlockArea(BeforeContent)` will loop over and display all blocks assigned to the `BeforeContent` area on the current page
91 |
92 | You can limit a block area to a maximum number of blocks using the second limit parameter
93 |
94 | ```html
95 |
96 | $BlockArea(NewsBlocks, 3)
97 |
98 | ```
99 |
100 | ### 3. Add Blocks to a page in the CMS
101 |
102 | You will now be able to add Blocks to Pages via the CMS page edit view and in the Blocks model admin. You can also define
103 | "BlockSets" in the Blocks model admin. BlockSets can be used to apply a common collection of blocks to pages that match the criteria you define on the set.
104 |
105 | This module ships with a basic `ContentBlock`, but this can be disabled through the `BlockManager::use_default_blocks config.
106 |
107 |
108 | ## Help
109 |
110 |
111 | ### Restrict Blocks to viewer groups or logged in users
112 |
113 | When editing a block, you can restrict who can see it in the frontend by selecting "logged in users" or "users from these groups" under the Viewer Groups tab.
114 |
115 | ### Templates
116 |
117 | There are 2 types of templates you should be aware of.
118 |
119 | ### BlockArea Template
120 |
121 | The `BlockArea` template is responsible for looping over and rendering all blocks in that area. You can override this by
122 | creating a copy of the default `BlockArea.ss` and placing it in your `templates/Includes` folder.
123 |
124 | It's likely that your block areas may require different templates. You can achieve this by creating a `BlockArea_{AreaName}.ss` template.
125 |
126 | ### Block Template
127 |
128 | Each subclass of Block requires it's own template with the same name as the class. So, `SlideshowBlock.php` would have a
129 | `SlideshowBlock.ss` template. If your block requires different templates depending on the `BlockArea` it's in, you can
130 | create `SlideshowBlock_{AreaName}.ss`
131 |
132 | The current page scope can be accessed from Block templates with `$CurrentPage`.
133 |
134 | ### Block Area Preview
135 |
136 | To aid website admins in identifying the areas they can apply blocks to, a "Preview Block Areas for this page" button
137 | is available in the cms. This opens the frontend view of the page in a new tab with `?block_preview=1`.
138 | In Block Preview mode, Block Areas in the template are highlighted and labeled.
139 |
140 | There is some markup required in your BlockArea templates to facilitate this: The css class `block-area` and the
141 | `data-areaid='$AreaID'` attribute.
142 |
143 | ```html
144 |
145 | <% loop BlockArea %>
146 | $BlockHTML
147 | <% end_loop %>
148 |
149 | ```
150 |
151 | ### Form Blocks
152 |
153 | As of v1.0 Blocks can now handle forms. See this gist for as an example:
154 |
155 | * [Block with Form example](https://gist.github.com/sheadawson/e584b0771f6b124701b4)
156 |
157 | ### Remove the Blocks button from the main CMS menu
158 |
159 | The BlockAdmin section is not always needed to be used. If you wish, you can remove the button from the menu by inserting this to `mysite/_config.php`:
160 |
161 | ``` php
162 | CMSMenu::remove_menu_item('BlockAdmin');
163 | ```
164 |
165 | ### Block icons
166 |
167 | Until this module properly supports icons, you can define icons by creating a `getTypeForGridfield` method in your block.
168 | Here's an example that uses font awesome:
169 |
170 |
171 | ```php
172 | public function getIcon()
173 | {
174 | return '';
175 | }
176 | public function getTypeForGridfield()
177 | {
178 | $icon = $this->getIcon();
179 | if ($icon) {
180 | $obj = HTMLText::create();
181 | $obj->setValue($icon);
182 | return $obj;
183 | } else {
184 | return parent::getTypeForGridfield();
185 | }
186 | }
187 | ```
188 |
189 | ## Translatable Blocks
190 |
191 | 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.
192 |
193 | ## Screenshots
194 |
195 | 
196 | Overview
197 |
198 | 
199 | Preview of block locations
200 |
201 | 
202 | Edit a block
203 |
204 | 
205 | Add an existing block
206 |
207 | ## TODO
208 |
209 | - [ ] Re-add: Sorting primarily by Area (in order of declaration in config), on Pages (removed in favor of dr'ndr sorting)
210 | - [ ] Add icon/pic to base Block as method of recognition when dealing with lots of different blocks
211 |
--------------------------------------------------------------------------------
/src/extensions/BlocksSiteTreeExtension.php:
--------------------------------------------------------------------------------
1 |
27 | */
28 | class BlocksSiteTreeExtension extends SiteTreeExtension
29 | {
30 | private static $db = [
31 | 'InheritBlockSets' => 'Boolean',
32 | ];
33 |
34 | private static $many_many = [
35 | "Blocks" => Block::class,
36 | "DisabledBlocks" => Block::class,
37 | ];
38 |
39 | public static $many_many_extraFields = [
40 | 'Blocks' => [
41 | 'Sort' => 'Int',
42 | 'BlockArea' => 'Varchar',
43 | ],
44 | ];
45 |
46 | private static $defaults = [
47 | 'InheritBlockSets' => 1,
48 | ];
49 |
50 | private static $dependencies = [
51 | 'blockManager' => '%$blockManager',
52 | ];
53 |
54 | public $blockManager;
55 |
56 |
57 | /**
58 | * Check if the Blocks CMSFields should be displayed for this Page
59 | *
60 | * @return boolean
61 | **/
62 | public function showBlocksFields()
63 | {
64 | $whiteList = $this->blockManager->getWhiteListedPageTypes();
65 | $blackList = $this->blockManager->getBlackListedPageTypes();
66 |
67 | if (in_array($this->owner->ClassName, $blackList)) {
68 | return false;
69 | }
70 |
71 | if (count($whiteList) && !in_array($this->owner->ClassName, $whiteList)) {
72 | return false;
73 | }
74 |
75 | if (!Permission::check('BLOCK_EDIT')) {
76 | return false;
77 | }
78 |
79 | return true;
80 | }
81 |
82 | /**
83 | * Block manager for Pages.
84 | * */
85 | public function updateCMSFields(FieldList $fields)
86 | {
87 | if ($fields->fieldByName('Root.Blocks') || !$this->showBlocksFields()) {
88 | return;
89 | }
90 |
91 | $areas = $this->blockManager->getAreasForPageType($this->owner->ClassName);
92 |
93 | if ($areas && count($areas)) {
94 | $fields->addFieldToTab('Root', new Tab('Blocks', _t('Block.PLURALNAME', 'Blocks')));
95 | if (BlockManager::config()->get('block_area_preview')) {
96 | $fields->addFieldToTab('Root.Blocks',
97 | LiteralField::create('PreviewLink', $this->areasPreviewButton()));
98 | }
99 |
100 | // Blocks related directly to this Page
101 | $gridConfig = GridFieldConfigBlockManager::create(true, true, true, true)
102 | ->addExisting($this->owner->class)
103 | //->addBulkEditing()
104 | // ->addComponent(new GridFieldOrderableRows()) // Comment until below TODO is complete.
105 | ;
106 |
107 |
108 | // TODO it seems this sort is not being applied...
109 | $gridSource = $this->owner->Blocks();
110 | // ->sort(array(
111 | // "FIELD(SiteTree_Blocks.BlockArea, '" . implode("','", array_keys($areas)) . "')" => '',
112 | // 'SiteTree_Blocks.Sort' => 'ASC',
113 | // 'Name' => 'ASC'
114 | // ));
115 |
116 | $fields->addFieldToTab('Root.Blocks', GridField::create('Blocks', _t('Block.PLURALNAME', 'Blocks'), $gridSource, $gridConfig));
117 |
118 |
119 | // Blocks inherited from BlockSets
120 | if ($this->blockManager->getUseBlockSets()) {
121 | $inheritedBlocks = $this->getBlocksFromAppliedBlockSets(null, true);
122 |
123 | if ($inheritedBlocks->count()) {
124 | $activeInherited = $this->getBlocksFromAppliedBlockSets(null, false);
125 |
126 | if ($activeInherited->count()) {
127 | $fields->addFieldsToTab('Root.Blocks', [
128 | GridField::create('InheritedBlockList', _t('BlocksSiteTreeExtension.BlocksInheritedFromBlockSets', 'Blocks Inherited from Block Sets'), $activeInherited,
129 | GridFieldConfigBlockManager::create(false, false, false)),
130 | LiteralField::create('InheritedBlockListTip', ""._t('BlocksSiteTreeExtension.InheritedBlocksEditLink', 'Tip: Inherited blocks can be edited in the {link_start}Block Admin area{link_end}', '', ['link_start' => '', 'link_end' => '']).'
'),
131 | ]);
132 | }
133 |
134 | $fields->addFieldToTab('Root.Blocks',
135 | ListboxField::create('DisabledBlocks', _t('BlocksSiteTreeExtension.DisableInheritedBlocks', 'Disable Inherited Blocks'),
136 | $inheritedBlocks->map('ID', 'Title'), null, null, true)
137 | ->setDescription(_t('BlocksSiteTreeExtension.DisableInheritedBlocksDescription', 'Select any inherited blocks that you would not like displayed on this page.'))
138 | );
139 | } else {
140 | $fields->addFieldToTab('Root.Blocks',
141 | ReadonlyField::create('DisabledBlocksReadOnly', _t('BlocksSiteTreeExtension.DisableInheritedBlocks', 'Disable Inherited Blocks'),
142 | _t('BlocksSiteTreeExtension.NoInheritedBlocksToDisable','This page has no inherited blocks to disable.')));
143 | }
144 |
145 | $fields->addFieldToTab('Root.Blocks',
146 | CheckboxField::create('InheritBlockSets', _t('BlocksSiteTreeExtension.InheritBlocksFromBlockSets', 'Inherit Blocks from Block Sets')));
147 | }
148 | }
149 | }
150 |
151 | /**
152 | * Called from templates to get rendered blocks for the given area.
153 | *
154 | * @param string $area
155 | * @param int $limit Limit the items to this number, or null for no limit
156 | */
157 | public function BlockArea($area, $limit = null)
158 | {
159 | if ($this->owner->ID <= 0) {
160 | return;
161 | } // blocks break on fake pages ie Security/login
162 |
163 | $list = $this->getBlockList($area);
164 |
165 | foreach ($list as $block) {
166 | if (!$block->canView()) {
167 | $list->remove($block);
168 | }
169 | }
170 |
171 | if ($limit !== null) {
172 | $list = $list->limit($limit);
173 | }
174 |
175 | $data = [];
176 | $data['HasBlockArea'] = ($this->owner->canEdit() && isset($_REQUEST['block_preview']) && $_REQUEST['block_preview']) || $list->Count() > 0;
177 | $data['BlockArea'] = $list;
178 | $data['AreaID'] = $area;
179 |
180 | $data = $this->owner->customise($data);
181 |
182 | $template = ['BlockArea_'.$area];
183 |
184 | if (SSViewer::hasTemplate($template)) {
185 | return $data->renderWith($template);
186 | } else {
187 | return $data->renderWith('BlockArea');
188 | }
189 | }
190 |
191 | public function HasBlockArea($area)
192 | {
193 | if ($this->owner->canEdit() && isset($_REQUEST['block_preview']) && $_REQUEST['block_preview']) {
194 | return true;
195 | }
196 |
197 | $list = $this->getBlockList($area);
198 |
199 | foreach ($list as $block) {
200 | if (!$block->canView()) {
201 | $list->remove($block);
202 | }
203 | }
204 |
205 | return $list->Count() > 0;
206 | }
207 |
208 | /**
209 | * Get a merged list of all blocks on this page and ones inherited from BlockSets.
210 | *
211 | * @param string|null $area filter by block area
212 | * @param bool $includeDisabled Include blocks that have been explicitly excluded from this page
213 | * i.e. blocks from block sets added to the "disable inherited blocks" list
214 | *
215 | * @return ArrayList
216 | * */
217 | public function getBlockList($area = null, $includeDisabled = false)
218 | {
219 | $includeSets = $this->blockManager->getUseBlockSets() && $this->owner->InheritBlockSets;
220 | $blocks = ArrayList::create();
221 |
222 | // get blocks directly linked to this page
223 | $nativeBlocks = $this->owner->Blocks()->sort('Sort');
224 | if ($area) {
225 | $nativeBlocks = $nativeBlocks->filter('BlockArea', $area);
226 | }
227 |
228 | if ($nativeBlocks->count()) {
229 | foreach ($nativeBlocks as $block) {
230 | $blocks->add($block);
231 | }
232 | }
233 |
234 | // get blocks from BlockSets
235 | if ($includeSets) {
236 | $blocksFromSets = $this->getBlocksFromAppliedBlockSets($area, $includeDisabled);
237 | if ($blocksFromSets->count()) {
238 | // merge set sources
239 | foreach ($blocksFromSets as $block) {
240 | if (!$blocks->find('ID', $block->ID)) {
241 | if ($block->AboveOrBelow == 'Above') {
242 | $blocks->unshift($block);
243 | } else {
244 | $blocks->push($block);
245 | }
246 | }
247 | }
248 | }
249 | }
250 |
251 | return $blocks;
252 | }
253 |
254 | /**
255 | * Get Any BlockSets that apply to this page.
256 | *
257 | * @return ArrayList
258 | * */
259 | public function getAppliedSets()
260 | {
261 | $list = ArrayList::create();
262 | if (!$this->owner->InheritBlockSets) {
263 | return $list;
264 | }
265 |
266 | $sets = BlockSet::get()->where("(PageTypesValue IS NULL) OR (PageTypesValue LIKE '%:\"{$this->owner->ClassName}%')");
267 | $ancestors = $this->owner->getAncestors()->column('ID');
268 |
269 | foreach ($sets as $set) {
270 | $restrictedToParerentIDs = $set->PageParents()->column('ID');
271 | if (count($restrictedToParerentIDs)) {
272 | // check whether the set should include selected parent, in which case check whether
273 | // it was in the restricted parents list. If it's not, or if include parentpage
274 | // wasn't selected, we check the ancestors of this page.
275 | if ($set->IncludePageParent && in_array($this->owner->ID, $restrictedToParerentIDs)) {
276 | $list->add($set);
277 | } else {
278 | if (count($ancestors)) {
279 | foreach ($ancestors as $ancestor) {
280 | if (in_array($ancestor, $restrictedToParerentIDs)) {
281 | $list->add($set);
282 | continue;
283 | }
284 | }
285 | }
286 | }
287 | } else {
288 | $list->add($set);
289 | }
290 | }
291 |
292 | return $list;
293 | }
294 |
295 | /**
296 | * Get all Blocks from BlockSets that apply to this page.
297 | *
298 | * @return ArrayList
299 | * */
300 | public function getBlocksFromAppliedBlockSets($area = null, $includeDisabled = false)
301 | {
302 | $blocks = ArrayList::create();
303 | $sets = $this->getAppliedSets();
304 |
305 | if (!$sets->count()) {
306 | return $blocks;
307 | }
308 |
309 | foreach ($sets as $set) {
310 | $setBlocks = $set->Blocks()->sort('Sort DESC');
311 |
312 | if (!$includeDisabled) {
313 | $setBlocks = $setBlocks->exclude('ID', $this->owner->DisabledBlocks()->column('ID'));
314 | }
315 |
316 | if ($area) {
317 | $setBlocks = $setBlocks->filter('BlockArea', $area);
318 | }
319 |
320 | $blocks->merge($setBlocks);
321 | }
322 |
323 | $blocks->removeDuplicates();
324 |
325 | return $blocks;
326 | }
327 |
328 | /**
329 | * Get's the link for a block area preview button.
330 | *
331 | * @return string
332 | * */
333 | public function areasPreviewLink()
334 | {
335 | return Controller::join_links($this->owner->Link(), '?block_preview=1');
336 | }
337 |
338 | /**
339 | * Get's html for a block area preview button.
340 | *
341 | * @return string
342 | * */
343 | public function areasPreviewButton()
344 | {
345 | return ""._t('BlocksSiteTreeExtension.PreviewBlockAreasLink', 'Preview Block Areas for this page').'';
346 | }
347 | }
348 |
--------------------------------------------------------------------------------
/src/model/Block.php:
--------------------------------------------------------------------------------
1 |
33 | */
34 | class Block extends DataObject implements PermissionProvider
35 | {
36 |
37 | private static $table_name = 'Block';
38 |
39 | /**
40 | * @var array
41 | */
42 | private static $db = [
43 | 'Title' => 'Varchar(255)',
44 | 'CanViewType' => "Enum('Anyone, LoggedInUsers, OnlyTheseUsers', 'Anyone')",
45 | 'ExtraCSSClasses' => 'Varchar'
46 | ];
47 |
48 | /**
49 | * @var array
50 | */
51 | private static $many_many = [
52 | "ViewerGroups" => Group::class,
53 | ];
54 |
55 | /**
56 | * @var array
57 | */
58 | private static $belongs_many_many = [
59 | "Pages" => SiteTree::class,
60 | "BlockSets" => BlockSet::class,
61 | ];
62 |
63 | private static $summary_fields = [
64 | 'singular_name',
65 | 'Title',
66 | 'isPublishedField',
67 | 'UsageListAsString',
68 | ];
69 |
70 | private static $searchable_fields = [
71 | 'Title',
72 | 'ClassName',
73 | ];
74 |
75 | public function fieldLabels($includerelations = true)
76 | {
77 | $labels = parent::fieldLabels($includerelations);
78 |
79 | $labels = array_merge($labels, [
80 | 'singular_name' => _t('Block.BlockType', 'Block Type'),
81 | 'Title' => _t('Block.Title', 'Title'),
82 | 'isPublishedField' => _t('Block.IsPublishedField', 'Published'),
83 | 'UsageListAsString' => _t('Block.UsageListAsString', 'Used on'),
84 | 'ExtraCSSClasses' => _t('Block.ExtraCSSClasses', 'Extra CSS Classes'),
85 | 'ClassName' => _t('Block.BlockType', 'Block Type'),
86 | ]);
87 |
88 | return $labels;
89 | }
90 |
91 | public function getDefaultSearchContext()
92 | {
93 | $context = parent::getDefaultSearchContext();
94 |
95 | $results = $this->blockManager->getBlockClasses();
96 | if (sizeof($results) > 1) {
97 | $classfield = new DropdownField('ClassName', _t('Block.BlockType', 'Block Type'));
98 | $classfield->setSource($results);
99 | $classfield->setEmptyString(_t('Block.Any', '(any)'));
100 | $context->addField($classfield);
101 | }
102 |
103 | return $context;
104 | }
105 |
106 | /**
107 | * @var array
108 | */
109 | private static $default_sort = ['Title' => 'ASC'];
110 |
111 | /**
112 | * @var array
113 | */
114 | private static $dependencies = [
115 | 'blockManager' => '%$blockManager',
116 | ];
117 |
118 | /**
119 | * @var BlockManager
120 | */
121 | public $blockManager;
122 |
123 | /**
124 | * @var BlockController
125 | */
126 | protected $controller;
127 |
128 | /**
129 | * @return mixed
130 | */
131 | public function getTypeForGridfield()
132 | {
133 | return $this->singular_name();
134 | }
135 |
136 | public function getCMSFields()
137 | {
138 | $self = $this;
139 | $this->beforeUpdateCMSFields(function($fields) use($self) {
140 | /** @var FieldList $fields */
141 | Requirements::add_i18n_javascript(BLOCKS_DIR . '/javascript/lang');
142 |
143 | // this line is a temporary patch until I can work out why this dependency isn't being
144 | // loaded in some cases...
145 | if (!$self->blockManager) {
146 | $self->blockManager = singleton("SheaDawson\\Blocks\\BlockManager");
147 | }
148 |
149 | // ClassNmae - block type/class field
150 | $classes = $self->blockManager->getBlockClasses();
151 | $fields->addFieldToTab('Root.Main', DropdownField::create('ClassName', _t('Block.BlockType', 'Block Type'), $classes)->addExtraClass('block-type'), 'Title');
152 |
153 | // BlockArea - display areas field if on page edit controller
154 | if (Controller::curr()->class == 'CMSPageEditController') {
155 | $currentPage = Controller::curr()->currentPage();
156 | $areas = $self->blockManager->getAreasForPageType($currentPage->ClassName);
157 | $fields->addFieldToTab(
158 | 'Root.Main',
159 | $blockAreaField = DropdownField::create('ManyMany[BlockArea]', _t('Block.BlockArea', 'Block Area'), $areas),
160 | 'ClassName'
161 | );
162 |
163 |
164 |
165 | if (count($areas) > 1) {
166 | $blockAreaField->setEmptyString('(Select one)');
167 | }
168 |
169 | if (BlockManager::config()->get('block_area_preview')) {
170 | $blockAreaField->setRightTitle($currentPage->areasPreviewButton());
171 | }
172 | }
173 |
174 | $fields->removeFieldFromTab('Root', 'BlockSets');
175 | $fields->removeFieldFromTab('Root', 'Pages');
176 |
177 | // legacy fields, will be removed in later release
178 | $fields->removeByName('Weight');
179 | $fields->removeByName('Area');
180 | $fields->removeByName('Published');
181 |
182 | if ($self->blockManager->getUseExtraCSSClasses()) {
183 | $fields->addFieldToTab('Root.Main', $fields->dataFieldByName('ExtraCSSClasses'), 'Title');
184 | } else {
185 | $fields->removeByName('ExtraCSSClasses');
186 | }
187 |
188 | // Viewer groups
189 | $fields->removeFieldFromTab('Root', 'ViewerGroups');
190 | $groupsMap = Group::get()->map('ID', 'Breadcrumbs')->toArray();
191 | asort($groupsMap);
192 | $viewersOptionsField = new OptionsetField(
193 | 'CanViewType',
194 | _t('SiteTree.ACCESSHEADER', 'Who can view this page?')
195 | );
196 | $viewerGroupsField = ListboxField::create('ViewerGroups', _t('SiteTree.VIEWERGROUPS', 'Viewer Groups'))
197 | ->setSource($groupsMap)
198 | ->setAttribute(
199 | 'data-placeholder',
200 | _t('SiteTree.GroupPlaceholder', 'Click to select group')
201 | );
202 | $viewersOptionsSource = [];
203 | $viewersOptionsSource['Anyone'] = _t('SiteTree.ACCESSANYONE', 'Anyone');
204 | $viewersOptionsSource['LoggedInUsers'] = _t('SiteTree.ACCESSLOGGEDIN', 'Logged-in users');
205 | $viewersOptionsSource['OnlyTheseUsers'] = _t('SiteTree.ACCESSONLYTHESE', 'Only these people (choose from list)');
206 | $viewersOptionsField->setSource($viewersOptionsSource)->setValue('Anyone');
207 |
208 | $fields->addFieldToTab('Root', new Tab('ViewerGroups', _t('Block.ViewerGroups', 'Viewer Groups')));
209 | $fields->addFieldsToTab('Root.ViewerGroups', [
210 | $viewersOptionsField,
211 | $viewerGroupsField,
212 | ]);
213 |
214 | // Disabled for now, until we can list ALL pages this block is applied to (inc via sets)
215 | // As otherwise it could be misleading
216 | // Show a GridField (list only) with pages which this block is used on
217 | // $fields->removeFieldFromTab('Root.Pages', 'Pages');
218 | // $fields->addFieldsToTab('Root.Pages',
219 | // new GridField(
220 | // 'Pages',
221 | // 'Used on pages',
222 | // $self->Pages(),
223 | // $gconf = GridFieldConfig_Base::create()));
224 | // enhance gridfield with edit links to pages if GFEditSiteTreeItemButtons is available
225 | // a GFRecordEditor (default) combined with BetterButtons already gives the possibility to
226 | // edit versioned records (Pages), but STbutton loads them in their own interface instead
227 | // of GFdetailform
228 | // if(class_exists('GridFieldEditSiteTreeItemButton')) {
229 | // $gconf->addComponent(new GridFieldEditSiteTreeItemButton());
230 | // }
231 | });
232 | return parent::getCMSFields();
233 | }
234 |
235 | /**
236 | * Renders this block with appropriate templates
237 | * looks for templates that match BlockClassName_AreaName
238 | * falls back to BlockClassName.
239 | *
240 | * @return string
241 | **/
242 | public function forTemplate()
243 | {
244 | if ($this->BlockArea) {
245 | $template = [$this->class.'_'.$this->BlockArea];
246 |
247 | if (SSViewer::hasTemplate($template)) {
248 | return $this->renderWith($template);
249 | }
250 | }
251 |
252 | return $this->renderWith($this->ClassName, $this->getController());
253 | }
254 |
255 | /**
256 | * @return string
257 | */
258 | public function BlockHTML()
259 | {
260 | return $this->forTemplate();
261 | }
262 |
263 | /*
264 | * Checks if deletion was an actual deletion, not just unpublish
265 | * If so, remove relations
266 | */
267 | public function onAfterDelete()
268 | {
269 | parent::onAfterDelete();
270 | if (Versioned::get_stage() == 'Stage') {
271 | $this->Pages()->removeAll();
272 | $this->BlockSets()->removeAll();
273 | }
274 | }
275 |
276 | /**
277 | * Remove relations onAfterDuplicate.
278 | */
279 | public function onAfterDuplicate()
280 | {
281 | // remove relations to pages & blocksets duplicated from the original item
282 | $this->Pages()->removeAll();
283 | $this->BlockSets()->removeAll();
284 | }
285 |
286 | /*
287 | * Base permissions
288 | */
289 | public function canView($member = null)
290 | {
291 | if (!$member || !(is_a($member, 'Member')) || is_numeric($member)) {
292 | $member = Security::getCurrentUser()->ID;
293 | }
294 |
295 | // admin override
296 | if ($member && Permission::checkMember($member, ['ADMIN', 'SITETREE_VIEW_ALL'])) {
297 | return true;
298 | }
299 |
300 | // Standard mechanism for accepting permission changes from extensions
301 | $extended = $this->extendedCan('canView', $member);
302 | if ($extended !== null) {
303 | return $extended;
304 | }
305 |
306 | // check for empty spec
307 | if (!$this->CanViewType || $this->CanViewType == 'Anyone') {
308 | return true;
309 | }
310 |
311 | // check for any logged-in users
312 | if ($this->CanViewType == 'LoggedInUsers' && $member) {
313 | return true;
314 | }
315 |
316 | // check for specific groups
317 | if ($member && is_numeric($member)) {
318 | $member = Member::get()->byID($member);
319 | }
320 | if ($this->CanViewType == 'OnlyTheseUsers' && $member && $member->inGroups($this->ViewerGroups())) {
321 | return true;
322 | }
323 |
324 | return false;
325 | }
326 |
327 | public function canEdit($member = null)
328 | {
329 | return Permission::check('ADMIN', 'any', $member) || Permission::check('BLOCK_EDIT', 'any', $member);
330 | }
331 |
332 | public function canDelete($member = null)
333 | {
334 | return Permission::check('ADMIN', 'any', $member) || Permission::check('BLOCK_DELETE', 'any', $member);
335 | }
336 |
337 | public function canCreate($member = null, $context = [])
338 | {
339 | return Permission::check('ADMIN', 'any', $member) || Permission::check('BLOCK_CREATE', 'any', $member);
340 | }
341 |
342 | public function canPublish($member = null)
343 | {
344 | return Permission::check('ADMIN', 'any', $member) || Permission::check('BLOCK_PUBLISH', 'any', $member);
345 | }
346 |
347 | public function providePermissions()
348 | {
349 | return [
350 | 'BLOCK_EDIT' => [
351 | 'name' => _t('Block.EditBlock', 'Edit a Block'),
352 | 'category' => _t('Block.PermissionCategory', 'Blocks'),
353 | ],
354 | 'BLOCK_DELETE' => [
355 | 'name' => _t('Block.DeleteBlock', 'Delete a Block'),
356 | 'category' => _t('Block.PermissionCategory', 'Blocks'),
357 | ],
358 | 'BLOCK_CREATE' => [
359 | 'name' => _t('Block.CreateBlock', 'Create a Block'),
360 | 'category' => _t('Block.PermissionCategory', 'Blocks'),
361 | ],
362 | 'BLOCK_PUBLISH' => [
363 | 'name' => _t('Block.PublishBlock', 'Publish a Block'),
364 | 'category' => _t('Block.PermissionCategory', 'Blocks'),
365 | ],
366 | ];
367 | }
368 |
369 | public function onAfterWrite()
370 | {
371 | parent::onAfterWrite();
372 | if ($this->hasExtension('FilesystemPublisher')) {
373 | $this->republish($this);
374 | }
375 | }
376 |
377 | /**
378 | * Get a list of URL's to republish when this block changes
379 | * if using StaticPublisher module.
380 | */
381 | public function pagesAffectedByChanges()
382 | {
383 | $pages = $this->Pages();
384 | $urls = [];
385 | foreach ($pages as $page) {
386 | $urls[] = $page->Link();
387 | }
388 |
389 | return $urls;
390 | }
391 |
392 | /*
393 | * Get a list of Page and Blockset titles this block is used on
394 | */
395 | public function UsageListAsString()
396 | {
397 | $pages = implode(', ', $this->Pages()->column('URLSegment'));
398 | $sets = implode(', ', $this->BlockSets()->column('Title'));
399 | $_t_pages = _t('Block.PagesAsString', 'Pages: {pages}', '', ['pages' => $pages]);
400 | $_t_sets = _t('Block.BlockSetsAsString', 'Block Sets: {sets}', '', ['sets' => $sets]);
401 | if ($pages && $sets) {
402 | return "$_t_pages
$_t_sets";
403 | }
404 | if ($pages) {
405 | return $_t_pages;
406 | }
407 | if ($sets) {
408 | return $_t_sets;
409 | }
410 | }
411 |
412 | /**
413 | * Check if this block has been published.
414 | *
415 | * @return bool True if this page has been published.
416 | */
417 | public function isPublished()
418 | {
419 | if (!$this->exists()) {
420 | return false;
421 | }
422 |
423 | return (DB::query("SELECT \"ID\" FROM \"Block_Live\" WHERE \"ID\" = $this->ID")->value())
424 | ? 1
425 | : 0;
426 | }
427 |
428 | /**
429 | * Check if this block has been published.
430 | *
431 | * @return bool True if this page has been published.
432 | */
433 | public function isPublishedNice()
434 | {
435 | $field = DBBoolean::create('isPublished');
436 | $field->setValue($this->isPublished());
437 |
438 | return $field->Nice();
439 | }
440 |
441 | /**
442 | * @return DBHTMLText
443 | */
444 | public function isPublishedIcon()
445 | {
446 | $obj = DBHTMLText::create();
447 | if ($this->isPublished()) {
448 | $obj->setValue('
');
449 | } else {
450 | $obj->setValue('
');
451 | }
452 | return $obj;
453 | }
454 |
455 | /**
456 | * CSS Classes to apply to block element in template.
457 | *
458 | * @return string $classes
459 | */
460 | public function CSSClasses($stopAtClass = 'DataObject')
461 | {
462 | $classes = strtolower(parent::CSSClasses($stopAtClass));
463 |
464 | if (!empty($classes) && ($prefix = $this->blockManager->getPrefixDefaultCSSClasses())) {
465 | $classes = $prefix.str_replace(' ', " {$prefix}", $classes);
466 | }
467 |
468 | if ($this->blockManager->getUseExtraCSSClasses()) {
469 | $classes = $this->ExtraCSSClasses ? $classes." $this->ExtraCSSClasses" : $classes;
470 | }
471 |
472 | return $classes;
473 | }
474 |
475 | /**
476 | * Access current page scope from Block templates with $CurrentPage
477 | *
478 | * @return Controller
479 | */
480 | public function getCurrentPage()
481 | {
482 | return Controller::curr();
483 | }
484 |
485 | /**
486 | * @throws Exception
487 | *
488 | * @return BlockController
489 | */
490 | public function getController()
491 | {
492 | if ($this->controller) {
493 | return $this->controller;
494 | }
495 | foreach (array_reverse(ClassInfo::ancestry($this->class)) as $blockClass) {
496 | $controllerClass = "{$blockClass}Controller";
497 | if (class_exists($controllerClass)) {
498 | break;
499 | }
500 | }
501 | if (!class_exists($controllerClass)) {
502 | throw new ValidationException("Could not find controller class for $this->classname");
503 | }
504 |
505 | $this->controller = Injector::inst()->create($controllerClass, $this);
506 | $this->controller->init();
507 |
508 | return $this->controller;
509 | }
510 | }
511 |
--------------------------------------------------------------------------------