Feature #12005 » workflow_hidden_field_DR_v0.04.patch
app/helpers/issues_helper.rb (working copy) | ||
---|---|---|
146 | 146 |
end |
147 | 147 | |
148 | 148 |
def render_custom_fields_rows(issue) |
149 |
return if issue.custom_field_values.empty? |
|
149 |
local_viewablecf=issue.viewable_custom_field_values |
|
150 |
return if local_viewablecf.empty? |
|
150 | 151 |
ordered_values = [] |
151 |
half = (issue.custom_field_values.size / 2.0).ceil
|
|
152 |
half = (local_viewablecf.size / 2.0).ceil
|
|
152 | 153 |
half.times do |i| |
153 |
ordered_values << issue.custom_field_values[i]
|
|
154 |
ordered_values << issue.custom_field_values[i + half]
|
|
154 |
ordered_values << local_viewablecf[i]
|
|
155 |
ordered_values << local_viewablecf[i + half]
|
|
155 | 156 |
end |
156 | 157 |
s = "<tr>\n" |
157 | 158 |
n = 0 |
... | ... | |
221 | 222 |
strings = [] |
222 | 223 |
values_by_field = {} |
223 | 224 |
details.each do |detail| |
225 |
unless detail.journal.issue.hidden_attribute?(detail.prop_key) |
|
224 | 226 |
if detail.property == 'cf' |
225 | 227 |
field_id = detail.prop_key |
226 | 228 |
field = CustomField.find_by_id(field_id) |
... | ... | |
237 | 239 |
end |
238 | 240 |
strings << show_detail(detail, no_html, options) |
239 | 241 |
end |
242 |
end |
|
240 | 243 |
values_by_field.each do |field_id, changes| |
244 |
unless detail.journal.issue.hidden_attribute?(detail.prop_key) |
|
241 | 245 |
detail = JournalDetail.new(:property => 'cf', :prop_key => field_id) |
242 | 246 |
if changes[:added].any? |
243 | 247 |
detail.value = changes[:added] |
... | ... | |
246 | 250 |
detail.old_value = changes[:deleted] |
247 | 251 |
strings << show_detail(detail, no_html, options) |
248 | 252 |
end |
253 |
end |
|
249 | 254 |
end |
250 | 255 |
strings |
251 | 256 |
end |
... | ... | |
381 | 386 |
# csv lines |
382 | 387 |
issues.each do |issue| |
383 | 388 |
col_values = columns.collect do |column| |
389 |
hidden_fields = issue.hidden_attribute_names.map {|field| field.sub(/_id$/, '')} |
|
390 |
if hidden_fields.include?(column.is_a?(QueryCustomFieldColumn) ? column.custom_field.id.to_s : column.name.to_s) |
|
391 |
"" |
|
392 |
else |
|
384 | 393 |
s = if column.is_a?(QueryCustomFieldColumn) |
385 | 394 |
cv = issue.custom_field_values.detect {|v| v.custom_field_id == column.custom_field.id} |
386 | 395 |
show_value(cv) |
... | ... | |
398 | 407 |
end |
399 | 408 |
s.to_s |
400 | 409 |
end |
410 |
end |
|
401 | 411 |
csv << [ issue.id.to_s ] + col_values.collect {|c| Redmine::CodesetUtil.from_utf8(c.to_s, encoding) } + |
402 | 412 |
(options[:description] ? [Redmine::CodesetUtil.from_utf8(issue.description, encoding)] : []) |
403 | 413 |
end |
app/models/issue.rb (working copy) | ||
---|---|---|
446 | 446 |
end |
447 | 447 |
end |
448 | 448 | |
449 |
# Returns the custom_field_values that can be viewed by the given user |
|
450 |
# For now: just exclude Fix Info and RNs, as it is printed seperately below description. |
|
451 |
def viewable_custom_field_values(user=nil) |
|
452 |
custom_field_values.reject do |value| |
|
453 |
hidden_attribute_names(user).include?(value.custom_field_id.to_s) |
|
454 |
end |
|
455 |
end |
|
456 | ||
449 | 457 |
# Returns the names of attributes that are read-only for user or the current user |
450 | 458 |
# For users with multiple roles, the read-only fields are the intersection of |
451 | 459 |
# read-only fields of each role |
... | ... | |
455 | 463 |
# issue.read_only_attribute_names # => ['due_date', '2'] |
456 | 464 |
# issue.read_only_attribute_names(user) # => [] |
457 | 465 |
def read_only_attribute_names(user=nil) |
458 |
workflow_rule_by_attribute(user).reject {|attr, rule| rule != 'readonly'}.keys |
|
466 |
workflow_rule_by_attribute(user).reject {|attr, rule| rule != 'readonly' and rule != 'hidden'}.keys
|
|
459 | 467 |
end |
460 | 468 | |
469 |
# Same as above, but for hidden fields |
|
470 |
def hidden_attribute_names(user=nil) |
|
471 |
workflow_rule_by_attribute(user).reject {|attr, rule| rule != 'hidden'}.keys |
|
472 |
end |
|
473 | ||
461 | 474 |
# Returns the names of required attributes for user or the current user |
462 | 475 |
# For users with multiple roles, the required fields are the intersection of |
463 | 476 |
# required fields of each role |
... | ... | |
475 | 488 |
required_attribute_names(user).include?(name.to_s) |
476 | 489 |
end |
477 | 490 | |
491 |
# Returns true if the attribute should be hidden for user |
|
492 |
def hidden_attribute?(name, user=nil) |
|
493 |
logger.info("For " + name.to_s + ", It will return " + hidden_attribute_names(user).include?(name.to_s).to_s ) |
|
494 |
# hidden_attribute_names(user).each do |n| |
|
495 |
# logger.info(n.to_s) |
|
496 |
# end |
|
497 |
hidden_attribute_names(user).include?(name.to_s) |
|
498 |
end |
|
499 | ||
500 | ||
478 | 501 |
# Returns a hash of the workflow rule by attribute for the given user |
479 | 502 |
# |
480 | 503 |
# Examples: |
app/models/project.rb (working copy) | ||
---|---|---|
146 | 146 |
user.allowed_to?(:view_project, self) |
147 | 147 |
end |
148 | 148 | |
149 |
# Returns list of attributes that are hidden on all statuses of all trackers for +user+ or the current user. |
|
150 |
def completely_hidden_attribute_names(user=nil) |
|
151 |
user_real = user || User.current |
|
152 |
roles = user_real.admin ? Role.all : user_real.roles_for_project(self) |
|
153 |
return {} if roles.empty? |
|
154 | ||
155 |
result = {} |
|
156 |
workflow_permissions = WorkflowPermission.where(:tracker_id => trackers.map(&:id), :old_status_id => IssueStatus.all.map(&:id), :role_id => roles.map(&:id), :rule => 'hidden').all |
|
157 | ||
158 |
if workflow_permissions.any? |
|
159 |
workflow_rules = workflow_permissions.inject({}) do |h, wp| |
|
160 |
h[wp.field_name] ||= [] |
|
161 |
h[wp.field_name] << wp.rule |
|
162 |
h |
|
163 |
end |
|
164 |
workflow_rules.each do |attr, rules| |
|
165 |
next if rules.size < (roles.size * trackers.size * IssueStatus.all.size) |
|
166 |
uniq_rules = rules.uniq |
|
167 |
if uniq_rules.size == 1 |
|
168 |
result[attr] = uniq_rules.first |
|
169 |
else |
|
170 |
result[attr] = 'required' |
|
171 |
end |
|
172 |
end |
|
173 |
end |
|
174 | ||
175 |
result.keys |
|
176 |
end |
|
177 | ||
178 | ||
149 | 179 |
# Returns a SQL conditions string used to find all projects visible by the specified user. |
150 | 180 |
# |
151 | 181 |
# Examples: |
app/models/query.rb (working copy) | ||
---|---|---|
44 | 44 |
end |
45 | 45 | |
46 | 46 |
def value(issue) |
47 |
hidden_fields = issue.hidden_attribute_names.map {|field| field.sub(/_id$/, '')} |
|
48 |
if hidden_fields.include?(name.to_s) |
|
49 |
"" |
|
50 |
else |
|
47 | 51 |
issue.send name |
48 | 52 |
end |
53 |
end |
|
49 | 54 | |
50 | 55 |
def css_classes |
51 | 56 |
name |
... | ... | |
360 | 365 |
Tracker.disabled_core_fields(trackers).each {|field| |
361 | 366 |
@available_filters.delete field |
362 | 367 |
} |
368 | ||
369 |
project.completely_hidden_attribute_names.each {|field| |
|
370 |
@available_filters.delete field |
|
371 |
} |
|
372 | ||
363 | 373 |
@available_filters.each do |field, options| |
364 | 374 |
options[:name] ||= l(options[:label] || "field_#{field}".gsub(/_id$/, '')) |
365 | 375 |
end |
... | ... | |
451 | 461 |
def available_columns |
452 | 462 |
return @available_columns if @available_columns |
453 | 463 |
@available_columns = ::Query.available_columns.dup |
464 | ||
465 |
hidden_fields = project.completely_hidden_attribute_names.map {|field| field.sub(/_id$/, '')} |
|
466 | ||
454 | 467 |
@available_columns += (project ? |
455 | 468 |
project.all_issue_custom_fields : |
456 | 469 |
IssueCustomField.find(:all) |
457 |
).collect {|cf| QueryCustomFieldColumn.new(cf) } |
|
470 |
).collect {|cf| QueryCustomFieldColumn.new(cf) }.reject{|column| hidden_fields.include?(column.custom_field.id.to_s) }
|
|
458 | 471 | |
459 | 472 |
if User.current.allowed_to?(:view_time_entries, project, :global => true) |
460 | 473 |
index = nil |
... | ... | |
477 | 490 |
@available_columns.reject! {|column| |
478 | 491 |
disabled_fields.include?(column.name.to_s) |
479 | 492 |
} |
493 |
|
|
494 |
@available_columns.reject! {|column| |
|
495 |
hidden_fields.include?(column.name.to_s) |
|
496 |
} |
|
480 | 497 | |
481 | 498 |
@available_columns |
482 | 499 |
end |
... | ... | |
966 | 983 |
@available_filters ||= {} |
967 | 984 | |
968 | 985 |
custom_fields.select(&:is_filter?).each do |field| |
986 |
unless project.completely_hidden_attribute_names.include?(field.id.to_s) |
|
969 | 987 |
case field.field_format |
970 | 988 |
when "text" |
971 | 989 |
options = { :type => :text, :order => 20 } |
... | ... | |
1002 | 1020 |
}) |
1003 | 1021 |
end |
1004 | 1022 |
end |
1023 |
end |
|
1005 | 1024 | |
1006 | 1025 |
def add_associations_custom_fields_filters(*associations) |
1007 | 1026 |
fields_by_class = CustomField.where(:is_filter => true).group_by(&:class) |
app/views/issues/_form_custom_fields.html.erb (working copy) | ||
---|---|---|
1 | 1 |
<div class="splitcontent"> |
2 | 2 |
<div class="splitcontentleft"> |
3 | 3 |
<% i = 0 %> |
4 |
<% split_on = (@issue.custom_field_values.size / 2.0).ceil - 1 %> |
|
4 |
<% split_on = (@issue.editable_custom_field_values.size / 2.0).ceil - 1 %>
|
|
5 | 5 |
<% @issue.editable_custom_field_values.each do |value| %> |
6 | 6 |
<p><%= custom_field_tag_with_label :issue, value, :required => @issue.required_attribute?(value.custom_field_id) %></p> |
7 | 7 |
<% if i == split_on -%> |
app/views/issues/_history.html.erb (working copy) | ||
---|---|---|
1 | 1 |
<% reply_links = authorize_for('issues', 'edit') -%> |
2 | 2 |
<% for journal in journals %> |
3 |
<% if details_to_strings(journal.details).any? || journal.notes.blank? == false %> |
|
3 | 4 |
<div id="change-<%= journal.id %>" class="<%= journal.css_classes %>"> |
4 | 5 |
<div id="note-<%= journal.indice %>"> |
5 | 6 |
<h4><%= link_to "##{journal.indice}", {:anchor => "note-#{journal.indice}"}, :class => "journal-link" %> |
6 | 7 |
<%= avatar(journal.user, :size => "24") %> |
7 | 8 |
<%= authoring journal.created_on, journal.user, :label => :label_updated_time_by %></h4> |
8 | ||
9 |
|
|
9 | 10 |
<% if journal.details.any? %> |
10 | 11 |
<ul class="details"> |
11 | 12 |
<% details_to_strings(journal.details).each do |string| %> |
... | ... | |
17 | 18 |
</div> |
18 | 19 |
</div> |
19 | 20 |
<%= call_hook(:view_issues_history_journal_bottom, { :journal => journal }) %> |
21 |
<% end %> |
|
20 | 22 |
<% end %> |
21 | 23 | |
22 | 24 |
<% heads_for_wiki_formatter if User.current.allowed_to?(:edit_issue_notes, issue.project) || User.current.allowed_to?(:edit_own_issue_notes, issue.project) %> |
app/views/issues/show.html.erb (working copy) | ||
---|---|---|
33 | 33 | |
34 | 34 |
<table class="attributes"> |
35 | 35 |
<%= issue_fields_rows do |rows| |
36 |
unless @issue.hidden_attribute?('status') |
|
36 | 37 |
rows.left l(:field_status), h(@issue.status.name), :class => 'status' |
38 |
end |
|
39 |
unless @issue.hidden_attribute?('priority') |
|
37 | 40 |
rows.left l(:field_priority), h(@issue.priority.name), :class => 'priority' |
41 |
end |
|
38 | 42 | |
39 |
unless @issue.disabled_core_fields.include?('assigned_to_id') |
|
43 |
unless @issue.disabled_core_fields.include?('assigned_to_id') || @issue.hidden_attribute?('assigned_to_id')
|
|
40 | 44 |
rows.left l(:field_assigned_to), avatar(@issue.assigned_to, :size => "14").to_s.html_safe + (@issue.assigned_to ? link_to_user(@issue.assigned_to) : "-"), :class => 'assigned-to' |
41 | 45 |
end |
42 |
unless @issue.disabled_core_fields.include?('category_id') |
|
46 |
unless @issue.disabled_core_fields.include?('category_id') || @issue.hidden_attribute?('category_id')
|
|
43 | 47 |
rows.left l(:field_category), h(@issue.category ? @issue.category.name : "-"), :class => 'category' |
44 | 48 |
end |
45 |
unless @issue.disabled_core_fields.include?('fixed_version_id') |
|
49 |
unless @issue.disabled_core_fields.include?('fixed_version_id') || @issue.hidden_attribute?('fixed_version_id')
|
|
46 | 50 |
rows.left l(:field_fixed_version), (@issue.fixed_version ? link_to_version(@issue.fixed_version) : "-"), :class => 'fixed-version' |
47 | 51 |
end |
48 | 52 | |
49 |
unless @issue.disabled_core_fields.include?('start_date') |
|
53 |
unless @issue.disabled_core_fields.include?('start_date') || @issue.hidden_attribute?('start_date')
|
|
50 | 54 |
rows.right l(:field_start_date), format_date(@issue.start_date), :class => 'start-date' |
51 | 55 |
end |
52 |
unless @issue.disabled_core_fields.include?('due_date') |
|
56 |
unless @issue.disabled_core_fields.include?('due_date') || @issue.hidden_attribute?('due_date')
|
|
53 | 57 |
rows.right l(:field_due_date), format_date(@issue.due_date), :class => 'due-date' |
54 | 58 |
end |
55 |
unless @issue.disabled_core_fields.include?('done_ratio') |
|
59 |
unless @issue.disabled_core_fields.include?('done_ratio') || @issue.hidden_attribute?('done_ratio')
|
|
56 | 60 |
rows.right l(:field_done_ratio), progress_bar(@issue.done_ratio, :width => '80px', :legend => "#{@issue.done_ratio}%"), :class => 'progress' |
57 | 61 |
end |
58 |
unless @issue.disabled_core_fields.include?('estimated_hours') |
|
62 |
unless @issue.disabled_core_fields.include?('estimated_hours') || @issue.hidden_attribute?('estimated_hours')
|
|
59 | 63 |
unless @issue.estimated_hours.nil? |
60 | 64 |
rows.right l(:field_estimated_hours), l_hours(@issue.estimated_hours), :class => 'estimated-hours' |
61 | 65 |
end |
config/locales/en.yml (working copy) | ||
---|---|---|
879 | 879 |
label_fields_permissions: Fields permissions |
880 | 880 |
label_readonly: Read-only |
881 | 881 |
label_required: Required |
882 |
label_hidden: " Hidden " |
|
882 | 883 |
label_attribute_of_project: "Project's %{name}" |
883 | 884 |
label_attribute_of_author: "Author's %{name}" |
884 | 885 |
label_attribute_of_assigned_to: "Assignee's %{name}" |
lib/redmine/export/pdf.rb (working copy) | ||
---|---|---|
520 | 520 |
pdf.Ln |
521 | 521 | |
522 | 522 |
left = [] |
523 |
left << [l(:field_status), issue.status] |
|
524 |
left << [l(:field_priority), issue.priority] |
|
525 |
left << [l(:field_assigned_to), issue.assigned_to] unless issue.disabled_core_fields.include?('assigned_to_id') |
|
526 |
left << [l(:field_category), issue.category] unless issue.disabled_core_fields.include?('category_id') |
|
527 |
left << [l(:field_fixed_version), issue.fixed_version] unless issue.disabled_core_fields.include?('fixed_version_id') |
|
523 |
left << [l(:field_status), issue.status] unless issue.hidden_attribute?('status')
|
|
524 |
left << [l(:field_priority), issue.priority] unless issue.hidden_attribute?('priority')
|
|
525 |
left << [l(:field_assigned_to), issue.assigned_to] unless issue.disabled_core_fields.include?('assigned_to_id') or issue.hidden_attribute?('assigned_to_id')
|
|
526 |
left << [l(:field_category), issue.category] unless issue.disabled_core_fields.include?('category_id') or issue.hidden_attribute?('category_id')
|
|
527 |
left << [l(:field_fixed_version), issue.fixed_version] unless issue.disabled_core_fields.include?('fixed_version_id') or issue.hidden_attribute?('fixed_version_id')
|
|
528 | 528 | |
529 | 529 |
right = [] |
530 |
right << [l(:field_start_date), format_date(issue.start_date)] unless issue.disabled_core_fields.include?('start_date') |
|
531 |
right << [l(:field_due_date), format_date(issue.due_date)] unless issue.disabled_core_fields.include?('due_date') |
|
532 |
right << [l(:field_done_ratio), "#{issue.done_ratio}%"] unless issue.disabled_core_fields.include?('done_ratio') |
|
533 |
right << [l(:field_estimated_hours), l_hours(issue.estimated_hours)] unless issue.disabled_core_fields.include?('estimated_hours') |
|
530 |
right << [l(:field_start_date), format_date(issue.start_date)] unless issue.disabled_core_fields.include?('start_date') or issue.hidden_attribute?('start_date')
|
|
531 |
right << [l(:field_due_date), format_date(issue.due_date)] unless issue.disabled_core_fields.include?('due_date') or issue.hidden_attribute?('due_date')
|
|
532 |
right << [l(:field_done_ratio), "#{issue.done_ratio}%"] unless issue.disabled_core_fields.include?('done_ratio') or issue.hidden_attribute?('done_ratio')
|
|
533 |
right << [l(:field_estimated_hours), l_hours(issue.estimated_hours)] unless issue.disabled_core_fields.include?('estimated_hours') or issue.hidden_attribute?('estimated_hours')
|
|
534 | 534 |
right << [l(:label_spent_time), l_hours(issue.total_spent_hours)] if User.current.allowed_to?(:view_time_entries, issue.project) |
535 | 535 | |
536 | 536 |
rows = left.size > right.size ? left.size : right.size |
... | ... | |
541 | 541 |
right << nil |
542 | 542 |
end |
543 | 543 | |
544 |
half = (issue.custom_field_values.size / 2.0).ceil |
|
545 |
issue.custom_field_values.each_with_index do |custom_value, i| |
|
546 |
(i < half ? left : right) << [custom_value.custom_field.name, show_value(custom_value)] |
|
544 |
half = (issue.viewable_custom_field_values.size / 2.0).ceil
|
|
545 |
issue.viewable_custom_field_values.each_with_index do |custom_value, i|
|
|
546 |
(i < half ? left : right) << [custom_value.custom_field.name, show_value(custom_value)] unless issue.hidden_attribute?(custom_value.custom_field.name)
|
|
547 | 547 |
end |
548 | 548 | |
549 | 549 |
rows = left.size > right.size ? left.size : right.size |