This tutorial walks you through building a simple Redmine plugin called Polls that adds a basic polling feature to projects. By the end you will have a working plugin with a controller, views, routes, a menu item, and a database migration.
All commands run from your Redmine root directory. Your Redmine installation must be working before you begin.
Prerequisites
Redmine 5.0 or higher installed and running
Bundler and Ruby available on your $PATH
Basic familiarity with Ruby on Rails
Step 1 — Generate the plugin scaffold
Run the plugin generator
Redmine includes a Rails generator that creates the full directory structure and required starter files. bundle exec rails generate redmine_plugin polls
This creates the following layout under plugins/polls/: plugins/polls/
├── init.rb
├── README.rdoc
├── app/
│ ├── controllers/
│ ├── helpers/
│ ├── models/
│ └── views/
├── assets/
│ ├── images/
│ ├── javascripts/
│ └── stylesheets/
├── config/
│ ├── locales/
│ │ └── en.yml
│ └── routes.rb
├── db/
│ └── migrate/
└── test/
Review the generated init.rb
The generator creates a minimal init.rb with placeholder values: # plugins/polls/init.rb
Redmine :: Plugin . register :polls do
name 'Polls plugin'
author 'Author name'
description 'This is a plugin for Redmine'
version '0.0.1'
url 'http://example.com/path/to/plugin'
author_url 'http://example.com/about'
end
Update the metadata with your own values.
Step 2 — Create a database migration
Generate the migration
bundle exec rails generate redmine_plugin_migration polls create_polls
Open the generated migration file in plugins/polls/db/migrate/ and define the table: class CreatePolls < ActiveRecord::Migration [ 7.0 ]
def change
create_table :polls do | t |
t. string :question , null: false
t. integer :yes , default: 0
t. integer :no , default: 0
end
end
end
Run the migration
bundle exec rake redmine:plugins:migrate
To migrate only your plugin: bundle exec rake redmine:plugins:migrate NAME=polls
Step 3 — Create the model
Create plugins/polls/app/models/poll.rb:
class Poll < ActiveRecord::Base
validates :question , presence: true
def vote ( answer )
increment answer == 'yes' ? :yes : :no
save
end
end
Step 4 — Generate a controller
Run the controller generator
bundle exec rails generate redmine_plugin_controller polls polls index vote
This creates:
plugins/polls/app/controllers/polls_controller.rb
plugins/polls/app/helpers/polls_helper.rb
plugins/polls/app/views/polls/index.html.erb
plugins/polls/app/views/polls/vote.html.erb
plugins/polls/test/functional/polls_controller_test.rb
Implement the controller
Edit plugins/polls/app/controllers/polls_controller.rb: class PollsController < ApplicationController
def index
@polls = Poll . all
end
def vote
@poll = Poll . find (params[ :id ])
@poll . vote (params[ :answer ])
redirect_to polls_path
end
end
Step 5 — Create views
Edit plugins/polls/app/views/polls/index.html.erb:
< h2 > Polls </ h2 >
<% @polls . each do | poll | %>
< p >
<%= poll. question %> < br />
<%= link_to "Yes ( #{ poll. yes } )" , vote_poll_path (poll, answer: 'yes' ), method: :post %>
<%= link_to "No ( #{ poll. no } )" , vote_poll_path (poll, answer: 'no' ), method: :post %>
</ p >
<% end %>
Step 6 — Add routes
Edit plugins/polls/config/routes.rb:
# Plugin's routes
# See: http://guides.rubyonrails.org/routing.html
resources :polls , only: [ :index ] do
member do
post :vote
end
end
Update plugins/polls/init.rb to add an application-level menu entry:
Redmine :: Plugin . register :polls do
name 'Polls plugin'
author 'Jane Smith'
description 'Adds polls to Redmine'
version '0.0.1'
requires_redmine version_or_higher: '5.0.0'
menu :application_menu , :polls ,
{ controller: 'polls' , action: 'index' },
caption: 'Polls'
end
To add the item to the project sidebar instead, use :project_menu. Redmine automatically appends the project id parameter to project menu URLs:
menu :project_menu , :polls ,
{ controller: 'polls' , action: 'index' },
caption: 'Polls' ,
after: :activity
Step 8 — Add permissions
Wrap your menu registration with a project_module block to let project administrators enable or disable the feature per project:
Redmine :: Plugin . register :polls do
# ... metadata ...
project_module :polls do
permission :view_polls , { polls: [ :index ] }, public: true
permission :vote_polls , { polls: [ :vote ] }, require: :loggedin
end
menu :project_menu , :polls ,
{ controller: 'polls' , action: 'index' },
caption: 'Polls'
end
Then protect your controller actions:
class PollsController < ApplicationController
before_action :find_project
before_action { authorize }
def index
@polls = Poll . all
end
def vote
@poll = Poll . find (params[ :id ])
@poll . vote (params[ :answer ])
redirect_to polls_path
end
private
def find_project
@project = Project . find (params[ :project_id ])
rescue ActiveRecord :: RecordNotFound
render_404
end
end
Step 9 — Restart and verify
Navigate to Administration > Plugins to confirm your plugin appears in the list. Then visit the Polls menu item.
During development, restart the server after any change to init.rb. View and controller changes are picked up automatically in development mode.
Next steps
Hook system Respond to Redmine events and inject content into existing views.
Plugin settings Add a configuration page so administrators can tune your plugin.