Index: app/helpers/issues_helper.rb =================================================================== --- app/helpers/issues_helper.rb (revision 10764) +++ app/helpers/issues_helper.rb (working copy) @@ -146,12 +146,13 @@ end def render_custom_fields_rows(issue) - return if issue.custom_field_values.empty? + local_viewablecf=issue.viewable_custom_field_values + return if local_viewablecf.empty? ordered_values = [] - half = (issue.custom_field_values.size / 2.0).ceil + half = (local_viewablecf.size / 2.0).ceil half.times do |i| - ordered_values << issue.custom_field_values[i] - ordered_values << issue.custom_field_values[i + half] + ordered_values << local_viewablecf[i] + ordered_values << local_viewablecf[i + half] end s = "\n" n = 0 @@ -221,31 +222,35 @@ strings = [] values_by_field = {} details.each do |detail| - if detail.property == 'cf' - field_id = detail.prop_key - field = CustomField.find_by_id(field_id) - if field && field.multiple? - values_by_field[field_id] ||= {:added => [], :deleted => []} - if detail.old_value - values_by_field[field_id][:deleted] << detail.old_value - end - if detail.value - values_by_field[field_id][:added] << detail.value - end - next - end + unless detail.journal.issue.hidden_attribute?(detail.prop_key, options[:user]) + if detail.property == 'cf' + field_id = detail.prop_key + field = CustomField.find_by_id(field_id) + if field && field.multiple? + values_by_field[field_id] ||= {:added => [], :deleted => []} + if detail.old_value + values_by_field[field_id][:deleted] << detail.old_value + end + if detail.value + values_by_field[field_id][:added] << detail.value + end + next + end + end + strings << show_detail(detail, no_html, options) end - strings << show_detail(detail, no_html, options) end values_by_field.each do |field_id, changes| - detail = JournalDetail.new(:property => 'cf', :prop_key => field_id) - if changes[:added].any? - detail.value = changes[:added] - strings << show_detail(detail, no_html, options) - elsif changes[:deleted].any? - detail.old_value = changes[:deleted] - strings << show_detail(detail, no_html, options) - end + unless detail.journal.issue.hidden_attribute?(detail.prop_key, options[:user]) + detail = JournalDetail.new(:property => 'cf', :prop_key => field_id) + if changes[:added].any? + detail.value = changes[:added] + strings << show_detail(detail, no_html, options) + elsif changes[:deleted].any? + detail.old_value = changes[:deleted] + strings << show_detail(detail, no_html, options) + end + end end strings end @@ -381,22 +386,27 @@ # csv lines issues.each do |issue| col_values = columns.collect do |column| - s = if column.is_a?(QueryCustomFieldColumn) - cv = issue.custom_field_values.detect {|v| v.custom_field_id == column.custom_field.id} - show_value(cv) + hidden_fields = issue.hidden_attribute_names.map {|field| field.sub(/_id$/, '')} + if hidden_fields.include?(column.is_a?(QueryCustomFieldColumn) ? column.custom_field.id.to_s : column.name.to_s) + "" else - value = column.value(issue) - if value.is_a?(Date) - format_date(value) - elsif value.is_a?(Time) - format_time(value) - elsif value.is_a?(Float) - ("%.2f" % value).gsub('.', decimal_separator) + s = if column.is_a?(QueryCustomFieldColumn) + cv = issue.custom_field_values.detect {|v| v.custom_field_id == column.custom_field.id} + show_value(cv) else - value + value = column.value(issue) + if value.is_a?(Date) + format_date(value) + elsif value.is_a?(Time) + format_time(value) + elsif value.is_a?(Float) + ("%.2f" % value).gsub('.', decimal_separator) + else + value + end end + s.to_s end - s.to_s end csv << [ issue.id.to_s ] + col_values.collect {|c| Redmine::CodesetUtil.from_utf8(c.to_s, encoding) } + (options[:description] ? [Redmine::CodesetUtil.from_utf8(issue.description, encoding)] : []) Index: app/helpers/workflows_helper.rb =================================================================== --- app/helpers/workflows_helper.rb (revision 10764) +++ app/helpers/workflows_helper.rb (working copy) @@ -25,6 +25,7 @@ def field_permission_tag(permissions, status, field) name = field.is_a?(CustomField) ? field.id.to_s : field options = [["", ""], [l(:label_readonly), "readonly"]] + options << [l(:label_hidden), "hidden"] options << [l(:label_required), "required"] unless field_required?(field) select_tag("permissions[#{name}][#{status.id}]", options_for_select(options, permissions[status.id][name])) Index: app/models/issue.rb =================================================================== --- app/models/issue.rb (revision 10764) +++ app/models/issue.rb (working copy) @@ -446,6 +446,14 @@ end end + # Returns the custom_field_values that can be viewed by the given user + # For now: just exclude Fix Info and RNs, as it is printed seperately below description. + def viewable_custom_field_values(user=nil) + custom_field_values.reject do |value| + hidden_attribute_names(user).include?(value.custom_field_id.to_s) + end + end + # Returns the names of attributes that are read-only for user or the current user # For users with multiple roles, the read-only fields are the intersection of # read-only fields of each role @@ -455,9 +463,14 @@ # issue.read_only_attribute_names # => ['due_date', '2'] # issue.read_only_attribute_names(user) # => [] def read_only_attribute_names(user=nil) - workflow_rule_by_attribute(user).reject {|attr, rule| rule != 'readonly'}.keys + workflow_rule_by_attribute(user).reject {|attr, rule| rule != 'readonly' and rule != 'hidden'}.keys end + # Same as above, but for hidden fields + def hidden_attribute_names(user=nil) + workflow_rule_by_attribute(user).reject {|attr, rule| rule != 'hidden'}.keys + end + # Returns the names of required attributes for user or the current user # For users with multiple roles, the required fields are the intersection of # required fields of each role @@ -475,6 +488,12 @@ required_attribute_names(user).include?(name.to_s) end + # Returns true if the attribute should be hidden for user + def hidden_attribute?(name, user=nil) + hidden_attribute_names(user).include?(name.to_s) + end + + # Returns a hash of the workflow rule by attribute for the given user # # Examples: Index: app/models/issue_observer.rb =================================================================== --- app/models/issue_observer.rb (revision 10764) +++ app/models/issue_observer.rb (working copy) @@ -16,7 +16,21 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. class IssueObserver < ActiveRecord::Observer - def after_create(issue) - Mailer.issue_add(issue).deliver if Setting.notified_events.include?('issue_added') + def after_create(issue) + if Setting.notified_events.include?('issue_added') + recipients = issue.notified_users + issue.watcher_recipient_users + + if recipients.any? + variations = recipients.collect { |user| issue.hidden_attribute_names(user) }.uniq + recipient_groups = Array.new(variations.count) { Array.new } + recipients.each do |user| + recipient_groups[variations.index(issue.hidden_attribute_names(user))] << user + end + + recipient_groups.each do |group| + Mailer.issue_add(issue, group).deliver + end + end + end end end Index: app/models/journal_observer.rb =================================================================== --- app/models/journal_observer.rb (revision 10764) +++ app/models/journal_observer.rb (working copy) @@ -23,7 +23,25 @@ (Setting.notified_events.include?('issue_status_updated') && journal.new_status.present?) || (Setting.notified_events.include?('issue_priority_updated') && journal.new_value_for('priority_id').present?) ) - Mailer.issue_edit(journal).deliver + + recipients = journal.journalized.notified_users + recipients += journal.journalized.watcher_recipient_users + if journal.private_notes? + recipients = recipients.select {|user| user.allowed_to?(:view_private_notes, journal.journalized.project)} + end + + if recipients.any? + variations = recipients.collect { |user| journal.issue.hidden_attribute_names(user) }.uniq + recipient_groups = Array.new(variations.count) { Array.new } + recipients.each do |user| + recipient_groups[variations.index(journal.issue.hidden_attribute_names(user))] << user + end + + + recipient_groups.each do |group| + Mailer.issue_edit(journal, group).deliver + end + end end end end Index: app/models/mailer.rb =================================================================== --- app/models/mailer.rb (revision 10764) +++ app/models/mailer.rb (working copy) @@ -32,7 +32,7 @@ # Example: # issue_add(issue) => Mail::Message object # Mailer.issue_add(issue).deliver => sends an email to issue recipients - def issue_add(issue) + def issue_add(issue, ausers) redmine_headers 'Project' => issue.project.identifier, 'Issue-Id' => issue.id, 'Issue-Author' => issue.author.login @@ -41,19 +41,23 @@ @author = issue.author @issue = issue @issue_url = url_for(:controller => 'issues', :action => 'show', :id => issue) - recipients = issue.recipients - cc = issue.watcher_recipients - recipients + + @auser = ausers[0] + recipients = ausers.map(&:mail) + cc = issue.watcher_recipient_users.map(&:mail).select {|rcpt| recipients.include?(rcpt)} mail :to => recipients, :cc => cc, :subject => "[#{issue.project.name} - #{issue.tracker.name} ##{issue.id}] (#{issue.status.name}) #{issue.subject}" end + + # Builds a Mail::Message object used to email recipients of the edited issue. # # Example: - # issue_edit(journal) => Mail::Message object - # Mailer.issue_edit(journal).deliver => sends an email to issue recipients - def issue_edit(journal) + # issue_edit(journal, users) => Mail::Message object + # Mailer.issue_edit(journal, users).deliver => sends an email to +users+ + def issue_edit(journal, ausers) issue = journal.journalized.reload redmine_headers 'Project' => issue.project.identifier, 'Issue-Id' => issue.id, @@ -62,9 +66,11 @@ message_id journal references issue @author = journal.user - recipients = journal.recipients - # Watchers in cc - cc = issue.watcher_recipients - recipients + @auser = ausers[0] + recipients = ausers.map(&:mail) + cc = issue.watcher_recipient_users.map(&:mail).select {|rcpt| recipients.include?(rcpt)} + recipients.reject! {|rcpt| cc.include?(rcpt)} + s = "[#{issue.project.name} - #{issue.tracker.name} ##{issue.id}] " s << "(#{issue.status.name}) " if journal.new_value_for('status_id') s << issue.subject Index: app/models/project.rb =================================================================== --- app/models/project.rb (revision 10764) +++ app/models/project.rb (working copy) @@ -146,6 +146,36 @@ user.allowed_to?(:view_project, self) end + # Returns list of attributes that are hidden on all statuses of all trackers for +user+ or the current user. + def completely_hidden_attribute_names(user=nil) + user_real = user || User.current + roles = user_real.admin ? Role.all : user_real.roles_for_project(self) + return {} if roles.empty? + + result = {} + workflow_permissions = WorkflowPermission.where(:tracker_id => trackers.map(&:id), :old_status_id => IssueStatus.all.map(&:id), :role_id => roles.map(&:id), :rule => 'hidden').all + + if workflow_permissions.any? + workflow_rules = workflow_permissions.inject({}) do |h, wp| + h[wp.field_name] ||= [] + h[wp.field_name] << wp.rule + h + end + workflow_rules.each do |attr, rules| + next if rules.size < (roles.size * trackers.size * IssueStatus.all.size) + uniq_rules = rules.uniq + if uniq_rules.size == 1 + result[attr] = uniq_rules.first + else + result[attr] = 'required' + end + end + end + + result.keys + end + + # Returns a SQL conditions string used to find all projects visible by the specified user. # # Examples: Index: app/models/query.rb =================================================================== --- app/models/query.rb (revision 10764) +++ app/models/query.rb (working copy) @@ -44,7 +44,12 @@ end def value(issue) - issue.send name + hidden_fields = issue.hidden_attribute_names.map {|field| field.sub(/_id$/, '')} + if hidden_fields.include?(name.to_s) + "" + else + issue.send name + end end def css_classes @@ -360,6 +365,13 @@ Tracker.disabled_core_fields(trackers).each {|field| @available_filters.delete field } + + if project != nil + project.completely_hidden_attribute_names.each {|field| + @available_filters.delete field + } + end + @available_filters.each do |field, options| options[:name] ||= l(options[:label] || "field_#{field}".gsub(/_id$/, '')) end @@ -451,10 +463,13 @@ def available_columns return @available_columns if @available_columns @available_columns = ::Query.available_columns.dup + + hidden_fields = project == nil ? [] : project.completely_hidden_attribute_names.map {|field| field.sub(/_id$/, '')} + @available_columns += (project ? project.all_issue_custom_fields : IssueCustomField.find(:all) - ).collect {|cf| QueryCustomFieldColumn.new(cf) } + ).collect {|cf| QueryCustomFieldColumn.new(cf) }.reject{|column| hidden_fields.include?(column.custom_field.id.to_s) } if User.current.allowed_to?(:view_time_entries, project, :global => true) index = nil @@ -477,6 +492,10 @@ @available_columns.reject! {|column| disabled_fields.include?(column.name.to_s) } + + @available_columns.reject! {|column| + hidden_fields.include?(column.name.to_s) + } @available_columns end @@ -966,40 +985,42 @@ @available_filters ||= {} custom_fields.select(&:is_filter?).each do |field| - case field.field_format - when "text" - options = { :type => :text, :order => 20 } - when "list" - options = { :type => :list_optional, :values => field.possible_values, :order => 20} - when "date" - options = { :type => :date, :order => 20 } - when "bool" - options = { :type => :list, :values => [[l(:general_text_yes), "1"], [l(:general_text_no), "0"]], :order => 20 } - when "int" - options = { :type => :integer, :order => 20 } - when "float" - options = { :type => :float, :order => 20 } - when "user", "version" - next unless project - values = field.possible_values_options(project) - if User.current.logged? && field.field_format == 'user' - values.unshift ["<< #{l(:label_me)} >>", "me"] + unless project == nil or project.completely_hidden_attribute_names.include?(field.id.to_s) + case field.field_format + when "text" + options = { :type => :text, :order => 20 } + when "list" + options = { :type => :list_optional, :values => field.possible_values, :order => 20} + when "date" + options = { :type => :date, :order => 20 } + when "bool" + options = { :type => :list, :values => [[l(:general_text_yes), "1"], [l(:general_text_no), "0"]], :order => 20 } + when "int" + options = { :type => :integer, :order => 20 } + when "float" + options = { :type => :float, :order => 20 } + when "user", "version" + next unless project + values = field.possible_values_options(project) + if User.current.logged? && field.field_format == 'user' + values.unshift ["<< #{l(:label_me)} >>", "me"] + end + options = { :type => :list_optional, :values => values, :order => 20} + else + options = { :type => :string, :order => 20 } end - options = { :type => :list_optional, :values => values, :order => 20} - else - options = { :type => :string, :order => 20 } + filter_id = "cf_#{field.id}" + filter_name = field.name + if assoc.present? + filter_id = "#{assoc}.#{filter_id}" + filter_name = l("label_attribute_of_#{assoc}", :name => filter_name) + end + @available_filters[filter_id] = options.merge({ + :name => filter_name, + :format => field.field_format, + :field => field + }) end - filter_id = "cf_#{field.id}" - filter_name = field.name - if assoc.present? - filter_id = "#{assoc}.#{filter_id}" - filter_name = l("label_attribute_of_#{assoc}", :name => filter_name) - end - @available_filters[filter_id] = options.merge({ - :name => filter_name, - :format => field.field_format, - :field => field - }) end end Index: app/models/workflow_permission.rb =================================================================== --- app/models/workflow_permission.rb (revision 10764) +++ app/models/workflow_permission.rb (working copy) @@ -16,13 +16,13 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. class WorkflowPermission < WorkflowRule - validates_inclusion_of :rule, :in => %w(readonly required) + validates_inclusion_of :rule, :in => %w(readonly required hidden) validate :validate_field_name # Replaces the workflow permissions for the given tracker and role # # Example: - # WorkflowPermission.replace_permissions role, tracker, {'due_date' => {'1' => 'readonly', '2' => 'required'}} + # WorkflowPermission.replace_permissions role, tracker, {'due_date' => {'1' => 'readonly', '2' => 'required', '3' => 'hidden'}} def self.replace_permissions(tracker, role, permissions) destroy_all(:tracker_id => tracker.id, :role_id => role.id) Index: app/views/issues/_form_custom_fields.html.erb =================================================================== --- app/views/issues/_form_custom_fields.html.erb (revision 10764) +++ app/views/issues/_form_custom_fields.html.erb (working copy) @@ -1,7 +1,7 @@
<% i = 0 %> -<% split_on = (@issue.custom_field_values.size / 2.0).ceil - 1 %> +<% split_on = (@issue.editable_custom_field_values.size / 2.0).ceil - 1 %> <% @issue.editable_custom_field_values.each do |value| %>

