From 320ebd889f4a9515f04ec6ece18b948cfb5193e9 Mon Sep 17 00:00:00 2001 From: MAEDA Go Date: Sat, 21 Mar 2026 18:39:20 +0900 Subject: [PATCH] Add current date option for date format custom field defaults --- app/models/custom_field.rb | 14 ++++- .../custom_fields/formats/_date.html.erb | 25 ++++++++- app/views/custom_fields/index.api.rsb | 3 +- lib/redmine/field_format.rb | 9 ++++ .../custom_fields_controller_test.rb | 16 ++++++ .../api_test/custom_fields_test.rb | 1 + test/integration/api_test/issues_test.rb | 51 +++++++++++++++++++ test/unit/custom_field_test.rb | 22 ++++++++ test/unit/custom_value_test.rb | 9 ++++ 9 files changed, 145 insertions(+), 5 deletions(-) diff --git a/app/models/custom_field.rb b/app/models/custom_field.rb index 0eed2bf31..bebf9a79c 100644 --- a/app/models/custom_field.rb +++ b/app/models/custom_field.rb @@ -89,6 +89,7 @@ class CustomField < ApplicationRecord 'position', 'searchable', 'default_value', + 'default_value_mode', 'editable', 'visible', 'multiple', @@ -129,6 +130,14 @@ class CustomField < ApplicationRecord @format ||= Redmine::FieldFormat.find(field_format) end + def default_value + if field_format == 'date' && default_value_mode == 'current_date' + User.current.today.to_s + else + self[:default_value] + end + end + def field_format=(arg) # cannot change format of a saved custom field if new_record? @@ -158,8 +167,9 @@ class CustomField < ApplicationRecord end end - if default_value.present? && errors[:regexp].blank? - validate_field_value(default_value).each do |message| + if self[:default_value].present? && errors[:regexp].blank? && + !(field_format == 'date' && default_value_mode == 'current_date') + validate_field_value(self[:default_value]).each do |message| errors.add :default_value, message end end diff --git a/app/views/custom_fields/formats/_date.html.erb b/app/views/custom_fields/formats/_date.html.erb index b52c06310..951aa22f8 100644 --- a/app/views/custom_fields/formats/_date.html.erb +++ b/app/views/custom_fields/formats/_date.html.erb @@ -1,3 +1,24 @@ -

<%= f.date_field(:default_value, :value => @custom_field.default_value, :size => 10) %>

-<%= calendar_for('custom_field_default_value') %> +<% default_value_mode = @custom_field.default_value_mode.presence || 'fixed_date' %> +

+ <%= f.label :default_value, l(:field_default_value) %> + + <%= f.date_field(:default_value, + :value => @custom_field[:default_value], + :size => 10, + :disabled => default_value_mode == 'current_date') %> + <%= calendar_for('custom_field_default_value') %> +

+

+ +

<%= f.text_field :url_pattern, :size => 50, :label => :label_link_values_to %>

