annotate app/helpers/issues_helper.rb @ 8:0c83d98252d9 yuya

* Add custom repo prefix and proper auth realm, remove auth cache (seems like an unwise feature), pass DB handle around, various other bits of tidying
author Chris Cannam
date Thu, 12 Aug 2010 15:31:37 +0100
parents 513646585e45
children 1d32c0a0efbf
rev   line source
Chris@0 1 # redMine - project management software
Chris@0 2 # Copyright (C) 2006 Jean-Philippe Lang
Chris@0 3 #
Chris@0 4 # This program is free software; you can redistribute it and/or
Chris@0 5 # modify it under the terms of the GNU General Public License
Chris@0 6 # as published by the Free Software Foundation; either version 2
Chris@0 7 # of the License, or (at your option) any later version.
Chris@0 8 #
Chris@0 9 # This program is distributed in the hope that it will be useful,
Chris@0 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
Chris@0 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
Chris@0 12 # GNU General Public License for more details.
Chris@0 13 #
Chris@0 14 # You should have received a copy of the GNU General Public License
Chris@0 15 # along with this program; if not, write to the Free Software
Chris@0 16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
Chris@0 17
Chris@0 18 module IssuesHelper
Chris@0 19 include ApplicationHelper
Chris@0 20
Chris@0 21 def issue_list(issues, &block)
Chris@0 22 ancestors = []
Chris@0 23 issues.each do |issue|
Chris@0 24 while (ancestors.any? && !issue.is_descendant_of?(ancestors.last))
Chris@0 25 ancestors.pop
Chris@0 26 end
Chris@0 27 yield issue, ancestors.size
Chris@0 28 ancestors << issue unless issue.leaf?
Chris@0 29 end
Chris@0 30 end
Chris@0 31
Chris@0 32 def render_issue_tooltip(issue)
Chris@0 33 @cached_label_start_date ||= l(:field_start_date)
Chris@0 34 @cached_label_due_date ||= l(:field_due_date)
Chris@0 35 @cached_label_assigned_to ||= l(:field_assigned_to)
Chris@0 36 @cached_label_priority ||= l(:field_priority)
Chris@0 37
Chris@0 38 link_to_issue(issue) + "<br /><br />" +
Chris@0 39 "<strong>#{@cached_label_start_date}</strong>: #{format_date(issue.start_date)}<br />" +
Chris@0 40 "<strong>#{@cached_label_due_date}</strong>: #{format_date(issue.due_date)}<br />" +
Chris@0 41 "<strong>#{@cached_label_assigned_to}</strong>: #{issue.assigned_to}<br />" +
Chris@0 42 "<strong>#{@cached_label_priority}</strong>: #{issue.priority.name}"
Chris@0 43 end
Chris@0 44
Chris@0 45 def render_issue_subject_with_tree(issue)
Chris@0 46 s = ''
Chris@0 47 issue.ancestors.each do |ancestor|
Chris@0 48 s << '<div>' + content_tag('p', link_to_issue(ancestor))
Chris@0 49 end
Chris@0 50 s << '<div>' + content_tag('h3', h(issue.subject))
Chris@0 51 s << '</div>' * (issue.ancestors.size + 1)
Chris@0 52 s
Chris@0 53 end
Chris@0 54
Chris@0 55 def render_descendants_tree(issue)
Chris@0 56 s = '<form><table class="list issues">'
Chris@0 57 issue_list(issue.descendants.sort_by(&:lft)) do |child, level|
Chris@0 58 s << content_tag('tr',
Chris@0 59 content_tag('td', check_box_tag("ids[]", child.id, false, :id => nil), :class => 'checkbox') +
Chris@0 60 content_tag('td', link_to_issue(child, :truncate => 60), :class => 'subject') +
Chris@0 61 content_tag('td', h(child.status)) +
Chris@0 62 content_tag('td', link_to_user(child.assigned_to)) +
Chris@0 63 content_tag('td', progress_bar(child.done_ratio, :width => '80px')),
Chris@0 64 :class => "issue issue-#{child.id} hascontextmenu #{level > 0 ? "idnt idnt-#{level}" : nil}")
Chris@0 65 end
Chris@0 66 s << '</form></table>'
Chris@0 67 s
Chris@0 68 end
Chris@0 69
Chris@0 70 def render_custom_fields_rows(issue)
Chris@0 71 return if issue.custom_field_values.empty?
Chris@0 72 ordered_values = []
Chris@0 73 half = (issue.custom_field_values.size / 2.0).ceil
Chris@0 74 half.times do |i|
Chris@0 75 ordered_values << issue.custom_field_values[i]
Chris@0 76 ordered_values << issue.custom_field_values[i + half]
Chris@0 77 end
Chris@0 78 s = "<tr>\n"
Chris@0 79 n = 0
Chris@0 80 ordered_values.compact.each do |value|
Chris@0 81 s << "</tr>\n<tr>\n" if n > 0 && (n % 2) == 0
Chris@0 82 s << "\t<th>#{ h(value.custom_field.name) }:</th><td>#{ simple_format_without_paragraph(h(show_value(value))) }</td>\n"
Chris@0 83 n += 1
Chris@0 84 end
Chris@0 85 s << "</tr>\n"
Chris@0 86 s
Chris@0 87 end
Chris@0 88
Chris@0 89 def sidebar_queries
Chris@0 90 unless @sidebar_queries
Chris@0 91 # User can see public queries and his own queries
Chris@0 92 visible = ARCondition.new(["is_public = ? OR user_id = ?", true, (User.current.logged? ? User.current.id : 0)])
Chris@0 93 # Project specific queries and global queries
Chris@0 94 visible << (@project.nil? ? ["project_id IS NULL"] : ["project_id IS NULL OR project_id = ?", @project.id])
Chris@0 95 @sidebar_queries = Query.find(:all,
Chris@0 96 :select => 'id, name',
Chris@0 97 :order => "name ASC",
Chris@0 98 :conditions => visible.conditions)
Chris@0 99 end
Chris@0 100 @sidebar_queries
Chris@0 101 end
Chris@0 102
Chris@0 103 def show_detail(detail, no_html=false)
Chris@0 104 case detail.property
Chris@0 105 when 'attr'
Chris@0 106 field = detail.prop_key.to_s.gsub(/\_id$/, "")
Chris@0 107 label = l(("field_" + field).to_sym)
Chris@0 108 case
Chris@0 109 when ['due_date', 'start_date'].include?(detail.prop_key)
Chris@0 110 value = format_date(detail.value.to_date) if detail.value
Chris@0 111 old_value = format_date(detail.old_value.to_date) if detail.old_value
Chris@0 112
Chris@0 113 when ['project_id', 'status_id', 'tracker_id', 'assigned_to_id', 'priority_id', 'category_id', 'fixed_version_id'].include?(detail.prop_key)
Chris@0 114 value = find_name_by_reflection(field, detail.value)
Chris@0 115 old_value = find_name_by_reflection(field, detail.old_value)
Chris@0 116
Chris@0 117 when detail.prop_key == 'estimated_hours'
Chris@0 118 value = "%0.02f" % detail.value.to_f unless detail.value.blank?
Chris@0 119 old_value = "%0.02f" % detail.old_value.to_f unless detail.old_value.blank?
Chris@0 120
Chris@0 121 when detail.prop_key == 'parent_id'
Chris@0 122 label = l(:field_parent_issue)
Chris@0 123 value = "##{detail.value}" unless detail.value.blank?
Chris@0 124 old_value = "##{detail.old_value}" unless detail.old_value.blank?
Chris@0 125 end
Chris@0 126 when 'cf'
Chris@0 127 custom_field = CustomField.find_by_id(detail.prop_key)
Chris@0 128 if custom_field
Chris@0 129 label = custom_field.name
Chris@0 130 value = format_value(detail.value, custom_field.field_format) if detail.value
Chris@0 131 old_value = format_value(detail.old_value, custom_field.field_format) if detail.old_value
Chris@0 132 end
Chris@0 133 when 'attachment'
Chris@0 134 label = l(:label_attachment)
Chris@0 135 end
Chris@0 136 call_hook(:helper_issues_show_detail_after_setting, {:detail => detail, :label => label, :value => value, :old_value => old_value })
Chris@0 137
Chris@0 138 label ||= detail.prop_key
Chris@0 139 value ||= detail.value
Chris@0 140 old_value ||= detail.old_value
Chris@0 141
Chris@0 142 unless no_html
Chris@0 143 label = content_tag('strong', label)
Chris@0 144 old_value = content_tag("i", h(old_value)) if detail.old_value
Chris@0 145 old_value = content_tag("strike", old_value) if detail.old_value and (!detail.value or detail.value.empty?)
Chris@0 146 if detail.property == 'attachment' && !value.blank? && a = Attachment.find_by_id(detail.prop_key)
Chris@0 147 # Link to the attachment if it has not been removed
Chris@0 148 value = link_to_attachment(a)
Chris@0 149 else
Chris@0 150 value = content_tag("i", h(value)) if value
Chris@0 151 end
Chris@0 152 end
Chris@0 153
Chris@0 154 if !detail.value.blank?
Chris@0 155 case detail.property
Chris@0 156 when 'attr', 'cf'
Chris@0 157 if !detail.old_value.blank?
Chris@0 158 l(:text_journal_changed, :label => label, :old => old_value, :new => value)
Chris@0 159 else
Chris@0 160 l(:text_journal_set_to, :label => label, :value => value)
Chris@0 161 end
Chris@0 162 when 'attachment'
Chris@0 163 l(:text_journal_added, :label => label, :value => value)
Chris@0 164 end
Chris@0 165 else
Chris@0 166 l(:text_journal_deleted, :label => label, :old => old_value)
Chris@0 167 end
Chris@0 168 end
Chris@0 169
Chris@0 170 # Find the name of an associated record stored in the field attribute
Chris@0 171 def find_name_by_reflection(field, id)
Chris@0 172 association = Issue.reflect_on_association(field.to_sym)
Chris@0 173 if association
Chris@0 174 record = association.class_name.constantize.find_by_id(id)
Chris@0 175 return record.name if record
Chris@0 176 end
Chris@0 177 end
Chris@0 178
Chris@0 179 def issues_to_csv(issues, project = nil)
Chris@0 180 ic = Iconv.new(l(:general_csv_encoding), 'UTF-8')
Chris@0 181 decimal_separator = l(:general_csv_decimal_separator)
Chris@0 182 export = FCSV.generate(:col_sep => l(:general_csv_separator)) do |csv|
Chris@0 183 # csv header fields
Chris@0 184 headers = [ "#",
Chris@0 185 l(:field_status),
Chris@0 186 l(:field_project),
Chris@0 187 l(:field_tracker),
Chris@0 188 l(:field_priority),
Chris@0 189 l(:field_subject),
Chris@0 190 l(:field_assigned_to),
Chris@0 191 l(:field_category),
Chris@0 192 l(:field_fixed_version),
Chris@0 193 l(:field_author),
Chris@0 194 l(:field_start_date),
Chris@0 195 l(:field_due_date),
Chris@0 196 l(:field_done_ratio),
Chris@0 197 l(:field_estimated_hours),
Chris@0 198 l(:field_parent_issue),
Chris@0 199 l(:field_created_on),
Chris@0 200 l(:field_updated_on)
Chris@0 201 ]
Chris@0 202 # Export project custom fields if project is given
Chris@0 203 # otherwise export custom fields marked as "For all projects"
Chris@0 204 custom_fields = project.nil? ? IssueCustomField.for_all : project.all_issue_custom_fields
Chris@0 205 custom_fields.each {|f| headers << f.name}
Chris@0 206 # Description in the last column
Chris@0 207 headers << l(:field_description)
Chris@0 208 csv << headers.collect {|c| begin; ic.iconv(c.to_s); rescue; c.to_s; end }
Chris@0 209 # csv lines
Chris@0 210 issues.each do |issue|
Chris@0 211 fields = [issue.id,
Chris@0 212 issue.status.name,
Chris@0 213 issue.project.name,
Chris@0 214 issue.tracker.name,
Chris@0 215 issue.priority.name,
Chris@0 216 issue.subject,
Chris@0 217 issue.assigned_to,
Chris@0 218 issue.category,
Chris@0 219 issue.fixed_version,
Chris@0 220 issue.author.name,
Chris@0 221 format_date(issue.start_date),
Chris@0 222 format_date(issue.due_date),
Chris@0 223 issue.done_ratio,
Chris@0 224 issue.estimated_hours.to_s.gsub('.', decimal_separator),
Chris@0 225 issue.parent_id,
Chris@0 226 format_time(issue.created_on),
Chris@0 227 format_time(issue.updated_on)
Chris@0 228 ]
Chris@0 229 custom_fields.each {|f| fields << show_value(issue.custom_value_for(f)) }
Chris@0 230 fields << issue.description
Chris@0 231 csv << fields.collect {|c| begin; ic.iconv(c.to_s); rescue; c.to_s; end }
Chris@0 232 end
Chris@0 233 end
Chris@0 234 export
Chris@0 235 end
Chris@0 236
Chris@0 237 def gantt_zoom_link(gantt, in_or_out)
Chris@0 238 img_attributes = {:style => 'height:1.4em; width:1.4em; margin-left: 3px;'} # em for accessibility
Chris@0 239
Chris@0 240 case in_or_out
Chris@0 241 when :in
Chris@0 242 if gantt.zoom < 4
Chris@0 243 link_to_remote(l(:text_zoom_in) + image_tag('zoom_in.png', img_attributes.merge(:alt => l(:text_zoom_in))),
Chris@0 244 {:url => gantt.params.merge(:zoom => (gantt.zoom+1)), :update => 'content'},
Chris@0 245 {:href => url_for(gantt.params.merge(:zoom => (gantt.zoom+1)))})
Chris@0 246 else
Chris@0 247 l(:text_zoom_in) +
Chris@0 248 image_tag('zoom_in_g.png', img_attributes.merge(:alt => l(:text_zoom_in)))
Chris@0 249 end
Chris@0 250
Chris@0 251 when :out
Chris@0 252 if gantt.zoom > 1
Chris@0 253 link_to_remote(l(:text_zoom_out) + image_tag('zoom_out.png', img_attributes.merge(:alt => l(:text_zoom_out))),
Chris@0 254 {:url => gantt.params.merge(:zoom => (gantt.zoom-1)), :update => 'content'},
Chris@0 255 {:href => url_for(gantt.params.merge(:zoom => (gantt.zoom-1)))})
Chris@0 256 else
Chris@0 257 l(:text_zoom_out) +
Chris@0 258 image_tag('zoom_out_g.png', img_attributes.merge(:alt => l(:text_zoom_out)))
Chris@0 259 end
Chris@0 260 end
Chris@0 261 end
Chris@0 262 end