Project

General

Profile

Defect #28340 » plugin_tutorial.diff

Mizuki ISHIKAWA, 2020-04-21 06:22

View differences:

Plugin Tutorial.textile
1 1
h1. Plugin Tutorial
2 2

  
3
This tutorial is based on Redmine 2.x. You can view a previous version of this tutorial for Redmine 1.x "here":/projects/redmine/wiki/Plugin_Tutorial?version=66.
4
It assumes that you're familiar with the Ruby on Rails framework.
5

  
6
h3. NOTE: Redmine 3.x (Rails 4) script
3
This tutorial is based on Redmine 4.x and 3.x.
4
You can view a previous version of this tutorial.
5
"Redmine 1.x":/projects/redmine/wiki/Plugin_Tutorial?version=66
6
"Redmine 2.x":/projects/redmine/wiki/Plugin_Tutorial?version=104
7 7

  
8
This wiki uses @ruby script/rails@ on Redmine 2.x (Rails 3).
9
You need to use @ruby bin/rails@ or @rails@ on Redmine 3.x (Rails 4).
8
It assumes that you're familiar with the Ruby on Rails framework.
10 9

  
11 10
{{>toc}}
12 11

  
......
27 26
Creating a new plugin can be done using the Redmine plugin generator.
28 27
Syntax for this generator is:
29 28

  
30
<pre>bundle exec ruby bin/rails generate redmine_plugin <plugin_name></pre>
29
<pre>bundle exec rails generate redmine_plugin <plugin_name></pre>
31 30

  
32 31
So open up a command prompt and "cd" to your redmine directory, then execute the following command:
33 32

  
34 33
<pre>
35
$ bundle exec ruby script/rails generate redmine_plugin Polls
34
$ bundle exec rails generate redmine_plugin Polls
36 35
      create  plugins/polls/app
37 36
      create  plugins/polls/app/controllers
38 37
      create  plugins/polls/app/helpers
......
45 44
      create  plugins/polls/assets/stylesheets
46 45
      create  plugins/polls/config/locales
47 46
      create  plugins/polls/test
47
      create  plugins/polls/test/fixtures
48
      create  plugins/polls/test/unit
49
      create  plugins/polls/test/functional
50
      create  plugins/polls/test/integration
51
      create  plugins/polls/test/system
48 52
      create  plugins/polls/README.rdoc
49 53
      create  plugins/polls/init.rb
50 54
      create  plugins/polls/config/routes.rb
......
57 61
<pre><code class="ruby">
58 62
Redmine::Plugin.register :polls do
59 63
  name 'Polls plugin'
60
  author 'John Smith'
61
  description 'A plugin for managing polls'
64
  author 'Author name'
65
  description 'This is a plugin for Redmine'
62 66
  version '0.0.1'
67
  url 'http://example.com/path/to/plugin'
68
  author_url 'http://example.com/about'
63 69
end
64 70
</code></pre>
65 71

  
......
75 81
For now plugin doesn't store anything. Let's create a simple Poll model for our plugin. Syntax is:
76 82

  
77 83
<pre>
78
   bundle exec ruby script/rails generate redmine_plugin_model <plugin_name> <model_name> [field[:type][:index] field[:type][:index] ...]
84
   bundle exec rails generate redmine_plugin_model <plugin_name> <model_name> [field[:type][:index] field[:type][:index] ...]
79 85
</pre>
80 86

  
81 87
So, go to the command prompt and run:
82 88

  
83 89
<pre>
84
$ bundle exec ruby script/rails generate redmine_plugin_model polls poll question:string yes:integer no:integer
90
$ bundle exec rails generate redmine_plugin_model polls poll question:string yes:integer no:integer
85 91
      create  plugins/polls/app/models/poll.rb
86 92
      create  plugins/polls/test/unit/poll_test.rb
87
      create  plugins/polls/db/migrate/001_create_polls.rb
93
      create  plugins/polls/db/migrate/xxxxxxxxxxxx_create_polls.rb
