Skip to content

Commit

Permalink
Improve compatibility with Redmine 5
Browse files Browse the repository at this point in the history
  • Loading branch information
nanego committed Jun 30, 2023
1 parent 596cd62 commit 61fdf95
Show file tree
Hide file tree
Showing 8 changed files with 166 additions and 134 deletions.
9 changes: 1 addition & 8 deletions init.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,4 @@
end

# Custom patches
require_dependency 'redmine_datetime_custom_field/hooks'
Rails.application.config.to_prepare do
require_dependency 'redmine_datetime_custom_field/application_helper_patch'
require_dependency 'redmine_datetime_custom_field/field_format_patch'
require_dependency 'redmine_datetime_custom_field/custom_fields_helper_patch'
require_dependency 'redmine_datetime_custom_field/query_patch'
require_dependency 'redmine_datetime_custom_field/custom_field_patch'
end
require_relative 'lib/redmine_datetime_custom_field/hooks'
6 changes: 3 additions & 3 deletions lib/redmine_datetime_custom_field/application_helper_patch.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
Date::DATE_FORMATS[:default] = '%d/%m/%Y'
end

module PluginDatetimeCustomField
module ApplicationHelper
module RedmineDatetimeCustomField
module ApplicationHelperPatch
def format_object(object, html=true, &block)
if (object.class.name=='CustomValue' || object.class.name== 'CustomFieldValue') && object.custom_field
return "" unless object.customized&.visible?
Expand All @@ -25,7 +25,7 @@ def format_object(object, html=true, &block)
end
end
end
ApplicationHelper.prepend PluginDatetimeCustomField::ApplicationHelper
ApplicationHelper.prepend RedmineDatetimeCustomField::ApplicationHelperPatch
ActionView::Base.prepend ApplicationHelper

module ApplicationHelper
Expand Down
19 changes: 17 additions & 2 deletions lib/redmine_datetime_custom_field/custom_field_patch.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,20 @@
require_dependency 'custom_field'

class CustomField < ActiveRecord::Base
safe_attributes 'show_hours', 'show_shortcut'
module RedmineDatetimeCustomField
module CustomFieldPatch
def self.included(base)
base.send(:include, InstanceMethods)
base.class_eval do
unloadable
safe_attributes 'show_hours', 'show_shortcut'
end
end

module InstanceMethods
end
end
end

unless CustomField.included_modules.include?(RedmineDatetimeCustomField::CustomFieldPatch)
CustomField.send(:include, RedmineDatetimeCustomField::CustomFieldPatch)
end
25 changes: 15 additions & 10 deletions lib/redmine_datetime_custom_field/custom_fields_helper_patch.rb
Original file line number Diff line number Diff line change
@@ -1,15 +1,20 @@
require_dependency 'custom_fields_helper'

module CustomFieldsHelper

# Return a string used to display a custom value
def format_value(value, custom_field)
formatted_value = custom_field.format.formatted_value(self, custom_field, value, false)
if formatted_value.class.name == 'Time'
format_time_without_zone(formatted_value, true)
else
format_object(formatted_value, false)
module RedmineDatetimeCustomField
module CustomFieldsHelperPatch

# Return a string used to display a custom value
def format_value(value, custom_field)
formatted_value = custom_field.format.formatted_value(self, custom_field, value, false)
if formatted_value.class.name == 'Time'
format_time_without_zone(formatted_value, true)
else
format_object(formatted_value, false)
end
end
end

end
end

ApplicationHelper.prepend RedmineDatetimeCustomField::CustomFieldsHelperPatch
ActionView::Base.prepend ApplicationHelper
75 changes: 41 additions & 34 deletions lib/redmine_datetime_custom_field/field_format_patch.rb
Original file line number Diff line number Diff line change
@@ -1,49 +1,55 @@
require 'redmine/field_format'

module Redmine
module FieldFormat
class DateFormat < Unbounded
module RedmineDatetimeCustomField
module FieldFormatPatch

field_attributes :show_hours, :show_shortcut
def cast_single_value(custom_field, value, customized = nil)
(custom_field.show_hours == '1' ? value.to_time : value.to_date) rescue nil
end

def cast_single_value(custom_field, value, customized = nil)
(custom_field.show_hours == '1' ? value.to_time : value.to_date) rescue nil
def validate_single_value(custom_field, value, customized = nil)
if (((value =~ /^\d{4}-\d{2}-\d{2}$/ || value =~ /^\d{2}\/\d{2}\/\d{4}$/) && custom_field.show_hours != '1') || ((value =~ /^\d{4}-\d{2}-\d{2} \d{2}:\d{2}$/ || value =~ /^\d{2}\/\d{2}\/\d{4} \d{2}:\d{2}$/) && custom_field.show_hours == '1')) && (value.to_date rescue false)
[]
else
[::I18n.t('activerecord.errors.messages.not_a_date')]
end
end

