Feature #1474 » add_last_notes_column_v2.diff
| app/helpers/queries_helper.rb | ||
|---|---|---|
| 183 | 183 | 
          value ? (value.visible? ? link_to_issue(value, :subject => false) : "##{value.id}") : ''
   | 
| 184 | 184 | 
    when :description  | 
| 185 | 185 | 
          item.description? ? content_tag('div', textilizable(item, :description), :class => "wiki") : ''
   | 
| 186 | 
    when :last_notes  | 
|
| 187 | 
          item.last_notes.present? ? content_tag('div', textilizable(item, :last_notes), :class => "wiki") : ''
   | 
|
| 186 | 188 | 
    when :done_ratio  | 
| 187 | 189 | 
    progress_bar(value)  | 
| 188 | 190 | 
    when :relations  | 
| app/models/issue.rb | ||
|---|---|---|
| 244 | 244 | 
    @spent_hours = nil  | 
| 245 | 245 | 
    @total_spent_hours = nil  | 
| 246 | 246 | 
    @total_estimated_hours = nil  | 
| 247 | 
    @last_notes = nil  | 
|
| 247 | 248 | 
    base_reload(*args)  | 
| 248 | 249 | 
    end  | 
| 249 | 250 | |
| ... | ... | |
| 1048 | 1049 | 
    @relations ||= IssueRelation::Relations.new(self, (relations_from + relations_to).sort)  | 
| 1049 | 1050 | 
    end  | 
| 1050 | 1051 | |
| 1052 | 
    def last_notes  | 
|
| 1053 | 
    return self.visible_journals  | 
|
| 1054 | 
    if @last_notes  | 
|
| 1055 | 
    @last_notes  | 
|
| 1056 | 
    else  | 
|
| 1057 | 
    notes = self.journals.where.not(notes: '').to_a  | 
|
| 1058 | 
    notes.reject!(&:private_notes?) unless User.current.allowed_to?(:view_private_notes, self.project)  | 
|
| 1059 | 
    notes.last.notes unless notes.empty?  | 
|
| 1060 | 
    end  | 
|
| 1061 | 
    end  | 
|
| 1062 | ||
| 1051 | 1063 | 
    # Preloads relations for a collection of issues  | 
| 1052 | 1064 | 
    def self.load_relations(issues)  | 
| 1053 | 1065 | 
    if issues.any?  | 
| ... | ... | |
| 1102 | 1114 | 
    end  | 
| 1103 | 1115 | 
    end  | 
| 1104 | 1116 | |
| 1117 | 
    # Preloads visible last notes for a collection of issues  | 
|
| 1118 | 
    def self.load_visible_last_notes(issues, user=User.current)  | 
|
| 1119 | 
    if issues.any?  | 
|
| 1120 | 
    issue_ids = issues.map(&:id)  | 
|
| 1121 | 
          logger.debug("="*30)
   | 
|
| 1122 | 
    notes = Journal.joins(issue: :project).where.not(notes: '').  | 
|
| 1123 | 
            where(:issues => {:id => issue_ids}).order("#{Journal.table_name}.id ASC").to_a
   | 
|
| 1124 | ||
| 1125 | 
    issues.each do |issue|  | 
|
| 1126 | 
            note = notes.select{|note| note.journalized_id == issue.id}
   | 
|
| 1127 | 
    note.reject!(&:private_notes?) unless User.current.allowed_to?(:view_private_notes, issue.project)  | 
|
| 1128 | 
    issue.instance_variable_set "@last_notes", (note.empty? ? '' : note.last.notes)  | 
|
| 1129 | 
    end  | 
|
| 1130 | 
    end  | 
|
| 1131 | 
    end  | 
|
| 1132 | ||
| 1105 | 1133 | 
    # Finds an issue relation given its id.  | 
| 1106 | 1134 | 
    def find_relation(relation_id)  | 
| 1107 | 1135 | 
        IssueRelation.where("issue_to_id = ? OR issue_from_id = ?", id, id).find(relation_id)
   | 
| app/models/issue_query.rb | ||
|---|---|---|
| 44 | 44 | 
        QueryColumn.new(:created_on, :sortable => "#{Issue.table_name}.created_on", :default_order => 'desc'),
   | 
| 45 | 45 | 
        QueryColumn.new(:closed_on, :sortable => "#{Issue.table_name}.closed_on", :default_order => 'desc'),
   | 
| 46 | 46 | 
    QueryColumn.new(:relations, :caption => :label_related_issues),  | 
| 47 | 
    QueryColumn.new(:description, :inline => false)  | 
|
| 47 | 
    QueryColumn.new(:description, :inline => false),  | 
|
| 48 | 
    QueryColumn.new(:last_notes, :caption => :label_last_notes, :inline => false)  | 
|
| 48 | 49 | 
    ]  | 
| 49 | 50 | |
| 50 | 51 | 
    def initialize(attributes=nil, *args)  | 
| ... | ... | |
| 330 | 331 | 
    if has_column?(:relations)  | 
| 331 | 332 | 
    Issue.load_visible_relations(issues)  | 
| 332 | 333 | 
    end  | 
| 334 | 
    if has_column?(:last_notes)  | 
|
| 335 | 
    Issue.load_visible_last_notes(issues)  | 
|
| 336 | 
    end  | 
|
| 333 | 337 | 
    issues  | 
| 334 | 338 | 
    rescue ::ActiveRecord::StatementInvalid => e  | 
| 335 | 339 | 
    raise StatementInvalid.new(e.message)  | 
| app/views/issues/_list.html.erb | ||
|---|---|---|
| 33 | 33 | 
    <% @query.block_columns.each do |column|  | 
| 34 | 34 | 
    if (text = column_content(column, issue)) && text.present? -%>  | 
| 35 | 35 | 
    <tr class="<%= current_cycle %>">  | 
| 36 | 
    <td colspan="<%= @query.inline_columns.size + 1 %>" class="<%= column.css_classes %>"><%= text %></td>  | 
