So You Want To Make Your Own Extension (Part 1)

That’s great! Extensions are a great way to add functionality to an existing web app. This blog post is going to help you through some of the initial stages of creating your extension for ReviewBoard.

Oh, but I’ll make it clear that this is not supposed to replace the [Extensions Documentation].

The documentation is meant to be a general outline on how one creates Extensions. Actually, why don’t you go take a look at it right now?

…Alright, are you back? So you might be wondering now, “Well, if there’s already a documentation, what is this tutorial for?” The documentation covers a lot of scope and is very general in the way it approaches extension creation. My purpose in this blog post is slightly more particular to getting your extension’s front-end up and running, and bridging that with your back-end. Specifically, we will look at:

Part 1:
– Starting your extension
– Managing your static files

Part 2:
– WebAPIResource
– BaseResource

Basically, I am hoping that this tutorial will let you do more of this:

And less of this:

But seriously, if you feel like this at any point in the semester, you should probably talk to one of the mentors ASAP. Trust me. It helps.

Example Extension

For this tutorial, I’ll show you how I setup my Checklist extension. The Checklist extension allows users to create a list of items for a review so that they’ll remember what kind of things to mention for a review request. I will be posting snippets of my code here, but if you want to check out the entire thing, here is the GitHub page for it.

I know some of you might be total newbies to Django, which is perfectly fine; however, for this tutorial, I’m hoping you know at least how Django implements the MVC framework more or less. Also, while I want to be as helpful as possible, I know for sure that this tutorial may not be 100% of what you need. Maybe some things will be missing, and maybe some stuff I say here may be completely useless to you. It’s a testament to Reviewboard’s flexibility that so many different types of extensions can be created. The extension we’re going to be working with is heavy on the front-end. And this is why we will be starting there. It’s nice to get a prototype up and running, just to get a feel for what you really want your extension to do.

Extension Boilerplate Generator

The good news is that a lot of the startup code you need would be generated for you automatically using the generate_extension command.

cd reviewboard
./contrib/tools/generate_extension.py checklist

You can follow the directions from there. Mostly it’s going to ask you about some preliminary configurations. After that, you should have checklist directory inside reviewboard. The checklist directory should look something like this:

checklist
— setup.py
— checklist
—— admin_urls.py
—— extension.py
—— __init__.py

RB-Extension-Pack

You will need a place to put your extension. Even though RB generated it in reviewboard, we have to put it somewhere else. We can’t squeeze it into the Reviewboard directory, because that’s for the main RB code. For extensions, clone the rb-extension-pack from Github.

git clone https://github.com/reviewboard/rb-extension-pack.git
cd rb-extension-pack

Then copy over the checklist directory into rb-extension-pack.

Extension.py

Alright, so this is where we start to get some meat. Extension.py is where you put the bulk of the configuration for your extensions. At this point, it should look like this:

# checklist Extension for Review Board.
from reviewboard.extensions.base import Extension
from reviewboard.extensions.hooks import TemplateHook

class Checklist(Extension):
    def __init__(self, *args, **kwargs):
        super(Checklist, self).__init__(*args, **kwargs)

Here are some more things you should add in your class:

    metadata = {
        'Name': 'Review Checklist',
    }

    js_model_class = 'Checklist.Extension'

We will be coming back periodically to the extension.py file, but for now, this is all we have to do.

Managing Your Static Files

The Template Hook

So, for Extensions, there are these things called “hooks”. These allow your extension to hook into certain parts of ReviewBoard, hence the name. Here is the list of hooks that you can use for your extension: [Extension Hooks]. The important part for checklist extension is the Template Hook, because in order for us to proceed with our prototype, we want our front-end to show up where it needs to be. For example, for our checklist, we want it to show up on pages where creating a review is possible, such as any of the Review UI templates, or the diff templates. Basically, anywhere that a user can comment on code, images, pdf files, etc. we want a checklist to be created.

We can tell our extension to do this by adding a Template Hook in the __init__ function.

    def __init__(self, *args, **kwargs):
        super(Checklist, self).__init__(*args, **kwargs)
        TemplateHook(self, "base-scripts-post", "checklist/template.html",
                     apply_to=["view_diff", "view_diff_revisions",
                               "file_attachment"])

