Custom BBCode tags¶
While django-precise-bbcode comes with some built-in BBCode tags, there will be times when you need to add your own.
Defining BBCode tags through the admin site¶
The easy way.
Django-precise-bbcode provides a BBCodeTag
model which can be seen as a helper to allow end users to easily define BBCode tags. Just go to the admin page and you will see a new ‘BBCodes’ section. In this section you can create and edit custom BBCode tags. These are then used by the built-in BBCode parser to render any BBCode content.
Adding a custom BBCode tag consists in defining at least two values in the associated admin form, both its usage (tag definition) and its HTML replacement code.
Tag definition¶
The tag definition is the expression of the bbcode usage. It’s where you enter your bbcode. All you need to do is to add a string containing your BBCode and the associated placeholders (special uppercase words surrounded by { and } – they are similar to the “replacement fields” that you define in Python format strings):
[foo]{TEXT}[/foo]
In this example, we have a bbcode named foo
which can contain some text ({TEXT}
placeholder).
So a bbcode definition takes the form of what users will enter when using this bbcode, except that all parts of the bbcode where data is required are expressed as placeholders. The placeholders that you can use in such a tag definition are typed. This means that some semantic verifications are done before rendering in order to ensure that data containing non-allowed characters are not converted to HTML.
Django-precise-bbcode provides some placeholders by default, such as {TEXT}
, {SIMPLETEXT}
, {COLOR}
, {NUMBER}
, {URL}
or {EMAIL}
. For a full list of the available placeholders and the techniques used to define custom placeholders, please refer to Custom BBCode placeholders.
Note that you can specify an option to your bbcode in its definition:
[foo={COLOR}]{TEXT}[/foo]
In this case, the data associated with the {COLOR}
placeholder is not required at runtime. If you wish to use two placeholders of the same type in your bbcode definition, you have to append a number to their respective names (eg. {TEXT1}
):
[foo={TEXT1}]{TEXT2}[/foo]
HTML replacement code¶
The HTML replacement code is where you enter the HTML for the bbcode you defined previously. All the placeholders you used in your bbcode definition must be present in the HTML replacement code. For example, the HTML replacement code associated with the last [foo]
bbcode example could be:
<div style="background:{COLOR};">{TEXT}</div>
BBCode options¶
Some specific options can be used when defining a custom bbcode to alter its default behavior. For example, you could want to forbid the rendering of any bbcode tags included inside your new bbcode. All these options are boolean fields and are indicated in the following table:
Option |
Definition |
Default |
---|---|---|
newline_closes |
Force the closing of a tag after a newline |
False |
same_tag_closes |
Force the closing of a tag after the beginning of a similar tag |
False |
end_tag_closes |
Force the closing of a tag after the end of another tag |
False |
standalone |
Set this option if a tag does not have a closing tag (eg. [hr]) |
False |
transform_newlines |
Convert any line break to the equivalent markup |
True |
render_embedded |
Force the tags embedded in a tag to be rendered |
True |
escape_html |
Escape HTML characters (<, >, and &) inside a tag |
True |
replace_links |
Replace URLs with link markups inside a tag |
True |
strip |
Strip leading and trailing whitespace inside a tag |
False |
swallow_trailing_newline |
Swallow the first trailing newline inside a tag |
False |
Defining BBCode tags plugins¶
The fun part.
While the previous bbcode tag system allows you to easily define various bbcodes, you may want to do more complex treatments with your bbcodes (eg. handle other types of data). You may also want to write some reusable or generic bbcode tags.
To do so, you will have to write a subclass of precise_bbcode.bbcode.tag.BBCodeTag
for any tag you want to create. These class-based bbcode tags must be defined inside a bbcode_tags
Python module in your Django application (just add a file called bbcode_tags.py
to an existing Django application). And last, but not least, your class-based bbcode tags must be registered to the precise_bbcode.tag_pool.tag_pool
object by using its register_tag
method to be available to the BBCode parser.
Each of these tags must provide a name
attribute and can operate in two different ways:
The
BBCodeTag
subclass provides adefinition_string
attribute and aformat_string
attribute. In this case, the tag will operate as previously described. Thedefinition_string
corresponds to the tag definition and defines how the tag should be used. Theformat_string
is the HTML replacement code that will be used to generate the final HTML outputThe
BBCodeTag
subclass implements arender
method that will be used to transform the bbcode tag and its context (value, option if provided) to the corresponding HTML output
Defining bbcodes based on a definition string and a format string¶
In this case, you have to provide a definition_string
attribute and a format_string
attribute to your BBCodeTag
subclass, in addition to the name
attribute.
Let’s write a simple example. Consider we are trying to write a bar
bbcode which will strike any text placed inside its tags. So we could write:
# bbcode_tags.py
from precise_bbcode.bbcode.tag import BBCodeTag
from precise_bbcode.tag_pool import tag_pool
class BarBBCodeTag(BBCodeTag):
name = 'bar'
definition_string = '[bar]{TEXT}[/bar]'
format_string = '<strike>{TEXT}</strike>'
tag_pool.register_tag(BarBBCodeTag)
Note that you can use any BBCode options specified previously to alter the default behavior of your class-based tags (see BBCode options). To do so, give your bbcode tag options by using an inner class Options
, like so:
# bbcode_tags.py
from precise_bbcode.bbcode.tag import BBCodeTag
from precise_bbcode.tag_pool import tag_pool
class BarBBCodeTag(BBCodeTag):
name = 'bar'
definition_string = '[bar]{TEXT}[/bar]'
format_string = '<strike>{TEXT}</strike>'
class Options:
render_embedded = False
strip = False
tag_pool.register_tag(BarBBCodeTag)
Defining bbcodes based on a render
method¶
In this case, each of your BBCodeTag
subclasses must provide a name
attribute and must implement a render
method. The render
method is used to transform your bbcode tag and its context (value, option if provided) to the corresponding HTML output. The render
method takes three arguments:
value: the context between the start end the end tags, or None for standalone tags. Whether this has been rendered depends on the
render_embedded
tag optionoption: The value of an option passed to the tag ; defaults to None
parent: The options (instance of
precise_bbcode.bbcode.tag.BBCodeTagOptions
) associated with the parent bbcode if the tag is being rendered inside another tag, otherwise None
Keep in mind that your render
method may have to validate the data associated with your tag before rendering it. Any validation process should be triggered from this render
method.
Let’s write another example. Consider we are trying to write a rounded
bbcode which will surround inside a rounded frame any text placed inside the tags. If provided, the option passed to the tag is assumed to be a colour in order to modify the resulting HTML code. So we could write:
# bbcode_tags.py
import re
from precise_bbcode.bbcode.tag import BBCodeTag
from precise_bbcode.tag_pool import tag_pool
color_re = re.compile(r'^([a-z]+|#[0-9abcdefABCDEF]{3,6})$')
class RoundedBBCodeTag(BBCodeTag):
name = 'rounded'
class Options:
strip = False
def render(self, value, option=None, parent=None):
if option and re.search(color_re, option) is not None:
return '<div class="rounded" style="border-color:{};">{}</div>'.format(option, value)
return '<div class="rounded">{}</div>'.format(value)
tag_pool.register_tag(RoundedBBCodeTag)
Again, you can use any BBCode options as previously stated (see BBCode options).
Overriding default BBCode tags¶
When loaded, the parser provided by django-precise-bbcode provides some default bbcode tags (please refer to Built-in BBCode tags for the full list of default tags). These default tags can be overriden. You just have to create another tag with the same name either by defining it in the admin site or by defining it in a bbcode_tags
Python module as previously explained.