diff --git a/docs/en/topics/index.md b/docs/en/topics/index.md index b70885bff..1b31c3521 100644 --- a/docs/en/topics/index.md +++ b/docs/en/topics/index.md @@ -24,6 +24,7 @@ It is where most documentation should live, and is the natural "second step" aft * [Page Types](page-types): What is a "page type" and how do you create one? * [Search](search): Searching for properties in the database as well as other documents * [Security](security): How to develop secure SilverStripe applications with good code examples + * [Shortcodes](shortcodes): Use simple placeholders for powerful content replacements like multimedia embeds * [Templates](templates): SilverStripe template syntax: Variables, loops, includes and much more * [Testing](testing): Functional and Unit Testing with PHPUnit and SilverStripe's testing framework * [Developing Themes](theme-development): Package templates, images and CSS to a reusable theme diff --git a/docs/en/topics/shortcodes.md b/docs/en/topics/shortcodes.md new file mode 100644 index 000000000..d11b07091 --- /dev/null +++ b/docs/en/topics/shortcodes.md @@ -0,0 +1,143 @@ +# Shortcodes: Flexible Content Embedding + +## Overview + +The `[api:ShortcodeParser]` API is simple parser that allows you to map specifically +formatted content to a callback to transform them into something else. +You might know this concept from forum software which don't allow you to insert +direct HTML, instead resorting to a custom syntax. + +In the CMS, authors often want to insert content elements which go beyond +standard formatting, at an arbitrary position in their WYSIWYG editor. +Shortcodes are a semi-technical solution for this. A good example would +be embedding a 3D file viewer or a Google Map at a certain location. + +Here's some syntax variations: + + [myshortcode] + [myshortcode /] + [myshortcode,myparameter="value"] + [myshortcode,myparameter="value"]Enclosed Content[/myshortcode] + +## Usage + +In its most basic form, you can invoke the `[api:ShortcodeParser]` directly: + + :::php + ShortcodeParser::get_active()->parse($myvalue); + +In addition, shortcodes are automatically parsed on any database field which is declared +as `[api:HTMLValue]` or `[api:HTMLText]`, when rendered into a template. +This means you can use shortcodes on common fields like `SiteTree.Content`, +and any other `[api:DataObject::$db]` definitions of these types. + +In order to allow shortcodes in your own template placeholders, +ensure they're casted correctly: + + :::php + class MyObject extends DataObject { + static $db = array('Content' => 'HTMLText'); + static $casting = array('ContentHighlighted' => 'HTMLText'); + public function ContentHighlighted($term) { + return str_replace($term, "$term", $this->Content); + } + } + +There is currently no way to allow shortcodes directly in template markup +(as opposed to return values of template placeholders). + +## Defining Custom Shortcodes + +All you need to do to define a shortcode is to register a callback with the parser that will be called whenever a +shortcode is encountered. This callback will return a string to replace the shortcode with. +If the shortcode is used for template placeholders of type `HTMLText` or `HTMLVarchar`, +the returned value should be valid HTML + +To register a shortcode you call: + + ShortcodeParser::get('default')->register('myshortcode', ); + +These parameters are passed to the callback: + + - Any parameters attached to the shortcode as an associative array (keys are lower-case). + - Any content enclosed within the shortcode (if it is an enclosing shortcode). Note that any content within this + will not have been parsed, and can optionally be fed back into the parser. + - The ShortcodeParser instance used to parse the content. + - The shortcode tag name that was matched within the parsed content. + +## Example: Google Maps Iframe by Address + +To demonstrate how easy it is to build custom shortcodes, we'll build one to display +a Google Map based on a provided address. Format: + + [googlemap,width=500,height=300]97-99 Courtenay Place, Wellington, New Zealand[/googlemap] + +So we've got the address as "content" of our new `googlemap` shortcode tags, +plus some `width` and `height` arguments. We'll add defaults to those in our shortcode parser so they're optional. + + :::php + ShortcodeParser::get('default')->register('googlemap', function($arguments, $address, $parser, $shortcode) { + $iframeUrl = sprintf( + 'http://maps.google.com/maps?q=%s&hnear=%s&ie=UTF8&hq=&t=m&z=14&output=embed', + urlencode($address), + urlencode($address) + ); + $width = (isset($args['width']) && $args['width']) ? $args['width'] : 400; + $height = (isset($args['height']) && $args['height']) ? $args['height'] : 300; + return sprintf( + '', + $width, + $height, + $iframeUrl + ); + }); + +The hard bits are taken care of (parsing out the shortcodes), everything we need to do is a bit of string replacement. +CMS users still need to remember the specific syntax, but these shortcodes can form the basis +for more advanced editing interfaces (with visual placeholders). See the built-in `[embed]` shortcode as an example +for coupling shortcodes with a form to create and edit placeholders. + +## Built-in Shortcodes + +SilverStripe comes with several shortcode parsers already. + +### Links + +Internal page links keep references to their database IDs rather than +the URL, in order to make these links resilient against moving the target page to a different +location in the page tree. This is done through the `[sitetree_link]` shortcode, which +takes an `id` parameter. Example: `` + +Links to internal `File` database records work exactly the same, but with the `[file_link]` shortcode. + +### Media (Photo, Video and Rich Content) + +Many media formats can be embedded into websites through the `` +tag, but some require plugins like Flash or special markup and attributes. +OEmbed is a standard to discover these formats based on a simple URL, +for example a Youtube link pasted into the "Insert Media" form of the CMS. + +Since TinyMCE can't represent all these varations, we're showing a placeholder +instead, and storing the URL with a custom `[embed]` shortcode. + +Example: `.[embed width=480 height=270 class=left thumbnail=http://i1.ytimg.com/vi/lmWeD-vZAMY/hqdefault.jpg?r=8767]http://www.youtube.com/watch?v=lmWeD-vZAMY[/embed]` + + +## Limitations + +Since the shortcode parser is based on a simple regular expression it cannot properly handle nested shortcodes. For +example the below code will not work as expected: + + [shortcode] + [shortcode][/shortcode] + [/shortcode] + +The parser will recognise this as: + + [shortcode] + [shortcode] + [/shortcode] + +## Related + + * [Wordpress implementation](http://codex.wordpress.org/Shortcode_API) \ No newline at end of file diff --git a/parsers/ShortcodeParser.php b/parsers/ShortcodeParser.php index 57624bc22..0a7799d8f 100644 --- a/parsers/ShortcodeParser.php +++ b/parsers/ShortcodeParser.php @@ -1,61 +1,11 @@ - * [shortcode] - * [shortcode /] - * [shortcode parameter="value"] - * [shortcode parameter="value"]Enclosed Content[/shortcode] - * + * It is a simple regex based parser that allows you to replace simple bbcode-like tags + * within a HTMLText or HTMLVarchar field when rendered into a template. The API is inspired by and very similar to the + * [Wordpress implementation](http://codex.wordpress.org/Shortcode_API) of shortcodes. * - * Defining Custom Shortcodes - * - * All you need to do to define a shortcode is to register a callback with the parser that will be called whenever a - * shortcode is encountered. This callback will return a string to replace the shortcode with. - * - * To register a shortcode you call: - * - * - * ShortcodeParser::get('default')->register('shortcode_tag_name', 'callback'); - * - * - * These parameters are passed to the callback: - * - Any parameters attached to the shortcode as an associative array (keys are lower-case). - * - Any content enclosed within the shortcode (if it is an enclosing shortcode). Note that any content within this - * will not have been parsed, and can optionally be fed back into the parser. - * - The ShortcodeParser instance used to parse the content. - * - The shortcode tag name that was matched within the parsed content. - * - * Inbuilt Shortcodes - * - * From 2.4 onwards links inserted via the CMS into a content field are in the form '' - * '', and from 3.0 the comma is used as a separator instead ''''. At runtime this is - * replaced by a plain link to the page with the ID in question. - * - * Limitations - * - * Since the shortcode parser is based on a simple regular expression it cannot properly handle nested shortcodes. For - * example the below code will not work as expected: - * - * - * [shortcode] - * [shortcode][/shortcode] - * [/shortcode] - * - * - * The parser will recognise this as: - * - * - * [shortcode] - * [shortcode] - * [/shortcode] - * - * + * @see http://doc.silverstripe.org/framework/en/topics/shortcodes * @package framework * @subpackage misc */