Project

General

Profile

Actions

Plugin Internals » History » Revision 2

« Previous | Revision 2/24 (diff) | Next »
Mischa The Evil, 2009-04-01 00:05
Added requires_redmine info


Plugin Internals

This page will be used as a central place to store information about plugin-development in Redmine.

Require a certain Redmine version

Sometimes plugins require a specific feature implemented in the Redmine core or the plugin overrides a specific view which requires you to control on which (specific) versions of Redmine the plugin can be installed to assure that the required core is available. Such prevents a lot of issues regarding plugin-compatibility.

The above can be accomplished by utilizing the requires_redmine-method (see issue #2162 for the implementation dicussion and it's actual implementation in r2042). Utilisation of the method provides an easy, reliable way to create plugins that require a specific version of Redmine and which are setup to stop Redmine with a message about a non-supported version if the version-requirement is not met.

Overriding the Redmine Core

You can override views but not controllers or models in Redmine. Here's how Redmine/Rails works if you try to override a controller (or model) and a view for a fictional plugin MyPlugin:

Controllers (or models)

  1. Rails bootstraps and loads all it's framework
  2. Rails starts to load code in the plugins
  3. Rails finds IssueController in MyPlugin and see it defines a show action
  4. Rails loads all the other plugins
  5. Rails then loads the application from ../app
  6. Rails finds IssueController again and see it also defines a show action
  7. Rails (or rather Ruby) overwrites the show action from the plugin with the one from ../app
  8. Rails finishes loading and serves up requests

Views

View loading is very similar but with one small difference (because of Redmine's patch to Engines)

  1. Rails bootstraps and loads all it's framework
  2. Rails starts to load code in the plugins
  3. Rails finds a views directory in ../vendor/plugins/my_plugin/app/views and pre-pends it to the views path
  4. Rails loads all the other plugins
  5. Rails then loads the application from ../app
  6. Rails finishes loading and serves up requests
  7. Request comes in, and a view needs to be rendered
  8. Rails looks for a matching template and loads the plugin's template since it was pre-pended to the views path
  9. Rails renders the plugins'view

Due to the fact that it is so easy to extend models and controllers the Ruby way (via including modules), Redmine shouldn't (and doesn't) maintain an API for overriding the core's models and/or controllers. Views on the other hand are tricky (because of Rails magic) so an API for overriding them is way more useful (and thus implemented in Redmine).

To override an existing Redmine Core view just create a view file named exactly after the one in ../app/views/ and Redmine will use it. For example to override the project index page add a file to ../vendor/plugins/my_plugin/app/views/projects/index.rhtml.

Extending the Redmine Core

As explained above: you rarely want to override a model/controller. Instead you should either:
  • add new methods to a model/controller or
  • wrap an existing method.

Adding a new method

A quick example of adding a new method can be found on Eric Davis' Budget plugin. Here he added a new method to Issue called deliverable_subject and also declared a relationship.

Wrapping an existing method

A quick example of wrapping an existing method can be found on Eric Davis' Rate plugin. Here he uses the alias_method_chain to hook into the UsersHelper and wrap the user_settings_tabs method. So when the Redmine Core calls user_settings_tabs the codepath looks like:

  1. Redmine Core calls UsersHelper#user_settings_tabs
  2. UsersHelper#user_settings_tabs runs (which is actually UsersHelper#user_settings_tabs_with_rate_tab)
  3. UsersHelper#user_settings_tabs_with_rate_tab calls the original UsersHelper#user_settings_tabs (renamed to UsersHelper#user_settings_tabs_without_rate_tab)
  4. The result then has a new Hash added to it
  5. UsersHelper#user_settings_tabs_with_rate_tab returns the combined result to the Redmine core, which is then rendered

alias_method_chain is a pretty advanced method but it's also really powerful.

References

Updated by Mischa The Evil almost 15 years ago · 2 revisions