Plugin Internals » History » Version 2
Mischa The Evil, 2009-04-01 00:05
Added requires_redmine info
1 | 1 | Mischa The Evil | h1. Plugin Internals |
---|---|---|---|
2 | 1 | Mischa The Evil | |
3 | 1 | Mischa The Evil | {{>toc}} |
4 | 1 | Mischa The Evil | |
5 | 1 | Mischa The Evil | This page will be used as a central place to store information about plugin-development in Redmine. |
6 | 1 | Mischa The Evil | |
7 | 2 | Mischa The Evil | h2. Require a certain Redmine version |
8 | 2 | Mischa The Evil | |
9 | 2 | Mischa The Evil | 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. |
10 | 2 | Mischa The Evil | |
11 | 2 | Mischa The Evil | 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. |
12 | 2 | Mischa The Evil | |
13 | 1 | Mischa The Evil | h2. Overriding the Redmine Core |
14 | 1 | Mischa The Evil | |
15 | 1 | Mischa The Evil | 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@: |
16 | 1 | Mischa The Evil | |
17 | 1 | Mischa The Evil | h3. Controllers (or models) |
18 | 1 | Mischa The Evil | |
19 | 1 | Mischa The Evil | # Rails bootstraps and loads all it's framework |
20 | 1 | Mischa The Evil | # Rails starts to load code in the plugins |
21 | 1 | Mischa The Evil | # Rails finds @IssueController@ in MyPlugin and see it defines a @show@ action |
22 | 1 | Mischa The Evil | # Rails loads all the other plugins |
23 | 1 | Mischa The Evil | # Rails then loads the application from _../app_ |
24 | 1 | Mischa The Evil | # Rails finds @IssueController@ again and see it also defines a @show@ action |
25 | 1 | Mischa The Evil | # Rails (or rather Ruby) overwrites the @show@ action from the plugin with the one from _../app_ |
26 | 1 | Mischa The Evil | # Rails finishes loading and serves up requests |
27 | 1 | Mischa The Evil | |
28 | 1 | Mischa The Evil | h3. Views |
29 | 1 | Mischa The Evil | |
30 | 1 | Mischa The Evil | View loading is very similar but with one small difference (because of Redmine's patch to Engines) |
31 | 1 | Mischa The Evil | |
32 | 1 | Mischa The Evil | # Rails bootstraps and loads all it's framework |
33 | 1 | Mischa The Evil | # Rails starts to load code in the plugins |
34 | 1 | Mischa The Evil | # Rails finds a views directory in _../vendor/plugins/my_plugin/app/views_ and *pre-pends* it to the views path |
35 | 1 | Mischa The Evil | # Rails loads all the other plugins |
36 | 1 | Mischa The Evil | # Rails then loads the application from _../app_ |
37 | 1 | Mischa The Evil | # Rails finishes loading and serves up requests |
38 | 1 | Mischa The Evil | # Request comes in, and a view needs to be rendered |
39 | 1 | Mischa The Evil | # Rails looks for a matching template and loads the plugin's template since it was *pre-pended* to the views path |
40 | 1 | Mischa The Evil | # Rails renders the plugins'view |
41 | 1 | Mischa The Evil | |
42 | 1 | Mischa The Evil | 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). |
43 | 1 | Mischa The Evil | |
44 | 1 | Mischa The Evil | 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_. |
45 | 1 | Mischa The Evil | |
46 | 1 | Mischa The Evil | h2. Extending the Redmine Core |
47 | 1 | Mischa The Evil | |
48 | 1 | Mischa The Evil | As explained above: you rarely want to override a model/controller. Instead you should either: |
49 | 1 | Mischa The Evil | * add new methods to a model/controller or |
50 | 1 | Mischa The Evil | * wrap an existing method. |
51 | 1 | Mischa The Evil | |
52 | 1 | Mischa The Evil | h3. Adding a new method |
53 | 1 | Mischa The Evil | |
54 | 1 | Mischa The Evil | A quick example of *adding a new method* can be found on Eric Davis' "Budget plugin":http://github.com/edavis10/redmine-budget-plugin/blob/5076b1c88b57c2068aa92cdf694769dbd22d061a/lib/issue_patch.rb. Here he added a new method to Issue called @deliverable_subject@ and also declared a relationship. |
55 | 1 | Mischa The Evil | |
56 | 1 | Mischa The Evil | h3. Wrapping an existing method |
57 | 1 | Mischa The Evil | |
58 | 1 | Mischa The Evil | A quick example of *wrapping an existing method* can be found on Eric Davis' "Rate plugin":http://github.com/edavis10/redmine_rate/blob/4666ddb10e1061ca3ef362735d0d264676b99024/lib/rate_users_helper_patch.rb. 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: |
59 | 1 | Mischa The Evil | |
60 | 1 | Mischa The Evil | # Redmine Core calls @UsersHelper#user_settings_tabs@ |
61 | 1 | Mischa The Evil | # @UsersHelper#user_settings_tabs@ runs (which is actually @UsersHelper#user_settings_tabs_with_rate_tab@) |
62 | 1 | Mischa The Evil | # @UsersHelper#user_settings_tabs_with_rate_tab@ calls the original @UsersHelper#user_settings_tabs@ (renamed to @UsersHelper#user_settings_tabs_without_rate_tab@) |
63 | 1 | Mischa The Evil | # The result then has a new Hash added to it |
64 | 1 | Mischa The Evil | # @UsersHelper#user_settings_tabs_with_rate_tab@ returns the combined result to the Redmine core, which is then rendered |
65 | 1 | Mischa The Evil | |
66 | 1 | Mischa The Evil | "@alias_method_chain@":http://api.rubyonrails.org/classes/ActiveSupport/CoreExtensions/Module.html#M001188 is a pretty advanced method but it's also really powerful. |
67 | 1 | Mischa The Evil | |
68 | 1 | Mischa The Evil | h2. References |
69 | 1 | Mischa The Evil | |
70 | 2 | Mischa The Evil | * http://www.redmine.org/boards/3/topics/show/5121 (Which version of Redmine I need to use your plugin?) |
71 | 2 | Mischa The Evil | * http://www.redmine.org/boards/3/topics/show/4283 (Can a plugin modify the view of the projects page?) |
72 | 2 | Mischa The Evil | * http://www.redmine.org/boards/3/topics/show/4095 (Rails Engines and extending the issue model) |