|
| 36 | 
    <td colspan="<%= @query.inline_columns.size + 1 %>" class="<%= column.css_classes %>">  | 
|
| 37 | 
    <% if query.block_columns.count > 1 %>  | 
|
| 38 | 
    <span><%= column.caption %></span>  | 
|
| 39 | 
    <% end %>  | 
|
| 40 | 
    <%= text %>  | 
|
| 41 | 
    </td>  | 
|
| 37 | 42 | 
    </tr>  | 
| 38 | 43 | 
    <% end -%>  | 
| 39 | 44 | 
    <% end -%>  | 
| app/views/issues/index.html.erb | ||
|---|---|---|
| 37 | 37 | 
    </p>  | 
| 38 | 38 | 
    <p>  | 
| 39 | 39 | 
    <label><%= check_box_tag 'csv[description]', '1', @query.has_column?(:description) %> <%= l(:field_description) %></label>  | 
| 40 | 
    <label><%= check_box_tag 'csv[last_notes]', '1', @query.has_column?(:last_notes) %> <%= l(:label_last_notes) %></label>  | 
|
| 40 | 41 | 
    </p>  | 
| 41 | 42 | 
    <% if @issue_count > Setting.issues_export_limit.to_i %>  | 
| 42 | 43 | 
    <p class="icon icon-warning">  | 
| app/views/timelog/_list.html.erb | ||
|---|---|---|
| 50 | 50 | 
    <% @query.block_columns.each do |column|  | 
| 51 | 51 | 
    if (text = column_content(column, issue)) && text.present? -%>  | 
| 52 | 52 | 
    <tr class="<%= current_cycle %>">  | 
| 53 | 
    <td colspan="<%= @query.inline_columns.size + 1 %>" class="<%= column.css_classes %>"><%= text %></td>  | 
|
| 53 | 
    <td colspan="<%= @query.inline_columns.size + 1 %>" class="<%= column.css_classes %>">  | 
|
| 54 | 
    <% if query.block_columns.count > 1 %>  | 
|
| 55 | 
    <span><%= column.caption %></span>  | 
|
| 56 | 
    <% end %>  | 
|
| 57 | 
    <%= text %>  | 
|
| 58 | 
    </td>  | 
|
| 54 | 59 | 
    </tr>  | 
| 55 | 60 | 
    <% end -%>  | 
| 56 | 61 | 
    <% end -%>  | 
| config/locales/en.yml | ||
|---|---|---|
| 1009 | 1009 | 
    label_font_default: Default font  | 
| 1010 | 1010 | 
    label_font_monospace: Monospaced font  | 
| 1011 | 1011 | 
    label_font_proportional: Proportional font  | 
| 1012 | 
    label_last_notes: Last notes  | 
|
| 1012 | 1013 | |
| 1013 | 1014 | 
    button_login: Login  | 
| 1014 | 1015 | 
    button_submit: Submit  | 
| lib/redmine/export/pdf/issues_pdf_helper.rb | ||
|---|---|---|
| 45 | 45 | 
              pdf.SetFontStyle('',8)
   | 
| 46 | 46 | 
              pdf.RDMMultiCell(190, 5, "#{format_time(issue.created_on)} - #{issue.author}")
   | 
| 47 | 47 | 
    pdf.ln  | 
| 48 | 
     | 
|
| 48 | ||
| 49 | 49 | 
    left = []  | 
| 50 | 50 | 
    left << [l(:field_status), issue.status]  | 
| 51 | 51 | 
    left << [l(:field_priority), issue.priority]  | 
| 52 | 52 | 
              left << [l(:field_assigned_to), issue.assigned_to] unless issue.disabled_core_fields.include?('assigned_to_id')
   | 
| 53 | 53 | 
              left << [l(:field_category), issue.category] unless issue.disabled_core_fields.include?('category_id')
   | 
| 54 | 54 | 
              left << [l(:field_fixed_version), issue.fixed_version] unless issue.disabled_core_fields.include?('fixed_version_id')
   | 
| 55 | 
     | 
|
| 55 | ||
| 56 | 56 | 
    right = []  | 
| 57 | 57 | 
              right << [l(:field_start_date), format_date(issue.start_date)] unless issue.disabled_core_fields.include?('start_date')
   | 
| 58 | 58 | 
              right << [l(:field_due_date), format_date(issue.due_date)] unless issue.disabled_core_fields.include?('due_date')
   | 
| 59 | 59 | 
              right << [l(:field_done_ratio), "#{issue.done_ratio}%"] unless issue.disabled_core_fields.include?('done_ratio')
   | 
| 60 | 60 | 
              right << [l(:field_estimated_hours), l_hours(issue.estimated_hours)] unless issue.disabled_core_fields.include?('estimated_hours')
   | 
| 61 | 61 | 
    right << [l(:label_spent_time), l_hours(issue.total_spent_hours)] if User.current.allowed_to?(:view_time_entries, issue.project)  | 
| 62 | 
     | 
|
| 62 | ||
| 63 | 63 | 
    rows = left.size > right.size ? left.size : right.size  | 
| 64 | 64 | 
    while left.size < rows  | 
| 65 | 65 | 
    left << nil  | 
| ... | ... | |
| 67 | 67 | 
    while right.size < rows  | 
| 68 | 68 | 
    right << nil  | 
| 69 | 69 | 
    end  | 
| 70 | 
     | 
|
| 70 | ||
| 71 | 71 | 
    half = (issue.visible_custom_field_values.size / 2.0).ceil  | 
| 72 | 72 | 
    issue.visible_custom_field_values.each_with_index do |custom_value, i|  | 
| 73 | 73 | 
    (i < half ? left : right) << [custom_value.custom_field.name, show_value(custom_value, false)]  | 
| 74 | 74 | 
    end  | 
| 75 | 
     | 
|
| 75 | ||
| 76 | 76 | 
    if pdf.get_rtl  | 
