Skip to content
GitLab
Projects
Groups
Snippets
Help
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Redmine
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Analytics
Analytics
Repository
Value Stream
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Commits
Open sidebar
projects.thm.de
Redmine
Commits
403d7f50
Commit
403d7f50
authored
Dec 14, 2013
by
jplang
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Merged custom fields format refactoring.
git-svn-id:
https://svn.redmine.org/redmine/trunk@12400
e93f8b46-1217-0410-a6f0-8f06a7374b81
parent
631d10ea
Changes
45
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
45 changed files
with
1388 additions
and
519 deletions
+1388
-519
app/controllers/custom_fields_controller.rb
app/controllers/custom_fields_controller.rb
+2
-2
app/helpers/application_helper.rb
app/helpers/application_helper.rb
+14
-1
app/helpers/custom_fields_helper.rb
app/helpers/custom_fields_helper.rb
+44
-87
app/helpers/issues_helper.rb
app/helpers/issues_helper.rb
+5
-4
app/helpers/timelog_helper.rb
app/helpers/timelog_helper.rb
+3
-1
app/models/custom_field.rb
app/models/custom_field.rb
+52
-160
app/models/custom_field_value.rb
app/models/custom_field_value.rb
+8
-2
app/models/query.rb
app/models/query.rb
+6
-23
app/views/custom_fields/_form.html.erb
app/views/custom_fields/_form.html.erb
+19
-52
app/views/custom_fields/_index.html.erb
app/views/custom_fields/_index.html.erb
+1
-1
app/views/custom_fields/formats/_bool.html.erb
app/views/custom_fields/formats/_bool.html.erb
+2
-0
app/views/custom_fields/formats/_date.html.erb
app/views/custom_fields/formats/_date.html.erb
+2
-0
app/views/custom_fields/formats/_link.html.erb
app/views/custom_fields/formats/_link.html.erb
+3
-0
app/views/custom_fields/formats/_list.html.erb
app/views/custom_fields/formats/_list.html.erb
+6
-0
app/views/custom_fields/formats/_numeric.html.erb
app/views/custom_fields/formats/_numeric.html.erb
+2
-0
app/views/custom_fields/formats/_regexp.html.erb
app/views/custom_fields/formats/_regexp.html.erb
+10
-0
app/views/custom_fields/formats/_string.html.erb
app/views/custom_fields/formats/_string.html.erb
+3
-0
app/views/custom_fields/formats/_text.html.erb
app/views/custom_fields/formats/_text.html.erb
+3
-0
app/views/custom_fields/formats/_user.html.erb
app/views/custom_fields/formats/_user.html.erb
+24
-0
app/views/custom_fields/formats/_version.html.erb
app/views/custom_fields/formats/_version.html.erb
+24
-0
app/views/custom_fields/new.html.erb
app/views/custom_fields/new.html.erb
+2
-1
app/views/issues/bulk_edit.html.erb
app/views/issues/bulk_edit.html.erb
+1
-1
app/views/timelog/bulk_edit.html.erb
app/views/timelog/bulk_edit.html.erb
+1
-1
db/migrate/20131124175346_add_custom_fields_format_store.rb
db/migrate/20131124175346_add_custom_fields_format_store.rb
+9
-0
db/migrate/20131210180802_add_custom_fields_description.rb
db/migrate/20131210180802_add_custom_fields_description.rb
+9
-0
lib/plugins/acts_as_customizable/lib/acts_as_customizable.rb
lib/plugins/acts_as_customizable/lib/acts_as_customizable.rb
+1
-0
lib/redmine.rb
lib/redmine.rb
+0
-13
lib/redmine/custom_field_format.rb
lib/redmine/custom_field_format.rb
+0
-115
lib/redmine/export/pdf.rb
lib/redmine/export/pdf.rb
+2
-2
lib/redmine/field_format.rb
lib/redmine/field_format.rb
+646
-0
lib/redmine/helpers/time_report.rb
lib/redmine/helpers/time_report.rb
+2
-1
lib/redmine/views/labelled_form_builder.rb
lib/redmine/views/labelled_form_builder.rb
+5
-1
public/javascripts/application.js
public/javascripts/application.js
+14
-0
public/stylesheets/application.css
public/stylesheets/application.css
+30
-3
test/functional/custom_fields_controller_test.rb
test/functional/custom_fields_controller_test.rb
+20
-9
test/functional/issues_controller_test.rb
test/functional/issues_controller_test.rb
+4
-2
test/unit/custom_field_test.rb
test/unit/custom_field_test.rb
+16
-2
test/unit/custom_field_user_format_test.rb
test/unit/custom_field_user_format_test.rb
+0
-17
test/unit/custom_field_version_format_test.rb
test/unit/custom_field_version_format_test.rb
+0
-16
test/unit/helpers/custom_fields_helper_test.rb
test/unit/helpers/custom_fields_helper_test.rb
+3
-2
test/unit/lib/redmine/field_format/field_format_test.rb
test/unit/lib/redmine/field_format/field_format_test.rb
+55
-0
test/unit/lib/redmine/field_format/link_format_test.rb
test/unit/lib/redmine/field_format/link_format_test.rb
+79
-0
test/unit/lib/redmine/field_format/list_format_test.rb
test/unit/lib/redmine/field_format/list_format_test.rb
+135
-0
test/unit/lib/redmine/field_format/user_field_format_test.rb
test/unit/lib/redmine/field_format/user_field_format_test.rb
+60
-0
test/unit/lib/redmine/field_format/version_field_format_test.rb
...nit/lib/redmine/field_format/version_field_format_test.rb
+61
-0
No files found.
app/controllers/custom_fields_controller.rb
View file @
403d7f50
...
...
@@ -36,6 +36,8 @@ class CustomFieldsController < ApplicationController
end
def
new
@custom_field
.
field_format
=
'string'
if
@custom_field
.
field_format
.
blank?
@custom_field
.
default_value
=
nil
end
def
create
...
...
@@ -76,8 +78,6 @@ class CustomFieldsController < ApplicationController
@custom_field
=
CustomField
.
new_subclass_instance
(
params
[
:type
],
params
[
:custom_field
])
if
@custom_field
.
nil?
render_404
else
@custom_field
.
default_value
=
nil
end
end
...
...
app/helpers/application_helper.rb
View file @
403d7f50
...
...
@@ -158,6 +158,8 @@ module ApplicationHelper
# Helper that formats object for html or text rendering
def
format_object
(
object
,
html
=
true
)
case
object
.
class
.
name
when
'Array'
object
.
map
{
|
o
|
format_object
(
o
,
html
)}.
join
(
', '
).
html_safe
when
'Time'
format_time
(
object
)
when
'Date'
...
...
@@ -171,13 +173,24 @@ module ApplicationHelper
when
'Project'
html
?
link_to_project
(
object
)
:
object
.
to_s
when
'Version'
html
?
link_to
(
object
.
name
,
version_path
(
object
))
:
version
.
to_s
html
?
link_to
(
object
.
name
,
version_path
(
object
))
:
object
.
to_s
when
'TrueClass'
l
(
:general_text_Yes
)
when
'FalseClass'
l
(
:general_text_No
)
when
'Issue'
object
.
visible?
&&
html
?
link_to_issue
(
object
)
:
"#
#{
object
.
id
}
"
when
'CustomValue'
,
'CustomFieldValue'
if
object
.
custom_field
f
=
object
.
custom_field
.
format
.
formatted_custom_value
(
self
,
object
,
html
)
if
f
.
nil?
||
f
.
is_a?
(
String
)
f
else
format_object
(
f
,
html
)
end
else
object
.
value
.
to_s
end
else
html
?
h
(
object
)
:
object
.
to_s
end
...
...
app/helpers/custom_fields_helper.rb
View file @
403d7f50
...
...
@@ -44,51 +44,39 @@ module CustomFieldsHelper
CUSTOM_FIELDS_TABS
end
# Return custom field html tag corresponding to its format
def
custom_field_tag
(
name
,
custom_value
)
custom_field
=
custom_value
.
custom_field
field_name
=
"
#{
name
}
[custom_field_values][
#{
custom_field
.
id
}
]"
field_name
<<
"[]"
if
custom_field
.
multiple?
field_id
=
"
#{
name
}
_custom_field_values_
#{
custom_field
.
id
}
"
tag_options
=
{
:id
=>
field_id
,
:class
=>
"
#{
custom_field
.
field_format
}
_cf"
}
field_format
=
Redmine
::
CustomFieldFormat
.
find_by_name
(
custom_field
.
field_format
)
case
field_format
.
try
(
:edit_as
)
when
"date"
text_field_tag
(
field_name
,
custom_value
.
value
,
tag_options
.
merge
(
:size
=>
10
))
+
calendar_for
(
field_id
)
when
"text"
text_area_tag
(
field_name
,
custom_value
.
value
,
tag_options
.
merge
(
:rows
=>
3
))
when
"bool"
hidden_field_tag
(
field_name
,
'0'
)
+
check_box_tag
(
field_name
,
'1'
,
custom_value
.
true?
,
tag_options
)
when
"list"
blank_option
=
''
.
html_safe
unless
custom_field
.
multiple?
if
custom_field
.
is_required?
unless
custom_field
.
default_value
.
present?
blank_option
=
content_tag
(
'option'
,
"---
#{
l
(
:actionview_instancetag_blank_option
)
}
---"
,
:value
=>
''
)
end
else
blank_option
=
content_tag
(
'option'
)
end
end
s
=
select_tag
(
field_name
,
blank_option
+
options_for_select
(
custom_field
.
possible_values_options
(
custom_value
.
customized
),
custom_value
.
value
),
tag_options
.
merge
(
:multiple
=>
custom_field
.
multiple?
))
if
custom_field
.
multiple?
s
<<
hidden_field_tag
(
field_name
,
''
)
end
s
else
text_field_tag
(
field_name
,
custom_value
.
value
,
tag_options
)
def
render_custom_field_format_partial
(
form
,
custom_field
)
partial
=
custom_field
.
format
.
form_partial
if
partial
render
:partial
=>
custom_field
.
format
.
form_partial
,
:locals
=>
{
:f
=>
form
,
:custom_field
=>
custom_field
}
end
end
def
custom_field_tag_name
(
prefix
,
custom_field
)
name
=
"
#{
prefix
}
[custom_field_values][
#{
custom_field
.
id
}
]"
name
<<
"[]"
if
custom_field
.
multiple?
name
end
def
custom_field_tag_id
(
prefix
,
custom_field
)
"
#{
prefix
}
_custom_field_values_
#{
custom_field
.
id
}
"
end
# Return custom field html tag corresponding to its format
def
custom_field_tag
(
prefix
,
custom_value
)
custom_value
.
custom_field
.
format
.
edit_tag
self
,
custom_field_tag_id
(
prefix
,
custom_value
.
custom_field
),
custom_field_tag_name
(
prefix
,
custom_value
.
custom_field
),
custom_value
,
:class
=>
"
#{
custom_value
.
custom_field
.
field_format
}
_cf"
end
# Return custom field label tag
def
custom_field_label_tag
(
name
,
custom_value
,
options
=
{})
required
=
options
[
:required
]
||
custom_value
.
custom_field
.
is_required?
title
=
custom_value
.
custom_field
.
description
.
presence
content
=
content_tag
'span'
,
custom_value
.
custom_field
.
name
,
:title
=>
title
content_tag
"label"
,
h
(
custom_value
.
custom_field
.
name
)
+
content_tag
"label"
,
content
+
(
required
?
" <span class=
\"
required
\"
>*</span>"
.
html_safe
:
""
),
:for
=>
"
#{
name
}
_custom_field_values_
#{
custom_value
.
custom_field
.
id
}
"
end
...
...
@@ -98,65 +86,30 @@ module CustomFieldsHelper
custom_field_label_tag
(
name
,
custom_value
,
options
)
+
custom_field_tag
(
name
,
custom_value
)
end
def
custom_field_tag_for_bulk_edit
(
name
,
custom_field
,
projects
=
nil
,
value
=
''
)
field_name
=
"
#{
name
}
[custom_field_values][
#{
custom_field
.
id
}
]"
field_name
<<
"[]"
if
custom_field
.
multiple?
field_id
=
"
#{
name
}
_custom_field_values_
#{
custom_field
.
id
}
"
tag_options
=
{
:id
=>
field_id
,
:class
=>
"
#{
custom_field
.
field_format
}
_cf"
}
unset_tag
=
''
unless
custom_field
.
is_required?
unset_tag
=
content_tag
(
'label'
,
check_box_tag
(
field_name
,
'__none__'
,
(
value
==
'__none__'
),
:id
=>
nil
,
:data
=>
{
:disables
=>
"#
#{
field_id
}
"
})
+
l
(
:button_clear
),
:class
=>
'inline'
)
end
field_format
=
Redmine
::
CustomFieldFormat
.
find_by_name
(
custom_field
.
field_format
)
case
field_format
.
try
(
:edit_as
)
when
"date"
text_field_tag
(
field_name
,
value
,
tag_options
.
merge
(
:size
=>
10
))
+
calendar_for
(
field_id
)
+
unset_tag
when
"text"
text_area_tag
(
field_name
,
value
,
tag_options
.
merge
(
:rows
=>
3
))
+
'<br />'
.
html_safe
+
unset_tag
when
"bool"
select_tag
(
field_name
,
options_for_select
([[
l
(
:label_no_change_option
),
''
],
[
l
(
:general_text_yes
),
'1'
],
[
l
(
:general_text_no
),
'0'
]],
value
),
tag_options
)
when
"list"
options
=
[]
options
<<
[
l
(
:label_no_change_option
),
''
]
unless
custom_field
.
multiple?
options
<<
[
l
(
:label_none
),
'__none__'
]
unless
custom_field
.
is_required?
options
+=
custom_field
.
possible_values_options
(
projects
)
select_tag
(
field_name
,
options_for_select
(
options
,
value
),
tag_options
.
merge
(
:multiple
=>
custom_field
.
multiple?
))
else
text_field_tag
(
field_name
,
value
,
tag_options
)
+
unset_tag
end
# Returns the custom field tag for when bulk editing objects
def
custom_field_tag_for_bulk_edit
(
prefix
,
custom_field
,
objects
=
nil
,
value
=
''
)
custom_field
.
format
.
bulk_edit_tag
self
,
custom_field_tag_id
(
prefix
,
custom_field
),
custom_field_tag_name
(
prefix
,
custom_field
),
custom_field
,
objects
,
value
,
:class
=>
"
#{
custom_field
.
field_format
}
_cf"
end
# Return a string used to display a custom value
def
show_value
(
custom_value
)
return
""
unless
custom_value
format_value
(
custom_value
.
value
,
custom_value
.
custom_field
.
field_format
)
def
show_value
(
custom_value
,
html
=
true
)
format_object
(
custom_value
,
html
)
end
# Return a string used to display a custom value
def
format_value
(
value
,
field_format
)
if
value
.
is_a?
(
Array
)
value
.
collect
{
|
v
|
format_value
(
v
,
field_format
)}.
compact
.
sort
.
join
(
', '
)
else
Redmine
::
CustomFieldFormat
.
format_value
(
value
,
field_format
)
end
def
format_value
(
value
,
custom_field
)
format_object
(
custom_field
.
format
.
formatted_value
(
self
,
custom_field
,
value
,
false
),
false
)
end
# Return an array of custom field formats which can be used in select_tag
def
custom_field_formats_for_select
(
custom_field
)
Redmine
::
Custom
FieldFormat
.
as_select
(
custom_field
.
class
.
customized_class
.
name
)
Redmine
::
FieldFormat
.
as_select
(
custom_field
.
class
.
customized_class
.
name
)
end
# Renders the custom_values in api views
...
...
@@ -179,4 +132,8 @@ module CustomFieldsHelper
end
end
unless
custom_values
.
empty?
end
def
edit_tag_style_tag
(
form
)
form
.
select
:edit_tag_style
,
[[
l
(
:label_drop_down_list
),
''
],
[
l
(
:label_checkboxes
),
'check_box'
]],
:label
=>
:label_display
end
end
app/helpers/issues_helper.rb
View file @
403d7f50
...
...
@@ -171,8 +171,9 @@ module IssuesHelper
s
=
"<tr>
\n
"
n
=
0
ordered_values
.
compact
.
each
do
|
value
|
css
=
"cf_
#{
value
.
custom_field
.
id
}
"
s
<<
"</tr>
\n
<tr>
\n
"
if
n
>
0
&&
(
n
%
2
)
==
0
s
<<
"
\t
<th
>
#{
h
(
value
.
custom_field
.
name
)
}
:</th><td>
#{
simple_format_without_paragraph
(
h
(
show_value
(
value
)
))
}
</td>
\n
"
s
<<
"
\t
<th
class=
\"
#{
css
}
\"
>
#{
h
(
value
.
custom_field
.
name
)
}
:</th><td class=
\"
#{
css
}
\"
>
#{
h
(
show_value
(
value
))
}
</td>
\n
"
n
+=
1
end
s
<<
"</tr>
\n
"
...
...
@@ -239,7 +240,7 @@ module IssuesHelper
end
end
issue
.
visible_custom_field_values
(
user
).
each
do
|
value
|
items
<<
"
#{
value
.
custom_field
.
name
}
:
#{
show_value
(
value
)
}
"
items
<<
"
#{
value
.
custom_field
.
name
}
:
#{
show_value
(
value
,
false
)
}
"
end
items
end
...
...
@@ -324,8 +325,8 @@ module IssuesHelper
if
custom_field
multiple
=
custom_field
.
multiple?
label
=
custom_field
.
name
value
=
format_value
(
detail
.
value
,
custom_field
.
field_format
)
if
detail
.
value
old_value
=
format_value
(
detail
.
old_value
,
custom_field
.
field_format
)
if
detail
.
old_value
value
=
format_value
(
detail
.
value
,
custom_field
)
if
detail
.
value
old_value
=
format_value
(
detail
.
old_value
,
custom_field
)
if
detail
.
old_value
end
when
'attachment'
label
=
l
(
:label_attachment
)
...
...
app/helpers/timelog_helper.rb
View file @
403d7f50
...
...
@@ -96,8 +96,10 @@ module TimelogHelper
else
obj
end
elsif
cf
=
criteria_options
[
:custom_field
]
format_value
(
value
,
cf
)
else
format_value
(
value
,
criteria_options
[
:format
])
value
.
to_s
end
end
...
...
app/models/custom_field.rb
View file @
403d7f50
...
...
@@ -22,14 +22,18 @@ class CustomField < ActiveRecord::Base
has_and_belongs_to_many
:roles
,
:join_table
=>
"
#{
table_name_prefix
}
custom_fields_roles
#{
table_name_suffix
}
"
,
:foreign_key
=>
"custom_field_id"
acts_as_list
:scope
=>
'type = \'#{self.class}\''
serialize
:possible_values
store
:format_store
validates_presence_of
:name
,
:field_format
validates_uniqueness_of
:name
,
:scope
=>
:type
validates_length_of
:name
,
:maximum
=>
30
validates_inclusion_of
:field_format
,
:in
=>
Proc
.
new
{
Redmine
::
Custom
FieldFormat
.
available_formats
}
validates_inclusion_of
:field_format
,
:in
=>
Proc
.
new
{
Redmine
::
FieldFormat
.
available_formats
}
validate
:validate_custom_field
before_validation
:set_searchable
before_save
do
|
field
|
field
.
format
.
before_custom_field_save
(
field
)
end
after_save
:handle_multiplicity_change
after_save
do
|
field
|
if
field
.
visible_changed?
&&
field
.
visible
...
...
@@ -57,23 +61,29 @@ class CustomField < ActiveRecord::Base
visible?
||
user
.
admin?
end
def
format
@format
||=
Redmine
::
FieldFormat
.
find
(
field_format
)
end
def
field_format
=
(
arg
)
# cannot change format of a saved custom field
super
if
new_record?
if
new_record?
@format
=
nil
super
end
end
def
set_searchable
# make sure these fields are not searchable
self
.
searchable
=
false
if
%w(int float date bool)
.
include?
(
field_format
)
self
.
searchable
=
false
unless
format
.
class
.
searchable_supported
# make sure only these fields can have multiple values
self
.
multiple
=
false
unless
%w(list user version)
.
include?
(
field_format
)
self
.
multiple
=
false
unless
format
.
class
.
multiple_supported
true
end
def
validate_custom_field
if
self
.
field_format
==
"list"
errors
.
add
(
:possible_values
,
:blank
)
if
self
.
possible_values
.
nil?
||
self
.
possible_values
.
empty?
errors
.
add
(
:possible_values
,
:invalid
)
unless
self
.
possible_values
.
is_a?
Array
format
.
validate_custom_field
(
self
).
each
do
|
attribute
,
message
|
errors
.
add
attribute
,
message
end
if
regexp
.
present?
...
...
@@ -84,49 +94,34 @@ class CustomField < ActiveRecord::Base
end
end
if
default_value
.
present?
&&
!
valid_field_value?
(
default_value
)
errors
.
add
(
:default_value
,
:invalid
)
if
default_value
.
present?
validate_field_value
(
default_value
).
each
do
|
message
|
errors
.
add
:default_value
,
message
end
end
end
def
possible_values_options
(
obj
=
nil
)
case
field_format
when
'user'
,
'version'
if
obj
.
respond_to?
(
:project
)
&&
obj
.
project
case
field_format
when
'user'
obj
.
project
.
users
.
sort
.
collect
{
|
u
|
[
u
.
to_s
,
u
.
id
.
to_s
]}
when
'version'
obj
.
project
.
shared_versions
.
sort
.
collect
{
|
u
|
[
u
.
to_s
,
u
.
id
.
to_s
]}
end
elsif
obj
.
is_a?
(
Array
)
obj
.
collect
{
|
o
|
possible_values_options
(
o
)}.
reduce
(
:&
)
else
[]
end
when
'bool'
[[
l
(
:general_text_Yes
),
'1'
],
[
l
(
:general_text_No
),
'0'
]]
def
possible_custom_value_options
(
custom_value
)
format
.
possible_custom_value_options
(
custom_value
)
end
def
possible_values_options
(
object
=
nil
)
if
object
.
is_a?
(
Array
)
object
.
map
{
|
o
|
format
.
possible_values_options
(
self
,
o
)}.
reduce
(
:&
)
||
[]
else
possible_values
||
[]
format
.
possible_values_options
(
self
,
object
)
||
[]
end
end
def
possible_values
(
obj
=
nil
)
case
field_format
when
'user'
,
'version'
possible_values_options
(
obj
).
collect
(
&
:last
)
when
'bool'
[
'1'
,
'0'
]
else
values
=
super
()
if
values
.
is_a?
(
Array
)
values
.
each
do
|
value
|
value
.
force_encoding
(
'UTF-8'
)
if
value
.
respond_to?
(
:force_encoding
)
end
values
else
[]
def
possible_values
values
=
super
()
if
values
.
is_a?
(
Array
)
values
.
each
do
|
value
|
value
.
force_encoding
(
'UTF-8'
)
if
value
.
respond_to?
(
:force_encoding
)
end
values
else
[]
end
end
...
...
@@ -140,24 +135,7 @@ class CustomField < ActiveRecord::Base
end
def
cast_value
(
value
)
casted
=
nil
unless
value
.
blank?
case
field_format
when
'string'
,
'text'
,
'list'
casted
=
value
when
'date'
casted
=
begin
;
value
.
to_date
;
rescue
;
nil
end
when
'bool'
casted
=
(
value
==
'1'
?
true
:
false
)
when
'int'
casted
=
value
.
to_i
when
'float'
casted
=
value
.
to_f
when
'user'
,
'version'
casted
=
(
value
.
blank?
?
nil
:
field_format
.
classify
.
constantize
.
find_by_id
(
value
.
to_i
))
end
end
casted
format
.
cast_value
(
self
,
value
)
end
def
value_from_keyword
(
keyword
,
customized
)
...
...
@@ -181,83 +159,18 @@ class CustomField < ActiveRecord::Base
# Returns nil if the custom field can not be used for sorting.
def
order_statement
return
nil
if
multiple?
case
field_format
when
'string'
,
'text'
,
'list'
,
'date'
,
'bool'
# COALESCE is here to make sure that blank and NULL values are sorted equally
"COALESCE(
#{
join_alias
}
.value, '')"
when
'int'
,
'float'
# Make the database cast values into numeric
# Postgresql will raise an error if a value can not be casted!
# CustomValue validations should ensure that it doesn't occur
"CAST(CASE
#{
join_alias
}
.value WHEN '' THEN '0' ELSE
#{
join_alias
}
.value END AS decimal(30,3))"
when
'user'
,
'version'
value_class
.
fields_for_order_statement
(
value_join_alias
)
else
nil
end
format
.
order_statement
(
self
)
end
# Returns a GROUP BY clause that can used to group by custom value
# Returns nil if the custom field can not be used for grouping.
def
group_statement
return
nil
if
multiple?
case
field_format
when
'list'
,
'date'
,
'bool'
,
'int'
order_statement
when
'user'
,
'version'
"COALESCE(
#{
join_alias
}
.value, '')"
else
nil
end
format
.
group_statement
(
self
)
end
def
join_for_order_statement
case
field_format
when
'user'
,
'version'
"LEFT OUTER JOIN
#{
CustomValue
.
table_name
}
#{
join_alias
}
"
+
" ON
#{
join_alias
}
.customized_type = '
#{
self
.
class
.
customized_class
.
base_class
.
name
}
'"
+
" AND
#{
join_alias
}
.customized_id =
#{
self
.
class
.
customized_class
.
table_name
}
.id"
+
" AND
#{
join_alias
}
.custom_field_id =
#{
id
}
"
+
" AND (
#{
visibility_by_project_condition
}
)"
+
" AND
#{
join_alias
}
.value <> ''"
+
" AND
#{
join_alias
}
.id = (SELECT max(
#{
join_alias
}
_2.id) FROM
#{
CustomValue
.
table_name
}
#{
join_alias
}
_2"
+
" WHERE
#{
join_alias
}
_2.customized_type =
#{
join_alias
}
.customized_type"
+
" AND
#{
join_alias
}
_2.customized_id =
#{
join_alias
}
.customized_id"
+
" AND
#{
join_alias
}
_2.custom_field_id =
#{
join_alias
}
.custom_field_id)"
+
" LEFT OUTER JOIN
#{
value_class
.
table_name
}
#{
value_join_alias
}
"
+
" ON CAST(CASE
#{
join_alias
}
.value WHEN '' THEN '0' ELSE
#{
join_alias
}
.value END AS decimal(30,0)) =
#{
value_join_alias
}
.id"
when
'int'
,
'float'
"LEFT OUTER JOIN
#{
CustomValue
.
table_name
}
#{
join_alias
}
"
+
" ON
#{
join_alias
}
.customized_type = '
#{
self
.
class
.
customized_class
.
base_class
.
name
}
'"
+
" AND
#{
join_alias
}
.customized_id =
#{
self
.
class
.
customized_class
.
table_name
}
.id"
+
" AND
#{
join_alias
}
.custom_field_id =
#{
id
}
"
+
" AND (
#{
visibility_by_project_condition
}
)"
+
" AND
#{
join_alias
}
.value <> ''"
+
" AND
#{
join_alias
}
.id = (SELECT max(
#{
join_alias
}
_2.id) FROM
#{
CustomValue
.
table_name
}
#{
join_alias
}
_2"
+
" WHERE
#{
join_alias
}
_2.customized_type =
#{
join_alias
}
.customized_type"
+
" AND
#{
join_alias
}
_2.customized_id =
#{
join_alias
}
.customized_id"
+
" AND
#{
join_alias
}
_2.custom_field_id =
#{
join_alias
}
.custom_field_id)"
when
'string'
,
'text'
,
'list'
,
'date'
,
'bool'
"LEFT OUTER JOIN
#{
CustomValue
.
table_name
}
#{
join_alias
}
"
+
" ON
#{
join_alias
}
.customized_type = '
#{
self
.
class
.
customized_class
.
base_class
.
name
}
'"
+
" AND
#{
join_alias
}
.customized_id =
#{
self
.
class
.
customized_class
.
table_name
}
.id"
+
" AND
#{
join_alias
}
.custom_field_id =
#{
id
}
"
+
" AND (
#{
visibility_by_project_condition
}
)"
+
" AND
#{
join_alias
}
.id = (SELECT max(
#{
join_alias
}
_2.id) FROM
#{
CustomValue
.
table_name
}
#{
join_alias
}
_2"
+
" WHERE
#{
join_alias
}
_2.customized_type =
#{
join_alias
}
.customized_type"
+
" AND
#{
join_alias
}
_2.customized_id =
#{
join_alias
}
.customized_id"
+
" AND
#{
join_alias
}
_2.custom_field_id =
#{
join_alias
}
.custom_field_id)"
else
nil
end
end
def
join_alias
"cf_
#{
id
}
"
end
def
value_join_alias
join_alias
+
"_"
+
field_format
format
.
join_for_order_statement
(
self
)
end
def
visibility_by_project_condition
(
project_key
=
nil
,
user
=
User
.
current
)
...
...
@@ -293,12 +206,7 @@ class CustomField < ActiveRecord::Base
# Returns the class that values represent
def
value_class
case
field_format
when
'user'
,
'version'
field_format
.
classify
.
constantize
else
nil
end
format
.
target_class
if
format
.
respond_to?
(
:target_class
)
end
def
self
.
customized_class
...
...
@@ -317,7 +225,8 @@ class CustomField < ActiveRecord::Base
# Returns the error messages for the given value
# or an empty array if value is a valid value for the custom field
def
validate_field_value
(
value
)
def
validate_custom_value
(
custom_value
)
value
=
custom_value
.
value
errs
=
[]
if
value
.
is_a?
(
Array
)
if
!
multiple?
...
...
@@ -326,16 +235,22 @@ class CustomField < ActiveRecord::Base
if
is_required?
&&
value
.
detect
(
&
:present?
).
nil?
errs
<<
::
I18n
.
t
(
'activerecord.errors.messages.blank'
)
end
value
.
each
{
|
v
|
errs
+=
validate_field_value_format
(
v
)}
else
if
is_required?
&&
value
.
blank?
errs
<<
::
I18n
.
t
(
'activerecord.errors.messages.blank'
)
end
errs
+=
validate_field_value_format
(
value
)
end
if
custom_value
.
value
.
present?
errs
+=
format
.
validate_custom_value
(
custom_value
)
end
errs
end
# Returns the error messages for the default custom field value
def
validate_field_value
(
value
)
validate_custom_value
(
CustomValue
.
new
(
:custom_field
=>
self
,
:value
=>
value
))
end
# Returns true if value is a valid value for the custom field
def
valid_field_value?
(
value
)
validate_field_value
(
value
).
empty?
...
...
@@ -347,29 +262,6 @@ class CustomField < ActiveRecord::Base
protected
# Returns the error message for the given value regarding its format
def
validate_field_value_format
(
value
)
errs
=
[]
if
value
.
present?
errs
<<
::
I18n
.
t
(
'activerecord.errors.messages.invalid'
)
unless
regexp
.
blank?
or
value
=~
Regexp
.
new
(
regexp
)
errs
<<
::
I18n
.
t
(
'activerecord.errors.messages.too_short'
,
:count
=>
min_length
)
if
min_length
>
0
and
value
.
length
<
min_length
errs
<<
::
I18n
.
t
(
'activerecord.errors.messages.too_long'
,
:count
=>
max_length
)
if
max_length
>
0
and
value
.
length
>
max_length
# Format specific validations
case
field_format
when
'int'
errs
<<
::
I18n
.
t
(
'activerecord.errors.messages.not_a_number'
)
unless
value
=~
/^[+-]?\d+$/
when
'float'
begin
;
Kernel
.
Float
(
value
);
rescue
;
errs
<<
::
I18n
.
t
(
'activerecord.errors.messages.invalid'
)
end
when
'date'
errs
<<
::
I18n
.
t
(
'activerecord.errors.messages.not_a_date'
)
unless
value
=~
/^\d{4}-\d{2}-\d{2}$/
&&
begin
;
value
.
to_date
;
rescue
;
false
end
when
'list'
errs
<<
::
I18n
.
t
(
'activerecord.errors.messages.inclusion'
)
unless
possible_values
.
include?
(
value
)
end
end
errs
end
# Removes multiple values for the custom field after setting the multiple attribute to false
# We kepp the value with the highest id for each customized object
def
handle_multiplicity_change
...
...
app/models/custom_field_value.rb
View file @
403d7f50
...
...
@@ -16,7 +16,13 @@
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
class
CustomFieldValue
attr_accessor
:custom_field
,
:customized
,
:value
attr_accessor
:custom_field
,
:customized
,
:value
,
:value_was
def
initialize
(
attributes
=
{})
attributes
.
each
do
|
name
,
v
|
send
"
#{
name
}
="
,
v
end
end
def
custom_field_id
custom_field
.
id
...
...
@@ -43,7 +49,7 @@ class CustomFieldValue
end
def
validate_value
custom_field
.
validate_
field_value
(
value
).
each
do
|
message
|
custom_field
.
validate_
custom_value
(
self
).
each
do
|
message
|
customized
.
errors
.
add
(
:base
,
custom_field
.
name
+
' '
+
message
)
end
end
...
...
app/models/query.rb
View file @
403d7f50
...
...
@@ -587,7 +587,7 @@ class Query < ActiveRecord::Base
db_field
=
'value'
filter
=
@available_filters
[
field
]
return
nil
unless
filter
if
filter
[
:f
ormat
]
==
'user'
if
filter
[
:f
ield
].
format
.
target_class
&&
filter
[
:field
].
format
.
target_class
<=
User
if
value
.
delete
(
'me'
)
value
.
push
User
.
current
.
id
.
to_s