From 43d4198257c5e4a1ea7b0a11ff21383b2e993304 Mon Sep 17 00:00:00 2001 From: Vincent Robert Date: Thu, 6 Jul 2023 16:10:57 +0200 Subject: [PATCH] Improve compatibility with Redmine 5 --- .github/workflows/{4_1_7.yml => 5_0_5.yml} | 6 +- README.md | 12 +- init.rb | 9 +- .../application_helper_patch.rb | 6 +- .../custom_field_patch.rb | 19 ++- .../custom_fields_helper_patch.rb | 25 +-- .../field_format_patch.rb | 75 +++++---- lib/redmine_datetime_custom_field/hooks.rb | 10 ++ .../query_patch.rb | 154 +++++++++--------- spec/helpers/application_helper_patch_spec.rb | 2 +- 10 files changed, 175 insertions(+), 143 deletions(-) rename .github/workflows/{4_1_7.yml => 5_0_5.yml} (98%) diff --git a/.github/workflows/4_1_7.yml b/.github/workflows/5_0_5.yml similarity index 98% rename from .github/workflows/4_1_7.yml rename to .github/workflows/5_0_5.yml index 4ff512f..81da87c 100644 --- a/.github/workflows/4_1_7.yml +++ b/.github/workflows/5_0_5.yml @@ -1,8 +1,8 @@ -name: Tests 4.1.7 +name: Tests 5.0.5 env: PLUGIN_NAME: redmine_datetime_custom_field - REDMINE_VERSION: 4.1.7 + REDMINE_VERSION: 5.0.5 RAILS_ENV: test on: @@ -16,7 +16,7 @@ jobs: strategy: matrix: - ruby: ['2.6'] + ruby: ['2.7'] db: ['postgres'] fail-fast: false diff --git a/README.md b/README.md index 442065a..1601f1e 100644 --- a/README.md +++ b/README.md @@ -10,13 +10,13 @@ Note that this plugin has a dependency. You have to install this other plugin: ## Test status -|Plugin branch| Redmine Version | Test Status | -|-------------|-------------------|------------------| -|master | 4.2.10 | [![4.2.10][1]][5]| -|master | 4.1.7 | [![4.1.7][2]][5] | -|master | master | [![master][4]][5]| +|Plugin branch| Redmine Version | Test Status | +|-------------|-----------------|-------------------| +|master | 4.2.10 | [![4.2.10][1]][5] | +|master | 5.0.5 | [![5.0.5][2]][5] | +|master | master | [![master][4]][5] | [1]: https://github.com/nanego/redmine_datetime_custom_field/actions/workflows/4_2_10.yml/badge.svg -[2]: https://github.com/nanego/redmine_datetime_custom_field/actions/workflows/4_1_7.yml/badge.svg +[2]: https://github.com/nanego/redmine_datetime_custom_field/actions/workflows/5_0_5.yml/badge.svg [4]: https://github.com/nanego/redmine_datetime_custom_field/actions/workflows/master.yml/badge.svg [5]: https://github.com/nanego/redmine_datetime_custom_field/actions diff --git a/init.rb b/init.rb index 0253ab7..f3f8835 100644 --- a/init.rb +++ b/init.rb @@ -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' diff --git a/lib/redmine_datetime_custom_field/application_helper_patch.rb b/lib/redmine_datetime_custom_field/application_helper_patch.rb index 49ffe57..8d9529e 100644 --- a/lib/redmine_datetime_custom_field/application_helper_patch.rb +++ b/lib/redmine_datetime_custom_field/application_helper_patch.rb @@ -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? @@ -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 diff --git a/lib/redmine_datetime_custom_field/custom_field_patch.rb b/lib/redmine_datetime_custom_field/custom_field_patch.rb index 7c11e63..f505f06 100644 --- a/lib/redmine_datetime_custom_field/custom_field_patch.rb +++ b/lib/redmine_datetime_custom_field/custom_field_patch.rb @@ -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 diff --git a/lib/redmine_datetime_custom_field/custom_fields_helper_patch.rb b/lib/redmine_datetime_custom_field/custom_fields_helper_patch.rb index 2cb3865..cd6a21c 100644 --- a/lib/redmine_datetime_custom_field/custom_fields_helper_patch.rb +++ b/lib/redmine_datetime_custom_field/custom_fields_helper_patch.rb @@ -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 diff --git a/lib/redmine_datetime_custom_field/field_format_patch.rb b/lib/redmine_datetime_custom_field/field_format_patch.rb index cdab322..a734b6e 100644 --- a/lib/redmine_datetime_custom_field/field_format_patch.rb +++ b/lib/redmine_datetime_custom_field/field_format_patch.rb @@ -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 +module Redmine + module FieldFormat + class DateFormat < Unbounded + prepend RedmineDatetimeCustomField::FieldFormatPatch + field_attributes :show_hours, :show_shortcut end end end @@ -59,3 +65,4 @@ def validate_each(record, attribute, value) end end end + diff --git a/lib/redmine_datetime_custom_field/hooks.rb b/lib/redmine_datetime_custom_field/hooks.rb index de31ef6..d8b9c6a 100644 --- a/lib/redmine_datetime_custom_field/hooks.rb +++ b/lib/redmine_datetime_custom_field/hooks.rb @@ -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 diff --git a/lib/redmine_datetime_custom_field/query_patch.rb b/lib/redmine_datetime_custom_field/query_patch.rb index aef6c19..c8f3691 100644 --- a/lib/redmine_datetime_custom_field/query_patch.rb +++ b/lib/redmine_datetime_custom_field/query_patch.rb @@ -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+", "=", "<=", "><" + 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+", " 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) diff --git a/spec/helpers/application_helper_patch_spec.rb b/spec/helpers/application_helper_patch_spec.rb index 94d9838..e488f31 100644 --- a/spec/helpers/application_helper_patch_spec.rb +++ b/spec/helpers/application_helper_patch_spec.rb @@ -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