diff --git a/app/views/custom_fields/index.api.rsb b/app/views/custom_fields/index.api.rsb index d4b19d62b..ba466152b 100644 --- a/app/views/custom_fields/index.api.rsb +++ b/app/views/custom_fields/index.api.rsb @@ -13,7 +13,8 @@ api.array :custom_fields do api.is_filter field.is_filter? api.searchable field.searchable api.multiple field.multiple? - api.default_value field.default_value + api.default_value field[:default_value] + api.default_value_mode(field.default_value_mode.presence || 'fixed_date') if field.field_format == 'date' api.visible field.visible? api.editable field.editable? diff --git a/lib/redmine/field_format.rb b/lib/redmine/field_format.rb index bb0185b51..1758135ae 100644 --- a/lib/redmine/field_format.rb +++ b/lib/redmine/field_format.rb @@ -555,6 +555,7 @@ module Redmine class DateFormat < Unbounded add 'date' self.form_partial = 'custom_fields/formats/date' + field_attributes :default_value_mode def cast_single_value(custom_field, value, customized=nil) value.to_date rescue nil @@ -583,6 +584,14 @@ module Redmine {:type => :date} end + def before_custom_field_save(custom_field) + super + + custom_field.default_value_mode = + custom_field.default_value_mode == 'current_date' ? 'current_date' : 'fixed_date' + custom_field.default_value = nil if custom_field.default_value_mode == 'current_date' + end + def group_statement(custom_field) order_statement(custom_field) end diff --git a/test/functional/custom_fields_controller_test.rb b/test/functional/custom_fields_controller_test.rb index 9ed072d45..4f52b0901 100644 --- a/test/functional/custom_fields_controller_test.rb +++ b/test/functional/custom_fields_controller_test.rb @@ -230,6 +230,22 @@ class CustomFieldsControllerTest < Redmine::ControllerTest end end + def test_default_value_should_offer_fixed_date_and_current_date_for_date_custom_field + get( + :new, + :params => { + :type => 'IssueCustomField', + :custom_field => { + :field_format => 'date' + } + } + ) + assert_response :success + assert_select 'input[type=radio][name=?][value=?]', 'custom_field[default_value_mode]', 'fixed_date' + assert_select 'input[type=radio][name=?][value=?]', 'custom_field[default_value_mode]', 'current_date' + assert_select 'input[type=date][name=?]', 'custom_field[default_value]' + end + def test_default_value_should_not_be_present_for_user_custom_field get( :new, diff --git a/test/integration/api_test/custom_fields_test.rb b/test/integration/api_test/custom_fields_test.rb index 4fb06636e..9c0631fd3 100644 --- a/test/integration/api_test/custom_fields_test.rb +++ b/test/integration/api_test/custom_fields_test.rb @@ -56,4 +56,5 @@ class Redmine::ApiTest::CustomFieldsTest < Redmine::ApiTest::Base assert_select "value:contains(?) + label:contains(?)", bar.id.to_s, 'Bar' end end + end diff --git a/test/integration/api_test/issues_test.rb b/test/integration/api_test/issues_test.rb index b8f676a43..255e36409 100644 --- a/test/integration/api_test/issues_test.rb +++ b/test/integration/api_test/issues_test.rb @@ -681,6 +681,57 @@ class Redmine::ApiTest::IssuesTest < Redmine::ApiTest::Base assert_equal "", issue.custom_field_value(field) end + test "POST /issues.json with omitted date custom field should set current date default" do + User.current = User.find_by_login('jsmith') + field = + IssueCustomField.generate!( + :field_format => 'date', + :default_value_mode => 'current_date', + :trackers => Tracker.all, + :is_for_all => true + ) + issue = new_record(Issue) do + post( + '/issues.json', + :params => { + :issue => { + :project_id => 1, + :tracker_id => 1, + :subject => 'API date default', + :custom_field_values => {} + } + }, + :headers => credentials('jsmith')) + end + + assert_equal User.current.today.to_s, issue.custom_field_value(field) + end + + test "POST /issues.json with date custom field set to blank should not set current date default" do + field = + IssueCustomField.generate!( + :field_format => 'date', + :default_value_mode => 'current_date', + :trackers => Tracker.all, + :is_for_all => true + ) + issue = new_record(Issue) do + post( + '/issues.json', + :params => { + :issue => { + :project_id => 1, + :tracker_id => 1, + :subject => 'API blank date default', + :custom_field_values => {field.id.to_s => ''} + } + }, + :headers => credentials('jsmith')) + end + + assert_equal "", issue.custom_field_value(field) + end + test "POST /issues.json with failure should return errors" do assert_no_difference('Issue.count') do post( diff --git a/test/unit/custom_field_test.rb b/test/unit/custom_field_test.rb index 121e865d1..d8ea928af 100644 --- a/test/unit/custom_field_test.rb +++ b/test/unit/custom_field_test.rb @@ -72,6 +72,28 @@ class CustomFieldTest < ActiveSupport::TestCase assert field.valid? end + def test_date_default_value_should_not_be_validated_when_current_date_mode_is_selected + field = + IssueCustomField.new( + :name => 'Date', + :field_format => 'date', + :default_value_mode => 'current_date', + :default_value => 'invalid' + ) + + assert field.valid? + end + + def test_date_default_value_should_be_validated_when_fixed_date_mode_is_selected + field = IssueCustomField.new(:name => 'Date', :field_format => 'date', :default_value_mode => 'fixed_date') + + field.default_value = 'invalid' + assert field.invalid? + + field.default_value = '2026-03-21' + assert field.valid? + end + def test_field_format_should_be_validated field = CustomField.new(:name => 'Test', :field_format => 'foo') assert field.invalid? diff --git a/test/unit/custom_value_test.rb b/test/unit/custom_value_test.rb index a1f2e69a0..290812718 100644 --- a/test/unit/custom_value_test.rb +++ b/test/unit/custom_value_test.rb @@ -45,6 +45,15 @@ class CustomValueTest < ActiveSupport::TestCase assert_nil v.value end + def test_new_without_value_should_set_current_date_default_for_date_custom_field + user = User.generate! + User.current = user + field = IssueCustomField.generate!(:field_format => 'date', :default_value_mode => 'current_date') + + v = CustomValue.new(:custom_field => field) + assert_equal user.today.to_s, v.value + end + def test_sti_polymorphic_association # Rails uses top level sti class for polymorphic association. See #3978. assert !User.find(4).custom_values.empty? -- 2.50.1