| 77 | 77 | 
    border_first_top = 'RT'  | 
| 78 | 78 | 
    border_last_top = 'LT'  | 
| ... | ... | |
| 84 | 84 | 
    border_first = 'L'  | 
| 85 | 85 | 
    border_last = 'R'  | 
| 86 | 86 | 
    end  | 
| 87 | 
     | 
|
| 87 | ||
| 88 | 88 | 
    rows = left.size > right.size ? left.size : right.size  | 
| 89 | 89 | 
    rows.times do |i|  | 
| 90 | 90 | 
    heights = []  | 
| ... | ... | |
| 99 | 99 | 
    item = right[i]  | 
| 100 | 100 | 
    heights << pdf.get_string_height(60, item ? item.last.to_s : "")  | 
| 101 | 101 | 
    height = heights.max  | 
| 102 | 
     | 
|
| 102 | ||
| 103 | 103 | 
    item = left[i]  | 
| 104 | 104 | 
                pdf.SetFontStyle('B',9)
   | 
| 105 | 105 | 
                pdf.RDMMultiCell(35, height, item ? "#{item.first}:" : "", (i == 0 ? border_first_top : border_first), '', 0, 0)
   | 
| 106 | 106 | 
                pdf.SetFontStyle('',9)
   | 
| 107 | 107 | 
    pdf.RDMMultiCell(60, height, item ? item.last.to_s : "", (i == 0 ? border_last_top : border_last), '', 0, 0)  | 
| 108 | 
     | 
|
| 108 | ||
| 109 | 109 | 
    item = right[i]  | 
| 110 | 110 | 
                pdf.SetFontStyle('B',9)
   | 
| 111 | 111 | 
                pdf.RDMMultiCell(35, height, item ? "#{item.first}:" : "",  (i == 0 ? border_first_top : border_first), '', 0, 0)
   | 
| 112 | 112 | 
                pdf.SetFontStyle('',9)
   | 
| 113 | 113 | 
    pdf.RDMMultiCell(60, height, item ? item.last.to_s : "", (i == 0 ? border_last_top : border_last), '', 0, 2)  | 
| 114 | 
     | 
|
| 114 | ||
| 115 | 115 | 
    pdf.set_x(base_x)  | 
| 116 | 116 | 
    end  | 
| 117 | 
     | 
|
| 117 | ||
| 118 | 118 | 
              pdf.SetFontStyle('B',9)
   | 
| 119 | 119 | 
    pdf.RDMCell(35+155, 5, l(:field_description), "LRT", 1)  | 
| 120 | 120 | 
              pdf.SetFontStyle('',9)
   | 
| 121 | 
     | 
|
| 121 | ||
| 122 | 122 | 
    # Set resize image scale  | 
| 123 | 123 | 
    pdf.set_image_scale(1.6)  | 
| 124 | 124 | 
    text = textilizable(issue, :description,  | 
| ... | ... | |
| 128 | 128 | 
    :inline_attachments => false  | 
| 129 | 129 | 
    )  | 
| 130 | 130 | 
    pdf.RDMwriteFormattedCell(35+155, 5, '', '', text, issue.attachments, "LRB")  | 
| 131 | 
     | 
|
| 131 | ||
| 132 | 132 | 
    unless issue.leaf?  | 
| 133 | 133 | 
    truncate_length = (!is_cjk? ? 90 : 65)  | 
| 134 | 134 | 
                pdf.SetFontStyle('B',9)
   | 
| ... | ... | |
| 145 | 145 | 
    pdf.ln  | 
| 146 | 146 | 
    end  | 
| 147 | 147 | 
    end  | 
| 148 | 
     | 
|
| 148 | ||
| 149 | 149 | 
              relations = issue.relations.select { |r| r.other_issue(issue).visible? }
   | 
| 150 | 150 | 
    unless relations.empty?  | 
| 151 | 151 | 
    truncate_length = (!is_cjk? ? 80 : 60)  | 
| ... | ... | |
| 173 | 173 | 
    end  | 
| 174 | 174 | 
    pdf.RDMCell(190,5, "", "T")  | 
| 175 | 175 | 
    pdf.ln  | 
| 176 | 
     | 
|
| 176 | ||
| 177 | 177 | 
    if issue.changesets.any? &&  | 
| 178 | 178 | 
    User.current.allowed_to?(:view_changesets, issue.project)  | 
| 179 | 179 | 
                pdf.SetFontStyle('B',9)
   | 
| ... | ... | |
| 193 | 193 | 
    pdf.ln  | 
| 194 | 194 | 
    end  | 
| 195 | 195 | 
    end  | 
| 196 | 
     | 
|
| 196 | ||
| 197 | 197 | 
    if assoc[:journals].present?  | 
| 198 | 198 | 
                pdf.SetFontStyle('B',9)
   | 
| 199 | 199 | 
    pdf.RDMCell(190,5, l(:label_history), "B")  | 
| ... | ... | |
| 222 | 222 | 
    pdf.ln  | 
| 223 | 223 | 
    end  | 
| 224 | 224 | 
    end  | 
| 225 | 
     | 
|
| 225 | ||
| 226 | 226 | 
    if issue.attachments.any?  | 
| 227 | 227 | 
                pdf.SetFontStyle('B',9)
   | 
| 228 | 228 | 
    pdf.RDMCell(190,5, l(:label_attachment_plural), "B")  | 
| ... | ... | |
| 249 | 249 | 
    pdf.footer_date = format_date(User.current.today)  | 
| 250 | 250 | 
    pdf.set_auto_page_break(false)  | 
| 251 | 251 | 
              pdf.add_page("L")
   | 
| 252 | 
     | 
|
| 252 | ||
| 253 | 253 | 
    # Landscape A4 = 210 x 297 mm  | 
| 254 | 254 | 
    page_height = pdf.get_page_height # 210  | 
| 255 | 255 | 
    page_width = pdf.get_page_width # 297  | 
