Feature #12005 » workflow_hidden_field_DR_v0.06.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| | 
| 224 | if detail.property == 'cf' | |
| 225 | field_id = detail.prop_key | |
| 226 | field = CustomField.find_by_id(field_id) | |
| 227 | if field && field.multiple? | |
| 228 |           values_by_field[field_id] ||= {:added => [], :deleted => []}
 | |
| 229 | if detail.old_value | |
| 230 | values_by_field[field_id][:deleted] << detail.old_value | |
| 231 | end | |
| 232 | if detail.value | |
| 233 | values_by_field[field_id][:added] << detail.value | |
| 234 | end | |
| 235 | next | |
| 236 | end | |
| 225 | unless detail.journal.issue.hidden_attribute?(detail.prop_key, options[:user]) | |
| 226 | if detail.property == 'cf' | |
| 227 | field_id = detail.prop_key | |
| 228 | field = CustomField.find_by_id(field_id) | |
| 229 | if field && field.multiple? | |
| 230 |       		  values_by_field[field_id] ||= {:added => [], :deleted => []}
 | |
| 231 | if detail.old_value | |
| 232 | values_by_field[field_id][:deleted] << detail.old_value | |
| 233 | end | |
| 234 | if detail.value | |
| 235 | values_by_field[field_id][:added] << detail.value | |
| 236 | end | |
| 237 | next | |
| 238 | end | |
| 239 | end | |
| 240 | strings << show_detail(detail, no_html, options) | |
| 237 | 241 | end | 
| 238 | strings << show_detail(detail, no_html, options) | |
| 239 | 242 | end | 
| 240 | 243 | values_by_field.each do |field_id, changes| | 
| 241 | detail = JournalDetail.new(:property => 'cf', :prop_key => field_id) | |
| 242 | if changes[:added].any? | |
| 243 | detail.value = changes[:added] | |
| 244 | strings << show_detail(detail, no_html, options) | |
| 245 | elsif changes[:deleted].any? | |
| 246 | detail.old_value = changes[:deleted] | |
| 247 | strings << show_detail(detail, no_html, options) | |
| 248 | end | |
| 244 | unless detail.journal.issue.hidden_attribute?(detail.prop_key, options[:user]) | |
| 245 | detail = JournalDetail.new(:property => 'cf', :prop_key => field_id) | |
| 246 | if changes[:added].any? | |
| 247 | detail.value = changes[:added] | |
| 248 | strings << show_detail(detail, no_html, options) | |
| 249 | elsif changes[:deleted].any? | |
| 250 | detail.old_value = changes[:deleted] | |
| 251 | strings << show_detail(detail, no_html, options) | |
| 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| | 
| 384 |           s = if column.is_a?(QueryCustomFieldColumn)
 | |
| 385 |             cv = issue.custom_field_values.detect {|v| v.custom_field_id == column.custom_field.id}
 | |
| 386 |             show_value(cv)
 | |
| 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 |             ""
 | |
| 387 | 392 | else | 
| 388 | value = column.value(issue) | |
| 389 | if value.is_a?(Date) | |
| 390 | format_date(value) | |
| 391 | elsif value.is_a?(Time) | |
| 392 | format_time(value) | |
| 393 | elsif value.is_a?(Float) | |
| 394 |               ("%.2f" % value).gsub('.', decimal_separator)
 | |
| 393 | s = if column.is_a?(QueryCustomFieldColumn) | |
| 394 |               cv = issue.custom_field_values.detect {|v| v.custom_field_id == column.custom_field.id}
 | |
| 395 | show_value(cv) | |
| 395 | 396 | else | 
| 396 | value | |
| 397 | value = column.value(issue) | |
| 398 | if value.is_a?(Date) | |
| 399 | format_date(value) | |
| 400 | elsif value.is_a?(Time) | |
| 401 | format_time(value) | |
| 402 | elsif value.is_a?(Float) | |
| 403 |                 ("%.2f" % value).gsub('.', decimal_separator)
 | |
