Project

General

Profile

Plugin Tutorial » History » Version 6

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

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