Alright, so let’s dissect this a bit.

We’re instantiating a TemplateHook. “checklist/template.html” is the template that we want to inject into the other pages. The template.html defines how your extension looks like on the front end; it would also include links to your extension’s Javascript and CSS files. I will talk more about these later.

The apply_to list is a list of template names defining the pages into which we would like to inject our extension. If you look at reviewboard/reviews/urls.py, you will see that the views corresponding to the urls have names. You can use these names to specify where you want your extension to show.

For my case, since the checklist should show up anywhere a user can comment on a review request, we want it to show up on the diff pages of the request, as well as any file attachments included with the request.

Configuring your Static Files

So now that we have configured our extension to show up where we want it to, the next thing to do is to actually code up how it should appear. If you have done web programming before, you’ll know that we will need an html page at the very least. To add functionality on the front-end, we will need a Javascript file, and to make it look nice, we will need a CSS file.

First, we have to add a bit to our extension.py file again, to let RB know how to access our static files.

Add these just after the assignment to ‘js_model_class’:

    css_bundles = {
        'css_default': {
            'source_filenames': ['css/index.css']
        }
    }

    js_bundles = {
        'js_default': {
            'source_filenames': ['js/models/checklist.js',
                                 'js/models/checklistAPI.js',
                                 'js/views/checklistView.js']
        }
    }

Alright. So in the source_filenames list, we have to add the path to our static files. In the css_bundles list, we add the path to our CSS file, and in the js_bundles list, we add the path to our Javascript files. Pretty simple. The strings ‘css_default’ and ‘js_default’ are the names of the bundles. You can put whatever you want here, but remember them, because we’d need these names in our HTML template.

Now, we will take a look at template.html. You should put this file in a templates directory. More specifically, the path to template.html should be:

templates/checklist/template.html’

Django recommends that within the templates directory, the html files should be organized according to the apps. Even though we only have one app — the checklist app — we should still be organized.

You should still add the links to your static files in the template.html file. Here is where the bundles we declared in extension.py will help us. So instead of linking your static files the normal, html way, we can just do it like this:

{% load djblets_extensions %}
{% ext_js_bundle extension "js_default" %}
{% ext_css_bundle extension "css_default" %}

Note that we refer to the bundle we want to use by the name we gave it earlier.

If you’re using Backbone.js (which you really should), you don’t need to link the files to Backbone and its dependencies, because Reviewboard does that already.

In this file, you can put whatever you need for your extension’s front end. I used a Backbone.js View to instantiate my checklist.

<div id="checklist"></div>

<script>
    $(document).ready(function() {
        new Checklist.ChecklistView({
            user_id: "{{user.pk}}",
            review_request_id: "{{review_request.id}}"
        });
    });
</script>

The div will be where I’d add all my DOM elements. The script instantiates a new view.

But what’s {{user.pk}} and {{review_request.id}}? Respectively, they’re the id of the user creating the checklist, and the id of the review request. There are variables accessible through the Django templates, so make sure to check out if the ones you need are available right from the template. 

Alright. Now go to the Admin page of your localhost account. Click Extensions in the menu bar. You should see your extension listed there.

Click ‘Enable’.

Namespaces

One final tip! It’s recommended to use namespaces in your Javascript files. Basically, suppose you have a class called MyClass. It’s a good idea to append your extension name at the beginning of every single one of your class names, like so: MyExtension.MyClass.

Then in your Javascript file, make sure to instantiate your namespace object. As you can see in my models/checklist.js file, I instantiated a variable called ‘Checklist’.

var Checklist = {}

Whenever I refer to my javascript classes, I use Checklist.ClassName. Using namespaces prevents confusion, because there may be variables with the same name as yours from other apps.

Now, you can go to the pages where you injected your template. You are now free to play around with the front-end of your extension!

Advertisements

One thought on “So You Want To Make Your Own Extension (Part 1)

  1. Pingback: So You Want To Make Your Own Extension (Part 2) | Review Board Student Blog

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s