| 404 | else | |
| 405 | value | |
| 406 | end | |
| 397 | 407 | end | 
| 408 | s.to_s | |
| 398 | 409 | end | 
| 399 | s.to_s | |
| 400 | 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)] : []) | 
| app/helpers/workflows_helper.rb (working copy) | ||
|---|---|---|
| 25 | 25 | def field_permission_tag(permissions, status, field) | 
| 26 | 26 | name = field.is_a?(CustomField) ? field.id.to_s : field | 
| 27 | 27 | options = [["", ""], [l(:label_readonly), "readonly"]] | 
| 28 | options << [l(:label_hidden), "hidden"] | |
| 28 | 29 | options << [l(:label_required), "required"] unless field_required?(field) | 
| 29 | 30 |  | 
| 30 | 31 |     select_tag("permissions[#{name}][#{status.id}]", options_for_select(options, permissions[status.id][name]))
 | 
| 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 | hidden_attribute_names(user).include?(name.to_s) | |
| 494 | end | |
| 495 |  | |
| 496 |  | |
| 478 | 497 | # Returns a hash of the workflow rule by attribute for the given user | 
| 479 | 498 | # | 
| 480 | 499 | # Examples: | 
| app/models/issue_observer.rb (working copy) | ||
|---|---|---|
| 16 | 16 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | 
| 17 | 17 |  | 
| 18 | 18 | class IssueObserver < ActiveRecord::Observer | 
| 19 | def after_create(issue) | |
| 20 |     Mailer.issue_add(issue).deliver if Setting.notified_events.include?('issue_added')
 | |
| 19 | def after_create(issue) | |
| 20 |     if Setting.notified_events.include?('issue_added')    
 | |
| 21 | recipients = issue.notified_users + issue.watcher_recipient_users | |
| 22 |  | |
| 23 | if recipients.any? | |
| 24 |         variations = recipients.collect { |user| issue.hidden_attribute_names(user) }.uniq
 | |
| 25 |         recipient_groups = Array.new(variations.count) { Array.new }
 | |
| 26 | recipients.each do |user| | |
| 27 | recipient_groups[variations.index(issue.hidden_attribute_names(user))] << user | |
| 28 | end | |
| 29 |  | |
| 30 | recipient_groups.each do |group| | |
| 31 | Mailer.issue_add(issue, group).deliver | |
| 32 | end | |
| 33 | end | |
| 34 | end | |
| 21 | 35 | end | 
| 22 | 36 | end | 
| app/models/journal_observer.rb (working copy) | ||
|---|---|---|
| 23 | 23 |           (Setting.notified_events.include?('issue_status_updated') && journal.new_status.present?) ||
 | 
| 24 | 24 |           (Setting.notified_events.include?('issue_priority_updated') && journal.new_value_for('priority_id').present?)
 | 
| 25 | 25 | ) | 
| 26 | Mailer.issue_edit(journal).deliver | |
| 26 |  | |
| 27 | recipients = journal.journalized.notified_users | |
| 28 | recipients += journal.journalized.watcher_recipient_users | |
| 29 | if journal.private_notes? | |
| 30 |         recipients = recipients.select {|user| user.allowed_to?(:view_private_notes, journal.journalized.project)}
 | |
| 31 | end | |
| 32 |  | |
| 33 | if recipients.any? | |
| 34 |         variations = recipients.collect { |user| journal.issue.hidden_attribute_names(user) }.uniq
 | |
| 35 |         recipient_groups = Array.new(variations.count) { Array.new }
 | |
| 36 | recipients.each do |user| | |
| 37 | recipient_groups[variations.index(journal.issue.hidden_attribute_names(user))] << user | |
| 38 | end | |
| 39 |  | |
| 40 |  | |
| 41 | recipient_groups.each do |group| | |
| 42 | Mailer.issue_edit(journal, group).deliver | |
| 43 | end | |
| 44 | end | |
| 27 | 45 | end | 
| 28 | 46 | end | 
| 29 | 47 | end | 
| app/models/mailer.rb (working copy) | ||
|---|---|---|
| 32 | 32 | # Example: | 
| 33 | 33 | # issue_add(issue) => Mail::Message object | 
| 34 | 34 | # Mailer.issue_add(issue).deliver => sends an email to issue recipients | 
| 35 | def issue_add(issue) | |
| 35 |   def issue_add(issue, ausers)
 | |
| 36 | 36 | redmine_headers 'Project' => issue.project.identifier, | 
| 37 | 37 | 'Issue-Id' => issue.id, | 
| 38 | 38 | 'Issue-Author' => issue.author.login | 
| ... | ... | |
| 41 | 41 | @author = issue.author | 
| 42 | 42 | @issue = issue | 
| 43 | 43 | @issue_url = url_for(:controller => 'issues', :action => 'show', :id => issue) | 
| 44 | recipients = issue.recipients | |
| 45 | cc = issue.watcher_recipients - recipients | |
| 44 |  | |
| 45 | @auser = ausers[0] | |
| 46 | recipients = ausers.map(&:mail) | |
| 47 |     cc = issue.watcher_recipient_users.map(&:mail).select {|rcpt| recipients.include?(rcpt)}
 | |