88 94
</pre>
89 95

  
90
This creates the Poll model and the corresponding migration file @001_create_polls.rb@ in @plugins/polls/db/migrate@:
96
This creates the Poll model and the corresponding migration file @xxxxxxxxxxxx_create_polls.rb@ in @plugins/polls/db/migrate@:
91 97

  
92 98
<pre><code class="ruby">
93
class CreatePolls < ActiveRecord::Migration
99
class CreatePolls < ActiveRecord::Migration[5.2]
94 100
  def change
95 101
    create_table :polls do |t|
96 102
      t.string :question
97
      t.integer :yes, :default => 0
98
      t.integer :no, :default => 0
103
      t.integer :yes, default: 0
104
      t.integer :no, default: 0
99 105
    end
100 106
  end
101 107
end
102 108
</code></pre>
103 109

  
110
NOTE: For Redmine 3.x @class CreatePolls < ActiveRecord::Migration[5.2]@ is @class CreatePolls < ActiveRecord::Migration@.
111

  
104 112
You can adjust your migration file (eg. default values...) then migrate the database using the following command:
105 113

  
106 114
<pre>
......
118 126
Let's add some Polls in the console so we have something to work with.  The console is where you can interactively work and examine the Redmine environment and is very informative to play around in.  But for now we just need create two Poll objects
119 127

  
120 128
<pre>
121
bundle exec ruby script/rails console
122
[rails 3] rails console
123
>> Poll.create(:question => "Can you see this poll")
124
>> Poll.create(:question => "And can you see this other poll")
129
bundle exec rails console
130
>> Poll.create(question: "Can you see this poll")
131
>> Poll.create(question: "And can you see this other poll")
125 132
>> exit
126 133
</pre>
127 134

  
......
140 147
For now, the plugin doesn't do anything. So let's create a controller for our plugin.
141 148
We can use the plugin controller generator for that. Syntax is:
142 149

  
143
<pre>bundle exec ruby script/rails generate redmine_plugin_controller <plugin_name> <controller_name> [<actions>]</pre>
150
<pre>bundle exec rails generate redmine_plugin_controller <plugin_name> <controller_name> [<actions>]</pre>
144 151

  
145 152
So go back to the command prompt and run:
146 153

  
147 154
<pre>
148
$ bundle exec ruby script/rails generate redmine_plugin_controller Polls polls index vote
155
$ bundle exec rails generate redmine_plugin_controller Polls polls index vote
149 156
      create  plugins/polls/app/controllers/polls_controller.rb
150 157
      create  plugins/polls/app/helpers/polls_helper.rb
151 158
      create  plugins/polls/test/functional/polls_controller_test.rb
......
169 176
    if poll.save
170 177
      flash[:notice] = 'Vote saved.'
171 178
    end
172
    redirect_to :action => 'index'
179
    redirect_to polls_path(project_id: params[:project_id])
173 180
  end
174 181
end
175 182
</code></pre>
......
181 188

  
182 189
<% @polls.each do |poll| %>
183 190
  <p>
184
  <%= poll.question %>?
185
  <%= link_to 'Yes', { :action => 'vote', :id => poll[:id], :answer => 'yes' }, :method => :post %> (<%= poll.yes %>) /
186
  <%= link_to 'No', { :action => 'vote', :id => poll[:id], :answer => 'no' }, :method => :post %> (<%= poll.no %>)
191
    <%= poll.question %>?
192
    <%= link_to 'Yes', { action: 'vote', id: poll[:id], answer: 'yes', project_id: @project }, method: :post %> <%= poll.yes %> /
193
    <%= link_to 'No', { action: 'vote', id: poll[:id], answer: 'no', project_id: @project }, method: :post %> <%= poll.no %>
187 194
  </p>
188 195
<% end %>
189 196
</code></pre>
......
195 202
Redmine does not provide the default wildcard route (@':controller/:action/:id'@). Plugins have to declare the routes they need in their proper @config/routes.rb@ file. So edit @plugins/polls/config/routes.rb@ to add the 2 routes for the 2 actions:
196 203

  
197 204
<pre><code class="ruby">
198
get 'polls', :to => 'polls#index'
199
post 'post/:id/vote', :to => 'polls#vote'
205
get 'polls', to: 'polls#index'
206
post 'post/:id/vote', to: 'polls#vote'
200 207
</code></pre>
201 208

  
202 209
You can find more information about Rails routes here: http://guides.rubyonrails.org/routing.html.
......
223 230
Redmine::Plugin.register :redmine_polls do
224 231
  [...]
