How to extend the Toolbar¶
The django CMS toolbar provides an API that allows you to add, remove and manipulate toolbar items in your own code. It helps you to integrate django CMS’s frontend editing mode into your application, and provide your users with a streamlined editing experience.
See also
Extending the Toolbar in the tutorial
Create a cms_toolbars.py
file¶
In order to interact with the toolbar API, you need to create a
CMSToolbar
sub-class in your own code, and register it.
This class should be created in your application’s cms_toolbars.py
file, where it will be
discovered automatically when the Django runserver starts.
You can also use the CMS_TOOLBARS
to control which toolbar classes are loaded.
Define and register a CMSToolbar
sub-class¶
from cms.toolbar_base import CMSToolbar
from cms.toolbar_pool import toolbar_pool
class MyToolbarClass(CMSToolbar):
[...]
toolbar_pool.register(MyToolbarClass)
The cms.toolbar_pool.ToolbarPool.register
method can also be used as a decorator:
@toolbar_pool.register
class MyToolbarClass(CMSToolbar):
[...]
Populate the toolbar¶
Two methods are available to control what will appear in the django CMS toolbar:
populate()
, which is called before the rest of the page is renderedpost_template_populate()
, which is called after the page’s template is rendered
The latter method allows you to manage the toolbar based on the contents of the page, such as the
state of plugins or placeholders, but unless you need to do this, you should opt for the more
simple populate()
method.
class MyToolbar(CMSToolbar):
def populate(self):
# add items to the toolbar
Now you have to decide exactly what items will appear in your toolbar. These can include:
Finding existing toolbar items¶
find_items()
and find_first()
¶
Search for items by their type:
def populate(self):
self.toolbar.find_items(item_type=LinkItem)
will find all LinkItem
s in the toolbar (but not for example in the menus in the toolbar - it
doesn’t search other items in the toolbar for items of their own).
find_items()
returns a list of
ItemSearchResult
objects;
find_first()
returns the first object in that list. They
share similar behaviour so the examples here will use find_items()
only.
The item_type
argument is always required, but you can refine the search by using their other
attributes, for example:
self.toolbar.find_items(Menu, disabled=True))
Note that you can use these two methods to search Menu
and SubMenu
classes for items too.
Control the position of items in the toolbar¶
Methods to add menu items to the toolbar take an optional position
argument, that can be
used to control where the item will be inserted.
By default (position=None
) the item will be inserted after existing items in the same level of
the hierarchy (a new sub-menu will become the last sub-menu of the menu, a new menu will be become
the last menu in the toolbar, and so on).
A position of 0
will insert the item before all the others.
If you already have an object, you can use that as a reference too. For example:
def populate(self):
link = self.toolbar.add_link_item('Link', url=link_url)
self.toolbar.add_button('Button', url=button_url, position=link)
will add the new button before the link item.
Finally, you can use a ItemSearchResult
as a position:
def populate(self):
self.toolbar.add_link_item('Link', url=link_url)
link = self.toolbar.find_first(LinkItem)
self.toolbar.add_button('Button', url=button_url, position=link)
and since the ItemSearchResult
can be cast to an integer, you could even do:
self.toolbar.add_button(‘Button’, url=button_url, position=link+1)
Control how and when the toolbar appears¶
By default, your CMSToolbar
sub-class will be active (i.e. its
populate
methods will be called) in the toolbar on every page, when the user is_staff
.
Sometimes however a CMSToolbar
sub-class should only populate the toolbar when visiting pages
associated with a particular application.
A CMSToolbar
sub-class has a useful attribute that can help determine whether a toolbar should
be activated. is_current_app
is True
when the application containing the toolbar class
matches the application handling the request.
This allows you to activate it selectively, for example:
def populate(self):
if not self.is_current_app:
return
[...]
If your toolbar class is in another application than the one you want it to be active for, you can list any applications it should support when you create the class:
supported_apps = ['some_app']
supported_apps
is a tuple of application dotted paths (e.g: supported_apps =
('whatever.path.app', 'another.path.app')
.
The attribute app_path
will contain the name of the application handling the current request
- if app_path
is in supported_apps
, then is_current_app
will be True
.
Modifying an existing toolbar¶
If you need to modify an existing toolbar (say to change an attribute or the behaviour of a method) you can do this by creating a sub-class of it that implements the required changes, and registering that instead of the original.
The original can be unregistered using toolbar_pool.unregister()
, as in the example below.
Alternatively if you originally invoked the toolbar class using CMS_TOOLBARS
, you will
need to modify that to refer to the new one instead.
An example, in which we unregister the original and register our own:
from cms.toolbar_pool import toolbar_pool
from third_party_app.cms_toolbar import ThirdPartyToolbar
@toolbar_pool.register
class MyBarToolbar(ThirdPartyToolbar):
[...]
toolbar_pool.unregister(ThirdPartyToolbar)
Detecting URL changes to an object¶
If you want to watch for object creation or editing of models and redirect after they have been
added or changed add a watch_models
attribute to your toolbar.
Example:
class PollToolbar(CMSToolbar):
watch_models = [Poll]
def populate(self):
...
After you add this every change to an instance of Poll
via sideframe or modal window will
trigger a redirect to the URL of the poll instance that was edited, according to the toolbar
status:
in draft mode the
get_draft_url()
is returned (orget_absolute_url()
if the former does not exist)in live mode, and the method exists,
get_public_url()
is returned.
Frontend¶
If you need to interact with the toolbar, or otherwise account for it in your site’s frontend code, it provides CSS and JavaScript hooks for you to use.
It will add various classes to the page’s <html>
element:
cms-ready
, when the toolbar is readycms-toolbar-expanded
, when the toolbar is fully expandedcms-toolbar-expanding
andcms-toolbar-collapsing
during toolbar animation.
The toolbar also fires a JavaScript event called cms-ready
on the document.
You can listen to this event using jQuery:
CMS.$(document).on('cms-ready', function () { ... });