| ... | ... | |
| 257 | 257 | 
    right_margin = pdf.get_original_margins['right'] # 10  | 
| 258 | 258 | 
    bottom_margin = pdf.get_footer_margin  | 
| 259 | 259 | 
    row_height = 4  | 
| 260 | 
     | 
|
| 260 | ||
| 261 | 261 | 
    # column widths  | 
| 262 | 262 | 
    table_width = page_width - right_margin - left_margin  | 
| 263 | 263 | 
    col_width = []  | 
| ... | ... | |
| 265 | 265 | 
    col_width = calc_col_width(issues, query, table_width, pdf)  | 
| 266 | 266 | 
    table_width = col_width.inject(0, :+)  | 
| 267 | 267 | 
    end  | 
| 268 | 
     | 
|
| 269 | 
              # use full width if the description is displayed
   | 
|
| 270 | 
              if table_width > 0 && query.has_column?(:description)
   | 
|
| 268 | ||
| 269 | 
              # use full width if the description or last_notes are displayed
   | 
|
| 270 | 
              if table_width > 0 && (query.has_column?(:description) || query.has_column?(:last_notes))
   | 
|
| 271 | 271 | 
                col_width = col_width.map {|w| w * (page_width - right_margin - left_margin) / table_width}
   | 
| 272 | 272 | 
    table_width = col_width.inject(0, :+)  | 
| 273 | 273 | 
    end  | 
| 274 | 
     | 
|
| 274 | ||
| 275 | 275 | 
    # title  | 
| 276 | 276 | 
              pdf.SetFontStyle('B',11)
   | 
| 277 | 277 | 
    pdf.RDMCell(190, 8, title)  | 
| ... | ... | |
| 303 | 303 | 
    end  | 
| 304 | 304 | 
    previous_group = group  | 
| 305 | 305 | 
    end  | 
| 306 | 
     | 
|
| 306 | ||
| 307 | 307 | 
    # fetch row values  | 
| 308 | 308 | 
    col_values = fetch_row_values(issue, query, level)  | 
| 309 | 
     | 
|
| 309 | ||
| 310 | 310 | 
    # make new page if it doesn't fit on the current one  | 
| 311 | 311 | 
    base_y = pdf.get_y  | 
| 312 | 312 | 
    max_height = get_issues_to_pdf_write_cells(pdf, col_values, col_width)  | 
| ... | ... | |
| 316 | 316 | 
    render_table_header(pdf, query, col_width, row_height, table_width)  | 
| 317 | 317 | 
    base_y = pdf.get_y  | 
| 318 | 318 | 
    end  | 
| 319 | 
     | 
|
| 319 | ||
| 320 | 320 | 
    # write the cells on page  | 
| 321 | 321 | 
    issues_to_pdf_write_cells(pdf, col_values, col_width, max_height)  | 
| 322 | 322 | 
    pdf.set_y(base_y + max_height)  | 
| 323 | 
     | 
|
| 323 | ||
| 324 | 324 | 
    if query.has_column?(:description) && issue.description?  | 
| 325 | 325 | 
    pdf.set_x(10)  | 
| 326 | 326 | 
    pdf.set_auto_page_break(true, bottom_margin)  | 
| 327 | 327 | 
    pdf.RDMwriteHTMLCell(0, 5, 10, '', issue.description.to_s, issue.attachments, "LRBT")  | 
| 328 | 328 | 
    pdf.set_auto_page_break(false)  | 
| 329 | 329 | 
    end  | 
| 330 | ||
| 331 | 
    if query.has_column?(:last_notes) && issue.last_notes.present?  | 
|
| 332 | 
    pdf.set_x(10)  | 
|
| 333 | 
    pdf.set_auto_page_break(true, bottom_margin)  | 
|
| 334 | 
    pdf.RDMwriteHTMLCell(0, 5, 10, '', issue.last_notes.to_s, [], "LRBT")  | 
|
| 335 | 
    pdf.set_auto_page_break(false)  | 
|
| 336 | 
    end  | 
|
| 330 | 337 | 
    end  | 
| 331 | 
     | 
|
| 338 | ||
| 332 | 339 | 
    if issues.size == Setting.issues_export_limit.to_i  | 
| 333 | 340 | 
                pdf.SetFontStyle('B',10)
   | 
| 334 | 341 | 
    pdf.RDMCell(0, row_height, '...')  | 
| public/stylesheets/application.css | ||
|---|---|---|
| 255 | 255 | 
    tr.issue td.relations { text-align: left; }
   | 
| 256 | 256 | 
    tr.issue td.done_ratio table.progress { margin-left:auto; margin-right: auto;}
   | 
| 257 | 257 | 
    tr.issue td.relations span {white-space: nowrap;}
   | 
