restruct / silverstripe-blockbase
Various tweaks & enhancements on top of Elemental
Package info
Type:silverstripe-vendormodule
pkg:composer/restruct/silverstripe-blockbase
Fund package maintenance!
Requires
- php: ^8.1
- dnadesign/silverstripe-elemental: ^5.4 || ^6
- restruct/silverstripe-admintweaks: ^3 || ^4
- silverstripe/framework: ^5 || ^6
- silverstripe/vendor-plugin: ^2 || ^3
Requires (Dev)
- silverstripe/recipe-testing: ^3 || ^4
Suggests
- dnadesign/silverstripe-elemental-virtual: Virtual (clone) blocks; blockbase adds a block picker and inline-editing support for them
- silverstripe/subsites: Allow or disallow specific block types per subsite (subsites_allowed_elements / subsites_disallowed_elements)
Provides
None
Conflicts
None
Replaces
None
README
Maintained by Restruct. If this module saves you time, you can support ongoing maintenance.
This module serves as a base on top of Elemental, for blocks based projects by Restruct: a content block with configurable fields, editor tweaks for every block type, a CMS admin listing all blocks, and optional support for virtual (clone) blocks and subsites.
Requirements
- Silverstripe 5 or 6, with
dnadesign/silverstripe-elemental5.4+ or 6 - PHP 8.1 or newer (Silverstripe 6 itself needs 8.3)
restruct/silverstripe-admintweaks3 or 4 (installed automatically)
Optional, and supported when installed:
dnadesign/silverstripe-elemental-virtual: virtual (clone) blockssilverstripe/subsites: allow or disallow block types per subsite
Installation
composer require restruct/silverstripe-blockbase
Then run dev/build (Silverstripe 5) or sake db:build (Silverstripe 6) with a flush.
Version compatibility
| Branch | Module version | Silverstripe | Elemental | PHP |
|---|---|---|---|---|
main |
2.x |
^5 || ^6 |
^5.4 || ^6 |
^8.1 |
v1 |
1.0.x |
^4 |
^4.5 |
per Silverstripe 4 |
Silverstripe 4 is end of life and is no longer supported or tested here. Projects still on it should
stay on the 1.0.x tags (branch v1), which remain available.
main is the only maintained line: it supports every Silverstripe version this module still targets.
v1 holds the Silverstripe 4 line as it was released and gets no further changes. Another version
branch will be created only when a change cannot be made compatible across the supported range.
composer.json is the source of truth for exact constraints; this table is a quick reference.
What installing it does
The module's _config/config.yml applies the following to every project that installs it:
Pagegets Elemental'sElementalPageExtensionand this module'sContentBlocksToggleExtension(below).UserDefinedForm,RedirectorPage,VirtualPage,SubsitesVirtualPage, the newsgrid holder and the sitemap page are excluded viaElementalPageExtension.ignored_classes.- The page's own
Contentfield is kept alongside the blocks (ElementalAreasExtension.keep_content_fields: true). - Elemental's default front-end block styles are not included (
ElementController.include_default_styles: false). - Every block (
BaseElement) is inline editable, does not show its title in the template, renders in this module's holder template (controller_template: BlockHolder) and getsElementBlockBaseExtension. - A CMS stylesheet (
client/dist/css/admin-block-tweaks.css) is added to the admin. - In
devmode only, the routeadmin/blocktypeicons(below). Outside dev the page answers 404 and it never appears in the CMS menu.
Functionality
BlockContent: text/content block
Restruct\Silverstripe\BlockBase\Blocks\BlockContent has a Heading, IntroLine, Content (HTML),
Image and BackgroundImage. Each can be switched off per project or per subclass:
| Config | Default | Effect |
|---|---|---|
has_heading |
true |
false removes the Heading field from the CMS |
has_introline |
true |
false removes IntroLine |
has_content |
true |
false removes Content |
has_image |
true |
false removes Image |
has_bg_image |
true |
false removes BackgroundImage |
image_upload_dir |
null |
folder name for both image upload fields; unset uses the upload default |
FieldEnabled('Heading') (and IntroLine, Content, Image, BackgroundImage) returns the matching
setting, for use in templates.
Restruct\Silverstripe\BlockBase\Blocks\BlockContent: has_bg_image: false image_upload_dir: 'block-images'
ExtraData. Any field named ExtraData_<Name> added to a BlockContent (sub)class's CMS fields is saved
as <Name> in the block's ExtraDataJSON column, so project blocks can store extra settings without a
schema change. getExtraData() returns them as an array (or null when nothing was stored), and
loadExtraData() (called from getCMSFields()) puts them back on the record so the form shows them. This
works for plain fields (text, number, dropdown); more complex fields probably need additional work.
BlockBase is the (hidden) common parent of the module's blocks; project blocks can subclass it. Its
getRenderTemplates() adds plain theme template names after Elemental's namespaced ones, so a theme can
use Blocks/BlockContent.ss, BlockContent_<style>.ss and so on.
ElementBlockBaseExtension: applied to every block
BaseElement,ElementContentandBlockBasecannot be created (they are hidden from the add-block menu).- A new block without a title is named
"<description> Block"; a new block is not globally available (a workaround for elemental-virtual#42). getBlockDescription(): the block'sclass_description, falling back to its type name.- The editor summary reads
<description> block, then a dash and the block's summary in curly quotes with a trailing ellipsis (the exact characters are inElementBlockBaseExtension::updateBlockSchema()). - Style and styling fields sit on the Main tab; the
Styledropdown has no empty option. BlockHolderClasses()andgetPath()for templates and pickers.
Layout variants (styles)
A list of layout variants can be set via YAML or private static $styles, the first one being the default.
Restruct\Silverstripe\BlockBase\Blocks\BlockContent: styles: single-col-wide: 'Single wide column layout' single-col-narrow: 'Single narrow column layout' double-col-equal: 'Two equal-width columns layout'
The selected $StyleVariant is available in templates and is included as class on the wrapper element
(DNADesign/Elemental/Layout/BlockHolder.ss). It can also be used to switch between different templates,
eg BlockType.ss / BlockType_single-col-wide.ss / etc.
Styling options (style_options / ExtraClass)
Further styling variations can be offered as a dropdown for the block's ExtraClass. Without
style_options the free-text ExtraClass field is removed.
Restruct\Silverstripe\BlockBase\Blocks\BlockContent: style_options: light: 'Light' dark: 'Dark'
ContentBlocksToggleExtension: content, blocks, or both
Adds a ContentBlocksToggle dropdown before a page's Content field: content followed by blocks (the
default), content only, or blocks only. After saving, the unused editor is hidden.
Block admin
Restruct\Silverstripe\BlockBase\Admin\BlockAdmin ("Content Blocks", admin/blocks-admin) lists every
block with its type, icon, title and the page it is used on. Its edit form shows the block's settings on
the main tab and a "Linked to page" picker, which moves the block to another page's (first) elemental area.
Block type icons preview (dev only)
admin/blocktypeicons (CMS access required, dev mode only) lists every block type with its icon and
description, to check icon crops. It is not a CMS menu item, and outside dev it answers 404 on every
route, including the admin/admin/blocktypeicons route the admin gives every LeftAndMain.
Show block designs/thumbnails instead of icons in the admin UI
- copy & adapt below section to specific project code css to show designed block previews instead of icons
- set
private static $iconto'block-design block-section {block-name-offset}' - add stacked blocks img to app/client (
.block-name-offsetsets offset if multiple stacked in one image)
i.block-section, button.block-section:before { background-image: url(~app/client/imgs/block-group-designs_stacked.png); background-position: 0 0; } i.block-section, button.block-section { &.block-name-offset {&, &:before { background-position: 0 -128px; }} &.block-othername-offset {&, &:before { background-position: 0 -28px; }} }
Virtual (clone) blocks
With dnadesign/silverstripe-elemental-virtual installed, ElementVirtualExtension replaces its block
picker with a dropdown of globally available blocks (labelled with their page and type), explains what to
do when none are available, and removes the style fields that do not apply to a clone. Virtual blocks
use the module's holder template and are inline editable.
Allow/disallow specific block types on subsites
With silverstripe/subsites installed, SubsitesPageExtension is applied to Page. On a subsite (not on
the main site) it limits the available block types, and removes the block editor when none are allowed and
the page has no blocks left.
Page: # # Example: disallow any blocks on subsites # subsites_allowed_elements: false # # Example: allow specific elements on subsites # subsites_allowed_elements: # - Restruct\Silverstripe\BlockBase\Blocks\BlockContent # # Example: DISallow specific elements on subsites # subsites_disallowed_elements: # - Restruct\Silverstripe\BlockBase\Blocks\BlockContent
On Elemental 6 the available types are cached per page class for the rest of the request, so they are computed for whichever subsite is active first in that request.
Migrating page content to blocks
ElementContentMigrationExtension is not applied by default. Apply it to Elemental's
MigrateContentToElement task to migrate only plain Page records, skip pages that already have blocks,
and name each migrated block after its page. See Elemental's
content migration docs.
Notes
Template relations
The DNADesign/Elemental/Models/ElementalArea.ss template loops over each of the element controller
instances. Each controller instance renders $ElementHolder, which represents the element contained
within a holder div. This module's holder is DNADesign/Elemental/Layout/BlockHolder.ss.
Unset nested blocks .container padding (for first level nested)
In case your project already wraps blocks inside a .container, the block's .container will have double padding. Below CSS unsets this (based on a project which wraps all content in a subnav and a content .col, adapt to your specific project):
.blocks-container { // unset padding/margin of (second level ) .container if contained within .subnav-slot-* (before/after) &.subnav-slot-before, &.subnav-slot-after { & > .block-item-wrapper > .container > .row > [class*="col"] > .block-item-wrapper > .container { padding: 0; // .row & .col* should remain unchanged as there may be multiple columns with a block } } }
Advanced config
Advanced config pointers: https://github.com/silverstripe/silverstripe-elemental/blob/6/docs/en/02_advanced_setup.md
Running the tests
The module cannot be tested on its own: it needs a host Silverstripe project. Require it there through a
Composer path repository with symlink: true - /tests is export-ignore, so a dist or mirrored
install contains no tests - add its test namespace to the host's autoload-dev, and run in dev mode.
Install dnadesign/silverstripe-elemental-virtual and silverstripe/subsites too, or their tests are
skipped. Then:
# Silverstripe 5 (PHPUnit 9) - the path must come before flush=1 vendor/bin/phpunit vendor/restruct/silverstripe-blockbase/tests flush=1 # Silverstripe 6 (PHPUnit 11) - a flush=1 argument is ignored, use the env var SS_PHPUNIT_FLUSH=1 vendor/bin/phpunit vendor/restruct/silverstripe-blockbase/tests
CI runs the same suite against Silverstripe 5 (PHP 8.1, 8.3) and 6 (PHP 8.3, 8.4) on every push; see
.github/workflows/ci.yml.
Licence
MIT, see LICENSE.