def validate_single_value(custom_field, value, customized = nil)
if (((value =~ /^\d{4}-\d{2}-\d{2}$/ || value =~ /^\d{2}\/\d{2}\/\d{4}$/) && custom_field.show_hours != '1') || ((value =~ /^\d{4}-\d{2}-\d{2} \d{2}:\d{2}$/ || value =~ /^\d{2}\/\d{2}\/\d{4} \d{2}:\d{2}$/) && custom_field.show_hours == '1')) && (value.to_date rescue false)
[]
def edit_tag(view, tag_id, tag_name, custom_value, options = {})
edit_tag = view.text_field_tag(tag_name, custom_value.value, options.merge(:id => tag_id, :size => 15)) +
view.calendar_for(tag_id, custom_value.custom_field.show_hours == '1')
edit_tag += view.link_to(::I18n.t(:now_label), '#',
onclick: "nowShortcut(this.dataset.cfId, " + custom_value.custom_field.show_hours + "); return false",
data: { cf_id: custom_value.custom_field.id },
class: "now_shortcut") if custom_value.custom_field.show_shortcut == '1'
edit_tag
end

def bulk_edit_tag(view, tag_id, tag_name, custom_field, objects, value, options = {})
view.text_field_tag(tag_name, value, options.merge(:id => tag_id, :size => 15)) +
view.calendar_for(tag_id, custom_field.show_hours == '1') +
bulk_clear_tag(view, tag_id, tag_name, custom_field, value)
end

if !Rails.env.test?
def order_statement(custom_field)
if custom_field.class.connection.adapter_name.downcase.to_sym == :mysql2
Arel.sql "STR_TO_DATE(#{join_alias custom_field}.value, '%d/%m/%Y %H:%i')"
else
[::I18n.t('activerecord.errors.messages.not_a_date')]
Arel.sql "to_timestamp(#{join_alias custom_field}.value, 'DD/MM/YYYY HH24:MI')"
end
end
end

def edit_tag(view, tag_id, tag_name, custom_value, options = {})
edit_tag = view.text_field_tag(tag_name, custom_value.value, options.merge(:id => tag_id, :size => 15)) +
view.calendar_for(tag_id, custom_value.custom_field.show_hours == '1')
edit_tag += view.link_to(::I18n.t(:now_label), '#',
onclick: "nowShortcut(this.dataset.cfId, " + custom_value.custom_field.show_hours + "); return false",
data: { cf_id: custom_value.custom_field.id },
class: "now_shortcut") if custom_value.custom_field.show_shortcut == '1'
edit_tag
end

def bulk_edit_tag(view, tag_id, tag_name, custom_field, objects, value, options = {})
view.text_field_tag(tag_name, value, options.merge(:id => tag_id, :size => 15)) +
view.calendar_for(tag_id, custom_field.show_hours == '1') +
bulk_clear_tag(view, tag_id, tag_name, custom_field, value)
end
end
end

if !Rails.env.test?
def order_statement(custom_field)
if custom_field.class.connection.adapter_name.downcase.to_sym == :mysql2
Arel.sql "STR_TO_DATE(#{join_alias custom_field}.value, '%d/%m/%Y %H:%i')"
else
Arel.sql "to_timestamp(#{join_alias custom_field}.value, 'DD/MM/YYYY HH24:MI')"
end
end
end
Redmine::FieldFormat::DateFormat.send(:include, RedmineDatetimeCustomField::FieldFormatPatch)

module Redmine
module FieldFormat
class DateFormat < Unbounded
field_attributes :show_hours, :show_shortcut
end
end
end
Expand All @@ -59,3 +65,4 @@ def validate_each(record, attribute, value)
end
end
end

10 changes: 10 additions & 0 deletions lib/redmine_datetime_custom_field/hooks.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,15 @@ def view_layouts_base_html_head(context)
javascript_include_tag("datetime_custom_field", :plugin => "redmine_datetime_custom_field")
end

class ModelHook < Redmine::Hook::Listener
def after_plugins_loaded(_context = {})
require_relative 'application_helper_patch'
require_relative 'field_format_patch'
require_relative 'custom_fields_helper_patch'
require_relative 'query_patch'
require_relative 'custom_field_patch'
end
end

