Redmine 2.x support - Initial work

This commit is contained in:
Davis Zanetti Cabral
2012-10-23 02:26:09 -02:00
parent f42b419bf3
commit bc56e88e35
5 changed files with 31 additions and 41 deletions
+17 -17
View File
@@ -8,36 +8,36 @@ class Rate < ActiveRecord::Base
belongs_to :project belongs_to :project
belongs_to :user belongs_to :user
has_many :time_entries has_many :time_entries
validates_presence_of :user_id validates_presence_of :user_id
validates_presence_of :date_in_effect validates_presence_of :date_in_effect
validates_numericality_of :amount validates_numericality_of :amount
before_save :unlocked? before_save :unlocked?
after_save :update_time_entry_cost_cache after_save :update_time_entry_cost_cache
before_destroy :unlocked? before_destroy :unlocked?
after_destroy :update_time_entry_cost_cache after_destroy :update_time_entry_cost_cache
named_scope :history_for_user, lambda { |user, order| scope :history_for_user, lambda { |user, order|
{ {
:conditions => { :user_id => user.id }, :conditions => { :user_id => user.id },
:order => order, :order => order,
:include => :project :include => :project
} }
} }
def locked? def locked?
return self.time_entries.length > 0 return self.time_entries.length > 0
end end
def unlocked? def unlocked?
return !self.locked? return !self.locked?
end end
def default? def default?
return self.project.nil? return self.project.nil?
end end
def specific? def specific?
return !self.default? return !self.default?
end end
@@ -45,7 +45,7 @@ class Rate < ActiveRecord::Base
def update_time_entry_cost_cache def update_time_entry_cost_cache
TimeEntry.update_cost_cache(user, project) TimeEntry.update_cost_cache(user, project)
end end
# API to find the Rate for a +user+ on a +project+ at a +date+ # API to find the Rate for a +user+ on a +project+ at a +date+
def self.for(user, project = nil, date = Date.today.to_s) def self.for(user, project = nil, date = Date.today.to_s)
# Check input since it's a "public" API # Check input since it's a "public" API
@@ -56,13 +56,13 @@ class Rate < ActiveRecord::Base
end end
raise Rate::InvalidParameterException.new("project must be a Project instance") unless project.nil? || project.is_a?(Project) raise Rate::InvalidParameterException.new("project must be a Project instance") unless project.nil? || project.is_a?(Project)
Rate.check_date_string(date) Rate.check_date_string(date)
rate = self.for_user_project_and_date(user, project, date) rate = self.for_user_project_and_date(user, project, date)
# Check for a default (non-project) rate # Check for a default (non-project) rate
rate = self.default_for_user_and_date(user, date) if rate.nil? && project rate = self.default_for_user_and_date(user, date) if rate.nil? && project
rate rate
end end
# API to find the amount for a +user+ on a +project+ at a +date+ # API to find the amount for a +user+ on a +project+ at a +date+
def self.amount_for(user, project = nil, date = Date.today.to_s) def self.amount_for(user, project = nil, date = Date.today.to_s)
rate = self.for(user, project, date) rate = self.for(user, project, date)
@@ -96,7 +96,7 @@ class Rate < ActiveRecord::Base
end end
store_cache_timestamp('last_cache_clearing_run', Time.now.utc.to_s) store_cache_timestamp('last_cache_clearing_run', Time.now.utc.to_s)
end end
private private
def self.for_user_project_and_date(user, project, date) def self.for_user_project_and_date(user, project, date)
if project.nil? if project.nil?
@@ -107,7 +107,7 @@ class Rate < ActiveRecord::Base
user.id, user.id,
date date
]) ])
else else
return Rate.find(:first, return Rate.find(:first,
:order => 'date_in_effect DESC', :order => 'date_in_effect DESC',
@@ -117,9 +117,9 @@ class Rate < ActiveRecord::Base
project.id, project.id,
date date
]) ])
end end
end end
def self.default_for_user_and_date(user, date) def self.default_for_user_and_date(user, date)
self.for_user_project_and_date(user, nil, date) self.for_user_project_and_date(user, nil, date)
end end
@@ -128,7 +128,7 @@ class Rate < ActiveRecord::Base
# a Rate::InvalidParameterException otherwise # a Rate::InvalidParameterException otherwise
def self.check_date_string(date) def self.check_date_string(date)
raise Rate::InvalidParameterException.new("date must be a valid Date string (e.g. YYYY-MM-DD)") unless date.is_a?(String) raise Rate::InvalidParameterException.new("date must be a valid Date string (e.g. YYYY-MM-DD)") unless date.is_a?(String)
begin begin
Date.parse(date) Date.parse(date)
rescue ArgumentError rescue ArgumentError
@@ -144,7 +144,7 @@ class Rate < ActiveRecord::Base
# Wait 1 second after stealing a forced lock # Wait 1 second after stealing a forced lock
options = {:retries => 0, :suspend => 1} options = {:retries => 0, :suspend => 1}
options[:max_age] = 1 if force options[:max_age] = 1 if force
Lockfile(lock_file, options) do Lockfile(lock_file, options) do
block.call block.call
end end
+4 -4
View File
@@ -1,4 +1,4 @@
ActionController::Routing::Routes.draw do |map| resources :rates
map.resources :rates
map.connect 'rate_caches', :conditions => {:method => :put}, :controller => 'rate_caches', :action => 'update' match 'rate_caches', :to => 'rate_caches#index', :via => "get"
end match 'rate_caches', :to => 'rate_caches#update', :via => "put"
+1 -3
View File
@@ -1,9 +1,7 @@
require 'redmine' require 'redmine'
# Patches to the Redmine core # Patches to the Redmine core
require 'dispatcher' ActionDispatch::Callbacks.to_prepare do
Dispatcher.to_prepare :redmine_rate do
gem 'lockfile' gem 'lockfile'
require_dependency 'application_controller' require_dependency 'application_controller'
+8 -16
View File
@@ -1,10 +1,10 @@
# Hooks to attach to the Redmine Projects. # Hooks to attach to the Redmine Projects.
class RateProjectHook < Redmine::Hook::ViewListener class RateProjectHook < Redmine::Hook::ViewListener
def protect_against_forgery? def protect_against_forgery?
false false
end end
# Renders an additional table header to the membership setting # Renders an additional table header to the membership setting
# #
# Context: # Context:
@@ -14,7 +14,7 @@ class RateProjectHook < Redmine::Hook::ViewListener
return '' unless (User.current.allowed_to?(:view_rate, context[:project]) || User.current.admin?) return '' unless (User.current.allowed_to?(:view_rate, context[:project]) || User.current.admin?)
return "<th>#{l(:rate_label_rate)} #{l(:rate_label_currency)}</td>" return "<th>#{l(:rate_label_rate)} #{l(:rate_label_currency)}</td>"
end end
# Renders an AJAX from to update the member's billing rate # Renders an AJAX from to update the member's billing rate
# #
# Context: # Context:
@@ -37,7 +37,7 @@ class RateProjectHook < Redmine::Hook::ViewListener
end end
content = '' content = ''
if rate.nil? || rate.default? if rate.nil? || rate.default?
if rate && rate.default? if rate && rate.default?
content << "<em>#{number_to_currency(rate.amount)}</em> " content << "<em>#{number_to_currency(rate.amount)}</em> "
@@ -52,30 +52,22 @@ class RateProjectHook < Redmine::Hook::ViewListener
:protocol => Setting.protocol, :protocol => Setting.protocol,
:host => Setting.host_name :host => Setting.host_name
} }
# Build a form_remote_tag by hand since this isn't in the scope of a controller form = form_tag(url, :remote => true)
# and url_rewriter doesn't like that fact.
form = form_tag(url, :onsubmit => remote_function(:url => url,
:host => Setting.host_name,
:protocol => Setting.protocol,
:form => true,
:method => 'post',
:return => 'false' )+ '; return false;')
form << text_field(:rate, :amount) form << text_field(:rate, :amount)
form << hidden_field(:rate,:date_in_effect, :value => Date.today.to_s) form << hidden_field(:rate,:date_in_effect, :value => Date.today.to_s)
form << hidden_field(:rate, :project_id, :value => project.id) form << hidden_field(:rate, :project_id, :value => project.id)
form << hidden_field(:rate, :user_id, :value => member.user.id) form << hidden_field(:rate, :user_id, :value => member.user.id)
form << hidden_field_tag("back_url", url_for(:controller => 'projects', :action => 'settings', :id => project, :tab => 'members', :protocol => Setting.protocol, :host => Setting.host_name)) form << hidden_field_tag("back_url", url_for(:controller => 'projects', :action => 'settings', :id => project, :tab => 'members', :protocol => Setting.protocol, :host => Setting.host_name))
form << submit_tag(l(:rate_label_set_rate), :class => "small") form << submit_tag(l(:rate_label_set_rate), :class => "small")
form << "</form>" form << "</form>"
content << form content << form
end end
else else
if (User.current.admin?) if (User.current.admin?)
content << content_tag(:strong, link_to(number_to_currency(rate.amount), { content << content_tag(:strong, link_to(number_to_currency(rate.amount), {
:controller => 'users', :controller => 'users',
:action => 'edit', :action => 'edit',
:id => member.user, :id => member.user,
@@ -2,7 +2,7 @@ module RedmineRate
module Hooks module Hooks
class ViewLayoutsBaseHtmlHeadHook < Redmine::Hook::ViewListener class ViewLayoutsBaseHtmlHeadHook < Redmine::Hook::ViewListener
def view_layouts_base_html_head(context={}) def view_layouts_base_html_head(context={})
return content_tag(:style, "#admin-menu a.rate-caches { background-image: url('#{image_path('database_refresh.png', :plugin => 'redmine_rate')}'); }", :type => 'text/css') content_tag(:style, "#admin-menu a.rate-caches { background-image: url('#{image_path('database_refresh.png')}'); }", :type => 'text/css')
end end
end end
end end