Creating an Extension Class¶
Your extension will live in extension.py
and inherit from
Extension
. There’s a lot you can do
with this file, but it can start out very simple:
from reviewboard.extensions.base import Extension
class SampleExtension(Extension):
def initialize(self):
# Your extension initialization code belongs here.
That’s a pretty simplistic extension. You probably want to do a lot more with it. This section will cover some of the attributes and methods you can define.
Defining Extension Metadata¶
By default, your extension’s basic information (name, description, author,
etc.) will be taken from the package’s metadata. You may want to override some
or all of this. You can do so using the
metadata
attribute:
class SampleExtension(Extension):
metadata = {
'Name': 'My extension name',
'Version': '1.0',
'Summary': 'My summary.',
'Description': 'A longer description.',
'Author': 'My Name',
'Author-email': 'me@example.com',
'Author-home-page': 'https://me.example.com/',
'License': 'MIT',
'Home-page': 'https://myextension.example.com/',
}
These are used primarily for display purposes in the administration UI. The version, however, is also used for some state tracking, so whether you’re leaving it up to the package or defining it here, you’ll want to make sure to increase the version when you have a new release going into production.
Note
As a best practice, we recommend not overriding the version here, and instead using the Python package version, unless your package is shipping more than one extension and you want to keep their versions separate.
Handling Initialization and Shutdown¶
When your extension is enabled, its
initialize()
method will be
called. This is where you’ll put code to set up extension hooks or perform any other initialization.
When your extension is disabled (or Review Board is shutting down in a web
server process), the
shutdown()
method will be
called. You can use this to perform any cleanup you may need to do.
Note that hooks do not need to be cleaned up by your extension. This will happen automatically.
class SampleExtension(Extension):
def initialize(self):
logging.info('My extension is enabled!')
def shutdown(self):
logging.info('My extension is disabled!')
Requiring Other Extensions¶
Extensions can be written to extend or depend on other extensions. This is far
less common, but if you need it, you’ll want to know about the
requirements
attribute.
This is a list of extension IDs that will be enabled when enabling your
extension.
class SampleExtension(Extension):
requirements = [
'some_other_extension.extension.SomeOtherExtension',
]
Adding Django Apps¶
Your extension may ship with several sub-modules that work as Django “app”
modules, with their own models.py
or similar. It might require
third-party Django apps to be in INSTALLED_APPS
. In either
case, you can list these apps in the
apps
attribute.
class SampleExtension(Extension):
apps = [
'sample_extension.some_app1',
'sample_extension.some_app2',
'third_party_app',
]
When enabled, these apps will be added (if not already) to
INSTALLED_APPS
and initialized. When disabled, they’ll be
removed (if nothing else is using them).
Adding Django Context Processors¶
Context processors are a Django feature that provides additional variables to
all templates. If your extension needs to inject variables into most pages, or
you’re using a third-party Django app that expects a context processor to be
loaded in TEMPLATE_CONTEXT_PROCESSORS
, then you can add them
in the context_processors
attribute.
class SampleExtension(Extension):
context_processors = [
'sample_extension.context_processors.my_processor',
'third_party_app.context_processors.some_processor',
]
Adding Django Middleware¶
Middleware is another Django feature that’s used to inject logic into the
HTTP request/response process. They can be used to
process HTTP requests,
invoke views,
process responses,
process template responses, or
handle exceptions raised by views. These
can be added through the
middleware
attribute.
class SampleExtension(Extension):
middleware = [
'sample_extension.middleware.MyMiddleware',
'third_party_app.middleware.SomeMiddleware',
]
Defining Static Media Bundles¶
Static media bundles for your extension can be defined through the
css_bundles
and
js_bundles
attributes. These
are used to package up CSS/LessCSS/JavaScript files that can be loaded onto
any new or existing pages in Review Board. For example:
class SampleExtension(Extension):
css_bundles = {
'default': {
'source_filenames': ['css/common.less'],
},
}
js_bundles = {
'default': {
'source_filenames': [
'js/extension.js',
'js/common.js',
]
},
'admin': {
'source_filenames': ['js/admin.js'],
}
}
This is covered in more detail in Extension Static Media Files.
Custom Configuration and Settings¶
Extensions come with their own settings storage, and you can offer customization of these settings however you like.
Default settings can be specified by setting a
default_settings
dictionary. These are the fallbacks for any values not stored in the database
for the extension. Enabled extensions can then access the current settings or
set new ones through
settings
.
class SampleExtension(Extension):
default_settings = {
'enable_secret_message': True,
'days_until_secret_message': 42,
'secret_message_text': "It's a secret to everyone.",
}
If you want to enable configuration, you’ll need to set
is_configurable
to True
and define URLs and views for your configuration page.
class SampleExtension(Extension):
is_configurable = True
This is covered in more detail in Extension Configuration.
Adding API Resources¶
Your extension may want to define custom API for use by RBTools and other
clients or services. Any top-level API resources you define can be enabled
through resources
. You’ll
specify them as instances of your resource classes.
from my_extension.resources import my_resource_1, my_resource_2
class SampleExtension(Extension):
resources = [
my_resource_1,
my_resource_2,
]
This is covered in more detail in Extending the Web API.
Adding JavaScript Extensions¶
Review Board extensions can contain a JavaScript extension counterpart, which
can interact with the UI dynamically. These are added by subclassing
JSExtension
and listing the classes
in js_extensions
.
class SampleJSExtension(JSExtension):
...
class SampleExtension(Extension):
js_extensions = [SampleJSExtension]
This is covered in more detail in JavaScript Extensions.
Enabling an Administrator Site¶
If you’re defining custom database models, you may want to allow users to
create or modify entries for these models. You can do this by enabling a
database administrator site for your extension by setting
has_admin_site
to True
.
class SampleExtension(Extension):
has_admin_site = True
When the extension is enabled, a Database will be shown along with the extension’s information. This will be a miniature version of Review Board’s normal database viewer.
This is covered in more detail in Adding Models to the Admin Database Browser.
Supporting Read-Only Mode¶
Review Board can be put into read-only mode by the site administrator, which
disables API requests to the server and hides associated front-end features.
If you would like your extension to be compliant or have specific behavior in
read-only mode, is_site_read_only_for()
can be called with a User
to
check if the User should be affected by the read-only mode.
from reviewboard.admin.read_only import is_site_read_only_for
...
if is_site_read_only_for(user):
# Put code to run when in read-only mode here.