| 46 | 48 | mail :to => recipients, | 
| 47 | 49 | :cc => cc, | 
| 48 | 50 |       :subject => "[#{issue.project.name} - #{issue.tracker.name} ##{issue.id}] (#{issue.status.name}) #{issue.subject}"
 | 
| 49 | 51 | end | 
| 50 | 52 |  | 
| 53 |  | |
| 54 |  | |
| 51 | 55 | # Builds a Mail::Message object used to email recipients of the edited issue. | 
| 52 | 56 | # | 
| 53 | 57 | # Example: | 
| 54 | # issue_edit(journal) => Mail::Message object | |
| 55 |   #   Mailer.issue_edit(journal).deliver => sends an email to issue recipients
 | |
| 56 | def issue_edit(journal) | |
| 58 |   #   issue_edit(journal, users) => Mail::Message object
 | |
| 59 |   #   Mailer.issue_edit(journal, users).deliver => sends an email to +users+
 | |
| 60 |   def issue_edit(journal, ausers)
 | |
| 57 | 61 | issue = journal.journalized.reload | 
| 58 | 62 | redmine_headers 'Project' => issue.project.identifier, | 
| 59 | 63 | 'Issue-Id' => issue.id, | 
| ... | ... | |
| 62 | 66 | message_id journal | 
| 63 | 67 | references issue | 
| 64 | 68 | @author = journal.user | 
| 65 | recipients = journal.recipients | |
| 66 | # Watchers in cc | |
| 67 | cc = issue.watcher_recipients - recipients | |
| 69 | @auser = ausers[0] | |
| 70 | recipients = ausers.map(&:mail) | |
| 71 |     cc = issue.watcher_recipient_users.map(&:mail).select {|rcpt| recipients.include?(rcpt)}
 | |
| 72 |     recipients.reject! {|rcpt| cc.include?(rcpt)}
 | |
| 73 |  | |
| 68 | 74 |     s = "[#{issue.project.name} - #{issue.tracker.name} ##{issue.id}] "
 | 
| 69 | 75 |     s << "(#{issue.status.name}) " if journal.new_value_for('status_id')
 | 
| 70 | 76 | s << issue.subject | 
| 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 | issue.send name | |
| 47 |     hidden_fields = issue.hidden_attribute_names.map {|field| field.sub(/_id$/, '')}
 | |
| 48 | if hidden_fields.include?(name.to_s) | |
| 49 | "" | |
| 50 | else | |
| 51 | issue.send name | |
| 52 | end | |
| 48 | 53 | end | 
| 49 | 54 |  | 
| 50 | 55 | def css_classes | 
| ... | ... | |
| 360 | 365 |     Tracker.disabled_core_fields(trackers).each {|field|
 | 
| 361 | 366 | @available_filters.delete field | 
| 362 | 367 | } | 
| 368 |  | |
| 369 | if project != nil | |
| 370 |       project.completely_hidden_attribute_names.each {|field|
 | |
| 371 | @available_filters.delete field | |
| 372 | } | |
| 373 | end | |
| 374 |  | |
| 363 | 375 | @available_filters.each do |field, options| | 
| 364 | 376 |       options[:name] ||= l(options[:label] || "field_#{field}".gsub(/_id$/, ''))
 | 
| 365 | 377 | end | 
| ... | ... | |
| 451 | 463 | def available_columns | 
| 452 | 464 | return @available_columns if @available_columns | 
| 453 | 465 | @available_columns = ::Query.available_columns.dup | 
| 466 |  | |
| 467 |     hidden_fields = project == nil ? [] : project.completely_hidden_attribute_names.map {|field| field.sub(/_id$/, '')}
 | |
| 468 |  | |
| 454 | 469 | @available_columns += (project ? | 
| 455 | 470 | project.all_issue_custom_fields : | 
| 456 | 471 | IssueCustomField.find(:all) | 
| 457 |                            ).collect {|cf| QueryCustomFieldColumn.new(cf) }
 | |