| 258 | 
    table.issues td.description {color:#777; font-size:90%; padding:4px 4px 4px 24px; text-align:left; white-space:normal;}
   | 
|
| 259 | 
    table.issues td.description pre {white-space:normal;}
   | 
|
| 258 | 
    table.issues td.description, table.issues td.last_notes {color:#777; font-size:90%; padding:4px 4px 4px 24px; text-align:left; white-space:normal;}
   | 
|
| 259 | 
    table.issues td.description pre, table.issues td.last_notes pre {white-space:normal;}
   | 
|
| 260 | 260 | |
| 261 | 261 | 
    tr.issue.idnt td.subject {background: url(../images/bullet_arrow_right.png) no-repeat 0 50%;}
   | 
| 262 | 262 | 
    tr.issue.idnt-1 td.subject {padding-left: 24px; background-position: 8px 50%;}
   | 
| test/functional/issues_controller_test.rb | ||
|---|---|---|
| 561 | 561 | 
          str_big5  = "\xa4@\xa4\xeb".force_encoding('Big5')
   | 
| 562 | 562 | 
    issue = Issue.generate!(:subject => str_utf8)  | 
| 563 | 563 | |
| 564 | 
          get :index, :project_id => 1, 
   | 
|
| 565 | 
                      :f => ['subject'], 
   | 
|
| 564 | 
    get :index, :project_id => 1,  | 
|
| 565 | 
    :f => ['subject'],  | 
|
| 566 | 566 | 
    :op => '=', :values => [str_utf8],  | 
| 567 | 567 | 
    :format => 'csv'  | 
| 568 | 568 | 
    assert_equal 'text/csv; header=present', @response.content_type  | 
| ... | ... | |
| 580 | 580 | 
          str_utf8  = "\xe4\xbb\xa5\xe5\x86\x85".force_encoding('UTF-8')
   | 
| 581 | 581 | 
    issue = Issue.generate!(:subject => str_utf8)  | 
| 582 | 582 | |
| 583 | 
          get :index, :project_id => 1, 
   | 
|
| 584 | 
                      :f => ['subject'], 
   | 
|
| 583 | 
    get :index, :project_id => 1,  | 
|
| 584 | 
    :f => ['subject'],  | 
|
| 585 | 585 | 
    :op => '=', :values => [str_utf8],  | 
| 586 | 586 | 
    :c => ['status', 'subject'],  | 
| 587 | 587 | 
    :format => 'csv',  | 
| ... | ... | |
| 603 | 603 | 
    str1 = "test_index_csv_tw"  | 
| 604 | 604 | 
    issue = Issue.generate!(:subject => str1, :estimated_hours => '1234.5')  | 
| 605 | 605 | |
| 606 | 
          get :index, :project_id => 1, 
   | 
|
| 607 | 
                      :f => ['subject'], 
   | 
|
| 606 | 
    get :index, :project_id => 1,  | 
|
| 607 | 
    :f => ['subject'],  | 
|
| 608 | 608 | 
    :op => '=', :values => [str1],  | 
| 609 | 609 | 
    :c => ['estimated_hours', 'subject'],  | 
| 610 | 610 | 
    :format => 'csv',  | 
| ... | ... | |
| 620 | 620 | 
    str1 = "test_index_csv_fr"  | 
| 621 | 621 | 
    issue = Issue.generate!(:subject => str1, :estimated_hours => '1234.5')  | 
| 622 | 622 | |
| 623 | 
          get :index, :project_id => 1, 
   | 
|
| 624 | 
                      :f => ['subject'], 
   | 
|
| 623 | 
    get :index, :project_id => 1,  | 
|
| 624 | 
    :f => ['subject'],  | 
|
| 625 | 625 | 
    :op => '=', :values => [str1],  | 
| 626 | 626 | 
    :c => ['estimated_hours', 'subject'],  | 
| 627 | 627 | 
    :format => 'csv',  | 
| ... | ... | |
| 696 | 696 | 
    assert_response :success  | 
| 697 | 697 | 
    end  | 
| 698 | 698 | 
    end  | 
| 699 | 
     | 
|
| 699 | ||
| 700 | 700 | 
    def test_index_sort_by_assigned_to  | 
| 701 | 701 | 
    get :index, :sort => 'assigned_to'  | 
| 702 | 702 | 
    assert_response :success  | 
| 703 | 
     | 
|
| 703 | ||
| 704 | 704 | 
    assignees = issues_in_list.map(&:assigned_to).compact  | 
| 705 | 705 | 
    assert_equal assignees.sort, assignees  | 
| 706 | 706 | 
    assert_select 'table.issues.sort-by-assigned-to.sort-asc'  | 
| 707 | 707 | 
    end  | 
| 708 | 
     | 
|
| 708 | ||
| 709 | 709 | 
    def test_index_sort_by_assigned_to_desc  | 
| 710 | 710 | 
    get :index, :sort => 'assigned_to:desc'  | 
| 711 | 711 | 
    assert_response :success  | 
| 712 | 
     | 
|
| 712 | ||
| 713 | 713 | 
    assignees = issues_in_list.map(&:assigned_to).compact  | 
| 714 | 714 | 
    assert_equal assignees.sort.reverse, assignees  | 
| 715 | 715 | 
    assert_select 'table.issues.sort-by-assigned-to.sort-desc'  | 
| 716 | 716 | 
    end  | 
| 717 | 
     | 
|
| 717 | ||
| 718 | 718 | 
    def test_index_group_by_assigned_to  | 
| 719 | 719 | 
    get :index, :group_by => 'assigned_to', :sort => 'priority'  | 
| 720 | 720 | 
    assert_response :success  | 
| 721 | 721 | 
    end  | 
| 722 | 
     | 
|
| 722 | ||
| 723 | 723 | 
    def test_index_sort_by_author  | 
| 724 | 724 | 
    get :index, :sort => 'author', :c => ['author']  | 
| 725 | 725 | 
    assert_response :success  | 
| 726 | 
     | 
|
| 726 | ||
| 727 | 727 | 
    authors = issues_in_list.map(&:author)  | 
| 728 | 728 | 
    assert_equal authors.sort, authors  | 
| 729 | 729 | 
    end  | 
| ... | ... | |
| 731 | 731 | 
    def test_index_sort_by_author_desc  | 
| 732 | 732 | 
    get :index, :sort => 'author:desc'  | 
| 733 | 733 | 
    assert_response :success  | 
| 734 | 
     | 
|
| 734 | ||
| 735 | 735 | 
    authors = issues_in_list.map(&:author)  | 
| 736 | 736 | 
    assert_equal authors.sort.reverse, authors  | 
| 737 | 737 | 
    end  | 
| 738 | 
     | 
|
| 738 | ||
| 739 | 739 | 
    def test_index_group_by_author  | 
| 740 | 740 | 
    get :index, :group_by => 'author', :sort => 'priority'  | 
| 741 | 741 | 
    assert_response :success  | 
| 742 | 742 | 
    end  | 
| 743 | 
     | 
|
| 743 | ||
| 744 | 744 | 
    def test_index_sort_by_spent_hours  | 
| 745 | 745 | 
    get :index, :sort => 'spent_hours:desc'  | 
| 746 | 746 | 
    assert_response :success  | 
| 747 | 747 | 
    hours = issues_in_list.map(&:spent_hours)  | 
| 748 | 748 | 
    assert_equal hours.sort.reverse, hours  | 
| 749 | 749 | 
    end  | 
| 750 | 
     | 
|
| 750 | ||
| 751 | 751 | 
    def test_index_sort_by_total_spent_hours  | 
| 752 | 752 | 
    get :index, :sort => 'total_spent_hours:desc'  | 
| 753 | 753 | 
    assert_response :success  | 
| 754 | 754 | 
    hours = issues_in_list.map(&:total_spent_hours)  | 
| 755 | 755 | 
    assert_equal hours.sort.reverse, hours  | 
| 756 | 756 | 
    end  | 
| 757 | 
     | 
|
| 757 | ||
| 758 | 758 | 
    def test_index_sort_by_total_estimated_hours  | 
| 759 | 759 | 
    get :index, :sort => 'total_estimated_hours:desc'  | 
| 760 | 760 | 
    assert_response :success  | 
| ... | ... | |
| 959 | 959 | 
    assert_equal 'application/pdf', response.content_type  | 
| 960 | 960 | 
    end  | 
| 961 | 961 | |
| 962 | 
    def test_index_with_last_notes_column  | 
|
| 963 | 
    get :index, :set_filter => 1, :c => %w(subject last_notes)  | 
|
| 964 | ||
| 965 | 
    assert_response :success  | 
|
| 966 | 
    assert_select 'table.issues thead th', 3 # columns: chekbox + id + subject  | 
|
| 967 | ||
| 968 | 
    assert_select 'td.last_notes[colspan="3"]', :text => 'Some notes with Redmine links: #2, r2.'  | 
|
| 969 | 
    assert_select 'td.last_notes[colspan="3"]', :text => 'A comment with inline image: and a reference to #1 and r2.'  | 
|
| 970 | ||
| 971 | 
    get :index, :set_filter => 1, :c => %w(subject last_notes), :format => 'pdf'  | 
|
| 972 | 
    assert_response :success  | 
|
| 973 | 
    assert_equal 'application/pdf', response.content_type  | 
|
| 974 | 
    end  | 
|
| 975 | ||
| 976 | 
    def test_index_with_last_notes_column_should_display_private_notes_with_permission_only  | 
|
| 977 | 
    journal = Journal.create!(:journalized => Issue.find(2), :notes => 'Privates notes', :private_notes => true, :user_id => 1)  | 
|
| 978 | 
    @request.session[:user_id] = 2  | 
|
| 979 | ||
| 980 | 
    get :index, :set_filter => 1, :c => %w(subject last_notes)  | 
|
| 981 | 
    assert_response :success  | 
|
| 982 | 
    assert_select 'td.last_notes[colspan="3"]', :text => 'Privates notes'  | 
|
| 983 | ||
| 984 | 
    Role.find(1).remove_permission! :view_private_notes  | 
|
| 985 | ||
| 986 | 
    get :index, :set_filter => 1, :c => %w(subject last_notes)  | 
|
| 987 | 
    assert_response :success  | 
|
| 988 | 
    assert_select 'td.last_notes[colspan="3"]', :text => 'A comment with inline image: and a reference to #1 and r2.'  | 
|
| 989 | 
    end  | 
|
| 990 | ||
| 991 | 
    def test_index_with_description_and_last_notes_columns_should_display_column_name  | 
|
| 992 | 
    get :index, :set_filter => 1, :c => %w(subject last_notes description)  | 
|
| 993 | 
    assert_response :success  | 
|
| 994 | ||
| 995 | 
    assert_select 'td.last_notes[colspan="3"] span', :text => 'Last notes'  | 
|
| 996 | 
    assert_select 'td.description[colspan="3"] span', :text => 'Description'  | 
|
| 997 | 
    end  | 
|
| 998 | ||
| 962 | 999 | 
    def test_index_with_parent_column  | 
| 963 | 1000 | 
    Issue.delete_all  | 
| 964 | 1001 | 
    parent = Issue.generate!  | 
| ... | ... | |
| 1093 | 1130 | 
    def test_index_should_not_include_new_issue_tab_for_project_without_trackers  | 
| 1094 | 1131 | 
    with_settings :new_item_menu_tab => '1' do  | 
| 1095 | 1132 | 
    Project.find(1).trackers.clear  | 
| 1096 | 
     | 
|
| 1133 | ||
| 1097 | 1134 | 
    @request.session[:user_id] = 2  | 
| 1098 | 1135 | 
    get :index, :project_id => 1  | 
| 1099 | 1136 | 
    assert_select '#main-menu a.new-issue', 0  | 
| ... | ... | |
| 1105 | 1142 | 
    role = Role.find(1)  | 
| 1106 | 1143 | 
    role.remove_permission! :add_issues  | 
| 1107 | 1144 | 
    role.add_permission! :copy_issues  | 
| 1108 | 
     | 
|
| 1145 | ||
| 1109 | 1146 | 
    @request.session[:user_id] = 2  | 
| 1110 | 1147 | 
    get :index, :project_id => 1  | 
| 1111 | 1148 | 
    assert_select '#main-menu a.new-issue', 0  | 
| ... | ... | |
| 1381 | 1418 | |
| 1382 | 1419 | 
    def test_show_should_display_prev_next_links_with_query_and_sort_on_association  | 
| 1383 | 1420 | 
        @request.session[:issue_query] = {:filters => {'status_id' => {:values => [''], :operator => 'o'}}, :project_id => nil}
   | 
| 1384 | 
     | 
|
| 1421 | ||
| 1385 | 1422 | 
    %w(project tracker status priority author assigned_to category fixed_version).each do |assoc_sort|  | 
| 1386 | 1423 | 
    @request.session['issues_index_sort'] = assoc_sort  | 
| 1387 | 1424 | |
| ... | ... | |
| 1614 | 1651 | 
    end  | 
| 1615 | 1652 | |
| 1616 | 1653 | 
    def test_show_export_to_pdf  | 
| 1617 | 
        issue = Issue.find(3) 
   | 
|
| 1654 | 
    issue = Issue.find(3)  | 
|
| 1618 | 1655 | 
        assert issue.relations.select{|r| r.other_issue(issue).visible?}.present?
   | 
| 1619 | 1656 | 
    get :show, :id => 3, :format => 'pdf'  | 
| 1620 | 1657 | 
    assert_response :success  | 
| ... | ... | |
| 2062 | 2099 | 
    get :new, :project_id => 'invalid'  | 
| 2063 | 2100 | 
    assert_response 404  | 
| 2064 | 2101 | 
    end  | 
| 2065 | 
     | 
|
| 2102 | ||
| 2066 | 2103 | 
    def test_new_with_parent_id_should_only_propose_valid_trackers  | 
| 2067 | 2104 | 
    @request.session[:user_id] = 2  | 
| 2068 | 2105 | 
    t = Tracker.find(3)  | 
| ... | ... | |
| 2611 | 2648 | 
                                  :custom_field_values => {'2' => 'Value for field 2'}}
   | 
| 2612 | 2649 | 
    end  | 
| 2613 | 2650 | 
    assert_redirected_to :controller => 'issues', :action => 'show', :id => Issue.last.id  | 
| 2614 | 
     | 
|
| 2651 | ||
| 2615 | 2652 | 
    assert_equal 1, ActionMailer::Base.deliveries.size  | 
| 2616 | 2653 | 
    end  | 
| 2617 | 2654 | 
    end  | 
| ... | ... | |
| 2885 | 2922 | 
    assert_select 'option[value="1"][selected=selected]', :text => 'eCookbook'  | 
| 2886 | 2923 | 
    assert_select 'option[value="2"]:not([selected])', :text => 'OnlineStore'  | 
| 2887 | 2924 | 
    end  | 
| 2888 | 
          assert_select 'input[name=?][value=?]', 'issue[subject]', orig.subject 
   | 
|
| 2925 | 
    assert_select 'input[name=?][value=?]', 'issue[subject]', orig.subject  | 
|
| 2889 | 2926 | 
    assert_select 'input[name=copy_from][value="1"]'  | 
| 2890 | 2927 | 
    end  | 
| 2891 | 2928 | 
    end  | 
| ... | ... | |
| 3180 | 3217 | 
    def test_get_edit_should_display_the_time_entry_form_with_log_time_permission  | 
| 3181 | 3218 | 
    @request.session[:user_id] = 2  | 
| 3182 | 3219 | 
        Role.find_by_name('Manager').update_attribute :permissions, [:view_issues, :edit_issues, :log_time]
   | 
| 3183 | 
     | 
|
| 3220 | ||
| 3184 | 3221 | 
    get :edit, :id => 1  | 
| 3185 | 3222 | 
    assert_select 'input[name=?]', 'time_entry[hours]'  | 
| 3186 | 3223 | 
    end  | 
| ... | ... | |
| 3188 | 3225 | 
    def test_get_edit_should_not_display_the_time_entry_form_without_log_time_permission  | 
| 3189 | 3226 | 
    @request.session[:user_id] = 2  | 
| 3190 | 3227 | 
        Role.find_by_name('Manager').remove_permission! :log_time
   | 
| 3191 | 
     | 
|
| 3228 | ||
| 3192 | 3229 | 
    get :edit, :id => 1  | 
| 3193 | 3230 | 
    assert_select 'input[name=?]', 'time_entry[hours]', 0  | 
| 3194 | 3231 | 
    end  | 
| ... | ... | |
| 3833 | 3870 | 
    assert_response :redirect  | 
| 3834 | 3871 | 
    assert_redirected_to :controller => 'issues', :action => 'show', :id => issue.id  | 
| 3835 | 3872 | 
    end  | 
| 3836 | 
     | 
|
| 3873 | ||
| 3837 | 3874 | 
    def test_put_update_should_redirect_with_previous_and_next_issue_ids_params  | 
| 3838 | 3875 | 
    @request.session[:user_id] = 2  | 
| 3839 | 3876 | |
| ... | ... | |
| 3887 | 3924 | |
| 3888 | 3925 | 
    assert_select 'select[name=?]', 'issue[project_id]'  | 
| 3889 | 3926 | 
    assert_select 'input[name=?]', 'issue[parent_issue_id]'  | 
| 3890 | 
     | 
|
| 3927 | ||
| 3891 | 3928 | 
    # Project specific custom field, date type  | 
| 3892 | 3929 | 
    field = CustomField.find(9)  | 
| 3893 | 3930 | 
    assert !field.is_for_all?  | 
| 3894 | 3931 | 
    assert_equal 'date', field.field_format  | 
| 3895 | 3932 | 
    assert_select 'input[name=?]', 'issue[custom_field_values][9]'  | 
| 3896 | 
     | 
|
| 3933 | ||
| 3897 | 3934 | 
    # System wide custom field  | 
| 3898 | 3935 | 
    assert CustomField.find(1).is_for_all?  | 
| 3899 | 3936 | 
    assert_select 'select[name=?]', 'issue[custom_field_values][1]'  | 
| 3900 | 
     | 
|
| 3937 | ||
| 3901 | 3938 | 
    # Be sure we don't display inactive IssuePriorities  | 
| 3902 | 3939 | 
    assert ! IssuePriority.find(15).active?  | 
| 3903 | 3940 | 
    assert_select 'select[name=?]', 'issue[priority_id]' do  | 
| ... | ... | |
| 4084 | 4121 | 
                                           :issue => {:priority_id => '',
   | 
| 4085 | 4122 | 
    :assigned_to_id => group.id,  | 
| 4086 | 4123 | 
                                                      :custom_field_values => {'2' => ''}}
   | 
| 4087 | 
     | 
|
| 4124 | ||
| 4088 | 4125 | 
    assert_response 302  | 
| 4089 | 4126 | 
          assert_equal [group, group], Issue.where(:id => [1, 2]).collect {|i| i.assigned_to}
   | 
| 4090 | 4127 | 
    end  | 
| ... | ... | |
| 4390 | 4427 | 
    assert_select 'option[value="2"]'  | 
| 4391 | 4428 | 
    end  | 
| 4392 | 4429 | 
    end  | 
| 4393 | 
     | 
|
| 4430 | ||
| 4394 | 4431 | 
    def test_bulk_copy_to_another_project  | 
| 4395 | 4432 | 
    @request.session[:user_id] = 2  | 
| 4396 | 4433 | 
    assert_difference 'Issue.count', 2 do  | 
| ... | ... | |
| 4443 | 4480 | 
    :assigned_to_id => 2)  | 