end
end
154 changes: 78 additions & 76 deletions lib/redmine_datetime_custom_field/query_patch.rb
Original file line number Diff line number Diff line change
@@ -1,101 +1,103 @@
require_dependency 'query'
require_dependency 'issue_query'

class Query

def validate_query_filters
filters.each_key do |field|
if values_for(field)
case type_for(field)
when :integer
add_filter_error(field, :invalid) if values_for(field).detect {|v| v.present? && !/\A[+-]?\d+(,[+-]?\d+)*\z/.match?(v) }
when :float
add_filter_error(field, :invalid) if values_for(field).detect {|v| v.present? && !/\A[+-]?\d+(\.\d*)?\z/.match?(v) }
when :date, :date_past
case operator_for(field)
when "=", ">=", "<=", "><"
add_filter_error(field, :invalid) if values_for(field).detect {|v|
### CUSTOM BEGIN
# add new valid date format
v.present? && ((!/\A\d{4}-\d{2}-\d{2}(T\d{2}((:)?\d{2}){0,2}(Z|\d{2}:?\d{2})?)?\z/.match?(v) && !v.match(/\A\d{2}\/\d{2}\/\d{4}(T\d{2}((:)?\d{2}){0,2}(Z|\d{2}:?\d{2})?)?\z/)) || parse_date(v).nil?)
### CUSTOM END
}
when ">t-", "<t-", "t-", ">t+", "<t+", "t+", "><t+", "><t-"
add_filter_error(field, :invalid) if values_for(field).detect {|v| v.present? && !/^\d+$/.match?(v) }
module RedmineDatetimeCustomField
module QueryPatch
def validate_query_filters
filters.each_key do |field|
if values_for(field)
case type_for(field)
when :integer
add_filter_error(field, :invalid) if values_for(field).detect {|v| v.present? && !/\A[+-]?\d+(,[+-]?\d+)*\z/.match?(v) }
when :float
add_filter_error(field, :invalid) if values_for(field).detect {|v| v.present? && !/\A[+-]?\d+(\.\d*)?\z/.match?(v) }
when :date, :date_past
case operator_for(field)
when "=", ">=", "<=", "><"
add_filter_error(field, :invalid) if values_for(field).detect {|v|
### CUSTOM BEGIN
# add new valid date format
v.present? && ((!/\A\d{4}-\d{2}-\d{2}(T\d{2}((:)?\d{2}){0,2}(Z|\d{2}:?\d{2})?)?\z/.match?(v) && !v.match(/\A\d{2}\/\d{2}\/\d{4}(T\d{2}((:)?\d{2}){0,2}(Z|\d{2}:?\d{2})?)?\z/)) || parse_date(v).nil?)
### CUSTOM END
}
when ">t-", "<t-", "t-", ">t+", "<t+", "t+", "><t+", "><t-"
add_filter_error(field, :invalid) if values_for(field).detect {|v| v.present? && !/^\d+$/.match?(v) }
end
end
end
end

add_filter_error(field, :blank) unless
add_filter_error(field, :blank) unless
# filter requires one or more values
(values_for(field) and !values_for(field).first.blank?) or
# filter doesn't require any value
["o", "c", "!*", "*", "nd", "t", "ld", "nw", "w", "lw", "l2w", "nm", "m", "lm", "y", "*o", "!o"].include? operator_for(field)
end if filters
end
# filter doesn't require any value
["o", "c", "!*", "*", "nd", "t", "ld", "nw", "w", "lw", "l2w", "nm", "m", "lm", "y", "*o", "!o"].include? operator_for(field)
end if filters
end

def quoted_time(time, is_custom_filter)
if is_custom_filter
# Custom field values are stored as strings in the DB
# using this format that does not depend on DB date representation
if Rails.env.test?
time.strftime("%Y-%m-%d %H:%M:%S")
def quoted_time(time, is_custom_filter)
if is_custom_filter
# Custom field values are stored as strings in the DB
# using this format that does not depend on DB date representation
if Rails.env.test?
time.strftime("%Y-%m-%d %H:%M:%S")
else
time.strftime("%d/%m/%Y %H:%M") #Custom format
end
else
time.strftime("%d/%m/%Y %H:%M") #Custom format
self.class.connection.quoted_date(time)
end
else
self.class.connection.quoted_date(time)
end
end

