Redmine Plugin Development - Part2: Journal, Customization of Redmine Menu and Core
Posted by Sergey Enin 20 April 2011 at 23:36
Read previous part: "Redmine Plugin Development - Part1: Generation, Enumerations, Hooks"
In first part of the guide I reviewed Posibilities to add new functionality to Redmine, but what you if you would need/like to modify exists Redmine functionality.
As I wrote in previous part, I will not covert details described in at Redmine official plugin guide.
Menu
In redmine MenuManager is responsible to manage Menu, so you can direct access to it, for example for account_menu:
Redmine::MenuManager.map :account_menu do |menu|
menu.delete(:my_account)
menu.delete(:logout)
menu.push 'Dashboard', { :controller => 'my', :action => 'page' }, :if => Proc.new { User.current.logged? }
menu.push :logout, :signout_path, :if => Proc.new { User.current.logged? }
end
There are five menus that you can extend:
- :top_menu - the top left menu
- :account_menu - the top right menu with sign in/sign out links
- :application_menu - the main menu displayed when the user is not inside a project
- :project_menu - the main menu displayed when the user is inside a project
- :admin_menu - the menu displayed on the Administration page (can only insert after Settings, before Plugins)
You can also add point to menu with standart way:
Redmine::Plugin.register :redmine_polls do
[...]
menu :application_menu, :polls, { :controller => 'polls', :action => 'index' }, :caption => 'Polls'
end
There was 1 problem I faced with, when I wanted to implement my plugin menu view as project menu - there is not standart way to do it, so I implement it by myself.
1) You need to require proper files in init.rb of your plugin:
require 'redmine'
require 'menu_manager_patch' #this is your library
2) You need to define lib/menu_manager_patch.rb:
require 'action_view/helpers/capture_helper'
module Redmine
module MenuManager
module MenuHelper
include ActionView::Helpers::CaptureHelper #content_for Class
# Renders the application main menu
def render_main_menu_with_whistles(project)
result = ""
if ( @content_for_custom_menu )
result = instance_variable_get(:@content_for_custom_menu)
else
result = render_main_menu_without_whistles(project)
result = process_result_special_types(result, project)
end
result
end
alias_method_chain :render_main_menu, :whistles
def process_result_special_types(result, project)
result_value = result
return if ( (project.nil?) || (project.id.nil?) )
result_value
end
def display_main_menu_with_whistles?(project)
result = true
if ( @content_for_custom_menu )
result = true
else
result = display_main_menu_without_whistles?(project)
end
result
end
alias_method_chain :display_main_menu?, :whistles
end
end
end
Redmine core customization
Views
You can re define existed view redmine views simply create file with same path in your plugin folder, for example - ../vendor/plugins/my_plugin/app/views/projects/index.rhtml will be processed instead of standart projects/index.rhtml.
Redmine views lifecycle:
- Rails bootstraps and loads all it's framework
- Rails starts to load code in the plugins
- Rails finds a views directory in ../vendor/plugins/my_plugin/app/views and pre-pends it to the views path
- Rails loads all the other plugins
- Rails then loads the application from ../app
- Rails finishes loading and serves up requests
- Request comes in, and a view needs to be rendered
- Rails looks for a matching template and loads the plugin's template since it was pre-pended to the views path
- Rails renders the plugins'view
Redmine controllers(models) lifecycle:
- Rails bootstraps and loads all it's framework
- Rails starts to load code in the plugins
- Rails finds IssueController in MyPlugin and see it defines a show action
- Rails loads all the other plugins
- Rails then loads the application from ../app
- Rails finds IssueController again and see it also defines a show action
- Rails (or rather Ruby) overwrites the show action from the plugin with the one from ../app
- Rails finishes loading and serves up requests
The right way to add/modify redmine controller is
- wrap an existing method in your code with alias_method_chaing:
alias_method :foo_without_feature, :foo
alias_method :foo, :foo_with_feature
-
add new method defining to controller(model);
Adding new method to controller(mode)
Example from Budget plugin:
- In init.rb of your plugin:
require 'issue_patch'
unless Issue.included_modules.include? IssuePatch
Issue.send(:include, IssuePatch)
end
- in lib/issue_path.rb
module IssuePatch
def self.included(base) # :nodoc:
base.send(:include, InstanceMethods)
#base.extend(ClassMethods)
base.class_eval do
#the same as typing in Class itself
validates_presence_of :user_id, :activity_id, :project_id, :hours, :spent_on, :comments
alias_method_chain :doing_something, :my_features
end
end
module InstanceMethods
# Wraps the association to get the Deliverable subject. Needed for the
# Query and filtering
def deliverable_subject
unless self.deliverable.nil?
return self.deliverable.subject
end
end
def doing_something_with_my_feature
doing_something_without_my_feature if (sky==”blue”)
end
def update_journal(attrs)
journal = Journal.new
attrs.each do |attr|
journal.details.build(:property=>"attr", :prop_key=>attr[:prop_key].to_s, :old_value=>attr[:old_value], :value=>attr[:new_value])
end
journal.journalized_id = id
journal.journalized_type = "Issue"
journal.user_id = User.current.id
journal.notes = ""
journal.save
end
end
end
Using journal
In previous example was also code part to use redmine journal:
def update_journal(attrs)
journal = Journal.new
attrs.each do |attr|
journal.details.build(:property=>"attr", :prop_key=>attr[:prop_key].to_s, :old_value=>attr[:old_value], :value=>attr[:new_value])
end
journal.journalized_id = id
journal.journalized_type = "Issue"
journal.user_id = User.current.id
journal.notes = ""
journal.save
end
Thats all I wanted to review. You can ask questions in comments.
Thank you for attention.
Links:
- Redmine - http://www.redmine.org
- Redmine - Developers Guide http://www.redmine.org/projects/redmine/wiki/Developer_Guide
- Redmine - Plugin Tutorial http://www.redmine.org/projects/redmine/wiki/Plugin_Tutorial
- GitHub of Redmine creator with a lot of ready plugins http://github.com/edavis10

