Project

General

Profile

Plugin Tutorial » History » Version 9

Jean-Philippe Lang, 2008-08-10 18:50

1 3 Jean-Philippe Lang
{{>toc}}
2
3 1 Jean-Philippe Lang
h1. Plugin Tutorial
4
5
h2. Creating a new Plugin
6
7 9 Jean-Philippe Lang
Creating a new plugin can be done using the Redmine plugin generator.
8
Syntax for this generator is:
9 1 Jean-Philippe Lang
10 9 Jean-Philippe Lang
  ruby script/generate redmine_plugin <plugin_name>
11
12
So open up a command prompt and "cd" to your redmine directory, then execute the following command:
13
14 3 Jean-Philippe Lang
  % ruby script/generate redmine_plugin Pools
15 1 Jean-Philippe Lang
16
The plugin structure is created in @vendor/plugins/redmine_pools@:
17
18
<pre>
19
      create  vendor/plugins/redmine_pools/app/controllers
20
      create  vendor/plugins/redmine_pools/app/helpers
21
      create  vendor/plugins/redmine_pools/app/models
22
      create  vendor/plugins/redmine_pools/app/views
23
      create  vendor/plugins/redmine_pools/db/migrate
24
      create  vendor/plugins/redmine_pools/lib/tasks
25
      create  vendor/plugins/redmine_pools/assets/images
26
      create  vendor/plugins/redmine_pools/assets/javascripts
27
      create  vendor/plugins/redmine_pools/assets/stylesheets
28
      create  vendor/plugins/redmine_pools/lang
29
      create  vendor/plugins/redmine_pools/README
30
      create  vendor/plugins/redmine_pools/init.rb
31
      create  vendor/plugins/redmine_pools/lang/en.yml
32
</pre>
33
34 8 Jean-Philippe Lang
Edit @vendor/plugins/redmine_pools/init.rb@ to adjust plugin information (name, author, description and version):
35 1 Jean-Philippe Lang
36
<pre><code class="ruby">
37
require 'redmine'
38
39
Redmine::Plugin.register :redmine_pools do
40
  name 'Pools plugin'
41
  author 'John Smith'
42
  description 'A plugin for managing pools'
43
  version '0.0.1'
44
end
45
</code></pre>
46
47 2 Jean-Philippe Lang
Then restart the application and point your browser to http://localhost:3000/admin/info.
48 1 Jean-Philippe Lang
After logging in, you should see your new plugin in the plugins list:
49 4 Jean-Philippe Lang
50 2 Jean-Philippe Lang
p=. !plugins_list1.png!
51 1 Jean-Philippe Lang
52
h2. Generating a controller
53
54
For now, the plugin doesn't do anything. So let's create a controller for our plugin.
55 9 Jean-Philippe Lang
We can use the plugin controller generator for that. Syntax is:
56
57
  ruby script/generate redmine_plugin_controller <plugin_name> <controller_name> [<actions>]
58
59
So go back to the command prompt and run:
60 3 Jean-Philippe Lang
61
<pre>
62
% ruby script/generate redmine_plugin_controller Pools pools index vote
63
      exists  app/controllers/
64
      exists  app/helpers/
65
      create  app/views/pools
66
      create  test/functional/
67
      create  app/controllers/pools_controller.rb
68
      create  test/functional/pools_controller_test.rb
69
      create  app/helpers/pools_helper.rb
70
      create  app/views/pools/index.html.erb
71
      create  app/views/pools/vote.html.erb