| 472 |                            ).collect {|cf| QueryCustomFieldColumn.new(cf) }.reject{|column| hidden_fields.include?(column.custom_field.id.to_s) }
 | |
| 458 | 473 |  | 
| 459 | 474 | if User.current.allowed_to?(:view_time_entries, project, :global => true) | 
| 460 | 475 | index = nil | 
| ... | ... | |
| 477 | 492 |     @available_columns.reject! {|column|
 | 
| 478 | 493 | disabled_fields.include?(column.name.to_s) | 
| 479 | 494 | } | 
| 495 |  | |
| 496 |     @available_columns.reject! {|column|
 | |
| 497 | hidden_fields.include?(column.name.to_s) | |
| 498 | } | |
| 480 | 499 |  | 
| 481 | 500 | @available_columns | 
| 482 | 501 | end | 
| ... | ... | |
| 966 | 985 |     @available_filters ||= {}
 | 
| 967 | 986 |  | 
| 968 | 987 | custom_fields.select(&:is_filter?).each do |field| | 
| 969 | case field.field_format | |
| 970 | when "text" | |
| 971 |         options = { :type => :text, :order => 20 }
 | |
| 972 | when "list" | |
| 973 |         options = { :type => :list_optional, :values => field.possible_values, :order => 20}
 | |
| 974 | when "date" | |
| 975 |         options = { :type => :date, :order => 20 }
 | |
| 976 | when "bool" | |
| 977 |         options = { :type => :list, :values => [[l(:general_text_yes), "1"], [l(:general_text_no), "0"]], :order => 20 }
 | |
| 978 | when "int" | |
| 979 |         options = { :type => :integer, :order => 20 }
 | |
| 980 | when "float" | |
| 981 |         options = { :type => :float, :order => 20 }
 | |
| 982 | when "user", "version" | |
| 983 | next unless project | |
| 984 | values = field.possible_values_options(project) | |
| 985 | if User.current.logged? && field.field_format == 'user' | |
| 986 |           values.unshift ["<< #{l(:label_me)} >>", "me"]
 | |
| 988 | unless project == nil or project.completely_hidden_attribute_names.include?(field.id.to_s) | |
| 989 | case field.field_format | |
| 990 | when "text" | |
| 991 |           options = { :type => :text, :order => 20 }
 | |
| 992 | when "list" | |
| 993 |           options = { :type => :list_optional, :values => field.possible_values, :order => 20}
 | |
| 994 | when "date" | |
| 995 |           options = { :type => :date, :order => 20 }
 | |
| 996 | when "bool" | |
| 997 |           options = { :type => :list, :values => [[l(:general_text_yes), "1"], [l(:general_text_no), "0"]], :order => 20 }
 | |
| 998 | when "int" | |
| 999 |           options = { :type => :integer, :order => 20 }
 | |
| 1000 | when "float" | |
| 1001 |           options = { :type => :float, :order => 20 }
 | |
| 1002 | when "user", "version" | |
| 1003 | next unless project | |
| 1004 | values = field.possible_values_options(project) | |
| 1005 | if User.current.logged? && field.field_format == 'user' | |
| 1006 |             values.unshift ["<< #{l(:label_me)} >>", "me"]
 | |
| 1007 | end | |
| 1008 |           options = { :type => :list_optional, :values => values, :order => 20}
 | |
| 1009 | else | |
| 1010 |           options = { :type => :string, :order => 20 }
 | |
| 987 | 1011 | end | 
| 988 |         options = { :type => :list_optional, :values => values, :order => 20}
 | |
| 989 | else | |
| 990 |         options = { :type => :string, :order => 20 }
 | |
| 1012 |         filter_id = "cf_#{field.id}"
 | |
| 1013 | filter_name = field.name | |
| 1014 | if assoc.present? | |
| 1015 |           filter_id = "#{assoc}.#{filter_id}"
 | |
| 1016 |           filter_name = l("label_attribute_of_#{assoc}", :name => filter_name)
 | |
| 1017 | end | |
| 1018 |         @available_filters[filter_id] = options.merge({
 | |
| 1019 | :name => filter_name, | |
| 1020 | :format => field.field_format, | |
| 1021 | :field => field | |
| 1022 | }) | |
| 991 | 1023 | end | 
| 992 |       filter_id = "cf_#{field.id}"
 | |
| 993 | filter_name = field.name | |
| 994 | if assoc.present? | |
| 995 |         filter_id = "#{assoc}.#{filter_id}"
 | |