<%= custom_field_tag_with_label :issue, value, :required => @issue.required_attribute?(value.custom_field_id) %>

<% if i == split_on -%> Index: app/views/issues/_history.html.erb =================================================================== --- app/views/issues/_history.html.erb (revision 10764) +++ app/views/issues/_history.html.erb (working copy) @@ -1,22 +1,24 @@ <% reply_links = authorize_for('issues', 'edit') -%> <% for journal in journals %> -
-
-

<%= link_to "##{journal.indice}", {:anchor => "note-#{journal.indice}"}, :class => "journal-link" %> - <%= avatar(journal.user, :size => "24") %> - <%= authoring journal.created_on, journal.user, :label => :label_updated_time_by %>

- - <% if journal.details.any? %> -
    - <% details_to_strings(journal.details).each do |string| %> -
  • <%= string %>
  • + <% if details_to_strings(journal.details).any? || journal.notes.blank? == false %> +
    +
    +

    <%= link_to "##{journal.indice}", {:anchor => "note-#{journal.indice}"}, :class => "journal-link" %> + <%= avatar(journal.user, :size => "24") %> + <%= authoring journal.created_on, journal.user, :label => :label_updated_time_by %>

    + + <% if journal.details.any? %> +
      + <% details_to_strings(journal.details).each do |string| %> +
    • <%= string %>
    • + <% end %> +
    <% end %> -