72
</pre>
73
74
A controller @PoolsController@ with 2 actions (@#index@ and @#vote@) is created.
75
76 5 Jean-Philippe Lang
Edit @app/controllers/pools_controller.rb@ in @redmine_pools@ directory to implement these 2 actions.
77 3 Jean-Philippe Lang
78
<pre><code class="ruby">
79
class PoolsController < ApplicationController
80
  unloadable
81
  
82
  @@pools = [ {:id => 1, :title => 'First pool', :question => 'This is the first pool question', :yes => 0, :no => 0},
83
              {:id => 2, :title => 'Second pool', :question => 'This is the second pool question', :yes => 0, :no => 0} ]
84
85
  def index
86 7 Jean-Philippe Lang
    @pools = @@pools # Pool.find(:all)
87 3 Jean-Philippe Lang
  end
88
89
  def vote
90 7 Jean-Philippe Lang
    pool = @@pools.find {|p| p[:id].to_s == params[:id]} # Pool.find(params[:id])
91 3 Jean-Philippe Lang
    # saves the vote
92
    pool[params[:answer].to_sym] += 1
93
    flash[:notice] = 'Vote saved.'
94
    redirect_to :action => 'index'
95
  end
96
end
97 1 Jean-Philippe Lang
</code></pre>
98 5 Jean-Philippe Lang
99
For the sake of this example, we simulate a pool model in our @@@pools@ class variable.
100
We could of course use a ActiveRecord model just like we do it in a regular Rails app.
101 3 Jean-Philippe Lang
102
Then edit @app/views/pools/index.html.erb@ that will display existing pools:
103
104
105
<pre>
106
<h2>Pools</h2>
107
108
<% @pools.each do |pool| %>
109
  <p>
110
  <%= pool[:question] %>?
111
  <%= link_to 'Yes', {:action => 'vote', :id => pool[:id], :answer => 'yes'}, :method => :post %> (<%= pool[:yes] %>) /
112
  <%= link_to 'No', {:action => 'vote', :id => pool[:id], :answer => 'no'}, :method => :post %> (<%= pool[:no] %>)
113
  </p>
114
<% end %>
115
</pre>
116
117
You can remove @vote.html.erb@ since no rendering is done by the corresponding action.
118
119 1 Jean-Philippe Lang
Now, restart the application and point your browser to http://localhost:3000/pools.
120 4 Jean-Philippe Lang
You should see the 2 pools and you should be able to vote for them:
121
122
p=. !pools1.png!
123
124
Note that pool results are reset on each request if you don't run the application in production mode, since our pool "model" is stored in a class variable in this example.
125
126
h2. Extending menus
127
128
Our controller works fine but users have to know the url to see the pools. Using the Redmine plugin API, you can extend standard menus.
129
So let's add a new item to the application menu.
130
131
h3. Extending the application menu
132
133
Edit @init.rb@ at the root of your plugin directory to add the following line at the end of the plugin registration block:
134
135
<pre><code class="ruby">
136
Redmine::Plugin.register :redmine_pools do
137
  [...]
138
  
139
  menu :application_menu, :pools, { :controller => 'pools', :action => 'index' }, :caption => 'Pools'
140
end
141
</code></pre>
142
143
Syntax is:
144
145
  menu(menu_name, item_name, url, options={})
146
147
There are 4 menus that you can extend:
148
149
* @:top_menu@ - the top left menu
150
* @:account_menu@ - the top right menu with sign in/sign out links
151
* @:application_menu@ - the main menu displayed when the user is not inside a project
152
* @:project_menu@ - the main menu displayed when the user is inside a project
153
154
Available options are:
155
156
* @:param@ - the parameter key that is used for the project id (default is @:id@)
157
* @:if@ - a Proc that is called before rendering the item, the item is displayed only if it returns true
158
* @:caption@ - the menu caption that can be:
159
160
  * a localized string Symbol
161
  * a String
162
  * a Proc that can take the project as argument
163
164
* @:before@, @:after@ - specify where the menu item should be inserted (eg. @:after => :activity@)
165
* @:last@ - if set to true, the item will stay at the end of the menu (eg. @:last => true@)
166
* @:html_options@ - a hash of html options that are passed to @link_to@ when rendering the menu item
167
168
In our example, we've added an item to the application menu which is emtpy by default.
169
Restart the application and go to http://localhost:3000:
170
171
p=. !application_menu.png!
172
173
Now you can access the pools by clicking the Pools tab from the welcome screen.
174
175
h3. Extending the project menu
176
177 6 Jean-Philippe Lang
Now, let's consider that the pools are defined at project level (even if it's not the case in our example pool model). So we would like to add the Pools tab to the project menu instead.
178
Open @init.rb@ and replace the line that was added just before with these 2 lines:
179
180
<pre><code class="ruby">
181
Redmine::Plugin.register :redmine_pools do
182
  [...]
183
184
  permission :pools, {:pools => [:index, :vote]}, :public => true
185
  menu :project_menu, :pools, { :controller => 'pools', :action => 'index' }, :caption => 'Pools', :after => :activity, :param => :project_id
186
end
187
</code></pre>
188
189
The second line adds our Pools tab to the project menu, just after the activity tab.
190
The first line is required and declares that our 2 actions from @PoolsController@ are public. We'll come back later to explain this with more details.
191
192
Restart the application again and go to one of your projects:
193
194
p=. !project_menu.png!
195
196
If you click the Pools tab, you should notice that the project menu is no longer displayed.
197
To make the project menu visible, you have to initialize the controller's instance variable @@project@.
198
199
Edit your PoolsController to do so:
200
201
<pre><code class="ruby">
202
def index
203
  @project = Project.find(params[:project_id])
204 7 Jean-Philippe Lang
  @pools = @@pools # @project.pools
205 6 Jean-Philippe Lang
end
206
</code></pre>
207
208
The project id is available in the @:project_id@ param because of the @:param => :project_id@ option in the menu item declaration above.
209
210
Now, you should see the project menu when viewing the pools:
211
212
p=. !project_menu_pools.png!
213 4 Jean-Philippe Lang
214
h2. Adding new permissions
215
216
TODO
217
218
h2. Creating a project module
219
220
TODO