restruct / silverstripe-shortcodable
Provides a simple button to insert Shortcodes into the HTMLEditorField + an API for developers to define shortcodes
Package info
github.com/restruct/silverstripe-shortcodable
Type:silverstripe-vendormodule
pkg:composer/restruct/silverstripe-shortcodable
Fund package maintenance!
Requires
- php: ^8.1
- restruct/silverstripe-simpler: ~0.2 || ^1
- silverstripe/admin: ^2 || ^3
- silverstripe/framework: ^5 || ^6
- silverstripe/vendor-plugin: ^2 || ^3
Requires (Dev)
- silverstripe/recipe-testing: ^3 || ^4
Suggests
- silverstripe/htmleditor-tinymce: Required on Silverstripe 6: TinyMCE is a separate module there, and without it the shortcode button is not added to any editor
Provides
None
Conflicts
None
Replaces
README
Maintained by Restruct. If this module saves you time, you can support ongoing maintenance.
Adds a
button to HTMLEditorField for CMS users to insert Shortcodes in page content.
Shortcodes can optionally be represented in TinyMCE with a placeholder image.
This module is a partial-to-largely rewrite of sheadawson/silverstripe-shortcodable.
It depends on Silverstripe Simpler for some non-react UI functionalities (mainly the modal dialog).
Requirements
- Silverstripe 5 or 6 (
silverstripe/framework ^5 || ^6) - PHP 8.1 or newer (Silverstripe 6 itself needs 8.3)
- restruct/silverstripe-simpler, installed automatically:
its
0.xline (~0.2) on Silverstripe 5; on Silverstripe 6 it requires restruct/silverstripe-simpler 1.0.0 or later - Silverstripe 6 only:
silverstripe/htmleditor-tinymce. TinyMCE is a separate module on Silverstripe 6 andsilverstripe/recipe-cmsdoes not include it. Without it the site still boots, but there is no TinyMCE editor to add the shortcode button to.
Installation
composer require restruct/silverstripe-shortcodable
# Silverstripe 6, if your project does not have it yet:
composer require silverstripe/htmleditor-tinymce
Version Compatibility
| Branch | Module Version | Silverstripe | PHP |
|---|---|---|---|
main |
5.x (from 5.1.0) |
^5 || ^6 |
^8.1 |
v4 |
4.x |
^4 || ^5 |
^7.4 || ^8.0 |
main is the maintained line and supports every Silverstripe version this module still targets.
Silverstripe 4 reached end of life in April 2025 and is no longer supported or tested here; projects
still on it can stay on the 4.x tags, which remain available. The 5.0.x tags declared Silverstripe 6
only, and could not be installed from Packagist (see CHANGELOG.md); use ^5.1.
Note: composer.json is the source of truth for exact version constraints.
Configuration
Register DataObjects or classes as shortcodable via Yaml config:
--- name: my_shortcodables Only: classexists: Shortcodable\Shortcodable --- Shortcodable\Shortcodable: shortcodable_classes: - My\Namespaced\Shortcodes\CurrentYearShortcode - My\Namespaced\Shortcodes\SomeOtherShortcode - ... ---
Configuration options
| Option | Default | What it does |
|---|---|---|
Shortcodable\Shortcodable.shortcodable_classes |
[] |
Classes to register with the shortcode parser and offer in the dialog. |
Shortcodable\Shortcodable.htmleditor_names |
[cms] |
HTMLEditor configs that get the shortcode button. Only TinyMCE configs are changed; others are skipped. |
Shortcodable\Controllers\ShortcodableAdminController.default_placeholder |
see class | Size, font and colours of the default SVG placeholder (width, height, full_width, full_height, font, fontsize, fg, bg). |
Per shortcodable class (as private static or YAML on that class):
| Option | Default | What it does |
|---|---|---|
shortcode |
short class name | The shortcode tag, eg currentyear for [currentyear]. |
shortcode_callback |
parse_shortcode |
Name of the parser method. |
shortcode_close_parent |
false |
Block-level output: close the wrapping element before the shortcode and reopen it after (see below). Also gives the default placeholder the full size. |
placeholder_settings |
none | width/height overriding the default placeholder size for this shortcode. |
Required methods on shortcodable objects/classes
Implement these methods on your shortcodable classes (may also be added via an Extension):
(Required) shortcode parser callback:
public static function parse_shortcode(...)
OR:
public function MyCustomParser(...) combined with:
private static $shortcode_callback = 'MyCustomParser'
Parser method arguments: (see shortcode documentation)
($attrs, $content=null, $parser=null, $shortcode, $info)
NOTE: the parser method gets called on a singleton object instance.
(So there's no $this->ID or $this->owner->ID etc.)
Optional:
private static $shortcode(optional, else short ClassName will be used as [ClassName])getShortcodeLabel()(optional nice description for in dropdown,singular_name()or elseClassName.Shortnamewill be used as fallback)getShortcodeFields()(optional, attribute-formfields for popup)getShortcodableRecords()(optional, if applied to DataObject)getShortcodePlaceholder($attributes)(optional)
Wrapping (optional):
In this module, $shortcodable_is_block and $disable_wrapper have been replaced with $shortcode_close_parent:
private static $shortcode_close_parent(optional, set to true if your shortcode output is a block-type html element)
If you set $shortcode_close_parent to true, the parent node will be closed before your shortcode output and reopened after.
Eg <p>Some [shortcode] content</p> would become <p>Some </p>[shortcode]<p> content</p>;
Shortcode placeholder images
To display a nice image instead of the raw shortcode in the CMS editor, implement a getShortcodePlaceHolder method on your shortcodable object:
/** * Redirect to an image OR return image data directly to be displayed as shortcode placeholder in the editor * (getShortcodePlaceHolder gets loaded as/from the 'src' attribute of an <img> tag) * * @param array $attributes attribute key-value pairs of the shortcode * @return \SilverStripe\Control\HTTPResponse **/ public function getShortcodePlaceHolder($attributes) { // Flavour one: redirect to image URL (for this example we're also including the attributes array in the URL) Controller::curr()->redirect('https://www.silverstripe.org/apple-touch-icon-76x76.png?attrs='.json_encode($attributes)); // Flavour two: output image/svg data directly (any bitmap but may also be SVG) // Hint: process attributes eg to show a set of image thumbnails wrapped in an SVG as gallery-placeholder $response = Controller::curr()->getResponse(); $response->addHeader('Content-Type','image/svg+xml'); $response->addHeader('Vary','Accept-Encoding'); $response->setBody('<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-code-square" viewBox="0 0 16 16"> <path d="M14 1a1 1 0 0 1 1 1v12a1 1 0 0 1-1 1H2a1 1 0 0 1-1-1V2a1 1 0 0 1 1-1h12zM2 0a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2H2z"/> <path d="M6.854 4.646a.5.5 0 0 1 0 .708L4.207 8l2.647 2.646a.5.5 0 0 1-.708.708l-3-3a.5.5 0 0 1 0-.708l3-3a.5.5 0 0 1 .708 0zm2.292 0a.5.5 0 0 0 0 .708L11.793 8l-2.647 2.646a.5.5 0 0 0 .708.708l3-3a.5.5 0 0 0 0-.708l-3-3a.5.5 0 0 0-.708 0z"/> </svg>'); $response->output(); }
Default placeholder image
Without getShortcodePlaceHolder(), the editor shows an SVG generated by admin/shortcodable/placehold.img
(CMS access required). It takes w and h (a positive number of pixels, or 100%), bg and fg
(3 or 6 hex digits, no #), ff (font family), txtsize and txt. Invalid values fall back to the
default_placeholder config.
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 "Shortcodable\\Tests\\": "vendor/restruct/silverstripe-shortcodable/tests/"
to the host's autoload-dev, then:
# Silverstripe 5 (PHPUnit 9) - the path must come before flush=1 vendor/bin/phpunit vendor/restruct/silverstripe-shortcodable/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-shortcodable/tests
On Silverstripe 6 the host also needs silverstripe/htmleditor-tinymce, or the TinyMCE button test is
skipped. CI runs the suite against Silverstripe 5 and 6 on every push; see .github/workflows/ci.yml.
Status
- TinyMCE button/plugin
- Shortcode form in popup dialog
- Custom/configurable shortcode independent of class name
- Custom/configurable shortcode parser callback methods
- Inserting new & editing existing shortcode (+undo)
- Re-implement DataObjects as shortcodable (incl. getShortcodableRecords etc)
- Re-implement placeholders
- Check/re-implement(?) BOM fix and in ShortcodeController
- Check/re-implement(?) P/DIV wrapper fix
- Check/restore functionality for Uploadfields to load existing value
- Check/implement(?) wrapping shortcodes