- <% end %> - <%= render_notes(issue, journal, :reply_links => reply_links) unless journal.notes.blank? %> + <%= render_notes(issue, journal, :reply_links => reply_links) unless journal.notes.blank? %> +
-
- <%= call_hook(:view_issues_history_journal_bottom, { :journal => journal }) %> + <%= call_hook(:view_issues_history_journal_bottom, { :journal => journal }) %> + <% end %> <% end %> <% heads_for_wiki_formatter if User.current.allowed_to?(:edit_issue_notes, issue.project) || User.current.allowed_to?(:edit_own_issue_notes, issue.project) %> Index: app/views/issues/show.html.erb =================================================================== --- app/views/issues/show.html.erb (revision 10764) +++ app/views/issues/show.html.erb (working copy) @@ -33,29 +33,33 @@ <%= issue_fields_rows do |rows| - rows.left l(:field_status), h(@issue.status.name), :class => 'status' - rows.left l(:field_priority), h(@issue.priority.name), :class => 'priority' + unless @issue.hidden_attribute?('status') + rows.left l(:field_status), h(@issue.status.name), :class => 'status' + end + unless @issue.hidden_attribute?('priority') + rows.left l(:field_priority), h(@issue.priority.name), :class => 'priority' + end - unless @issue.disabled_core_fields.include?('assigned_to_id') + unless @issue.disabled_core_fields.include?('assigned_to_id') || @issue.hidden_attribute?('assigned_to_id') 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' end - unless @issue.disabled_core_fields.include?('category_id') + unless @issue.disabled_core_fields.include?('category_id') || @issue.hidden_attribute?('category_id') rows.left l(:field_category), h(@issue.category ? @issue.category.name : "-"), :class => 'category' end - unless @issue.disabled_core_fields.include?('fixed_version_id') + unless @issue.disabled_core_fields.include?('fixed_version_id') || @issue.hidden_attribute?('fixed_version_id') rows.left l(:field_fixed_version), (@issue.fixed_version ? link_to_version(@issue.fixed_version) : "-"), :class => 'fixed-version' end - unless @issue.disabled_core_fields.include?('start_date') + unless @issue.disabled_core_fields.include?('start_date') || @issue.hidden_attribute?('start_date') rows.right l(:field_start_date), format_date(@issue.start_date), :class => 'start-date' end - unless @issue.disabled_core_fields.include?('due_date') + unless @issue.disabled_core_fields.include?('due_date') || @issue.hidden_attribute?('due_date') rows.right l(:field_due_date), format_date(@issue.due_date), :class => 'due-date' end - unless @issue.disabled_core_fields.include?('done_ratio') + unless @issue.disabled_core_fields.include?('done_ratio') || @issue.hidden_attribute?('done_ratio') rows.right l(:field_done_ratio), progress_bar(@issue.done_ratio, :width => '80px', :legend => "#{@issue.done_ratio}%"), :class => 'progress' end - unless @issue.disabled_core_fields.include?('estimated_hours') + unless @issue.disabled_core_fields.include?('estimated_hours') || @issue.hidden_attribute?('estimated_hours') unless @issue.estimated_hours.nil? rows.right l(:field_estimated_hours), l_hours(@issue.estimated_hours), :class => 'estimated-hours' end Index: app/views/mailer/_issue.html.erb =================================================================== --- app/views/mailer/_issue.html.erb (revision 10764) +++ app/views/mailer/_issue.html.erb (working copy) @@ -2,13 +2,25 @@ Index: app/views/mailer/_issue.text.erb =================================================================== --- app/views/mailer/_issue.text.erb (revision 10764) +++ app/views/mailer/_issue.text.erb (working copy) @@ -2,12 +2,25 @@ <%= issue_url %> * <%=l(:field_author)%>: <%= issue.author %> +<% unless issue.hidden_attribute?('status', user) %> * <%=l(:field_status)%>: <%= issue.status %> +<% end %> +<% unless issue.hidden_attribute?('priority', user) %> * <%=l(:field_priority)%>: <%= issue.priority %> +<% end %> +<% unless issue.disabled_core_fields.include?('assigned_to_id') || issue.hidden_attribute?('assigned_to_id', user) %> * <%=l(:field_assigned_to)%>: <%= issue.assigned_to %> +<% end %> +<% unless issue.disabled_core_fields.include?('category_id') || issue.hidden_attribute?('category_id', user) %> * <%=l(:field_category)%>: <%= issue.category %> +<% end %> +<% unless issue.disabled_core_fields.include?('fixed_version_id') || issue.hidden_attribute?('fixed_version_id', user) %> * <%=l(:field_fixed_version)%>: <%= issue.fixed_version %> -<% issue.custom_field_values.each do |c| %>* <%= c.custom_field.name %>: <%= show_value(c) %> +<% end %> +<% issue.custom_field_values.each do |c| %> +<% unless issue.hidden_attribute?(c.custom_field.id, user) %> +* <%= c.custom_field.name %>: <%= show_value(c) %> +<% end %> <% end -%> ---------------------------------------- <%= issue.description %> Index: app/views/mailer/issue_add.html.erb =================================================================== --- app/views/mailer/issue_add.html.erb (revision 10764) +++ app/views/mailer/issue_add.html.erb (working copy) @@ -1,3 +1,3 @@ <%= l(:text_issue_added, :id => "##{@issue.id}", :author => h(@issue.author)) %>
-<%= render :partial => 'issue', :formats => [:html], :locals => { :issue => @issue, :issue_url => @issue_url } %> +<%= render :partial => 'issue', :formats => [:html], :locals => { :issue => @issue, :issue_url => @issue_url, :user => @auser } %> Index: app/views/mailer/issue_add.text.erb =================================================================== --- app/views/mailer/issue_add.text.erb (revision 10764) +++ app/views/mailer/issue_add.text.erb (working copy) @@ -1,4 +1,4 @@ <%= l(:text_issue_added, :id => "##{@issue.id}", :author => @issue.author) %> ---------------------------------------- -<%= render :partial => 'issue', :formats => [:text], :locals => { :issue => @issue, :issue_url => @issue_url } %> +<%= render :partial => 'issue', :formats => [:text], :locals => { :issue => @issue, :issue_url => @issue_url, :user => @auser } %> Index: app/views/mailer/issue_edit.html.erb =================================================================== --- app/views/mailer/issue_edit.html.erb (revision 10764) +++ app/views/mailer/issue_edit.html.erb (working copy) @@ -1,11 +1,11 @@ <%= l(:text_issue_updated, :id => "##{@issue.id}", :author => h(@journal.user)) %> <%= textilizable(@journal, :notes, :only_path => false) %>
-<%= render :partial => 'issue', :formats => [:html], :locals => { :issue => @issue, :issue_url => @issue_url } %> +<%= render :partial => 'issue', :formats => [:html], :locals => { :issue => @issue, :issue_url => @issue_url, :user => @auser } %> Index: app/views/mailer/issue_edit.text.erb =================================================================== --- app/views/mailer/issue_edit.text.erb (revision 10764) +++ app/views/mailer/issue_edit.text.erb (working copy) @@ -1,6 +1,6 @@ <%= l(:text_issue_updated, :id => "##{@issue.id}", :author => @journal.user) %> -<% details_to_strings(@journal.details, true).each do |string| -%> +<% details_to_strings(@journal.details, true, :user => @auser).each do |string| -%> <%= string %> <% end -%> @@ -9,4 +9,4 @@ <% end -%> ---------------------------------------- -<%= render :partial => 'issue', :formats => [:text], :locals => { :issue => @issue, :issue_url => @issue_url } %> +<%= render :partial => 'issue', :formats => [:text], :locals => { :issue => @issue, :issue_url => @issue_url, :user => @auser } %> Index: config/locales/en.yml =================================================================== --- config/locales/en.yml (revision 10764) +++ config/locales/en.yml (working copy) @@ -879,6 +879,7 @@ label_fields_permissions: Fields permissions label_readonly: Read-only label_required: Required + label_hidden: " Hidden " label_attribute_of_project: "Project's %{name}" label_attribute_of_author: "Author's %{name}" label_attribute_of_assigned_to: "Assignee's %{name}" Index: lib/plugins/acts_as_watchable/lib/acts_as_watchable.rb =================================================================== --- lib/plugins/acts_as_watchable/lib/acts_as_watchable.rb (revision 10764) +++ lib/plugins/acts_as_watchable/lib/acts_as_watchable.rb (working copy) @@ -78,6 +78,17 @@ notified.collect(&:mail).compact end + # Returns an array of watchers + def watcher_recipient_users + notified = watcher_users.active + notified.reject! {|user| user.mail_notification == 'none'} + + if respond_to?(:visible?) + notified.reject! {|user| !visible?(user)} + end + notified + end + module ClassMethods; end end end Index: lib/redmine/export/pdf.rb =================================================================== --- lib/redmine/export/pdf.rb (revision 10764) +++ lib/redmine/export/pdf.rb (working copy) @@ -520,17 +520,17 @@ pdf.Ln left = [] - left << [l(:field_status), issue.status] - left << [l(:field_priority), issue.priority] - left << [l(:field_assigned_to), issue.assigned_to] unless issue.disabled_core_fields.include?('assigned_to_id') - left << [l(:field_category), issue.category] unless issue.disabled_core_fields.include?('category_id') - left << [l(:field_fixed_version), issue.fixed_version] unless issue.disabled_core_fields.include?('fixed_version_id') + left << [l(:field_status), issue.status] unless issue.hidden_attribute?('status') + left << [l(:field_priority), issue.priority] unless issue.hidden_attribute?('priority') + left << [l(:field_assigned_to), issue.assigned_to] unless issue.disabled_core_fields.include?('assigned_to_id') or issue.hidden_attribute?('assigned_to_id') + left << [l(:field_category), issue.category] unless issue.disabled_core_fields.include?('category_id') or issue.hidden_attribute?('category_id') + left << [l(:field_fixed_version), issue.fixed_version] unless issue.disabled_core_fields.include?('fixed_version_id') or issue.hidden_attribute?('fixed_version_id') right = [] - right << [l(:field_start_date), format_date(issue.start_date)] unless issue.disabled_core_fields.include?('start_date') - right << [l(:field_due_date), format_date(issue.due_date)] unless issue.disabled_core_fields.include?('due_date') - right << [l(:field_done_ratio), "#{issue.done_ratio}%"] unless issue.disabled_core_fields.include?('done_ratio') - right << [l(:field_estimated_hours), l_hours(issue.estimated_hours)] unless issue.disabled_core_fields.include?('estimated_hours') + right << [l(:field_start_date), format_date(issue.start_date)] unless issue.disabled_core_fields.include?('start_date') or issue.hidden_attribute?('start_date') + right << [l(:field_due_date), format_date(issue.due_date)] unless issue.disabled_core_fields.include?('due_date') or issue.hidden_attribute?('due_date') + right << [l(:field_done_ratio), "#{issue.done_ratio}%"] unless issue.disabled_core_fields.include?('done_ratio') or issue.hidden_attribute?('done_ratio') + right << [l(:field_estimated_hours), l_hours(issue.estimated_hours)] unless issue.disabled_core_fields.include?('estimated_hours') or issue.hidden_attribute?('estimated_hours') right << [l(:label_spent_time), l_hours(issue.total_spent_hours)] if User.current.allowed_to?(:view_time_entries, issue.project) rows = left.size > right.size ? left.size : right.size @@ -541,9 +541,9 @@ right << nil end - half = (issue.custom_field_values.size / 2.0).ceil - issue.custom_field_values.each_with_index do |custom_value, i| - (i < half ? left : right) << [custom_value.custom_field.name, show_value(custom_value)] + half = (issue.viewable_custom_field_values.size / 2.0).ceil + issue.viewable_custom_field_values.each_with_index do |custom_value, i| + (i < half ? left : right) << [custom_value.custom_field.name, show_value(custom_value)] unless issue.hidden_attribute?(custom_value.custom_field.name) end rows = left.size > right.size ? left.size : right.size