| 4444 | 4481 | 
    ]  | 
| 4445 | 4482 | 
    assert_difference 'Issue.count', issues.size do  | 
| 4446 | 
          post :bulk_update, :ids => issues.map(&:id), :copy => '1', 
   | 
|
| 4483 | 
    post :bulk_update, :ids => issues.map(&:id), :copy => '1',  | 
|
| 4447 | 4484 | 
               :issue => {
   | 
| 4448 | 4485 | 
    :project_id => '', :tracker_id => '', :assigned_to_id => '',  | 
| 4449 | 4486 | 
    :status_id => '', :start_date => '', :due_date => ''  | 
| ... | ... | |
| 4475 | 4512 | 
    @request.session[:user_id] = 2  | 
| 4476 | 4513 | 
    assert_difference 'Issue.count', 2 do  | 
| 4477 | 4514 | 
    assert_no_difference 'Project.find(1).issues.count' do  | 
| 4478 | 
            post :bulk_update, :ids => [1, 2], :copy => '1', 
   | 
|
| 4515 | 
    post :bulk_update, :ids => [1, 2], :copy => '1',  | 
|
| 4479 | 4516 | 
                 :issue => {
   | 
| 4480 | 4517 | 
    :project_id => '2', :tracker_id => '', :assigned_to_id => '2',  | 
| 4481 | 4518 | 
    :status_id => '1', :start_date => '2009-12-01', :due_date => '2009-12-31'  | 
| test/unit/query_test.rb | ||
|---|---|---|
| 682 | 682 | 
    Member.create!(:project_id => 1, :principal => other_group, :role_ids => [1])  | 
| 683 | 683 | 
    User.current = user  | 
| 684 | 684 | |
| 685 | 
        with_settings :issue_group_assignment => '1' do 
   | 
|
| 685 | 
    with_settings :issue_group_assignment => '1' do  | 
|
| 686 | 686 | 
    i1 = Issue.generate!(:project_id => 1, :tracker_id => 1, :assigned_to => user)  | 
| 687 | 687 | 
    i2 = Issue.generate!(:project_id => 1, :tracker_id => 1, :assigned_to => group)  | 
| 688 | 688 | 
    i3 = Issue.generate!(:project_id => 1, :tracker_id => 1, :assigned_to => other_group)  | 
| 689 | 
     | 
|
| 689 | ||
| 690 | 690 | 
          query = IssueQuery.new(:name => '_', :filters => { 'assigned_to_id' => {:operator => '=', :values => ['me']}})
   | 
| 691 | 691 | 
    result = query.issues  | 
| 692 | 692 | 
    assert_equal Issue.visible.where(:assigned_to_id => ([2] + user.reload.group_ids)).sort_by(&:id), result.sort_by(&:id)  | 
| 693 | 
     | 
|
| 693 | ||
| 694 | 694 | 
    assert result.include?(i1)  | 
| 695 | 695 | 
    assert result.include?(i2)  | 
| 696 | 696 | 
    assert !result.include?(i3)  | 
| ... | ... | |
| 1107 | 1107 | |
| 1108 | 1108 | 
    def test_inline_and_block_columns  | 
| 1109 | 1109 | 
    q = IssueQuery.new  | 
| 1110 | 
    q.column_names = ['subject', 'description', 'tracker']  | 
|
| 1110 | 
        q.column_names = ['subject', 'description', 'tracker', 'last_notes']
   | 
|
| 1111 | 1111 | |
| 1112 | 1112 | 
    assert_equal [:id, :subject, :tracker], q.inline_columns.map(&:name)  | 
| 1113 | 
    assert_equal [:description], q.block_columns.map(&:name)  | 
|
| 1113 | 
        assert_equal [:description, :last_notes], q.block_columns.map(&:name)
   | 
|
| 1114 | 1114 | 
    end  | 
| 1115 | 1115 | |
| 1116 | 1116 | 
    def test_custom_field_columns_should_be_inline  | 
| ... | ... | |
| 1127 | 1127 | 
        assert_not_nil issues.first.instance_variable_get("@spent_hours")
   | 
| 1128 | 1128 | 
    end  | 
| 1129 | 1129 | |
| 1130 | 
    def test_query_should_preload_last_notes  | 
|
| 1131 | 
    q = IssueQuery.new(:name => '_', :column_names => [:subject, :last_notes])  | 
|
| 1132 | 
    assert q.has_column?(:last_notes)  | 
|
| 1133 | 
    issues = q.issues  | 
|
| 1134 | 
        assert_not_nil issues.first.instance_variable_get("@last_notes")
   | 
|
| 1135 | 
    end  | 
|
| 1136 | ||
| 1130 | 1137 | 
    def test_groupable_columns_should_include_custom_fields  | 
| 1131 | 1138 | 
    q = IssueQuery.new  | 
| 1132 | 1139 | 
        column = q.groupable_columns.detect {|c| c.name == :cf_1}
   |