| 996 |         filter_name = l("label_attribute_of_#{assoc}", :name => filter_name)
 | |
| 997 | end | |
| 998 |       @available_filters[filter_id] = options.merge({
 | |
| 999 | :name => filter_name, | |
| 1000 | :format => field.field_format, | |
| 1001 | :field => field | |
| 1002 | }) | |
| 1003 | 1024 | end | 
| 1004 | 1025 | end | 
| 1005 | 1026 |  | 
| app/models/workflow_permission.rb (working copy) | ||
|---|---|---|
| 16 | 16 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | 
| 17 | 17 |  | 
| 18 | 18 | class WorkflowPermission < WorkflowRule | 
| 19 | validates_inclusion_of :rule, :in => %w(readonly required) | |
| 19 |   validates_inclusion_of :rule, :in => %w(readonly required hidden)
 | |
| 20 | 20 | validate :validate_field_name | 
| 21 | 21 |  | 
| 22 | 22 | # Replaces the workflow permissions for the given tracker and role | 
| 23 | 23 | # | 
| 24 | 24 | # Example: | 
| 25 |   #   WorkflowPermission.replace_permissions role, tracker, {'due_date' => {'1' => 'readonly', '2' => 'required'}}
 | |
| 25 |   #   WorkflowPermission.replace_permissions role, tracker, {'due_date' => {'1' => 'readonly', '2' => 'required', '3' => 'hidden'}}
 | |
| 26 | 26 | def self.replace_permissions(tracker, role, permissions) | 
| 27 | 27 | destroy_all(:tracker_id => tracker.id, :role_id => role.id) | 
| 28 | 28 |  | 
| 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 | <div id="change-<%= journal.id %>" class="<%= journal.css_classes %>"> | |
| 4 | <div id="note-<%= journal.indice %>"> | |
| 5 |     <h4><%= link_to "##{journal.indice}", {:anchor => "note-#{journal.indice}"}, :class => "journal-link" %>
 | |
| 6 | <%= avatar(journal.user, :size => "24") %> | |
| 7 | <%= authoring journal.created_on, journal.user, :label => :label_updated_time_by %></h4> | |
| 8 |  | |
| 9 | <% if journal.details.any? %> | |
| 10 | <ul class="details"> | |
| 11 | <% details_to_strings(journal.details).each do |string| %> | |
| 12 | <li><%= string %></li> | |
| 3 | <% if details_to_strings(journal.details).any? || journal.notes.blank? == false %> | |
| 4 | <div id="change-<%= journal.id %>" class="<%= journal.css_classes %>"> | |
| 5 | <div id="note-<%= journal.indice %>"> | |
| 6 |       <h4><%= link_to "##{journal.indice}", {:anchor => "note-#{journal.indice}"}, :class => "journal-link" %>
 | |
| 7 | <%= avatar(journal.user, :size => "24") %> | |
| 8 | <%= authoring journal.created_on, journal.user, :label => :label_updated_time_by %></h4> | |
| 9 |  | |
| 10 | <% if journal.details.any? %> | |
| 11 | <ul class="details"> | |
| 12 | <% details_to_strings(journal.details).each do |string| %> | |
| 13 | <li><%= string %></li> | |
| 14 | <% end %> | |
| 15 | </ul> | |
| 13 | 16 | <% end %> | 
| 14 | </ul> | |
| 15 | <% end %> | |
| 16 | <%= render_notes(issue, journal, :reply_links => reply_links) unless journal.notes.blank? %> | |
| 17 | <%= render_notes(issue, journal, :reply_links => reply_links) unless journal.notes.blank? %> | |
| 18 | </div> | |
| 17 | 19 | </div> | 
| 18 |   </div>
 | |
| 19 |   <%= call_hook(:view_issues_history_journal_bottom, { :journal => journal }) %>
 | |
| 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 | rows.left l(:field_status), h(@issue.status.name), :class => 'status' | |
| 37 | rows.left l(:field_priority), h(@issue.priority.name), :class => 'priority' | |
| 36 |   unless @issue.hidden_attribute?('status')
 | |
| 37 | rows.left l(:field_status), h(@issue.status.name), :class => 'status' | |
| 38 | end | |
| 39 |   unless @issue.hidden_attribute?('priority')
 | |
