Redmine 2.x support - Initial work
This commit is contained in:
+17
-17
@@ -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
@@ -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,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'
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user