Jens Krämer

How to Create a Redmine Plugin

 |  plugin development, ruby, rails, redmine  |  4 comments

Redmine already can do a lot, but often a certain behaviour or feature is desirable in one person’s use case, but not in others. In order to keep Redmine usable for as many people as possible, such corner case features have a hard time making it into the core Redmine code base. And that’s not a problem at all, thanks to Redmine’s powerful plugin system.

Here I’ll walk you through the creation of a small but fully functional (and fully tested!) Redmine plugin.

The Redmine Issue Done Ratio Plugin

There is and has been a lot of discussion around how the ‘percent done’ ratio on issues should be set. Stock Redmine allows you to chose between setting the ratio either manually, or having it set automatically according to the issue’s status. Unfortunately both options are mutually exclusive, so if you want ‘New’ issues set to 0, ‘Resolved’ and ‘Closed’ set to 100 but manage everything in between manually you are out of luck.

There is at least one plugin out there that implements the automatic setting of the field to 100% for issue statuses that are marked as ‘closed’, but in my opinion that’s not enough - I would like to have my Resolved issues auto-set to 100%, but not closed yet.

So instead of setting any closed issues to 100%, lets make this a bit more flexible. Basically we will implement what is asked for in Redmine issue #6975.

Anatomy of a Redmine Plugin

Redmine plugins reside in the plugins subdirectory of any Redmine installation. The Redmine plugin system was built at the time of Rails 2, and Redmine plugins still look a lot like Rails plugins back in those days. The important things to remember are:

Here’s the first version of our plugin, doing nothing else but registering itself with Redmine. Have a look at init.rb, it should pretty much explain itself. Two things I’d like to point out:

You can put this version of the plugin into plugins/redmine_percent_done , restart Redmine and you should see the plugin listed with all the meta data from init.rb in Administration / Plugins. Yay!

Global Plugin Settings

If you already have some other Redmine plugins installed, you might see a Configure link behind the plugin version number. If your plugin requires any global settings this is the place to put them. Indeed we need a place for the administrator to configure which issue statuses should have an automatically set % Done ratio assigned, so let’s implement this!

In your init.rb, inside the Redmine::Plugin.register block, add


settings partial: 'settings/redmine_percent_done', default: {}

Next, create that file as app/views/settings/_redmine_percent_done.html.erb:


<% for status in IssueStatus.all do %>
  <p>
    <label for="settings_status-<%= status.id %>"><%= status.name %></label>
    <%= select_tag "settings[status-#{status.id}]",
          options_from_collection_for_select(
            [[l(:label_no_change_option), '']] +
              (0..10).to_a.collect{|r| ["#{r*10} %", r*10] },
            :last, :first, @settings["status-#{status.id}"]
          ) %>
<% end %>

We simply iterate over all issue statuses, showing a select box of possible % values for each. There’s also a no change option which is the default, telling the plugin to not touch the % Done value in this case. I chose to use an already existing Redmine label for this option. This has multiple benefits over inventing our own:

So, before you create any own i18n keys for your plugin, check if there is already something in Redmine core you could use.

Create the file or grab the second revision of the plugin, and try out the already fully functional settings form. Redmine’s plugin system takes care of saving our plugin settings, that’s why we didn’t have to write any controller code for this. If you’re interested, the relevant code is in SettingsController#plugin.

Global Plugin Settings

Testing Redmine Plugins

Redmine plugins are usually tested in the context of a working Redmine setup. In theory it might be possible to mock out all the surrounding Redmine APIs for testing a Redmine plugin in isolation, but in my opinion it’s just not worth the hassle. Let’s stick to the more pragmatic testing inside Redmine instead.

The added benefit of running tests this way is that your tests might help to discover conflicts with any other installed plugins early. Especially if users of your plugin run the tests in their environment.

Redmine plugin tests are invoked using a special rake task:


NAME=redmine_percent_done bin/rake redmine:plugins:test

Omitting the NAME environment variable will run the tests of all installed plugins.

The third revision of our plugin adds a basic test_helper file and a test for the already existing settings form. There is no need to check wether the settings are saved properly - that is part of Redmine and already tested there.

I also added a (for now failing) test for what we actually want to implement - automatically changing % Done when an issue’s status changes.

Patching Redmine Core Classes

It’s about time to implement the core functionality of our plugin and make those tests pass.

For that, we add a simple before_save hook to the core Issue class. I won’t go through the actual implementation here since there really isn’t much to explain. Instead I want to point you to a few things worth considering if you extend or otherwise change things in Redmine’s core classes:

If you look at the IssuePatch module in the next revision of the plugin, you will notice that I used prepend to add the module with the hook method to the Issue model. In this case it has no benefit over doing an include, it’s just my small contribution to spreading the word about this awesome feature of Ruby. So, prepend is a thing and by using it you will never need to use alias_method_chain again. Instead, just call super as you would if you were inheriting from Issue, for example. Did I already say this is awesome?

Wrapping it up

Looks like our quick and easy Redmine plugin is finished and we can set issues to ‘resolved but not closed’ and get the done ratio set to 100% automatically.

If you write your own plugin and intend to make it public, don’t forget to add a README, and be clear about the license. This topic is worth an article on its own, I try to make it short:

With that out of the way, go ahead and add it to the Redmine plugin directory.

That’s it for now, check out the finished plugin on Github!

PS: You might also be interested in Testing a Redmine Plugin With Travis CI.

Comments

Foton

As I read: You should update the setting.html.erb code with one from Github (here You have "text_field_tag", on following text and GitHub, there is a " select_tag")

Jens

Corrected, thanks!

Ariel

Hello, Is it compatible with redmine 2.5.x?

Jens

It might, to try it out change the minimum required Redmine version in the plugin's init.rb to 2.5.0. However I strongly suggest upgrading your Redmine, 2.5.0 is very old and lacks quite a few security related patches and bug fixes.

You can use Markdown here.

For the sake of spam checking any data you submit, including your IP address, will be transferred to the US based Akismet web service (akismet.com). If that's not acceptable for you, you can also reach me by other means.