| 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 | 
| app/views/mailer/_issue.html.erb (working copy) | ||
|---|---|---|
| 2 | 2 |  | 
| 3 | 3 | <ul> | 
| 4 | 4 | <li><%=l(:field_author)%>: <%=h issue.author %></li> | 
| 5 | <li><%=l(:field_status)%>: <%=h issue.status %></li> | |
| 6 | <li><%=l(:field_priority)%>: <%=h issue.priority %></li> | |
| 7 | <li><%=l(:field_assigned_to)%>: <%=h issue.assigned_to %></li> | |
| 8 | <li><%=l(:field_category)%>: <%=h issue.category %></li> | |
| 9 | <li><%=l(:field_fixed_version)%>: <%=h issue.fixed_version %></li> | |
| 5 | <% unless issue.hidden_attribute?('status', user) %>
 | |
| 6 | <li><%=l(:field_status)%>: <%=h issue.status %></li> | |
| 7 | <% end %> | |
| 8 | <% unless issue.hidden_attribute?('priority', user) %>
 | |
| 9 | <li><%=l(:field_priority)%>: <%=h issue.priority %></li> | |
| 10 | <% end %> | |
| 11 | <% unless issue.disabled_core_fields.include?('assigned_to_id') || issue.hidden_attribute?('assigned_to_id', user) %>
 | |
| 12 | <li><%=l(:field_assigned_to)%>: <%=h issue.assigned_to %></li> | |
| 13 | <% end %> | |
| 14 | <% unless issue.disabled_core_fields.include?('category_id') || issue.hidden_attribute?('category_id', user) %>
 | |
| 15 | <li><%=l(:field_category)%>: <%=h issue.category %></li> | |
| 16 | <% end %> | |
| 17 | <% unless issue.disabled_core_fields.include?('fixed_version_id') || issue.hidden_attribute?('fixed_version_id', user) %>
 | |
| 18 | <li><%=l(:field_fixed_version)%>: <%=h issue.fixed_version %></li> | |
| 19 | <% end %> | |
| 10 | 20 | <% issue.custom_field_values.each do |c| %> | 
| 11 | <li><%=h c.custom_field.name %>: <%=h show_value(c) %></li> | |
| 21 | <% unless issue.hidden_attribute?(c.custom_field.id, user) %> | |
| 22 | <li><%=h c.custom_field.name %>: <%=h show_value(c) %></li> | |
| 23 | <% end %> | |
| 12 | 24 | <% end %> | 
| 13 | 25 | </ul> | 
| 14 | 26 |  | 
| app/views/mailer/_issue.text.erb (working copy) | ||
|---|---|---|
| 2 | 2 | <%= issue_url %> | 
| 3 | 3 |  | 
| 4 | 4 | * <%=l(:field_author)%>: <%= issue.author %> | 
| 5 | <% unless issue.hidden_attribute?('status', user) %>
 | |
| 5 | 6 | * <%=l(:field_status)%>: <%= issue.status %> | 
| 7 | <% end %> | |
| 8 | <% unless issue.hidden_attribute?('priority', user) %>
 | |
| 6 | 9 | * <%=l(:field_priority)%>: <%= issue.priority %> | 
| 10 | <% end %> | |
| 11 | <% unless issue.disabled_core_fields.include?('assigned_to_id') || issue.hidden_attribute?('assigned_to_id', user) %>
 | |
| 7 | 12 | * <%=l(:field_assigned_to)%>: <%= issue.assigned_to %> | 
| 13 | <% end %> | |
| 14 | <% unless issue.disabled_core_fields.include?('category_id') || issue.hidden_attribute?('category_id', user) %>
 | |
| 8 | 15 | * <%=l(:field_category)%>: <%= issue.category %> | 
| 16 | <% end %> | |
| 17 | <% unless issue.disabled_core_fields.include?('fixed_version_id') || issue.hidden_attribute?('fixed_version_id', user) %>
 | |
| 9 | 18 | * <%=l(:field_fixed_version)%>: <%= issue.fixed_version %> | 
| 10 | <% issue.custom_field_values.each do |c| %>* <%= c.custom_field.name %>: <%= show_value(c) %> | |
| 19 | <% end %> | |
| 20 | <% issue.custom_field_values.each do |c| %> | |
| 21 | <% unless issue.hidden_attribute?(c.custom_field.id, user) %> | |
| 22 | * <%= c.custom_field.name %>: <%= show_value(c) %> | |
| 23 | <% end %> | |
| 11 | 24 | <% end -%> | 
| 12 | 25 | ---------------------------------------- | 
| 13 | 26 | <%= issue.description %> | 
| app/views/mailer/issue_add.html.erb (working copy) | ||
|---|---|---|
| 1 | 1 | <%= l(:text_issue_added, :id => "##{@issue.id}", :author => h(@issue.author)) %>
 | 
