Previous tutorials in this series:

Part 1: Creating a basic plugin
Part 2: Plugin outlets
Part 3: Custom Settings
Part 4: git setup

Sometimes site settings aren’t enough of an admin interface for your plugin to work the way you want. For example, if you install the discourse-akismet plugin, you might have noticed that it adds a navigation item to the admin plugins section in of your Discourse:

In this tutorial we’ll show you how to add an admin interface for your plugin. I’m going to call my plugin purple-tentacle, in honor of one of my favorite computer games. Seriously, I really love that game!

Setting up the Admin Route

Let’s start by adding a plugin.rb like we’ve done in previous parts of the tutorial.

# name: purple-tentacle
# about: A sample plugin showing how to add a plugin route
# version: 0.1
# authors: Robin Ward
# url:

add_admin_route ‘purple_tentacle.title’, ‘purple-tentacle’

Discourse::Application.routes.append do
get ‘/admin/plugins/purple-tentacle’ => ‘admin/plugins#index’, constraints:

The add_admin_route line tells Discourse that this plugin will need a link on the /admin/plugins page. Its title will be purple_tentacle.title from our i18n translations file and it will link to the purple-tentacle route.

The lines below that set up the server side mapping of routes for our plugin. One assumption Discourse makes is that almost every route on the front end has a server side route that provides data. For this example plugin we actually don’t need any data from the back end, but we need to tell Discourse to serve up something in case the user visits /admin/plugins/purple-tentacle directly. This line just tells it: ‘hey if the user visits that URL directly on the server side, serve the default plugins content!’

(If this is confusing don’t worry too much, we’ll come back to it in a future tutorial when we handle server side actions.)

Next, we’ll add a template that will be displayed when the user visits the /admin/plugins/purple-tentacle path. It will just be a button that shows an animated gif of purple tentacle when the user clicks a button:


If you've learned the basics of handlebars the template should be pretty simple to understand. The `d-button` is a component in discourse we use for showing a button with a label and icon.

To wire up our new template we need to create a route map:

export default {
  resource: 'admin.adminPlugins',
  path: '/plugins',
  map() {

A route map is something we added to discourse to make it so that plugins could add routes to the ember application. The syntax within map() is very similar to Ember’s router. In this case our route map is very simple, it just declares one route called purple-tentacle under /admin/plugins.

Finally, let’s add our translation strings:

title: “Purple Tentacle”
show: “Show Purple Tentacle”

If you restart your development server, you should be able to visit `/admin/plugins` and you'll see our link! If you click it, you'll see the button to show our purple tentacle:


Unfortunately, when you click the button, nothing happens :(  

If you look at your developer console, you should see an error that provides a clue to why this is: `Uncaught Error: Nothing handled the action 'showTentacle'`

Ah yes, the reason is in our handlebars template we are depending on a couple of things:

1. That when the user clicks the button, `showTentacle` will be called.
2. `showTentacle` should set the property `tentacleVisible` to true so that the image shows up.

If you haven't read the [Ember Guides on Controllers]( now is a good time to do so, because we'll implement a controller for our `purple-tentacle` template that will handle this logic.

Create the following file:

export default Ember.Controller.extend({
  tentacleVisible: false,

  actions: {
    showTentacle() {
      this.set('tentacleVisible', true);

And now when we refresh our page, clicking the button shows our animated character!

I’ll leave it as an extra exercise to the reader to add a button that hides the tentacle when clicked :smile:

If you are having trouble getting your version of this plugin working, I’ve pushed it to github.

### More in the series

Part 6: Acceptance Tests