# Returns a SQL clause for a date or datetime field.
def date_clause(table, field, from, to, is_custom_filter)
s = []
if from
if from.is_a?(Date)
from = date_for_user_time_zone(from.year, from.month, from.day).yesterday.end_of_day
else
from = from - 1 # second
end
if self.class.default_timezone == :utc
from = from.utc
end
# Returns a SQL clause for a date or datetime field.
def date_clause(table, field, from, to, is_custom_filter)
s = []
if from
if from.is_a?(Date)
from = date_for_user_time_zone(from.year, from.month, from.day).yesterday.end_of_day
else
from = from - 1 # second
end
if self.class.default_timezone == :utc
from = from.utc
end

### Patch Start
if is_custom_filter && self.class.connection.adapter_name.downcase.to_sym == :postgresql && !Rails.env.test?
s << ("to_timestamp(#{table}.#{field},'DD/MM/YYYY HH24:MI') > to_timestamp('%s','DD/MM/YYYY HH24:MI')" % [quoted_time(from, is_custom_filter)])
else
if Rails.env.test? || table.classify.constantize.columns_hash[field].type == :date || table.classify.constantize.columns_hash[field].type == :datetime
s << ("#{table}.#{field} > '%s'" % [quoted_time(from, is_custom_filter)])
### Patch Start
if is_custom_filter && self.class.connection.adapter_name.downcase.to_sym == :postgresql && !Rails.env.test?
s << ("to_timestamp(#{table}.#{field},'DD/MM/YYYY HH24:MI') > to_timestamp('%s','DD/MM/YYYY HH24:MI')" % [quoted_time(from, is_custom_filter)])
else
s << ("STR_TO_DATE(#{table}.#{field},'%d/%m/%Y') > STR_TO_DATE('" + quoted_time(from, is_custom_filter) + "','%d/%m/%Y')")
if Rails.env.test? || table.classify.constantize.columns_hash[field].type == :date || table.classify.constantize.columns_hash[field].type == :datetime
s << ("#{table}.#{field} > '%s'" % [quoted_time(from, is_custom_filter)])
else
s << ("STR_TO_DATE(#{table}.#{field},'%d/%m/%Y') > STR_TO_DATE('" + quoted_time(from, is_custom_filter) + "','%d/%m/%Y')")
end
end
end
### Patch End
### Patch End

end
if to
if to.is_a?(Date)
to = date_for_user_time_zone(to.year, to.month, to.day).end_of_day
end
if self.class.default_timezone == :utc
to = to.utc
end
if to
if to.is_a?(Date)
to = date_for_user_time_zone(to.year, to.month, to.day).end_of_day
end
if self.class.default_timezone == :utc
to = to.utc
end

### Patch Start
if is_custom_filter && self.class.connection.adapter_name.downcase.to_sym == :postgresql && !Rails.env.test?
s << ("to_timestamp(#{table}.#{field},'DD/MM/YYYY HH24:MI') <= to_timestamp('%s','DD/MM/YYYY HH24:MI')" % [quoted_time(to, is_custom_filter)])
else
if Rails.env.test? || table.classify.constantize.columns_hash[field].type == :date || table.classify.constantize.columns_hash[field].type == :datetime
s << ("#{table}.#{field} <= '%s'" % [quoted_time(to, is_custom_filter)])
### Patch Start
if is_custom_filter && self.class.connection.adapter_name.downcase.to_sym == :postgresql && !Rails.env.test?
s << ("to_timestamp(#{table}.#{field},'DD/MM/YYYY HH24:MI') <= to_timestamp('%s','DD/MM/YYYY HH24:MI')" % [quoted_time(to, is_custom_filter)])
else
s << ("STR_TO_DATE(#{table}.#{field},'%d/%m/%Y') <= STR_TO_DATE('" + quoted_time(to, is_custom_filter) + "','%d/%m/%Y')")
if Rails.env.test? || table.classify.constantize.columns_hash[field].type == :date || table.classify.constantize.columns_hash[field].type == :datetime
s << ("#{table}.#{field} <= '%s'" % [quoted_time(to, is_custom_filter)])
else
s << ("STR_TO_DATE(#{table}.#{field},'%d/%m/%Y') <= STR_TO_DATE('" + quoted_time(to, is_custom_filter) + "','%d/%m/%Y')")
end
end
end
### Patch End
### Patch End

end
s.join(' AND ')
end
s.join(' AND ')
end

end

Query.send(:include, RedmineDatetimeCustomField::QueryPatch)
2 changes: 1 addition & 1 deletion spec/helpers/application_helper_patch_spec.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
require File.dirname(__FILE__) + '/../spec_helper'
require 'redmine_datetime_custom_field/application_helper_patch'
require_relative '../../lib/redmine_datetime_custom_field/application_helper_patch'

describe ApplicationHelper, type: :helper do

Expand Down

0 comments on commit 61fdf95

Please sign in to comment.