| 2 | 2 | <hr /> | 
| 3 | <%= render :partial => 'issue', :formats => [:html], :locals => { :issue => @issue, :issue_url => @issue_url } %>
 | |
| 3 | <%= render :partial => 'issue', :formats => [:html], :locals => { :issue => @issue, :issue_url => @issue_url, :user => @auser } %>
 | |
| app/views/mailer/issue_add.text.erb (working copy) | ||
|---|---|---|
| 1 | 1 | <%= l(:text_issue_added, :id => "##{@issue.id}", :author => @issue.author) %>
 | 
| 2 | 2 |  | 
| 3 | 3 | ---------------------------------------- | 
| 4 | <%= render :partial => 'issue', :formats => [:text], :locals => { :issue => @issue, :issue_url => @issue_url } %>
 | |
| 4 | <%= render :partial => 'issue', :formats => [:text], :locals => { :issue => @issue, :issue_url => @issue_url, :user => @auser } %>
 | |
| app/views/mailer/issue_edit.html.erb (working copy) | ||
|---|---|---|
| 1 | 1 | <%= l(:text_issue_updated, :id => "##{@issue.id}", :author => h(@journal.user)) %>
 | 
| 2 | 2 |  | 
| 3 | 3 | <ul> | 
| 4 | <% details_to_strings(@journal.details, false, :only_path => false).each do |string| %> | |
| 4 | <% details_to_strings(@journal.details, false, :only_path => false, :user => @auser).each do |string| %>
 | |
| 5 | 5 | <li><%= string %></li> | 
| 6 | 6 | <% end %> | 
| 7 | 7 | </ul> | 
| 8 | 8 |  | 
| 9 | 9 | <%= textilizable(@journal, :notes, :only_path => false) %> | 
| 10 | 10 | <hr /> | 
| 11 | <%= render :partial => 'issue', :formats => [:html], :locals => { :issue => @issue, :issue_url => @issue_url } %>
 | |
| 11 | <%= render :partial => 'issue', :formats => [:html], :locals => { :issue => @issue, :issue_url => @issue_url, :user => @auser } %>
 | |
| app/views/mailer/issue_edit.text.erb (working copy) | ||
|---|---|---|
| 1 | 1 | <%= l(:text_issue_updated, :id => "##{@issue.id}", :author => @journal.user) %>
 | 
| 2 | 2 |  | 
| 3 | <% details_to_strings(@journal.details, true).each do |string| -%> | |
| 3 | <% details_to_strings(@journal.details, true, :user => @auser).each do |string| -%>
 | |
| 4 | 4 | <%= string %> | 
| 5 | 5 | <% end -%> | 
| 6 | 6 |  | 
| ... | ... | |
| 9 | 9 |  | 
| 10 | 10 | <% end -%> | 
| 11 | 11 | ---------------------------------------- | 
| 12 | <%= render :partial => 'issue', :formats => [:text], :locals => { :issue => @issue, :issue_url => @issue_url } %>
 | |
| 12 | <%= render :partial => 'issue', :formats => [:text], :locals => { :issue => @issue, :issue_url => @issue_url, :user => @auser } %>
 | |
| 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/plugins/acts_as_watchable/lib/acts_as_watchable.rb (working copy) | ||
|---|---|---|
| 78 | 78 | notified.collect(&:mail).compact | 
| 79 | 79 | end | 
| 80 | 80 |  | 
| 81 | # Returns an array of watchers | |
| 82 | def watcher_recipient_users | |
| 83 | notified = watcher_users.active | |
| 84 |           notified.reject! {|user| user.mail_notification == 'none'}
 | |
| 85 |  | |
| 86 | if respond_to?(:visible?) | |
| 87 |             notified.reject! {|user| !visible?(user)}
 | |
| 88 | end | |
| 89 | notified | |
| 90 | end | |
| 91 |  | |
| 81 | 92 | module ClassMethods; end | 
| 82 | 93 | end | 
| 83 | 94 | end | 
| 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 |