225 232
  
226
  menu :application_menu, :polls, { :controller => 'polls', :action => 'index' }, :caption => 'Polls'
233
  menu :application_menu, :polls, { controller: 'polls', action: 'index' }, caption: 'Polls'
227 234
end
228 235
</code></pre>
229 236

  
......
249 256
  * a String
250 257
  * a Proc that can take the project as argument
251 258

  
252
* @:before@, @:after@ - specify where the menu item should be inserted (eg. @:after => :activity@)
253
* @:first@, @:last@ - if set to true, the item will stay at the beginning/end of the menu (eg. @:last => true@)
259
* @:before@, @:after@ - specify where the menu item should be inserted (eg. @after: :activity@)
260
* @:first@, @:last@ - if set to true, the item will stay at the beginning/end of the menu (eg. @last: true@)
254 261
* @:html@ - a hash of html options that are passed to @link_to@ when rendering the menu item
255 262

  
256 263
In our example, we've added an item to the application menu which is empty by default.
257
Restart the application and go to http://localhost:3000:
264
Restart the application and go to http://localhost:3000/projects:
258 265

  
259 266
p=. !application_menu.png!
260 267

  
261
Now you can access the polls by clicking the Polls tab from the welcome screen.
268
Now you can access the polls by clicking the Polls tab that appears when  the user is not inside a project.
262 269

  
263 270
h3. Extending the project menu
264 271

  
......
269 276
Redmine::Plugin.register :redmine_polls do
270 277
  [...]
271 278

  
272
  permission :polls, { :polls => [:index, :vote] }, :public => true
273
  menu :project_menu, :polls, { :controller => 'polls', :action => 'index' }, :caption => 'Polls', :after => :activity, :param => :project_id
279
  permission :polls, { polls: [:index, :vote] }, public: true
280
  menu :project_menu, :polls, { controller: 'polls', action: 'index' }, caption: 'Polls', after: :activity, param: :project_id
274 281
end
275 282
</code></pre>
276 283

  
......
286 293
<pre><code class="ruby">
287 294
def index
288 295
  @project = Project.find(params[:project_id])
289
  @polls = Poll.find(:all) # @project.polls
296
  @polls = Poll.all # @project.polls
290 297
end
291 298
</code></pre>
292 299

  
293
The project id is available in the @:project_id@ param because of the @:param => :project_id@ option in the menu item declaration above.
300
The project id is available in the @:project_id@ param because of the @param: :project_id@ option in the menu item declaration above.
294 301

  
295 302
Now, you should see the project menu when viewing the polls:
296 303

  
......
315 322
h2. Adding new permissions
316 323

  
317 324
For now, anyone can vote for polls. Let's make it more configurable by changing the permission declaration.
318
We're going to declare 2 project based permissions, one for viewing the polls and an other one for voting. These permissions are no longer public (@:public => true@ option is removed).
325
We're going to declare 2 project based permissions, one for viewing the polls and an other one for voting. These permissions are no longer public (@public: true@ option is removed).
319 326

  
320 327
Edit @plugins/polls/init.rb@ to replace the previous permission declaration with these 2 lines:
321 328

  
322 329
<pre><code class="ruby">
323
  permission :view_polls, :polls => :index
324
  permission :vote_polls, :polls => :vote
330
  permission :view_polls, polls: :index
331
  permission :vote_polls, polls: :vote
325 332
</code></pre>
326 333

  
327 334
Restart the application and go to http://localhost:3000/roles/permissions:
......
336 343

  
337 344
<pre><code class="ruby">
338 345
class PollsController < ApplicationController
339
  before_filter :find_project, :authorize, :only => :index
346
  before_action :find_project, :authorize, only: :index
340 347

  
341 348
  [...]
342 349
  
343 350
  def index
344
    @polls = Poll.find(:all) # @project.polls
351
    @polls = Poll.all # @project.polls
345 352
  end
346 353

  
347 354
  [...]
(1-1/3)