Jump to >

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 expectes 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.