##// END OF EJS Templates
Display of multi custom fields....
Jean-Philippe Lang -
r8606:79b12c73d9ac
parent child
Show More
@@ -1,151 +1,151
1 # encoding: utf-8
1 # encoding: utf-8
2 #
2 #
3 # Redmine - project management software
3 # Redmine - project management software
4 # Copyright (C) 2006-2012 Jean-Philippe Lang
4 # Copyright (C) 2006-2012 Jean-Philippe Lang
5 #
5 #
6 # This program is free software; you can redistribute it and/or
6 # This program is free software; you can redistribute it and/or
7 # modify it under the terms of the GNU General Public License
7 # modify it under the terms of the GNU General Public License
8 # as published by the Free Software Foundation; either version 2
8 # as published by the Free Software Foundation; either version 2
9 # of the License, or (at your option) any later version.
9 # of the License, or (at your option) any later version.
10 #
10 #
11 # This program is distributed in the hope that it will be useful,
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
14 # GNU General Public License for more details.
15 #
15 #
16 # You should have received a copy of the GNU General Public License
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19
19
20 module CustomFieldsHelper
20 module CustomFieldsHelper
21
21
22 def custom_fields_tabs
22 def custom_fields_tabs
23 tabs = [{:name => 'IssueCustomField', :partial => 'custom_fields/index', :label => :label_issue_plural},
23 tabs = [{:name => 'IssueCustomField', :partial => 'custom_fields/index', :label => :label_issue_plural},
24 {:name => 'TimeEntryCustomField', :partial => 'custom_fields/index', :label => :label_spent_time},
24 {:name => 'TimeEntryCustomField', :partial => 'custom_fields/index', :label => :label_spent_time},
25 {:name => 'ProjectCustomField', :partial => 'custom_fields/index', :label => :label_project_plural},
25 {:name => 'ProjectCustomField', :partial => 'custom_fields/index', :label => :label_project_plural},
26 {:name => 'VersionCustomField', :partial => 'custom_fields/index', :label => :label_version_plural},
26 {:name => 'VersionCustomField', :partial => 'custom_fields/index', :label => :label_version_plural},
27 {:name => 'UserCustomField', :partial => 'custom_fields/index', :label => :label_user_plural},
27 {:name => 'UserCustomField', :partial => 'custom_fields/index', :label => :label_user_plural},
28 {:name => 'GroupCustomField', :partial => 'custom_fields/index', :label => :label_group_plural},
28 {:name => 'GroupCustomField', :partial => 'custom_fields/index', :label => :label_group_plural},
29 {:name => 'TimeEntryActivityCustomField', :partial => 'custom_fields/index', :label => TimeEntryActivity::OptionName},
29 {:name => 'TimeEntryActivityCustomField', :partial => 'custom_fields/index', :label => TimeEntryActivity::OptionName},
30 {:name => 'IssuePriorityCustomField', :partial => 'custom_fields/index', :label => IssuePriority::OptionName},
30 {:name => 'IssuePriorityCustomField', :partial => 'custom_fields/index', :label => IssuePriority::OptionName},
31 {:name => 'DocumentCategoryCustomField', :partial => 'custom_fields/index', :label => DocumentCategory::OptionName}
31 {:name => 'DocumentCategoryCustomField', :partial => 'custom_fields/index', :label => DocumentCategory::OptionName}
32 ]
32 ]
33 end
33 end
34
34
35 # Return custom field html tag corresponding to its format
35 # Return custom field html tag corresponding to its format
36 def custom_field_tag(name, custom_value)
36 def custom_field_tag(name, custom_value)
37 custom_field = custom_value.custom_field
37 custom_field = custom_value.custom_field
38 field_name = "#{name}[custom_field_values][#{custom_field.id}]"
38 field_name = "#{name}[custom_field_values][#{custom_field.id}]"
39 field_name << "[]" if custom_field.multiple?
39 field_name << "[]" if custom_field.multiple?
40 field_id = "#{name}_custom_field_values_#{custom_field.id}"
40 field_id = "#{name}_custom_field_values_#{custom_field.id}"
41
41
42 field_format = Redmine::CustomFieldFormat.find_by_name(custom_field.field_format)
42 field_format = Redmine::CustomFieldFormat.find_by_name(custom_field.field_format)
43 case field_format.try(:edit_as)
43 case field_format.try(:edit_as)
44 when "date"
44 when "date"
45 text_field_tag(field_name, custom_value.value, :id => field_id, :size => 10) +
45 text_field_tag(field_name, custom_value.value, :id => field_id, :size => 10) +
46 calendar_for(field_id)
46 calendar_for(field_id)
47 when "text"
47 when "text"
48 text_area_tag(field_name, custom_value.value, :id => field_id, :rows => 3, :style => 'width:90%')
48 text_area_tag(field_name, custom_value.value, :id => field_id, :rows => 3, :style => 'width:90%')
49 when "bool"
49 when "bool"
50 hidden_field_tag(field_name, '0') + check_box_tag(field_name, '1', custom_value.true?, :id => field_id)
50 hidden_field_tag(field_name, '0') + check_box_tag(field_name, '1', custom_value.true?, :id => field_id)
51 when "list"
51 when "list"
52 blank_option = ''
52 blank_option = ''
53 unless custom_field.multiple?
53 unless custom_field.multiple?
54 if custom_field.is_required?
54 if custom_field.is_required?
55 unless custom_field.default_value.present?
55 unless custom_field.default_value.present?
56 blank_option = "<option value=\"\">--- #{l(:actionview_instancetag_blank_option)} ---</option>"
56 blank_option = "<option value=\"\">--- #{l(:actionview_instancetag_blank_option)} ---</option>"
57 end
57 end
58 else
58 else
59 blank_option = '<option></option>'
59 blank_option = '<option></option>'
60 end
60 end
61 end
61 end
62 s = select_tag(field_name, blank_option.html_safe + options_for_select(custom_field.possible_values_options(custom_value.customized), custom_value.value),
62 s = select_tag(field_name, blank_option.html_safe + options_for_select(custom_field.possible_values_options(custom_value.customized), custom_value.value),
63 :id => field_id, :multiple => custom_field.multiple?)
63 :id => field_id, :multiple => custom_field.multiple?)
64 if custom_field.multiple?
64 if custom_field.multiple?
65 s << hidden_field_tag(field_name, '')
65 s << hidden_field_tag(field_name, '')
66 end
66 end
67 s
67 s
68 else
68 else
69 text_field_tag(field_name, custom_value.value, :id => field_id)
69 text_field_tag(field_name, custom_value.value, :id => field_id)
70 end
70 end
71 end
71 end
72
72
73 # Return custom field label tag
73 # Return custom field label tag
74 def custom_field_label_tag(name, custom_value)
74 def custom_field_label_tag(name, custom_value)
75 content_tag "label", h(custom_value.custom_field.name) +
75 content_tag "label", h(custom_value.custom_field.name) +
76 (custom_value.custom_field.is_required? ? " <span class=\"required\">*</span>".html_safe : ""),
76 (custom_value.custom_field.is_required? ? " <span class=\"required\">*</span>".html_safe : ""),
77 :for => "#{name}_custom_field_values_#{custom_value.custom_field.id}"
77 :for => "#{name}_custom_field_values_#{custom_value.custom_field.id}"
78 end
78 end
79
79
80 # Return custom field tag with its label tag
80 # Return custom field tag with its label tag
81 def custom_field_tag_with_label(name, custom_value)
81 def custom_field_tag_with_label(name, custom_value)
82 custom_field_label_tag(name, custom_value) + custom_field_tag(name, custom_value)
82 custom_field_label_tag(name, custom_value) + custom_field_tag(name, custom_value)
83 end
83 end
84
84
85 def custom_field_tag_for_bulk_edit(name, custom_field, projects=nil)
85 def custom_field_tag_for_bulk_edit(name, custom_field, projects=nil)
86 field_name = "#{name}[custom_field_values][#{custom_field.id}]"
86 field_name = "#{name}[custom_field_values][#{custom_field.id}]"
87 field_name << "[]" if custom_field.multiple?
87 field_name << "[]" if custom_field.multiple?
88 field_id = "#{name}_custom_field_values_#{custom_field.id}"
88 field_id = "#{name}_custom_field_values_#{custom_field.id}"
89 field_format = Redmine::CustomFieldFormat.find_by_name(custom_field.field_format)
89 field_format = Redmine::CustomFieldFormat.find_by_name(custom_field.field_format)
90 case field_format.try(:edit_as)
90 case field_format.try(:edit_as)
91 when "date"
91 when "date"
92 text_field_tag(field_name, '', :id => field_id, :size => 10) +
92 text_field_tag(field_name, '', :id => field_id, :size => 10) +
93 calendar_for(field_id)
93 calendar_for(field_id)
94 when "text"
94 when "text"
95 text_area_tag(field_name, '', :id => field_id, :rows => 3, :style => 'width:90%')
95 text_area_tag(field_name, '', :id => field_id, :rows => 3, :style => 'width:90%')
96 when "bool"
96 when "bool"
97 select_tag(field_name, options_for_select([[l(:label_no_change_option), ''],
97 select_tag(field_name, options_for_select([[l(:label_no_change_option), ''],
98 [l(:general_text_yes), '1'],
98 [l(:general_text_yes), '1'],
99 [l(:general_text_no), '0']]), :id => field_id)
99 [l(:general_text_no), '0']]), :id => field_id)
100 when "list"
100 when "list"
101 options = []
101 options = []
102 options << [l(:label_no_change_option), ''] unless custom_field.multiple?
102 options << [l(:label_no_change_option), ''] unless custom_field.multiple?
103 options += custom_field.possible_values_options(projects)
103 options += custom_field.possible_values_options(projects)
104 select_tag(field_name, options_for_select(options),
104 select_tag(field_name, options_for_select(options),
105 :id => field_id, :multiple => custom_field.multiple?)
105 :id => field_id, :multiple => custom_field.multiple?)
106 else
106 else
107 text_field_tag(field_name, '', :id => field_id)
107 text_field_tag(field_name, '', :id => field_id)
108 end
108 end
109 end
109 end
110
110
111 # Return a string used to display a custom value
111 # Return a string used to display a custom value
112 def show_value(custom_value)
112 def show_value(custom_value)
113 return "" unless custom_value
113 return "" unless custom_value
114 format_value(custom_value.value, custom_value.custom_field.field_format)
114 format_value(custom_value.value, custom_value.custom_field.field_format)
115 end
115 end
116
116
117 # Return a string used to display a custom value
117 # Return a string used to display a custom value
118 def format_value(value, field_format)
118 def format_value(value, field_format)
119 if value.is_a?(Array)
119 if value.is_a?(Array)
120 value.collect {|v| format_value(v, field_format)}.join(', ')
120 value.collect {|v| format_value(v, field_format)}.compact.sort.join(', ')
121 else
121 else
122 Redmine::CustomFieldFormat.format_value(value, field_format)
122 Redmine::CustomFieldFormat.format_value(value, field_format)
123 end
123 end
124 end
124 end
125
125
126 # Return an array of custom field formats which can be used in select_tag
126 # Return an array of custom field formats which can be used in select_tag
127 def custom_field_formats_for_select(custom_field)
127 def custom_field_formats_for_select(custom_field)
128 Redmine::CustomFieldFormat.as_select(custom_field.class.customized_class.name)
128 Redmine::CustomFieldFormat.as_select(custom_field.class.customized_class.name)
129 end
129 end
130
130
131 # Renders the custom_values in api views
131 # Renders the custom_values in api views
132 def render_api_custom_values(custom_values, api)
132 def render_api_custom_values(custom_values, api)
133 api.array :custom_fields do
133 api.array :custom_fields do
134 custom_values.each do |custom_value|
134 custom_values.each do |custom_value|
135 attrs = {:id => custom_value.custom_field_id, :name => custom_value.custom_field.name}
135 attrs = {:id => custom_value.custom_field_id, :name => custom_value.custom_field.name}
136 attrs.merge!(:multiple => true) if custom_value.custom_field.multiple?
136 attrs.merge!(:multiple => true) if custom_value.custom_field.multiple?
137 api.custom_field attrs do
137 api.custom_field attrs do
138 if custom_value.value.is_a?(Array)
138 if custom_value.value.is_a?(Array)
139 api.array :value do
139 api.array :value do
140 custom_value.value.each do |value|
140 custom_value.value.each do |value|
141 api.value value unless value.blank?
141 api.value value unless value.blank?
142 end
142 end
143 end
143 end
144 else
144 else
145 api.value custom_value.value
145 api.value custom_value.value
146 end
146 end
147 end
147 end
148 end
148 end
149 end unless custom_values.empty?
149 end unless custom_values.empty?
150 end
150 end
151 end
151 end
@@ -1,345 +1,345
1 # encoding: utf-8
1 # encoding: utf-8
2 #
2 #
3 # Redmine - project management software
3 # Redmine - project management software
4 # Copyright (C) 2006-2011 Jean-Philippe Lang
4 # Copyright (C) 2006-2011 Jean-Philippe Lang
5 #
5 #
6 # This program is free software; you can redistribute it and/or
6 # This program is free software; you can redistribute it and/or
7 # modify it under the terms of the GNU General Public License
7 # modify it under the terms of the GNU General Public License
8 # as published by the Free Software Foundation; either version 2
8 # as published by the Free Software Foundation; either version 2
9 # of the License, or (at your option) any later version.
9 # of the License, or (at your option) any later version.
10 #
10 #
11 # This program is distributed in the hope that it will be useful,
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
14 # GNU General Public License for more details.
15 #
15 #
16 # You should have received a copy of the GNU General Public License
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19
19
20 module IssuesHelper
20 module IssuesHelper
21 include ApplicationHelper
21 include ApplicationHelper
22
22
23 def issue_list(issues, &block)
23 def issue_list(issues, &block)
24 ancestors = []
24 ancestors = []
25 issues.each do |issue|
25 issues.each do |issue|
26 while (ancestors.any? && !issue.is_descendant_of?(ancestors.last))
26 while (ancestors.any? && !issue.is_descendant_of?(ancestors.last))
27 ancestors.pop
27 ancestors.pop
28 end
28 end
29 yield issue, ancestors.size
29 yield issue, ancestors.size
30 ancestors << issue unless issue.leaf?
30 ancestors << issue unless issue.leaf?
31 end
31 end
32 end
32 end
33
33
34 # Renders a HTML/CSS tooltip
34 # Renders a HTML/CSS tooltip
35 #
35 #
36 # To use, a trigger div is needed. This is a div with the class of "tooltip"
36 # To use, a trigger div is needed. This is a div with the class of "tooltip"
37 # that contains this method wrapped in a span with the class of "tip"
37 # that contains this method wrapped in a span with the class of "tip"
38 #
38 #
39 # <div class="tooltip"><%= link_to_issue(issue) %>
39 # <div class="tooltip"><%= link_to_issue(issue) %>
40 # <span class="tip"><%= render_issue_tooltip(issue) %></span>
40 # <span class="tip"><%= render_issue_tooltip(issue) %></span>
41 # </div>
41 # </div>
42 #
42 #
43 def render_issue_tooltip(issue)
43 def render_issue_tooltip(issue)
44 @cached_label_status ||= l(:field_status)
44 @cached_label_status ||= l(:field_status)
45 @cached_label_start_date ||= l(:field_start_date)
45 @cached_label_start_date ||= l(:field_start_date)
46 @cached_label_due_date ||= l(:field_due_date)
46 @cached_label_due_date ||= l(:field_due_date)
47 @cached_label_assigned_to ||= l(:field_assigned_to)
47 @cached_label_assigned_to ||= l(:field_assigned_to)
48 @cached_label_priority ||= l(:field_priority)
48 @cached_label_priority ||= l(:field_priority)
49 @cached_label_project ||= l(:field_project)
49 @cached_label_project ||= l(:field_project)
50
50
51 link_to_issue(issue) + "<br /><br />".html_safe +
51 link_to_issue(issue) + "<br /><br />".html_safe +
52 "<strong>#{@cached_label_project}</strong>: #{link_to_project(issue.project)}<br />".html_safe +
52 "<strong>#{@cached_label_project}</strong>: #{link_to_project(issue.project)}<br />".html_safe +
53 "<strong>#{@cached_label_status}</strong>: #{h(issue.status.name)}<br />".html_safe +
53 "<strong>#{@cached_label_status}</strong>: #{h(issue.status.name)}<br />".html_safe +
54 "<strong>#{@cached_label_start_date}</strong>: #{format_date(issue.start_date)}<br />".html_safe +
54 "<strong>#{@cached_label_start_date}</strong>: #{format_date(issue.start_date)}<br />".html_safe +
55 "<strong>#{@cached_label_due_date}</strong>: #{format_date(issue.due_date)}<br />".html_safe +
55 "<strong>#{@cached_label_due_date}</strong>: #{format_date(issue.due_date)}<br />".html_safe +
56 "<strong>#{@cached_label_assigned_to}</strong>: #{h(issue.assigned_to)}<br />".html_safe +
56 "<strong>#{@cached_label_assigned_to}</strong>: #{h(issue.assigned_to)}<br />".html_safe +
57 "<strong>#{@cached_label_priority}</strong>: #{h(issue.priority.name)}".html_safe
57 "<strong>#{@cached_label_priority}</strong>: #{h(issue.priority.name)}".html_safe
58 end
58 end
59
59
60 def issue_heading(issue)
60 def issue_heading(issue)
61 h("#{issue.tracker} ##{issue.id}")
61 h("#{issue.tracker} ##{issue.id}")
62 end
62 end
63
63
64 def render_issue_subject_with_tree(issue)
64 def render_issue_subject_with_tree(issue)
65 s = ''
65 s = ''
66 ancestors = issue.root? ? [] : issue.ancestors.visible.all
66 ancestors = issue.root? ? [] : issue.ancestors.visible.all
67 ancestors.each do |ancestor|
67 ancestors.each do |ancestor|
68 s << '<div>' + content_tag('p', link_to_issue(ancestor))
68 s << '<div>' + content_tag('p', link_to_issue(ancestor))
69 end
69 end
70 s << '<div>'
70 s << '<div>'
71 subject = h(issue.subject)
71 subject = h(issue.subject)
72 if issue.is_private?
72 if issue.is_private?
73 subject = content_tag('span', l(:field_is_private), :class => 'private') + ' ' + subject
73 subject = content_tag('span', l(:field_is_private), :class => 'private') + ' ' + subject
74 end
74 end
75 s << content_tag('h3', subject)
75 s << content_tag('h3', subject)
76 s << '</div>' * (ancestors.size + 1)
76 s << '</div>' * (ancestors.size + 1)
77 s.html_safe
77 s.html_safe
78 end
78 end
79
79
80 def render_descendants_tree(issue)
80 def render_descendants_tree(issue)
81 s = '<form><table class="list issues">'
81 s = '<form><table class="list issues">'
82 issue_list(issue.descendants.visible.sort_by(&:lft)) do |child, level|
82 issue_list(issue.descendants.visible.sort_by(&:lft)) do |child, level|
83 s << content_tag('tr',
83 s << content_tag('tr',
84 content_tag('td', check_box_tag("ids[]", child.id, false, :id => nil), :class => 'checkbox') +
84 content_tag('td', check_box_tag("ids[]", child.id, false, :id => nil), :class => 'checkbox') +
85 content_tag('td', link_to_issue(child, :truncate => 60), :class => 'subject') +
85 content_tag('td', link_to_issue(child, :truncate => 60), :class => 'subject') +
86 content_tag('td', h(child.status)) +
86 content_tag('td', h(child.status)) +
87 content_tag('td', link_to_user(child.assigned_to)) +
87 content_tag('td', link_to_user(child.assigned_to)) +
88 content_tag('td', progress_bar(child.done_ratio, :width => '80px')),
88 content_tag('td', progress_bar(child.done_ratio, :width => '80px')),
89 :class => "issue issue-#{child.id} hascontextmenu #{level > 0 ? "idnt idnt-#{level}" : nil}")
89 :class => "issue issue-#{child.id} hascontextmenu #{level > 0 ? "idnt idnt-#{level}" : nil}")
90 end
90 end
91 s << '</table></form>'
91 s << '</table></form>'
92 s.html_safe
92 s.html_safe
93 end
93 end
94
94
95 def render_custom_fields_rows(issue)
95 def render_custom_fields_rows(issue)
96 return if issue.custom_field_values.empty?
96 return if issue.custom_field_values.empty?
97 ordered_values = []
97 ordered_values = []
98 half = (issue.custom_field_values.size / 2.0).ceil
98 half = (issue.custom_field_values.size / 2.0).ceil
99 half.times do |i|
99 half.times do |i|
100 ordered_values << issue.custom_field_values[i]
100 ordered_values << issue.custom_field_values[i]
101 ordered_values << issue.custom_field_values[i + half]
101 ordered_values << issue.custom_field_values[i + half]
102 end
102 end
103 s = "<tr>\n"
103 s = "<tr>\n"
104 n = 0
104 n = 0
105 ordered_values.compact.each do |value|
105 ordered_values.compact.each do |value|
106 s << "</tr>\n<tr>\n" if n > 0 && (n % 2) == 0
106 s << "</tr>\n<tr>\n" if n > 0 && (n % 2) == 0
107 s << "\t<th>#{ h(value.custom_field.name) }:</th><td>#{ simple_format_without_paragraph(h(show_value(value))) }</td>\n"
107 s << "\t<th>#{ h(value.custom_field.name) }:</th><td>#{ simple_format_without_paragraph(h(show_value(value))) }</td>\n"
108 n += 1
108 n += 1
109 end
109 end
110 s << "</tr>\n"
110 s << "</tr>\n"
111 s.html_safe
111 s.html_safe
112 end
112 end
113
113
114 def issues_destroy_confirmation_message(issues)
114 def issues_destroy_confirmation_message(issues)
115 issues = [issues] unless issues.is_a?(Array)
115 issues = [issues] unless issues.is_a?(Array)
116 message = l(:text_issues_destroy_confirmation)
116 message = l(:text_issues_destroy_confirmation)
117 descendant_count = issues.inject(0) {|memo, i| memo += (i.right - i.left - 1)/2}
117 descendant_count = issues.inject(0) {|memo, i| memo += (i.right - i.left - 1)/2}
118 if descendant_count > 0
118 if descendant_count > 0
119 issues.each do |issue|
119 issues.each do |issue|
120 next if issue.root?
120 next if issue.root?
121 issues.each do |other_issue|
121 issues.each do |other_issue|
122 descendant_count -= 1 if issue.is_descendant_of?(other_issue)
122 descendant_count -= 1 if issue.is_descendant_of?(other_issue)
123 end
123 end
124 end
124 end
125 if descendant_count > 0
125 if descendant_count > 0
126 message << "\n" + l(:text_issues_destroy_descendants_confirmation, :count => descendant_count)
126 message << "\n" + l(:text_issues_destroy_descendants_confirmation, :count => descendant_count)
127 end
127 end
128 end
128 end
129 message
129 message
130 end
130 end
131
131
132 def sidebar_queries
132 def sidebar_queries
133 unless @sidebar_queries
133 unless @sidebar_queries
134 @sidebar_queries = Query.visible.all(
134 @sidebar_queries = Query.visible.all(
135 :order => "#{Query.table_name}.name ASC",
135 :order => "#{Query.table_name}.name ASC",
136 # Project specific queries and global queries
136 # Project specific queries and global queries
137 :conditions => (@project.nil? ? ["project_id IS NULL"] : ["project_id IS NULL OR project_id = ?", @project.id])
137 :conditions => (@project.nil? ? ["project_id IS NULL"] : ["project_id IS NULL OR project_id = ?", @project.id])
138 )
138 )
139 end
139 end
140 @sidebar_queries
140 @sidebar_queries
141 end
141 end
142
142
143 def query_links(title, queries)
143 def query_links(title, queries)
144 # links to #index on issues/show
144 # links to #index on issues/show
145 url_params = controller_name == 'issues' ? {:controller => 'issues', :action => 'index', :project_id => @project} : params
145 url_params = controller_name == 'issues' ? {:controller => 'issues', :action => 'index', :project_id => @project} : params
146
146
147 content_tag('h3', h(title)) +
147 content_tag('h3', h(title)) +
148 queries.collect {|query|
148 queries.collect {|query|
149 css = 'query'
149 css = 'query'
150 css << ' selected' if query == @query
150 css << ' selected' if query == @query
151 link_to(h(query.name), url_params.merge(:query_id => query), :class => css)
151 link_to(h(query.name), url_params.merge(:query_id => query), :class => css)
152 }.join('<br />').html_safe
152 }.join('<br />').html_safe
153 end
153 end
154
154
155 def render_sidebar_queries
155 def render_sidebar_queries
156 out = ''.html_safe
156 out = ''.html_safe
157 queries = sidebar_queries.select {|q| !q.is_public?}
157 queries = sidebar_queries.select {|q| !q.is_public?}
158 out << query_links(l(:label_my_queries), queries) if queries.any?
158 out << query_links(l(:label_my_queries), queries) if queries.any?
159 queries = sidebar_queries.select {|q| q.is_public?}
159 queries = sidebar_queries.select {|q| q.is_public?}
160 out << query_links(l(:label_query_plural), queries) if queries.any?
160 out << query_links(l(:label_query_plural), queries) if queries.any?
161 out
161 out
162 end
162 end
163
163
164 # Returns the textual representation of a journal details
164 # Returns the textual representation of a journal details
165 # as an array of strings
165 # as an array of strings
166 def details_to_strings(details, no_html=false)
166 def details_to_strings(details, no_html=false)
167 strings = []
167 strings = []
168 values_by_field = {}
168 values_by_field = {}
169 details.each do |detail|
169 details.each do |detail|
170 if detail.property == 'cf'
170 if detail.property == 'cf'
171 field_id = detail.prop_key
171 field_id = detail.prop_key
172 field = CustomField.find_by_id(field_id)
172 field = CustomField.find_by_id(field_id)
173 if field && field.multiple?
173 if field && field.multiple?
174 values_by_field[field_id] ||= {:added => [], :deleted => []}
174 values_by_field[field_id] ||= {:added => [], :deleted => []}
175 if detail.old_value
175 if detail.old_value
176 values_by_field[field_id][:deleted] << detail.old_value
176 values_by_field[field_id][:deleted] << detail.old_value
177 end
177 end
178 if detail.value
178 if detail.value
179 values_by_field[field_id][:added] << detail.value
179 values_by_field[field_id][:added] << detail.value
180 end
180 end
181 next
181 next
182 end
182 end
183 end
183 end
184 strings << show_detail(detail, no_html)
184 strings << show_detail(detail, no_html)
185 end
185 end
186 values_by_field.each do |field_id, changes|
186 values_by_field.each do |field_id, changes|
187 detail = JournalDetail.new(:property => 'cf', :prop_key => field_id)
187 detail = JournalDetail.new(:property => 'cf', :prop_key => field_id)
188 if changes[:added].any?
188 if changes[:added].any?
189 detail.value = changes[:added]
189 detail.value = changes[:added]
190 strings << show_detail(detail, no_html)
190 strings << show_detail(detail, no_html)
191 elsif changes[:deleted].any?
191 elsif changes[:deleted].any?
192 detail.old_value = changes[:deleted]
192 detail.old_value = changes[:deleted]
193 strings << show_detail(detail, no_html)
193 strings << show_detail(detail, no_html)
194 end
194 end
195 end
195 end
196 strings
196 strings
197 end
197 end
198
198
199 # Returns the textual representation of a single journal detail
199 # Returns the textual representation of a single journal detail
200 def show_detail(detail, no_html=false)
200 def show_detail(detail, no_html=false)
201 multiple = false
201 multiple = false
202 case detail.property
202 case detail.property
203 when 'attr'
203 when 'attr'
204 field = detail.prop_key.to_s.gsub(/\_id$/, "")
204 field = detail.prop_key.to_s.gsub(/\_id$/, "")
205 label = l(("field_" + field).to_sym)
205 label = l(("field_" + field).to_sym)
206 case detail.prop_key
206 case detail.prop_key
207 when 'due_date', 'start_date'
207 when 'due_date', 'start_date'
208 value = format_date(detail.value.to_date) if detail.value
208 value = format_date(detail.value.to_date) if detail.value
209 old_value = format_date(detail.old_value.to_date) if detail.old_value
209 old_value = format_date(detail.old_value.to_date) if detail.old_value
210
210
211 when 'project_id', 'status_id', 'tracker_id', 'assigned_to_id',
211 when 'project_id', 'status_id', 'tracker_id', 'assigned_to_id',
212 'priority_id', 'category_id', 'fixed_version_id'
212 'priority_id', 'category_id', 'fixed_version_id'
213 value = find_name_by_reflection(field, detail.value)
213 value = find_name_by_reflection(field, detail.value)
214 old_value = find_name_by_reflection(field, detail.old_value)
214 old_value = find_name_by_reflection(field, detail.old_value)
215
215
216 when 'estimated_hours'
216 when 'estimated_hours'
217 value = "%0.02f" % detail.value.to_f unless detail.value.blank?
217 value = "%0.02f" % detail.value.to_f unless detail.value.blank?
218 old_value = "%0.02f" % detail.old_value.to_f unless detail.old_value.blank?
218 old_value = "%0.02f" % detail.old_value.to_f unless detail.old_value.blank?
219
219
220 when 'parent_id'
220 when 'parent_id'
221 label = l(:field_parent_issue)
221 label = l(:field_parent_issue)
222 value = "##{detail.value}" unless detail.value.blank?
222 value = "##{detail.value}" unless detail.value.blank?
223 old_value = "##{detail.old_value}" unless detail.old_value.blank?
223 old_value = "##{detail.old_value}" unless detail.old_value.blank?
224
224
225 when 'is_private'
225 when 'is_private'
226 value = l(detail.value == "0" ? :general_text_No : :general_text_Yes) unless detail.value.blank?
226 value = l(detail.value == "0" ? :general_text_No : :general_text_Yes) unless detail.value.blank?
227 old_value = l(detail.old_value == "0" ? :general_text_No : :general_text_Yes) unless detail.old_value.blank?
227 old_value = l(detail.old_value == "0" ? :general_text_No : :general_text_Yes) unless detail.old_value.blank?
228 end
228 end
229 when 'cf'
229 when 'cf'
230 custom_field = CustomField.find_by_id(detail.prop_key)
230 custom_field = CustomField.find_by_id(detail.prop_key)
231 if custom_field
231 if custom_field
232 multiple = custom_field.multiple?
232 multiple = custom_field.multiple?
233 label = custom_field.name
233 label = custom_field.name
234 value = format_value(detail.value, custom_field.field_format) if detail.value
234 value = format_value(detail.value, custom_field.field_format) if detail.value
235 old_value = format_value(detail.old_value, custom_field.field_format) if detail.old_value
235 old_value = format_value(detail.old_value, custom_field.field_format) if detail.old_value
236 end
236 end
237 when 'attachment'
237 when 'attachment'
238 label = l(:label_attachment)
238 label = l(:label_attachment)
239 end
239 end
240 call_hook(:helper_issues_show_detail_after_setting,
240 call_hook(:helper_issues_show_detail_after_setting,
241 {:detail => detail, :label => label, :value => value, :old_value => old_value })
241 {:detail => detail, :label => label, :value => value, :old_value => old_value })
242
242
243 label ||= detail.prop_key
243 label ||= detail.prop_key
244 value ||= detail.value
244 value ||= detail.value
245 old_value ||= detail.old_value
245 old_value ||= detail.old_value
246
246
247 unless no_html
247 unless no_html
248 label = content_tag('strong', label)
248 label = content_tag('strong', label)
249 old_value = content_tag("i", h(old_value)) if detail.old_value
249 old_value = content_tag("i", h(old_value)) if detail.old_value
250 old_value = content_tag("strike", old_value) if detail.old_value and detail.value.blank?
250 old_value = content_tag("strike", old_value) if detail.old_value and detail.value.blank?
251 if detail.property == 'attachment' && !value.blank? && a = Attachment.find_by_id(detail.prop_key)
251 if detail.property == 'attachment' && !value.blank? && a = Attachment.find_by_id(detail.prop_key)
252 # Link to the attachment if it has not been removed
252 # Link to the attachment if it has not been removed
253 value = link_to_attachment(a, :download => true)
253 value = link_to_attachment(a, :download => true)
254 else
254 else
255 value = content_tag("i", h(value)) if value
255 value = content_tag("i", h(value)) if value
256 end
256 end
257 end
257 end
258
258
259 if detail.property == 'attr' && detail.prop_key == 'description'
259 if detail.property == 'attr' && detail.prop_key == 'description'
260 s = l(:text_journal_changed_no_detail, :label => label)
260 s = l(:text_journal_changed_no_detail, :label => label)
261 unless no_html
261 unless no_html
262 diff_link = link_to 'diff',
262 diff_link = link_to 'diff',
263 {:controller => 'journals', :action => 'diff', :id => detail.journal_id, :detail_id => detail.id},
263 {:controller => 'journals', :action => 'diff', :id => detail.journal_id, :detail_id => detail.id},
264 :title => l(:label_view_diff)
264 :title => l(:label_view_diff)
265 s << " (#{ diff_link })"
265 s << " (#{ diff_link })"
266 end
266 end
267 s.html_safe
267 s.html_safe
268 elsif detail.value.present?
268 elsif detail.value.present?
269 case detail.property
269 case detail.property
270 when 'attr', 'cf'
270 when 'attr', 'cf'
271 if detail.old_value.present?
271 if detail.old_value.present?
272 l(:text_journal_changed, :label => label, :old => old_value, :new => value).html_safe
272 l(:text_journal_changed, :label => label, :old => old_value, :new => value).html_safe
273 elsif multiple
273 elsif multiple
274 l(:text_journal_added, :label => label, :value => value).html_safe
274 l(:text_journal_added, :label => label, :value => value).html_safe
275 else
275 else
276 l(:text_journal_set_to, :label => label, :value => value).html_safe
276 l(:text_journal_set_to, :label => label, :value => value).html_safe
277 end
277 end
278 when 'attachment'
278 when 'attachment'
279 l(:text_journal_added, :label => label, :value => value).html_safe
279 l(:text_journal_added, :label => label, :value => value).html_safe
280 end
280 end
281 else
281 else
282 l(:text_journal_deleted, :label => label, :old => old_value).html_safe
282 l(:text_journal_deleted, :label => label, :old => old_value).html_safe
283 end
283 end
284 end
284 end
285
285
286 # Find the name of an associated record stored in the field attribute
286 # Find the name of an associated record stored in the field attribute
287 def find_name_by_reflection(field, id)
287 def find_name_by_reflection(field, id)
288 association = Issue.reflect_on_association(field.to_sym)
288 association = Issue.reflect_on_association(field.to_sym)
289 if association
289 if association
290 record = association.class_name.constantize.find_by_id(id)
290 record = association.class_name.constantize.find_by_id(id)
291 return record.name if record
291 return record.name if record
292 end
292 end
293 end
293 end
294
294
295 # Renders issue children recursively
295 # Renders issue children recursively
296 def render_api_issue_children(issue, api)
296 def render_api_issue_children(issue, api)
297 return if issue.leaf?
297 return if issue.leaf?
298 api.array :children do
298 api.array :children do
299 issue.children.each do |child|
299 issue.children.each do |child|
300 api.issue(:id => child.id) do
300 api.issue(:id => child.id) do
301 api.tracker(:id => child.tracker_id, :name => child.tracker.name) unless child.tracker.nil?
301 api.tracker(:id => child.tracker_id, :name => child.tracker.name) unless child.tracker.nil?
302 api.subject child.subject
302 api.subject child.subject
303 render_api_issue_children(child, api)
303 render_api_issue_children(child, api)
304 end
304 end
305 end
305 end
306 end
306 end
307 end
307 end
308
308
309 def issues_to_csv(issues, project, query, options={})
309 def issues_to_csv(issues, project, query, options={})
310 decimal_separator = l(:general_csv_decimal_separator)
310 decimal_separator = l(:general_csv_decimal_separator)
311 encoding = l(:general_csv_encoding)
311 encoding = l(:general_csv_encoding)
312 columns = (options[:columns] == 'all' ? query.available_columns : query.columns)
312 columns = (options[:columns] == 'all' ? query.available_columns : query.columns)
313
313
314 export = FCSV.generate(:col_sep => l(:general_csv_separator)) do |csv|
314 export = FCSV.generate(:col_sep => l(:general_csv_separator)) do |csv|
315 # csv header fields
315 # csv header fields
316 csv << [ "#" ] + columns.collect {|c| Redmine::CodesetUtil.from_utf8(c.caption.to_s, encoding) } +
316 csv << [ "#" ] + columns.collect {|c| Redmine::CodesetUtil.from_utf8(c.caption.to_s, encoding) } +
317 (options[:description] ? [Redmine::CodesetUtil.from_utf8(l(:field_description), encoding)] : [])
317 (options[:description] ? [Redmine::CodesetUtil.from_utf8(l(:field_description), encoding)] : [])
318
318
319 # csv lines
319 # csv lines
320 issues.each do |issue|
320 issues.each do |issue|
321 col_values = columns.collect do |column|
321 col_values = columns.collect do |column|
322 s = if column.is_a?(QueryCustomFieldColumn)
322 s = if column.is_a?(QueryCustomFieldColumn)
323 cv = issue.custom_values.detect {|v| v.custom_field_id == column.custom_field.id}
323 cv = issue.custom_field_values.detect {|v| v.custom_field_id == column.custom_field.id}
324 show_value(cv)
324 show_value(cv)
325 else
325 else
326 value = issue.send(column.name)
326 value = issue.send(column.name)
327 if value.is_a?(Date)
327 if value.is_a?(Date)
328 format_date(value)
328 format_date(value)
329 elsif value.is_a?(Time)
329 elsif value.is_a?(Time)
330 format_time(value)
330 format_time(value)
331 elsif value.is_a?(Float)
331 elsif value.is_a?(Float)
332 value.to_s.gsub('.', decimal_separator)
332 value.to_s.gsub('.', decimal_separator)
333 else
333 else
334 value
334 value
335 end
335 end
336 end
336 end
337 s.to_s
337 s.to_s
338 end
338 end
339 csv << [ issue.id.to_s ] + col_values.collect {|c| Redmine::CodesetUtil.from_utf8(c.to_s, encoding) } +
339 csv << [ issue.id.to_s ] + col_values.collect {|c| Redmine::CodesetUtil.from_utf8(c.to_s, encoding) } +
340 (options[:description] ? [Redmine::CodesetUtil.from_utf8(issue.description, encoding)] : [])
340 (options[:description] ? [Redmine::CodesetUtil.from_utf8(issue.description, encoding)] : [])
341 end
341 end
342 end
342 end
343 export
343 export
344 end
344 end
345 end
345 end
@@ -1,196 +1,196
1 # encoding: utf-8
1 # encoding: utf-8
2 #
2 #
3 # Redmine - project management software
3 # Redmine - project management software
4 # Copyright (C) 2006-2011 Jean-Philippe Lang
4 # Copyright (C) 2006-2011 Jean-Philippe Lang
5 #
5 #
6 # This program is free software; you can redistribute it and/or
6 # This program is free software; you can redistribute it and/or
7 # modify it under the terms of the GNU General Public License
7 # modify it under the terms of the GNU General Public License
8 # as published by the Free Software Foundation; either version 2
8 # as published by the Free Software Foundation; either version 2
9 # of the License, or (at your option) any later version.
9 # of the License, or (at your option) any later version.
10 #
10 #
11 # This program is distributed in the hope that it will be useful,
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
14 # GNU General Public License for more details.
15 #
15 #
16 # You should have received a copy of the GNU General Public License
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19
19
20 module TimelogHelper
20 module TimelogHelper
21 include ApplicationHelper
21 include ApplicationHelper
22
22
23 def render_timelog_breadcrumb
23 def render_timelog_breadcrumb
24 links = []
24 links = []
25 links << link_to(l(:label_project_all), {:project_id => nil, :issue_id => nil})
25 links << link_to(l(:label_project_all), {:project_id => nil, :issue_id => nil})
26 links << link_to(h(@project), {:project_id => @project, :issue_id => nil}) if @project
26 links << link_to(h(@project), {:project_id => @project, :issue_id => nil}) if @project
27 if @issue
27 if @issue
28 if @issue.visible?
28 if @issue.visible?
29 links << link_to_issue(@issue, :subject => false)
29 links << link_to_issue(@issue, :subject => false)
30 else
30 else
31 links << "##{@issue.id}"
31 links << "##{@issue.id}"
32 end
32 end
33 end
33 end
34 breadcrumb links
34 breadcrumb links
35 end
35 end
36
36
37 # Returns a collection of activities for a select field. time_entry
37 # Returns a collection of activities for a select field. time_entry
38 # is optional and will be used to check if the selected TimeEntryActivity
38 # is optional and will be used to check if the selected TimeEntryActivity
39 # is active.
39 # is active.
40 def activity_collection_for_select_options(time_entry=nil, project=nil)
40 def activity_collection_for_select_options(time_entry=nil, project=nil)
41 project ||= @project
41 project ||= @project
42 if project.nil?
42 if project.nil?
43 activities = TimeEntryActivity.shared.active
43 activities = TimeEntryActivity.shared.active
44 else
44 else
45 activities = project.activities
45 activities = project.activities
46 end
46 end
47
47
48 collection = []
48 collection = []
49 if time_entry && time_entry.activity && !time_entry.activity.active?
49 if time_entry && time_entry.activity && !time_entry.activity.active?
50 collection << [ "--- #{l(:actionview_instancetag_blank_option)} ---", '' ]
50 collection << [ "--- #{l(:actionview_instancetag_blank_option)} ---", '' ]
51 else
51 else
52 collection << [ "--- #{l(:actionview_instancetag_blank_option)} ---", '' ] unless activities.detect(&:is_default)
52 collection << [ "--- #{l(:actionview_instancetag_blank_option)} ---", '' ] unless activities.detect(&:is_default)
53 end
53 end
54 activities.each { |a| collection << [a.name, a.id] }
54 activities.each { |a| collection << [a.name, a.id] }
55 collection
55 collection
56 end
56 end
57
57
58 def select_hours(data, criteria, value)
58 def select_hours(data, criteria, value)
59 if value.to_s.empty?
59 if value.to_s.empty?
60 data.select {|row| row[criteria].blank? }
60 data.select {|row| row[criteria].blank? }
61 else
61 else
62 data.select {|row| row[criteria].to_s == value.to_s}
62 data.select {|row| row[criteria].to_s == value.to_s}
63 end
63 end
64 end
64 end
65
65
66 def sum_hours(data)
66 def sum_hours(data)
67 sum = 0
67 sum = 0
68 data.each do |row|
68 data.each do |row|
69 sum += row['hours'].to_f
69 sum += row['hours'].to_f
70 end
70 end
71 sum
71 sum
72 end
72 end
73
73
74 def options_for_period_select(value)
74 def options_for_period_select(value)
75 options_for_select([[l(:label_all_time), 'all'],
75 options_for_select([[l(:label_all_time), 'all'],
76 [l(:label_today), 'today'],
76 [l(:label_today), 'today'],
77 [l(:label_yesterday), 'yesterday'],
77 [l(:label_yesterday), 'yesterday'],
78 [l(:label_this_week), 'current_week'],
78 [l(:label_this_week), 'current_week'],
79 [l(:label_last_week), 'last_week'],
79 [l(:label_last_week), 'last_week'],
80 [l(:label_last_n_days, 7), '7_days'],
80 [l(:label_last_n_days, 7), '7_days'],
81 [l(:label_this_month), 'current_month'],
81 [l(:label_this_month), 'current_month'],
82 [l(:label_last_month), 'last_month'],
82 [l(:label_last_month), 'last_month'],
83 [l(:label_last_n_days, 30), '30_days'],
83 [l(:label_last_n_days, 30), '30_days'],
84 [l(:label_this_year), 'current_year']],
84 [l(:label_this_year), 'current_year']],
85 value)
85 value)
86 end
86 end
87
87
88 def entries_to_csv(entries)
88 def entries_to_csv(entries)
89 decimal_separator = l(:general_csv_decimal_separator)
89 decimal_separator = l(:general_csv_decimal_separator)
90 custom_fields = TimeEntryCustomField.find(:all)
90 custom_fields = TimeEntryCustomField.find(:all)
91 export = FCSV.generate(:col_sep => l(:general_csv_separator)) do |csv|
91 export = FCSV.generate(:col_sep => l(:general_csv_separator)) do |csv|
92 # csv header fields
92 # csv header fields
93 headers = [l(:field_spent_on),
93 headers = [l(:field_spent_on),
94 l(:field_user),
94 l(:field_user),
95 l(:field_activity),
95 l(:field_activity),
96 l(:field_project),
96 l(:field_project),
97 l(:field_issue),
97 l(:field_issue),
98 l(:field_tracker),
98 l(:field_tracker),
99 l(:field_subject),
99 l(:field_subject),
100 l(:field_hours),
100 l(:field_hours),
101 l(:field_comments)
101 l(:field_comments)
102 ]
102 ]
103 # Export custom fields
103 # Export custom fields
104 headers += custom_fields.collect(&:name)
104 headers += custom_fields.collect(&:name)
105
105
106 csv << headers.collect {|c| Redmine::CodesetUtil.from_utf8(
106 csv << headers.collect {|c| Redmine::CodesetUtil.from_utf8(
107 c.to_s,
107 c.to_s,
108 l(:general_csv_encoding) ) }
108 l(:general_csv_encoding) ) }
109 # csv lines
109 # csv lines
110 entries.each do |entry|
110 entries.each do |entry|
111 fields = [format_date(entry.spent_on),
111 fields = [format_date(entry.spent_on),
112 entry.user,
112 entry.user,
113 entry.activity,
113 entry.activity,
114 entry.project,
114 entry.project,
115 (entry.issue ? entry.issue.id : nil),
115 (entry.issue ? entry.issue.id : nil),
116 (entry.issue ? entry.issue.tracker : nil),
116 (entry.issue ? entry.issue.tracker : nil),
117 (entry.issue ? entry.issue.subject : nil),
117 (entry.issue ? entry.issue.subject : nil),
118 entry.hours.to_s.gsub('.', decimal_separator),
118 entry.hours.to_s.gsub('.', decimal_separator),
119 entry.comments
119 entry.comments
120 ]
120 ]
121 fields += custom_fields.collect {|f| show_value(entry.custom_value_for(f)) }
121 fields += custom_fields.collect {|f| show_value(entry.custom_field_values.detect {|v| v.custom_field_id == f.id}) }
122
122
123 csv << fields.collect {|c| Redmine::CodesetUtil.from_utf8(
123 csv << fields.collect {|c| Redmine::CodesetUtil.from_utf8(
124 c.to_s,
124 c.to_s,
125 l(:general_csv_encoding) ) }
125 l(:general_csv_encoding) ) }
126 end
126 end
127 end
127 end
128 export
128 export
129 end
129 end
130
130
131 def format_criteria_value(criteria_options, value)
131 def format_criteria_value(criteria_options, value)
132 if value.blank?
132 if value.blank?
133 "[#{l(:label_none)}]"
133 "[#{l(:label_none)}]"
134 elsif k = criteria_options[:klass]
134 elsif k = criteria_options[:klass]
135 obj = k.find_by_id(value.to_i)
135 obj = k.find_by_id(value.to_i)
136 if obj.is_a?(Issue)
136 if obj.is_a?(Issue)
137 obj.visible? ? "#{obj.tracker} ##{obj.id}: #{obj.subject}" : "##{obj.id}"
137 obj.visible? ? "#{obj.tracker} ##{obj.id}: #{obj.subject}" : "##{obj.id}"
138 else
138 else
139 obj
139 obj
140 end
140 end
141 else
141 else
142 format_value(value, criteria_options[:format])
142 format_value(value, criteria_options[:format])
143 end
143 end
144 end
144 end
145
145
146 def report_to_csv(report)
146 def report_to_csv(report)
147 decimal_separator = l(:general_csv_decimal_separator)
147 decimal_separator = l(:general_csv_decimal_separator)
148 export = FCSV.generate(:col_sep => l(:general_csv_separator)) do |csv|
148 export = FCSV.generate(:col_sep => l(:general_csv_separator)) do |csv|
149 # Column headers
149 # Column headers
150 headers = report.criteria.collect {|criteria| l(report.available_criteria[criteria][:label]) }
150 headers = report.criteria.collect {|criteria| l(report.available_criteria[criteria][:label]) }
151 headers += report.periods
151 headers += report.periods
152 headers << l(:label_total)
152 headers << l(:label_total)
153 csv << headers.collect {|c| Redmine::CodesetUtil.from_utf8(
153 csv << headers.collect {|c| Redmine::CodesetUtil.from_utf8(
154 c.to_s,
154 c.to_s,
155 l(:general_csv_encoding) ) }
155 l(:general_csv_encoding) ) }
156 # Content
156 # Content
157 report_criteria_to_csv(csv, report.available_criteria, report.columns, report.criteria, report.periods, report.hours)
157 report_criteria_to_csv(csv, report.available_criteria, report.columns, report.criteria, report.periods, report.hours)
158 # Total row
158 # Total row
159 str_total = Redmine::CodesetUtil.from_utf8(l(:label_total), l(:general_csv_encoding))
159 str_total = Redmine::CodesetUtil.from_utf8(l(:label_total), l(:general_csv_encoding))
160 row = [ str_total ] + [''] * (report.criteria.size - 1)
160 row = [ str_total ] + [''] * (report.criteria.size - 1)
161 total = 0
161 total = 0
162 report.periods.each do |period|
162 report.periods.each do |period|
163 sum = sum_hours(select_hours(report.hours, report.columns, period.to_s))
163 sum = sum_hours(select_hours(report.hours, report.columns, period.to_s))
164 total += sum
164 total += sum
165 row << (sum > 0 ? ("%.2f" % sum).gsub('.',decimal_separator) : '')
165 row << (sum > 0 ? ("%.2f" % sum).gsub('.',decimal_separator) : '')
166 end
166 end
167 row << ("%.2f" % total).gsub('.',decimal_separator)
167 row << ("%.2f" % total).gsub('.',decimal_separator)
168 csv << row
168 csv << row
169 end
169 end
170 export
170 export
171 end
171 end
172
172
173 def report_criteria_to_csv(csv, available_criteria, columns, criteria, periods, hours, level=0)
173 def report_criteria_to_csv(csv, available_criteria, columns, criteria, periods, hours, level=0)
174 decimal_separator = l(:general_csv_decimal_separator)
174 decimal_separator = l(:general_csv_decimal_separator)
175 hours.collect {|h| h[criteria[level]].to_s}.uniq.each do |value|
175 hours.collect {|h| h[criteria[level]].to_s}.uniq.each do |value|
176 hours_for_value = select_hours(hours, criteria[level], value)
176 hours_for_value = select_hours(hours, criteria[level], value)
177 next if hours_for_value.empty?
177 next if hours_for_value.empty?
178 row = [''] * level
178 row = [''] * level
179 row << Redmine::CodesetUtil.from_utf8(
179 row << Redmine::CodesetUtil.from_utf8(
180 format_criteria_value(available_criteria[criteria[level]], value).to_s,
180 format_criteria_value(available_criteria[criteria[level]], value).to_s,
181 l(:general_csv_encoding) )
181 l(:general_csv_encoding) )
182 row += [''] * (criteria.length - level - 1)
182 row += [''] * (criteria.length - level - 1)
183 total = 0
183 total = 0
184 periods.each do |period|
184 periods.each do |period|
185 sum = sum_hours(select_hours(hours_for_value, columns, period.to_s))
185 sum = sum_hours(select_hours(hours_for_value, columns, period.to_s))
186 total += sum
186 total += sum
187 row << (sum > 0 ? ("%.2f" % sum).gsub('.',decimal_separator) : '')
187 row << (sum > 0 ? ("%.2f" % sum).gsub('.',decimal_separator) : '')
188 end
188 end
189 row << ("%.2f" % total).gsub('.',decimal_separator)
189 row << ("%.2f" % total).gsub('.',decimal_separator)
190 csv << row
190 csv << row
191 if criteria.length > level + 1
191 if criteria.length > level + 1
192 report_criteria_to_csv(csv, available_criteria, columns, criteria, periods, hours_for_value, level + 1)
192 report_criteria_to_csv(csv, available_criteria, columns, criteria, periods, hours_for_value, level + 1)
193 end
193 end
194 end
194 end
195 end
195 end
196 end
196 end
@@ -1,32 +1,32
1 <% if version.completed? %>
1 <% if version.completed? %>
2 <p><%= format_date(version.effective_date) %></p>
2 <p><%= format_date(version.effective_date) %></p>
3 <% elsif version.effective_date %>
3 <% elsif version.effective_date %>
4 <p><strong><%= due_date_distance_in_words(version.effective_date) %></strong> (<%= format_date(version.effective_date) %>)</p>
4 <p><strong><%= due_date_distance_in_words(version.effective_date) %></strong> (<%= format_date(version.effective_date) %>)</p>
5 <% end %>
5 <% end %>
6
6
7 <p><%=h version.description %></p>
7 <p><%=h version.description %></p>
8 <% if version.custom_values.any? %>
8 <% if version.custom_field_values.any? %>
9 <ul>
9 <ul>
10 <% version.custom_field_values.each do |custom_value| %>
10 <% version.custom_field_values.each do |custom_value| %>
11 <% if custom_value.value.present? %>
11 <% if custom_value.value.present? %>
12 <li><%=h custom_value.custom_field.name %>: <%=h show_value(custom_value) %></li>
12 <li><%=h custom_value.custom_field.name %>: <%=h show_value(custom_value) %></li>
13 <% end %>
13 <% end %>
14 <% end %>
14 <% end %>
15 </ul>
15 </ul>
16 <% end %>
16 <% end %>
17
17
18 <% if version.fixed_issues.count > 0 %>
18 <% if version.fixed_issues.count > 0 %>
19 <%= progress_bar([version.closed_pourcent, version.completed_pourcent], :width => '40em', :legend => ('%0.0f%' % version.completed_pourcent)) %>
19 <%= progress_bar([version.closed_pourcent, version.completed_pourcent], :width => '40em', :legend => ('%0.0f%' % version.completed_pourcent)) %>
20 <p class="progress-info">
20 <p class="progress-info">
21 <%= link_to(l(:label_x_issues, :count => version.fixed_issues.count),
21 <%= link_to(l(:label_x_issues, :count => version.fixed_issues.count),
22 project_issues_path(version.project, :status_id => '*', :fixed_version_id => version, :set_filter => 1)) %>
22 project_issues_path(version.project, :status_id => '*', :fixed_version_id => version, :set_filter => 1)) %>
23 &nbsp;
23 &nbsp;
24 (<%= link_to_if(version.closed_issues_count > 0, l(:label_x_closed_issues_abbr, :count => version.closed_issues_count),
24 (<%= link_to_if(version.closed_issues_count > 0, l(:label_x_closed_issues_abbr, :count => version.closed_issues_count),
25 project_issues_path(version.project, :status_id => 'c', :fixed_version_id => version, :set_filter => 1)) %>
25 project_issues_path(version.project, :status_id => 'c', :fixed_version_id => version, :set_filter => 1)) %>
26 &#8212;
26 &#8212;
27 <%= link_to_if(version.open_issues_count > 0, l(:label_x_open_issues_abbr, :count => version.open_issues_count),
27 <%= link_to_if(version.open_issues_count > 0, l(:label_x_open_issues_abbr, :count => version.open_issues_count),
28 project_issues_path(version.project, :status_id => 'o', :fixed_version_id => version, :set_filter => 1)) %>)
28 project_issues_path(version.project, :status_id => 'o', :fixed_version_id => version, :set_filter => 1)) %>)
29 </p>
29 </p>
30 <% else %>
30 <% else %>
31 <p class="progress-info"><%= l(:label_roadmap_no_issues) %></p>
31 <p class="progress-info"><%= l(:label_roadmap_no_issues) %></p>
32 <% end %>
32 <% end %>
@@ -1,553 +1,553
1 # encoding: utf-8
1 # encoding: utf-8
2 #
2 #
3 # Redmine - project management software
3 # Redmine - project management software
4 # Copyright (C) 2006-2011 Jean-Philippe Lang
4 # Copyright (C) 2006-2011 Jean-Philippe Lang
5 #
5 #
6 # This program is free software; you can redistribute it and/or
6 # This program is free software; you can redistribute it and/or
7 # modify it under the terms of the GNU General Public License
7 # modify it under the terms of the GNU General Public License
8 # as published by the Free Software Foundation; either version 2
8 # as published by the Free Software Foundation; either version 2
9 # of the License, or (at your option) any later version.
9 # of the License, or (at your option) any later version.
10 #
10 #
11 # This program is distributed in the hope that it will be useful,
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
14 # GNU General Public License for more details.
15 #
15 #
16 # You should have received a copy of the GNU General Public License
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19
19
20 require 'iconv'
20 require 'iconv'
21 require 'fpdf/chinese'
21 require 'fpdf/chinese'
22 require 'fpdf/japanese'
22 require 'fpdf/japanese'
23 require 'fpdf/korean'
23 require 'fpdf/korean'
24 require 'core/rmagick'
24 require 'core/rmagick'
25
25
26 module Redmine
26 module Redmine
27 module Export
27 module Export
28 module PDF
28 module PDF
29 include ActionView::Helpers::TextHelper
29 include ActionView::Helpers::TextHelper
30 include ActionView::Helpers::NumberHelper
30 include ActionView::Helpers::NumberHelper
31 include IssuesHelper
31 include IssuesHelper
32
32
33 class ITCPDF < TCPDF
33 class ITCPDF < TCPDF
34 include Redmine::I18n
34 include Redmine::I18n
35 attr_accessor :footer_date
35 attr_accessor :footer_date
36
36
37 def initialize(lang)
37 def initialize(lang)
38 @@k_path_cache = Rails.root.join('tmp', 'pdf')
38 @@k_path_cache = Rails.root.join('tmp', 'pdf')
39 FileUtils.mkdir_p @@k_path_cache unless File::exist?(@@k_path_cache)
39 FileUtils.mkdir_p @@k_path_cache unless File::exist?(@@k_path_cache)
40 set_language_if_valid lang
40 set_language_if_valid lang
41 pdf_encoding = l(:general_pdf_encoding).upcase
41 pdf_encoding = l(:general_pdf_encoding).upcase
42 super('P', 'mm', 'A4', (pdf_encoding == 'UTF-8'), pdf_encoding)
42 super('P', 'mm', 'A4', (pdf_encoding == 'UTF-8'), pdf_encoding)
43 case current_language.to_s.downcase
43 case current_language.to_s.downcase
44 when 'vi'
44 when 'vi'
45 @font_for_content = 'DejaVuSans'
45 @font_for_content = 'DejaVuSans'
46 @font_for_footer = 'DejaVuSans'
46 @font_for_footer = 'DejaVuSans'
47 else
47 else
48 case pdf_encoding
48 case pdf_encoding
49 when 'UTF-8'
49 when 'UTF-8'
50 @font_for_content = 'FreeSans'
50 @font_for_content = 'FreeSans'
51 @font_for_footer = 'FreeSans'
51 @font_for_footer = 'FreeSans'
52 when 'CP949'
52 when 'CP949'
53 extend(PDF_Korean)
53 extend(PDF_Korean)
54 AddUHCFont()
54 AddUHCFont()
55 @font_for_content = 'UHC'
55 @font_for_content = 'UHC'
56 @font_for_footer = 'UHC'
56 @font_for_footer = 'UHC'
57 when 'CP932', 'SJIS', 'SHIFT_JIS'
57 when 'CP932', 'SJIS', 'SHIFT_JIS'
58 extend(PDF_Japanese)
58 extend(PDF_Japanese)
59 AddSJISFont()
59 AddSJISFont()
60 @font_for_content = 'SJIS'
60 @font_for_content = 'SJIS'
61 @font_for_footer = 'SJIS'
61 @font_for_footer = 'SJIS'
62 when 'GB18030'
62 when 'GB18030'
63 extend(PDF_Chinese)
63 extend(PDF_Chinese)
64 AddGBFont()
64 AddGBFont()
65 @font_for_content = 'GB'
65 @font_for_content = 'GB'
66 @font_for_footer = 'GB'
66 @font_for_footer = 'GB'
67 when 'BIG5'
67 when 'BIG5'
68 extend(PDF_Chinese)
68 extend(PDF_Chinese)
69 AddBig5Font()
69 AddBig5Font()
70 @font_for_content = 'Big5'
70 @font_for_content = 'Big5'
71 @font_for_footer = 'Big5'
71 @font_for_footer = 'Big5'
72 else
72 else
73 @font_for_content = 'Arial'
73 @font_for_content = 'Arial'
74 @font_for_footer = 'Helvetica'
74 @font_for_footer = 'Helvetica'
75 end
75 end
76 end
76 end
77 SetCreator(Redmine::Info.app_name)
77 SetCreator(Redmine::Info.app_name)
78 SetFont(@font_for_content)
78 SetFont(@font_for_content)
79 end
79 end
80
80
81 def SetFontStyle(style, size)
81 def SetFontStyle(style, size)
82 SetFont(@font_for_content, style, size)
82 SetFont(@font_for_content, style, size)
83 end
83 end
84
84
85 def SetTitle(txt)
85 def SetTitle(txt)
86 txt = begin
86 txt = begin
87 utf16txt = Iconv.conv('UTF-16BE', 'UTF-8', txt)
87 utf16txt = Iconv.conv('UTF-16BE', 'UTF-8', txt)
88 hextxt = "<FEFF" # FEFF is BOM
88 hextxt = "<FEFF" # FEFF is BOM
89 hextxt << utf16txt.unpack("C*").map {|x| sprintf("%02X",x) }.join
89 hextxt << utf16txt.unpack("C*").map {|x| sprintf("%02X",x) }.join
90 hextxt << ">"
90 hextxt << ">"
91 rescue
91 rescue
92 txt
92 txt
93 end || ''
93 end || ''
94 super(txt)
94 super(txt)
95 end
95 end
96
96
97 def textstring(s)
97 def textstring(s)
98 # Format a text string
98 # Format a text string
99 if s =~ /^</ # This means the string is hex-dumped.
99 if s =~ /^</ # This means the string is hex-dumped.
100 return s
100 return s
101 else
101 else
102 return '('+escape(s)+')'
102 return '('+escape(s)+')'
103 end
103 end
104 end
104 end
105
105
106 def fix_text_encoding(txt)
106 def fix_text_encoding(txt)
107 RDMPdfEncoding::rdm_from_utf8(txt, l(:general_pdf_encoding))
107 RDMPdfEncoding::rdm_from_utf8(txt, l(:general_pdf_encoding))
108 end
108 end
109
109
110 def RDMCell(w ,h=0, txt='', border=0, ln=0, align='', fill=0, link='')
110 def RDMCell(w ,h=0, txt='', border=0, ln=0, align='', fill=0, link='')
111 Cell(w, h, fix_text_encoding(txt), border, ln, align, fill, link)
111 Cell(w, h, fix_text_encoding(txt), border, ln, align, fill, link)
112 end
112 end
113
113
114 def RDMMultiCell(w, h=0, txt='', border=0, align='', fill=0, ln=1)
114 def RDMMultiCell(w, h=0, txt='', border=0, align='', fill=0, ln=1)
115 MultiCell(w, h, fix_text_encoding(txt), border, align, fill, ln)
115 MultiCell(w, h, fix_text_encoding(txt), border, align, fill, ln)
116 end
116 end
117
117
118 def RDMwriteHTMLCell(w, h, x, y, txt='', attachments=[], border=0, ln=1, fill=0)
118 def RDMwriteHTMLCell(w, h, x, y, txt='', attachments=[], border=0, ln=1, fill=0)
119 @attachments = attachments
119 @attachments = attachments
120 writeHTMLCell(w, h, x, y,
120 writeHTMLCell(w, h, x, y,
121 fix_text_encoding(
121 fix_text_encoding(
122 Redmine::WikiFormatting.to_html(Setting.text_formatting, txt)),
122 Redmine::WikiFormatting.to_html(Setting.text_formatting, txt)),
123 border, ln, fill)
123 border, ln, fill)
124 end
124 end
125
125
126 def getImageFilename(attrname)
126 def getImageFilename(attrname)
127 # attrname: general_pdf_encoding string file/uri name
127 # attrname: general_pdf_encoding string file/uri name
128 atta = RDMPdfEncoding.attach(@attachments, attrname, l(:general_pdf_encoding))
128 atta = RDMPdfEncoding.attach(@attachments, attrname, l(:general_pdf_encoding))
129 if atta
129 if atta
130 return atta.diskfile
130 return atta.diskfile
131 else
131 else
132 return nil
132 return nil
133 end
133 end
134 end
134 end
135
135
136 def Footer
136 def Footer
137 SetFont(@font_for_footer, 'I', 8)
137 SetFont(@font_for_footer, 'I', 8)
138 SetY(-15)
138 SetY(-15)
139 SetX(15)
139 SetX(15)
140 RDMCell(0, 5, @footer_date, 0, 0, 'L')
140 RDMCell(0, 5, @footer_date, 0, 0, 'L')
141 SetY(-15)
141 SetY(-15)
142 SetX(-30)
142 SetX(-30)
143 RDMCell(0, 5, PageNo().to_s + '/{nb}', 0, 0, 'C')
143 RDMCell(0, 5, PageNo().to_s + '/{nb}', 0, 0, 'C')
144 end
144 end
145 end
145 end
146
146
147 # Returns a PDF string of a list of issues
147 # Returns a PDF string of a list of issues
148 def issues_to_pdf(issues, project, query)
148 def issues_to_pdf(issues, project, query)
149 pdf = ITCPDF.new(current_language)
149 pdf = ITCPDF.new(current_language)
150 title = query.new_record? ? l(:label_issue_plural) : query.name
150 title = query.new_record? ? l(:label_issue_plural) : query.name
151 title = "#{project} - #{title}" if project
151 title = "#{project} - #{title}" if project
152 pdf.SetTitle(title)
152 pdf.SetTitle(title)
153 pdf.alias_nb_pages
153 pdf.alias_nb_pages
154 pdf.footer_date = format_date(Date.today)
154 pdf.footer_date = format_date(Date.today)
155 pdf.SetAutoPageBreak(false)
155 pdf.SetAutoPageBreak(false)
156 pdf.AddPage("L")
156 pdf.AddPage("L")
157
157
158 # Landscape A4 = 210 x 297 mm
158 # Landscape A4 = 210 x 297 mm
159 page_height = 210
159 page_height = 210
160 page_width = 297
160 page_width = 297
161 right_margin = 10
161 right_margin = 10
162 bottom_margin = 20
162 bottom_margin = 20
163 col_id_width = 10
163 col_id_width = 10
164 row_height = 5
164 row_height = 5
165
165
166 # column widths
166 # column widths
167 table_width = page_width - right_margin - 10 # fixed left margin
167 table_width = page_width - right_margin - 10 # fixed left margin
168 col_width = []
168 col_width = []
169 unless query.columns.empty?
169 unless query.columns.empty?
170 col_width = query.columns.collect do |c|
170 col_width = query.columns.collect do |c|
171 (c.name == :subject || (c.is_a?(QueryCustomFieldColumn) &&
171 (c.name == :subject || (c.is_a?(QueryCustomFieldColumn) &&
172 ['string', 'text'].include?(c.custom_field.field_format))) ? 4.0 : 1.0
172 ['string', 'text'].include?(c.custom_field.field_format))) ? 4.0 : 1.0
173 end
173 end
174 ratio = (table_width - col_id_width) / col_width.inject(0) {|s,w| s += w}
174 ratio = (table_width - col_id_width) / col_width.inject(0) {|s,w| s += w}
175 col_width = col_width.collect {|w| w * ratio}
175 col_width = col_width.collect {|w| w * ratio}
176 end
176 end
177
177
178 # title
178 # title
179 pdf.SetFontStyle('B',11)
179 pdf.SetFontStyle('B',11)
180 pdf.RDMCell(190,10, title)
180 pdf.RDMCell(190,10, title)
181 pdf.Ln
181 pdf.Ln
182
182
183 # headers
183 # headers
184 pdf.SetFontStyle('B',8)
184 pdf.SetFontStyle('B',8)
185 pdf.SetFillColor(230, 230, 230)
185 pdf.SetFillColor(230, 230, 230)
186
186
187 # render it background to find the max height used
187 # render it background to find the max height used
188 base_x = pdf.GetX
188 base_x = pdf.GetX
189 base_y = pdf.GetY
189 base_y = pdf.GetY
190 max_height = issues_to_pdf_write_cells(pdf, query.columns, col_width, row_height, true)
190 max_height = issues_to_pdf_write_cells(pdf, query.columns, col_width, row_height, true)
191 pdf.Rect(base_x, base_y, table_width, max_height, 'FD');
191 pdf.Rect(base_x, base_y, table_width, max_height, 'FD');
192 pdf.SetXY(base_x, base_y);
192 pdf.SetXY(base_x, base_y);
193
193
194 # write the cells on page
194 # write the cells on page
195 pdf.RDMCell(col_id_width, row_height, "#", "T", 0, 'C', 1)
195 pdf.RDMCell(col_id_width, row_height, "#", "T", 0, 'C', 1)
196 issues_to_pdf_write_cells(pdf, query.columns, col_width, row_height, true)
196 issues_to_pdf_write_cells(pdf, query.columns, col_width, row_height, true)
197 issues_to_pdf_draw_borders(pdf, base_x, base_y, base_y + max_height, col_id_width, col_width)
197 issues_to_pdf_draw_borders(pdf, base_x, base_y, base_y + max_height, col_id_width, col_width)
198 pdf.SetY(base_y + max_height);
198 pdf.SetY(base_y + max_height);
199
199
200 # rows
200 # rows
201 pdf.SetFontStyle('',8)
201 pdf.SetFontStyle('',8)
202 pdf.SetFillColor(255, 255, 255)
202 pdf.SetFillColor(255, 255, 255)
203 previous_group = false
203 previous_group = false
204 issue_list(issues) do |issue, level|
204 issue_list(issues) do |issue, level|
205 if query.grouped? &&
205 if query.grouped? &&
206 (group = query.group_by_column.value(issue)) != previous_group
206 (group = query.group_by_column.value(issue)) != previous_group
207 pdf.SetFontStyle('B',9)
207 pdf.SetFontStyle('B',9)
208 pdf.RDMCell(277, row_height,
208 pdf.RDMCell(277, row_height,
209 (group.blank? ? 'None' : group.to_s) + " (#{query.issue_count_by_group[group]})",
209 (group.blank? ? 'None' : group.to_s) + " (#{query.issue_count_by_group[group]})",
210 1, 1, 'L')
210 1, 1, 'L')
211 pdf.SetFontStyle('',8)
211 pdf.SetFontStyle('',8)
212 previous_group = group
212 previous_group = group
213 end
213 end
214 # fetch all the row values
214 # fetch all the row values
215 col_values = query.columns.collect do |column|
215 col_values = query.columns.collect do |column|
216 s = if column.is_a?(QueryCustomFieldColumn)
216 s = if column.is_a?(QueryCustomFieldColumn)
217 cv = issue.custom_values.detect {|v| v.custom_field_id == column.custom_field.id}
217 cv = issue.custom_field_values.detect {|v| v.custom_field_id == column.custom_field.id}
218 show_value(cv)
218 show_value(cv)
219 else
219 else
220 value = issue.send(column.name)
220 value = issue.send(column.name)
221 if column.name == :subject
221 if column.name == :subject
222 value = " " * level + value
222 value = " " * level + value
223 end
223 end
224 if value.is_a?(Date)
224 if value.is_a?(Date)
225 format_date(value)
225 format_date(value)
226 elsif value.is_a?(Time)
226 elsif value.is_a?(Time)
227 format_time(value)
227 format_time(value)
228 else
228 else
229 value
229 value
230 end
230 end
231 end
231 end
232 s.to_s
232 s.to_s
233 end
233 end
234
234
235 # render it off-page to find the max height used
235 # render it off-page to find the max height used
236 base_x = pdf.GetX
236 base_x = pdf.GetX
237 base_y = pdf.GetY
237 base_y = pdf.GetY
238 pdf.SetY(2 * page_height)
238 pdf.SetY(2 * page_height)
239 max_height = issues_to_pdf_write_cells(pdf, col_values, col_width, row_height)
239 max_height = issues_to_pdf_write_cells(pdf, col_values, col_width, row_height)
240 pdf.SetXY(base_x, base_y)
240 pdf.SetXY(base_x, base_y)
241
241
242 # make new page if it doesn't fit on the current one
242 # make new page if it doesn't fit on the current one
243 space_left = page_height - base_y - bottom_margin
243 space_left = page_height - base_y - bottom_margin
244 if max_height > space_left
244 if max_height > space_left
245 pdf.AddPage("L")
245 pdf.AddPage("L")
246 base_x = pdf.GetX
246 base_x = pdf.GetX
247 base_y = pdf.GetY
247 base_y = pdf.GetY
248 end
248 end
249
249
250 # write the cells on page
250 # write the cells on page
251 pdf.RDMCell(col_id_width, row_height, issue.id.to_s, "T", 0, 'C', 1)
251 pdf.RDMCell(col_id_width, row_height, issue.id.to_s, "T", 0, 'C', 1)
252 issues_to_pdf_write_cells(pdf, col_values, col_width, row_height)
252 issues_to_pdf_write_cells(pdf, col_values, col_width, row_height)
253 issues_to_pdf_draw_borders(pdf, base_x, base_y, base_y + max_height, col_id_width, col_width)
253 issues_to_pdf_draw_borders(pdf, base_x, base_y, base_y + max_height, col_id_width, col_width)
254 pdf.SetY(base_y + max_height);
254 pdf.SetY(base_y + max_height);
255 end
255 end
256
256
257 if issues.size == Setting.issues_export_limit.to_i
257 if issues.size == Setting.issues_export_limit.to_i
258 pdf.SetFontStyle('B',10)
258 pdf.SetFontStyle('B',10)
259 pdf.RDMCell(0, row_height, '...')
259 pdf.RDMCell(0, row_height, '...')
260 end
260 end
261 pdf.Output
261 pdf.Output
262 end
262 end
263
263
264 # Renders MultiCells and returns the maximum height used
264 # Renders MultiCells and returns the maximum height used
265 def issues_to_pdf_write_cells(pdf, col_values, col_widths,
265 def issues_to_pdf_write_cells(pdf, col_values, col_widths,
266 row_height, head=false)
266 row_height, head=false)
267 base_y = pdf.GetY
267 base_y = pdf.GetY
268 max_height = row_height
268 max_height = row_height
269 col_values.each_with_index do |column, i|
269 col_values.each_with_index do |column, i|
270 col_x = pdf.GetX
270 col_x = pdf.GetX
271 if head == true
271 if head == true
272 pdf.RDMMultiCell(col_widths[i], row_height, column.caption, "T", 'L', 1)
272 pdf.RDMMultiCell(col_widths[i], row_height, column.caption, "T", 'L', 1)
273 else
273 else
274 pdf.RDMMultiCell(col_widths[i], row_height, column, "T", 'L', 1)
274 pdf.RDMMultiCell(col_widths[i], row_height, column, "T", 'L', 1)
275 end
275 end
276 max_height = (pdf.GetY - base_y) if (pdf.GetY - base_y) > max_height
276 max_height = (pdf.GetY - base_y) if (pdf.GetY - base_y) > max_height
277 pdf.SetXY(col_x + col_widths[i], base_y);
277 pdf.SetXY(col_x + col_widths[i], base_y);
278 end
278 end
279 return max_height
279 return max_height
280 end
280 end
281
281
282 # Draw lines to close the row (MultiCell border drawing in not uniform)
282 # Draw lines to close the row (MultiCell border drawing in not uniform)
283 def issues_to_pdf_draw_borders(pdf, top_x, top_y, lower_y,
283 def issues_to_pdf_draw_borders(pdf, top_x, top_y, lower_y,
284 id_width, col_widths)
284 id_width, col_widths)
285 col_x = top_x + id_width
285 col_x = top_x + id_width
286 pdf.Line(col_x, top_y, col_x, lower_y) # id right border
286 pdf.Line(col_x, top_y, col_x, lower_y) # id right border
287 col_widths.each do |width|
287 col_widths.each do |width|
288 col_x += width
288 col_x += width
289 pdf.Line(col_x, top_y, col_x, lower_y) # columns right border
289 pdf.Line(col_x, top_y, col_x, lower_y) # columns right border
290 end
290 end
291 pdf.Line(top_x, top_y, top_x, lower_y) # left border
291 pdf.Line(top_x, top_y, top_x, lower_y) # left border
292 pdf.Line(top_x, lower_y, col_x, lower_y) # bottom border
292 pdf.Line(top_x, lower_y, col_x, lower_y) # bottom border
293 end
293 end
294
294
295 # Returns a PDF string of a single issue
295 # Returns a PDF string of a single issue
296 def issue_to_pdf(issue)
296 def issue_to_pdf(issue)
297 pdf = ITCPDF.new(current_language)
297 pdf = ITCPDF.new(current_language)
298 pdf.SetTitle("#{issue.project} - ##{issue.tracker} #{issue.id}")
298 pdf.SetTitle("#{issue.project} - ##{issue.tracker} #{issue.id}")
299 pdf.alias_nb_pages
299 pdf.alias_nb_pages
300 pdf.footer_date = format_date(Date.today)
300 pdf.footer_date = format_date(Date.today)
301 pdf.AddPage
301 pdf.AddPage
302 pdf.SetFontStyle('B',11)
302 pdf.SetFontStyle('B',11)
303 buf = "#{issue.project} - #{issue.tracker} # #{issue.id}"
303 buf = "#{issue.project} - #{issue.tracker} # #{issue.id}"
304 pdf.RDMMultiCell(190, 5, buf)
304 pdf.RDMMultiCell(190, 5, buf)
305 pdf.Ln
305 pdf.Ln
306 pdf.SetFontStyle('',8)
306 pdf.SetFontStyle('',8)
307 base_x = pdf.GetX
307 base_x = pdf.GetX
308 i = 1
308 i = 1
309 issue.ancestors.each do |ancestor|
309 issue.ancestors.each do |ancestor|
310 pdf.SetX(base_x + i)
310 pdf.SetX(base_x + i)
311 buf = "#{ancestor.tracker} # #{ancestor.id} (#{ancestor.status.to_s}): #{ancestor.subject}"
311 buf = "#{ancestor.tracker} # #{ancestor.id} (#{ancestor.status.to_s}): #{ancestor.subject}"
312 pdf.RDMMultiCell(190 - i, 5, buf)
312 pdf.RDMMultiCell(190 - i, 5, buf)
313 i += 1 if i < 35
313 i += 1 if i < 35
314 end
314 end
315 pdf.Ln
315 pdf.Ln
316
316
317 pdf.SetFontStyle('B',9)
317 pdf.SetFontStyle('B',9)
318 pdf.RDMCell(35,5, l(:field_status) + ":","LT")
318 pdf.RDMCell(35,5, l(:field_status) + ":","LT")
319 pdf.SetFontStyle('',9)
319 pdf.SetFontStyle('',9)
320 pdf.RDMCell(60,5, issue.status.to_s,"RT")
320 pdf.RDMCell(60,5, issue.status.to_s,"RT")
321 pdf.SetFontStyle('B',9)
321 pdf.SetFontStyle('B',9)
322 pdf.RDMCell(35,5, l(:field_priority) + ":","LT")
322 pdf.RDMCell(35,5, l(:field_priority) + ":","LT")
323 pdf.SetFontStyle('',9)
323 pdf.SetFontStyle('',9)
324 pdf.RDMCell(60,5, issue.priority.to_s,"RT")
324 pdf.RDMCell(60,5, issue.priority.to_s,"RT")
325 pdf.Ln
325 pdf.Ln
326
326
327 pdf.SetFontStyle('B',9)
327 pdf.SetFontStyle('B',9)
328 pdf.RDMCell(35,5, l(:field_author) + ":","L")
328 pdf.RDMCell(35,5, l(:field_author) + ":","L")
329 pdf.SetFontStyle('',9)
329 pdf.SetFontStyle('',9)
330 pdf.RDMCell(60,5, issue.author.to_s,"R")
330 pdf.RDMCell(60,5, issue.author.to_s,"R")
331 pdf.SetFontStyle('B',9)
331 pdf.SetFontStyle('B',9)
332 pdf.RDMCell(35,5, l(:field_category) + ":","L")
332 pdf.RDMCell(35,5, l(:field_category) + ":","L")
333 pdf.SetFontStyle('',9)
333 pdf.SetFontStyle('',9)
334 pdf.RDMCell(60,5, issue.category.to_s,"R")
334 pdf.RDMCell(60,5, issue.category.to_s,"R")
335 pdf.Ln
335 pdf.Ln
336
336
337 pdf.SetFontStyle('B',9)
337 pdf.SetFontStyle('B',9)
338 pdf.RDMCell(35,5, l(:field_created_on) + ":","L")
338 pdf.RDMCell(35,5, l(:field_created_on) + ":","L")
339 pdf.SetFontStyle('',9)
339 pdf.SetFontStyle('',9)
340 pdf.RDMCell(60,5, format_date(issue.created_on),"R")
340 pdf.RDMCell(60,5, format_date(issue.created_on),"R")
341 pdf.SetFontStyle('B',9)
341 pdf.SetFontStyle('B',9)
342 pdf.RDMCell(35,5, l(:field_assigned_to) + ":","L")
342 pdf.RDMCell(35,5, l(:field_assigned_to) + ":","L")
343 pdf.SetFontStyle('',9)
343 pdf.SetFontStyle('',9)
344 pdf.RDMCell(60,5, issue.assigned_to.to_s,"R")
344 pdf.RDMCell(60,5, issue.assigned_to.to_s,"R")
345 pdf.Ln
345 pdf.Ln
346
346
347 pdf.SetFontStyle('B',9)
347 pdf.SetFontStyle('B',9)
348 pdf.RDMCell(35,5, l(:field_updated_on) + ":","LB")
348 pdf.RDMCell(35,5, l(:field_updated_on) + ":","LB")
349 pdf.SetFontStyle('',9)
349 pdf.SetFontStyle('',9)
350 pdf.RDMCell(60,5, format_date(issue.updated_on),"RB")
350 pdf.RDMCell(60,5, format_date(issue.updated_on),"RB")
351 pdf.SetFontStyle('B',9)
351 pdf.SetFontStyle('B',9)
352 pdf.RDMCell(35,5, l(:field_due_date) + ":","LB")
352 pdf.RDMCell(35,5, l(:field_due_date) + ":","LB")
353 pdf.SetFontStyle('',9)
353 pdf.SetFontStyle('',9)
354 pdf.RDMCell(60,5, format_date(issue.due_date),"RB")
354 pdf.RDMCell(60,5, format_date(issue.due_date),"RB")
355 pdf.Ln
355 pdf.Ln
356
356
357 for custom_value in issue.custom_field_values
357 for custom_value in issue.custom_field_values
358 pdf.SetFontStyle('B',9)
358 pdf.SetFontStyle('B',9)
359 pdf.RDMCell(35,5, custom_value.custom_field.name + ":","L")
359 pdf.RDMCell(35,5, custom_value.custom_field.name + ":","L")
360 pdf.SetFontStyle('',9)
360 pdf.SetFontStyle('',9)
361 pdf.RDMMultiCell(155,5, (show_value custom_value),"R")
361 pdf.RDMMultiCell(155,5, (show_value custom_value),"R")
362 end
362 end
363
363
364 y0 = pdf.GetY
364 y0 = pdf.GetY
365
365
366 pdf.SetFontStyle('B',9)
366 pdf.SetFontStyle('B',9)
367 pdf.RDMCell(35,5, l(:field_subject) + ":","LT")
367 pdf.RDMCell(35,5, l(:field_subject) + ":","LT")
368 pdf.SetFontStyle('',9)
368 pdf.SetFontStyle('',9)
369 pdf.RDMMultiCell(155,5, issue.subject,"RT")
369 pdf.RDMMultiCell(155,5, issue.subject,"RT")
370 pdf.Line(pdf.GetX, y0, pdf.GetX, pdf.GetY)
370 pdf.Line(pdf.GetX, y0, pdf.GetX, pdf.GetY)
371
371
372 pdf.SetFontStyle('B',9)
372 pdf.SetFontStyle('B',9)
373 pdf.RDMCell(35+155, 5, l(:field_description), "LRT", 1)
373 pdf.RDMCell(35+155, 5, l(:field_description), "LRT", 1)
374 pdf.SetFontStyle('',9)
374 pdf.SetFontStyle('',9)
375
375
376 # Set resize image scale
376 # Set resize image scale
377 pdf.SetImageScale(1.6)
377 pdf.SetImageScale(1.6)
378 pdf.RDMwriteHTMLCell(35+155, 5, 0, 0,
378 pdf.RDMwriteHTMLCell(35+155, 5, 0, 0,
379 issue.description.to_s, issue.attachments, "LRB")
379 issue.description.to_s, issue.attachments, "LRB")
380
380
381 unless issue.leaf?
381 unless issue.leaf?
382 # for CJK
382 # for CJK
383 truncate_length = ( l(:general_pdf_encoding).upcase == "UTF-8" ? 90 : 65 )
383 truncate_length = ( l(:general_pdf_encoding).upcase == "UTF-8" ? 90 : 65 )
384
384
385 pdf.SetFontStyle('B',9)
385 pdf.SetFontStyle('B',9)
386 pdf.RDMCell(35+155,5, l(:label_subtask_plural) + ":", "LTR")
386 pdf.RDMCell(35+155,5, l(:label_subtask_plural) + ":", "LTR")
387 pdf.Ln
387 pdf.Ln
388 issue_list(issue.descendants.sort_by(&:lft)) do |child, level|
388 issue_list(issue.descendants.sort_by(&:lft)) do |child, level|
389 buf = truncate("#{child.tracker} # #{child.id}: #{child.subject}",
389 buf = truncate("#{child.tracker} # #{child.id}: #{child.subject}",
390 :length => truncate_length)
390 :length => truncate_length)
391 level = 10 if level >= 10
391 level = 10 if level >= 10
392 pdf.SetFontStyle('',8)
392 pdf.SetFontStyle('',8)
393 pdf.RDMCell(35+135,5, (level >=1 ? " " * level : "") + buf, "L")
393 pdf.RDMCell(35+135,5, (level >=1 ? " " * level : "") + buf, "L")
394 pdf.SetFontStyle('B',8)
394 pdf.SetFontStyle('B',8)
395 pdf.RDMCell(20,5, child.status.to_s, "R")
395 pdf.RDMCell(20,5, child.status.to_s, "R")
396 pdf.Ln
396 pdf.Ln
397 end
397 end
398 end
398 end
399
399
400 relations = issue.relations.select { |r| r.other_issue(issue).visible? }
400 relations = issue.relations.select { |r| r.other_issue(issue).visible? }
401 unless relations.empty?
401 unless relations.empty?
402 # for CJK
402 # for CJK
403 truncate_length = ( l(:general_pdf_encoding).upcase == "UTF-8" ? 80 : 60 )
403 truncate_length = ( l(:general_pdf_encoding).upcase == "UTF-8" ? 80 : 60 )
404
404
405 pdf.SetFontStyle('B',9)
405 pdf.SetFontStyle('B',9)
406 pdf.RDMCell(35+155,5, l(:label_related_issues) + ":", "LTR")
406 pdf.RDMCell(35+155,5, l(:label_related_issues) + ":", "LTR")
407 pdf.Ln
407 pdf.Ln
408 relations.each do |relation|
408 relations.each do |relation|
409 buf = ""
409 buf = ""
410 buf += "#{l(relation.label_for(issue))} "
410 buf += "#{l(relation.label_for(issue))} "
411 if relation.delay && relation.delay != 0
411 if relation.delay && relation.delay != 0
412 buf += "(#{l('datetime.distance_in_words.x_days', :count => relation.delay)}) "
412 buf += "(#{l('datetime.distance_in_words.x_days', :count => relation.delay)}) "
413 end
413 end
414 if Setting.cross_project_issue_relations?
414 if Setting.cross_project_issue_relations?
415 buf += "#{relation.other_issue(issue).project} - "
415 buf += "#{relation.other_issue(issue).project} - "
416 end
416 end
417 buf += "#{relation.other_issue(issue).tracker}" +
417 buf += "#{relation.other_issue(issue).tracker}" +
418 " # #{relation.other_issue(issue).id}: #{relation.other_issue(issue).subject}"
418 " # #{relation.other_issue(issue).id}: #{relation.other_issue(issue).subject}"
419 buf = truncate(buf, :length => truncate_length)
419 buf = truncate(buf, :length => truncate_length)
420 pdf.SetFontStyle('', 8)
420 pdf.SetFontStyle('', 8)
421 pdf.RDMCell(35+155-60, 5, buf, "L")
421 pdf.RDMCell(35+155-60, 5, buf, "L")
422 pdf.SetFontStyle('B',8)
422 pdf.SetFontStyle('B',8)
423 pdf.RDMCell(20,5, relation.other_issue(issue).status.to_s, "")
423 pdf.RDMCell(20,5, relation.other_issue(issue).status.to_s, "")
424 pdf.RDMCell(20,5, format_date(relation.other_issue(issue).start_date), "")
424 pdf.RDMCell(20,5, format_date(relation.other_issue(issue).start_date), "")
425 pdf.RDMCell(20,5, format_date(relation.other_issue(issue).due_date), "R")
425 pdf.RDMCell(20,5, format_date(relation.other_issue(issue).due_date), "R")
426 pdf.Ln
426 pdf.Ln
427 end
427 end
428 end
428 end
429 pdf.RDMCell(190,5, "", "T")
429 pdf.RDMCell(190,5, "", "T")
430 pdf.Ln
430 pdf.Ln
431
431
432 if issue.changesets.any? &&
432 if issue.changesets.any? &&
433 User.current.allowed_to?(:view_changesets, issue.project)
433 User.current.allowed_to?(:view_changesets, issue.project)
434 pdf.SetFontStyle('B',9)
434 pdf.SetFontStyle('B',9)
435 pdf.RDMCell(190,5, l(:label_associated_revisions), "B")
435 pdf.RDMCell(190,5, l(:label_associated_revisions), "B")
436 pdf.Ln
436 pdf.Ln
437 for changeset in issue.changesets
437 for changeset in issue.changesets
438 pdf.SetFontStyle('B',8)
438 pdf.SetFontStyle('B',8)
439 csstr = "#{l(:label_revision)} #{changeset.format_identifier} - "
439 csstr = "#{l(:label_revision)} #{changeset.format_identifier} - "
440 csstr += format_time(changeset.committed_on) + " - " + changeset.author.to_s
440 csstr += format_time(changeset.committed_on) + " - " + changeset.author.to_s
441 pdf.RDMCell(190, 5, csstr)
441 pdf.RDMCell(190, 5, csstr)
442 pdf.Ln
442 pdf.Ln
443 unless changeset.comments.blank?
443 unless changeset.comments.blank?
444 pdf.SetFontStyle('',8)
444 pdf.SetFontStyle('',8)
445 pdf.RDMwriteHTMLCell(190,5,0,0,
445 pdf.RDMwriteHTMLCell(190,5,0,0,
446 changeset.comments.to_s, issue.attachments, "")
446 changeset.comments.to_s, issue.attachments, "")
447 end
447 end
448 pdf.Ln
448 pdf.Ln
449 end
449 end
450 end
450 end
451
451
452 pdf.SetFontStyle('B',9)
452 pdf.SetFontStyle('B',9)
453 pdf.RDMCell(190,5, l(:label_history), "B")
453 pdf.RDMCell(190,5, l(:label_history), "B")
454 pdf.Ln
454 pdf.Ln
455 indice = 0
455 indice = 0
456 for journal in issue.journals.find(
456 for journal in issue.journals.find(
457 :all, :include => [:user, :details],
457 :all, :include => [:user, :details],
458 :order => "#{Journal.table_name}.created_on ASC")
458 :order => "#{Journal.table_name}.created_on ASC")
459 indice = indice + 1
459 indice = indice + 1
460 pdf.SetFontStyle('B',8)
460 pdf.SetFontStyle('B',8)
461 pdf.RDMCell(190,5,
461 pdf.RDMCell(190,5,
462 "#" + indice.to_s +
462 "#" + indice.to_s +
463 " - " + format_time(journal.created_on) +
463 " - " + format_time(journal.created_on) +
464 " - " + journal.user.name)
464 " - " + journal.user.name)
465 pdf.Ln
465 pdf.Ln
466 pdf.SetFontStyle('I',8)
466 pdf.SetFontStyle('I',8)
467 details_to_strings(journal.details, true).each do |string|
467 details_to_strings(journal.details, true).each do |string|
468 pdf.RDMMultiCell(190,5, "- " + string)
468 pdf.RDMMultiCell(190,5, "- " + string)
469 end
469 end
470 if journal.notes?
470 if journal.notes?
471 pdf.Ln unless journal.details.empty?
471 pdf.Ln unless journal.details.empty?
472 pdf.SetFontStyle('',8)
472 pdf.SetFontStyle('',8)
473 pdf.RDMwriteHTMLCell(190,5,0,0,
473 pdf.RDMwriteHTMLCell(190,5,0,0,
474 journal.notes.to_s, issue.attachments, "")
474 journal.notes.to_s, issue.attachments, "")
475 end
475 end
476 pdf.Ln
476 pdf.Ln
477 end
477 end
478
478
479 if issue.attachments.any?
479 if issue.attachments.any?
480 pdf.SetFontStyle('B',9)
480 pdf.SetFontStyle('B',9)
481 pdf.RDMCell(190,5, l(:label_attachment_plural), "B")
481 pdf.RDMCell(190,5, l(:label_attachment_plural), "B")
482 pdf.Ln
482 pdf.Ln
483 for attachment in issue.attachments
483 for attachment in issue.attachments
484 pdf.SetFontStyle('',8)
484 pdf.SetFontStyle('',8)
485 pdf.RDMCell(80,5, attachment.filename)
485 pdf.RDMCell(80,5, attachment.filename)
486 pdf.RDMCell(20,5, number_to_human_size(attachment.filesize),0,0,"R")
486 pdf.RDMCell(20,5, number_to_human_size(attachment.filesize),0,0,"R")
487 pdf.RDMCell(25,5, format_date(attachment.created_on),0,0,"R")
487 pdf.RDMCell(25,5, format_date(attachment.created_on),0,0,"R")
488 pdf.RDMCell(65,5, attachment.author.name,0,0,"R")
488 pdf.RDMCell(65,5, attachment.author.name,0,0,"R")
489 pdf.Ln
489 pdf.Ln
490 end
490 end
491 end
491 end
492 pdf.Output
492 pdf.Output
493 end
493 end
494
494
495 # Returns a PDF string of a single wiki page
495 # Returns a PDF string of a single wiki page
496 def wiki_to_pdf(page, project)
496 def wiki_to_pdf(page, project)
497 pdf = ITCPDF.new(current_language)
497 pdf = ITCPDF.new(current_language)
498 pdf.SetTitle("#{project} - #{page.title}")
498 pdf.SetTitle("#{project} - #{page.title}")
499 pdf.alias_nb_pages
499 pdf.alias_nb_pages
500 pdf.footer_date = format_date(Date.today)
500 pdf.footer_date = format_date(Date.today)
501 pdf.AddPage
501 pdf.AddPage
502 pdf.SetFontStyle('B',11)
502 pdf.SetFontStyle('B',11)
503 pdf.RDMMultiCell(190,5,
503 pdf.RDMMultiCell(190,5,
504 "#{project} - #{page.title} - # #{page.content.version}")
504 "#{project} - #{page.title} - # #{page.content.version}")
505 pdf.Ln
505 pdf.Ln
506 # Set resize image scale
506 # Set resize image scale
507 pdf.SetImageScale(1.6)
507 pdf.SetImageScale(1.6)
508 pdf.SetFontStyle('',9)
508 pdf.SetFontStyle('',9)
509 pdf.RDMwriteHTMLCell(190,5,0,0,
509 pdf.RDMwriteHTMLCell(190,5,0,0,
510 page.content.text.to_s, page.attachments, "TLRB")
510 page.content.text.to_s, page.attachments, "TLRB")
511 if page.attachments.any?
511 if page.attachments.any?
512 pdf.Ln
512 pdf.Ln
513 pdf.SetFontStyle('B',9)
513 pdf.SetFontStyle('B',9)
514 pdf.RDMCell(190,5, l(:label_attachment_plural), "B")
514 pdf.RDMCell(190,5, l(:label_attachment_plural), "B")
515 pdf.Ln
515 pdf.Ln
516 for attachment in page.attachments
516 for attachment in page.attachments
517 pdf.SetFontStyle('',8)
517 pdf.SetFontStyle('',8)
518 pdf.RDMCell(80,5, attachment.filename)
518 pdf.RDMCell(80,5, attachment.filename)
519 pdf.RDMCell(20,5, number_to_human_size(attachment.filesize),0,0,"R")
519 pdf.RDMCell(20,5, number_to_human_size(attachment.filesize),0,0,"R")
520 pdf.RDMCell(25,5, format_date(attachment.created_on),0,0,"R")
520 pdf.RDMCell(25,5, format_date(attachment.created_on),0,0,"R")
521 pdf.RDMCell(65,5, attachment.author.name,0,0,"R")
521 pdf.RDMCell(65,5, attachment.author.name,0,0,"R")
522 pdf.Ln
522 pdf.Ln
523 end
523 end
524 end
524 end
525 pdf.Output
525 pdf.Output
526 end
526 end
527
527
528 class RDMPdfEncoding
528 class RDMPdfEncoding
529 def self.rdm_from_utf8(txt, encoding)
529 def self.rdm_from_utf8(txt, encoding)
530 txt ||= ''
530 txt ||= ''
531 txt = Redmine::CodesetUtil.from_utf8(txt, encoding)
531 txt = Redmine::CodesetUtil.from_utf8(txt, encoding)
532 if txt.respond_to?(:force_encoding)
532 if txt.respond_to?(:force_encoding)
533 txt.force_encoding('ASCII-8BIT')
533 txt.force_encoding('ASCII-8BIT')
534 end
534 end
535 txt
535 txt
536 end
536 end
537
537
538 def self.attach(attachments, filename, encoding)
538 def self.attach(attachments, filename, encoding)
539 filename_utf8 = Redmine::CodesetUtil.to_utf8(filename, encoding)
539 filename_utf8 = Redmine::CodesetUtil.to_utf8(filename, encoding)
540 atta = nil
540 atta = nil
541 if filename_utf8 =~ /^[^\/"]+\.(gif|jpg|jpe|jpeg|png)$/i
541 if filename_utf8 =~ /^[^\/"]+\.(gif|jpg|jpe|jpeg|png)$/i
542 atta = Attachment.latest_attach(attachments, filename_utf8)
542 atta = Attachment.latest_attach(attachments, filename_utf8)
543 end
543 end
544 if atta && atta.readable? && atta.visible?
544 if atta && atta.readable? && atta.visible?
545 return atta
545 return atta
546 else
546 else
547 return nil
547 return nil
548 end
548 end
549 end
549 end
550 end
550 end
551 end
551 end
552 end
552 end
553 end
553 end
@@ -1,2902 +1,2914
1 # Redmine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006-2012 Jean-Philippe Lang
2 # Copyright (C) 2006-2012 Jean-Philippe Lang
3 #
3 #
4 # This program is free software; you can redistribute it and/or
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
7 # of the License, or (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 require File.expand_path('../../test_helper', __FILE__)
18 require File.expand_path('../../test_helper', __FILE__)
19 require 'issues_controller'
19 require 'issues_controller'
20
20
21 class IssuesControllerTest < ActionController::TestCase
21 class IssuesControllerTest < ActionController::TestCase
22 fixtures :projects,
22 fixtures :projects,
23 :users,
23 :users,
24 :roles,
24 :roles,
25 :members,
25 :members,
26 :member_roles,
26 :member_roles,
27 :issues,
27 :issues,
28 :issue_statuses,
28 :issue_statuses,
29 :versions,
29 :versions,
30 :trackers,
30 :trackers,
31 :projects_trackers,
31 :projects_trackers,
32 :issue_categories,
32 :issue_categories,
33 :enabled_modules,
33 :enabled_modules,
34 :enumerations,
34 :enumerations,
35 :attachments,
35 :attachments,
36 :workflows,
36 :workflows,
37 :custom_fields,
37 :custom_fields,
38 :custom_values,
38 :custom_values,
39 :custom_fields_projects,
39 :custom_fields_projects,
40 :custom_fields_trackers,
40 :custom_fields_trackers,
41 :time_entries,
41 :time_entries,
42 :journals,
42 :journals,
43 :journal_details,
43 :journal_details,
44 :queries
44 :queries
45
45
46 include Redmine::I18n
46 include Redmine::I18n
47
47
48 def setup
48 def setup
49 @controller = IssuesController.new
49 @controller = IssuesController.new
50 @request = ActionController::TestRequest.new
50 @request = ActionController::TestRequest.new
51 @response = ActionController::TestResponse.new
51 @response = ActionController::TestResponse.new
52 User.current = nil
52 User.current = nil
53 end
53 end
54
54
55 def test_index
55 def test_index
56 Setting.default_language = 'en'
56 Setting.default_language = 'en'
57
57
58 get :index
58 get :index
59 assert_response :success
59 assert_response :success
60 assert_template 'index'
60 assert_template 'index'
61 assert_not_nil assigns(:issues)
61 assert_not_nil assigns(:issues)
62 assert_nil assigns(:project)
62 assert_nil assigns(:project)
63 assert_tag :tag => 'a', :content => /Can't print recipes/
63 assert_tag :tag => 'a', :content => /Can't print recipes/
64 assert_tag :tag => 'a', :content => /Subproject issue/
64 assert_tag :tag => 'a', :content => /Subproject issue/
65 # private projects hidden
65 # private projects hidden
66 assert_no_tag :tag => 'a', :content => /Issue of a private subproject/
66 assert_no_tag :tag => 'a', :content => /Issue of a private subproject/
67 assert_no_tag :tag => 'a', :content => /Issue on project 2/
67 assert_no_tag :tag => 'a', :content => /Issue on project 2/
68 # project column
68 # project column
69 assert_tag :tag => 'th', :content => /Project/
69 assert_tag :tag => 'th', :content => /Project/
70 end
70 end
71
71
72 def test_index_should_not_list_issues_when_module_disabled
72 def test_index_should_not_list_issues_when_module_disabled
73 EnabledModule.delete_all("name = 'issue_tracking' AND project_id = 1")
73 EnabledModule.delete_all("name = 'issue_tracking' AND project_id = 1")
74 get :index
74 get :index
75 assert_response :success
75 assert_response :success
76 assert_template 'index'
76 assert_template 'index'
77 assert_not_nil assigns(:issues)
77 assert_not_nil assigns(:issues)
78 assert_nil assigns(:project)
78 assert_nil assigns(:project)
79 assert_no_tag :tag => 'a', :content => /Can't print recipes/
79 assert_no_tag :tag => 'a', :content => /Can't print recipes/
80 assert_tag :tag => 'a', :content => /Subproject issue/
80 assert_tag :tag => 'a', :content => /Subproject issue/
81 end
81 end
82
82
83 def test_index_should_list_visible_issues_only
83 def test_index_should_list_visible_issues_only
84 get :index, :per_page => 100
84 get :index, :per_page => 100
85 assert_response :success
85 assert_response :success
86 assert_not_nil assigns(:issues)
86 assert_not_nil assigns(:issues)
87 assert_nil assigns(:issues).detect {|issue| !issue.visible?}
87 assert_nil assigns(:issues).detect {|issue| !issue.visible?}
88 end
88 end
89
89
90 def test_index_with_project
90 def test_index_with_project
91 Setting.display_subprojects_issues = 0
91 Setting.display_subprojects_issues = 0
92 get :index, :project_id => 1
92 get :index, :project_id => 1
93 assert_response :success
93 assert_response :success
94 assert_template 'index'
94 assert_template 'index'
95 assert_not_nil assigns(:issues)
95 assert_not_nil assigns(:issues)
96 assert_tag :tag => 'a', :content => /Can't print recipes/
96 assert_tag :tag => 'a', :content => /Can't print recipes/
97 assert_no_tag :tag => 'a', :content => /Subproject issue/
97 assert_no_tag :tag => 'a', :content => /Subproject issue/
98 end
98 end
99
99
100 def test_index_with_project_and_subprojects
100 def test_index_with_project_and_subprojects
101 Setting.display_subprojects_issues = 1
101 Setting.display_subprojects_issues = 1
102 get :index, :project_id => 1
102 get :index, :project_id => 1
103 assert_response :success
103 assert_response :success
104 assert_template 'index'
104 assert_template 'index'
105 assert_not_nil assigns(:issues)
105 assert_not_nil assigns(:issues)
106 assert_tag :tag => 'a', :content => /Can't print recipes/
106 assert_tag :tag => 'a', :content => /Can't print recipes/
107 assert_tag :tag => 'a', :content => /Subproject issue/
107 assert_tag :tag => 'a', :content => /Subproject issue/
108 assert_no_tag :tag => 'a', :content => /Issue of a private subproject/
108 assert_no_tag :tag => 'a', :content => /Issue of a private subproject/
109 end
109 end
110
110
111 def test_index_with_project_and_subprojects_should_show_private_subprojects
111 def test_index_with_project_and_subprojects_should_show_private_subprojects
112 @request.session[:user_id] = 2
112 @request.session[:user_id] = 2
113 Setting.display_subprojects_issues = 1
113 Setting.display_subprojects_issues = 1
114 get :index, :project_id => 1
114 get :index, :project_id => 1
115 assert_response :success
115 assert_response :success
116 assert_template 'index'
116 assert_template 'index'
117 assert_not_nil assigns(:issues)
117 assert_not_nil assigns(:issues)
118 assert_tag :tag => 'a', :content => /Can't print recipes/
118 assert_tag :tag => 'a', :content => /Can't print recipes/
119 assert_tag :tag => 'a', :content => /Subproject issue/
119 assert_tag :tag => 'a', :content => /Subproject issue/
120 assert_tag :tag => 'a', :content => /Issue of a private subproject/
120 assert_tag :tag => 'a', :content => /Issue of a private subproject/
121 end
121 end
122
122
123 def test_index_with_project_and_default_filter
123 def test_index_with_project_and_default_filter
124 get :index, :project_id => 1, :set_filter => 1
124 get :index, :project_id => 1, :set_filter => 1
125 assert_response :success
125 assert_response :success
126 assert_template 'index'
126 assert_template 'index'
127 assert_not_nil assigns(:issues)
127 assert_not_nil assigns(:issues)
128
128
129 query = assigns(:query)
129 query = assigns(:query)
130 assert_not_nil query
130 assert_not_nil query
131 # default filter
131 # default filter
132 assert_equal({'status_id' => {:operator => 'o', :values => ['']}}, query.filters)
132 assert_equal({'status_id' => {:operator => 'o', :values => ['']}}, query.filters)
133 end
133 end
134
134
135 def test_index_with_project_and_filter
135 def test_index_with_project_and_filter
136 get :index, :project_id => 1, :set_filter => 1,
136 get :index, :project_id => 1, :set_filter => 1,
137 :f => ['tracker_id'],
137 :f => ['tracker_id'],
138 :op => {'tracker_id' => '='},
138 :op => {'tracker_id' => '='},
139 :v => {'tracker_id' => ['1']}
139 :v => {'tracker_id' => ['1']}
140 assert_response :success
140 assert_response :success
141 assert_template 'index'
141 assert_template 'index'
142 assert_not_nil assigns(:issues)
142 assert_not_nil assigns(:issues)
143
143
144 query = assigns(:query)
144 query = assigns(:query)
145 assert_not_nil query
145 assert_not_nil query
146 assert_equal({'tracker_id' => {:operator => '=', :values => ['1']}}, query.filters)
146 assert_equal({'tracker_id' => {:operator => '=', :values => ['1']}}, query.filters)
147 end
147 end
148
148
149 def test_index_with_short_filters
149 def test_index_with_short_filters
150
150
151 to_test = {
151 to_test = {
152 'status_id' => {
152 'status_id' => {
153 'o' => { :op => 'o', :values => [''] },
153 'o' => { :op => 'o', :values => [''] },
154 'c' => { :op => 'c', :values => [''] },
154 'c' => { :op => 'c', :values => [''] },
155 '7' => { :op => '=', :values => ['7'] },
155 '7' => { :op => '=', :values => ['7'] },
156 '7|3|4' => { :op => '=', :values => ['7', '3', '4'] },
156 '7|3|4' => { :op => '=', :values => ['7', '3', '4'] },
157 '=7' => { :op => '=', :values => ['7'] },
157 '=7' => { :op => '=', :values => ['7'] },
158 '!3' => { :op => '!', :values => ['3'] },
158 '!3' => { :op => '!', :values => ['3'] },
159 '!7|3|4' => { :op => '!', :values => ['7', '3', '4'] }},
159 '!7|3|4' => { :op => '!', :values => ['7', '3', '4'] }},
160 'subject' => {
160 'subject' => {
161 'This is a subject' => { :op => '=', :values => ['This is a subject'] },
161 'This is a subject' => { :op => '=', :values => ['This is a subject'] },
162 'o' => { :op => '=', :values => ['o'] },
162 'o' => { :op => '=', :values => ['o'] },
163 '~This is part of a subject' => { :op => '~', :values => ['This is part of a subject'] },
163 '~This is part of a subject' => { :op => '~', :values => ['This is part of a subject'] },
164 '!~This is part of a subject' => { :op => '!~', :values => ['This is part of a subject'] }},
164 '!~This is part of a subject' => { :op => '!~', :values => ['This is part of a subject'] }},
165 'tracker_id' => {
165 'tracker_id' => {
166 '3' => { :op => '=', :values => ['3'] },
166 '3' => { :op => '=', :values => ['3'] },
167 '=3' => { :op => '=', :values => ['3'] }},
167 '=3' => { :op => '=', :values => ['3'] }},
168 'start_date' => {
168 'start_date' => {
169 '2011-10-12' => { :op => '=', :values => ['2011-10-12'] },
169 '2011-10-12' => { :op => '=', :values => ['2011-10-12'] },
170 '=2011-10-12' => { :op => '=', :values => ['2011-10-12'] },
170 '=2011-10-12' => { :op => '=', :values => ['2011-10-12'] },
171 '>=2011-10-12' => { :op => '>=', :values => ['2011-10-12'] },
171 '>=2011-10-12' => { :op => '>=', :values => ['2011-10-12'] },
172 '<=2011-10-12' => { :op => '<=', :values => ['2011-10-12'] },
172 '<=2011-10-12' => { :op => '<=', :values => ['2011-10-12'] },
173 '><2011-10-01|2011-10-30' => { :op => '><', :values => ['2011-10-01', '2011-10-30'] },
173 '><2011-10-01|2011-10-30' => { :op => '><', :values => ['2011-10-01', '2011-10-30'] },
174 '<t+2' => { :op => '<t+', :values => ['2'] },
174 '<t+2' => { :op => '<t+', :values => ['2'] },
175 '>t+2' => { :op => '>t+', :values => ['2'] },
175 '>t+2' => { :op => '>t+', :values => ['2'] },
176 't+2' => { :op => 't+', :values => ['2'] },
176 't+2' => { :op => 't+', :values => ['2'] },
177 't' => { :op => 't', :values => [''] },
177 't' => { :op => 't', :values => [''] },
178 'w' => { :op => 'w', :values => [''] },
178 'w' => { :op => 'w', :values => [''] },
179 '>t-2' => { :op => '>t-', :values => ['2'] },
179 '>t-2' => { :op => '>t-', :values => ['2'] },
180 '<t-2' => { :op => '<t-', :values => ['2'] },
180 '<t-2' => { :op => '<t-', :values => ['2'] },
181 't-2' => { :op => 't-', :values => ['2'] }},
181 't-2' => { :op => 't-', :values => ['2'] }},
182 'created_on' => {
182 'created_on' => {
183 '>=2011-10-12' => { :op => '>=', :values => ['2011-10-12'] },
183 '>=2011-10-12' => { :op => '>=', :values => ['2011-10-12'] },
184 '<t+2' => { :op => '=', :values => ['<t+2'] },
184 '<t+2' => { :op => '=', :values => ['<t+2'] },
185 '>t+2' => { :op => '=', :values => ['>t+2'] },
185 '>t+2' => { :op => '=', :values => ['>t+2'] },
186 't+2' => { :op => 't', :values => ['+2'] }},
186 't+2' => { :op => 't', :values => ['+2'] }},
187 'cf_1' => {
187 'cf_1' => {
188 'c' => { :op => '=', :values => ['c'] },
188 'c' => { :op => '=', :values => ['c'] },
189 '!c' => { :op => '!', :values => ['c'] },
189 '!c' => { :op => '!', :values => ['c'] },
190 '!*' => { :op => '!*', :values => [''] },
190 '!*' => { :op => '!*', :values => [''] },
191 '*' => { :op => '*', :values => [''] }},
191 '*' => { :op => '*', :values => [''] }},
192 'estimated_hours' => {
192 'estimated_hours' => {
193 '=13.4' => { :op => '=', :values => ['13.4'] },
193 '=13.4' => { :op => '=', :values => ['13.4'] },
194 '>=45' => { :op => '>=', :values => ['45'] },
194 '>=45' => { :op => '>=', :values => ['45'] },
195 '<=125' => { :op => '<=', :values => ['125'] },
195 '<=125' => { :op => '<=', :values => ['125'] },
196 '><10.5|20.5' => { :op => '><', :values => ['10.5', '20.5'] },
196 '><10.5|20.5' => { :op => '><', :values => ['10.5', '20.5'] },
197 '!*' => { :op => '!*', :values => [''] },
197 '!*' => { :op => '!*', :values => [''] },
198 '*' => { :op => '*', :values => [''] }}
198 '*' => { :op => '*', :values => [''] }}
199 }
199 }
200
200
201 default_filter = { 'status_id' => {:operator => 'o', :values => [''] }}
201 default_filter = { 'status_id' => {:operator => 'o', :values => [''] }}
202
202
203 to_test.each do |field, expression_and_expected|
203 to_test.each do |field, expression_and_expected|
204 expression_and_expected.each do |filter_expression, expected|
204 expression_and_expected.each do |filter_expression, expected|
205
205
206 get :index, :set_filter => 1, field => filter_expression
206 get :index, :set_filter => 1, field => filter_expression
207
207
208 assert_response :success
208 assert_response :success
209 assert_template 'index'
209 assert_template 'index'
210 assert_not_nil assigns(:issues)
210 assert_not_nil assigns(:issues)
211
211
212 query = assigns(:query)
212 query = assigns(:query)
213 assert_not_nil query
213 assert_not_nil query
214 assert query.has_filter?(field)
214 assert query.has_filter?(field)
215 assert_equal(default_filter.merge({field => {:operator => expected[:op], :values => expected[:values]}}), query.filters)
215 assert_equal(default_filter.merge({field => {:operator => expected[:op], :values => expected[:values]}}), query.filters)
216 end
216 end
217 end
217 end
218
218
219 end
219 end
220
220
221 def test_index_with_project_and_empty_filters
221 def test_index_with_project_and_empty_filters
222 get :index, :project_id => 1, :set_filter => 1, :fields => ['']
222 get :index, :project_id => 1, :set_filter => 1, :fields => ['']
223 assert_response :success
223 assert_response :success
224 assert_template 'index'
224 assert_template 'index'
225 assert_not_nil assigns(:issues)
225 assert_not_nil assigns(:issues)
226
226
227 query = assigns(:query)
227 query = assigns(:query)
228 assert_not_nil query
228 assert_not_nil query
229 # no filter
229 # no filter
230 assert_equal({}, query.filters)
230 assert_equal({}, query.filters)
231 end
231 end
232
232
233 def test_index_with_query
233 def test_index_with_query
234 get :index, :project_id => 1, :query_id => 5
234 get :index, :project_id => 1, :query_id => 5
235 assert_response :success
235 assert_response :success
236 assert_template 'index'
236 assert_template 'index'
237 assert_not_nil assigns(:issues)
237 assert_not_nil assigns(:issues)
238 assert_nil assigns(:issue_count_by_group)
238 assert_nil assigns(:issue_count_by_group)
239 end
239 end
240
240
241 def test_index_with_query_grouped_by_tracker
241 def test_index_with_query_grouped_by_tracker
242 get :index, :project_id => 1, :query_id => 6
242 get :index, :project_id => 1, :query_id => 6
243 assert_response :success
243 assert_response :success
244 assert_template 'index'
244 assert_template 'index'
245 assert_not_nil assigns(:issues)
245 assert_not_nil assigns(:issues)
246 assert_not_nil assigns(:issue_count_by_group)
246 assert_not_nil assigns(:issue_count_by_group)
247 end
247 end
248
248
249 def test_index_with_query_grouped_by_list_custom_field
249 def test_index_with_query_grouped_by_list_custom_field
250 get :index, :project_id => 1, :query_id => 9
250 get :index, :project_id => 1, :query_id => 9
251 assert_response :success
251 assert_response :success
252 assert_template 'index'
252 assert_template 'index'
253 assert_not_nil assigns(:issues)
253 assert_not_nil assigns(:issues)
254 assert_not_nil assigns(:issue_count_by_group)
254 assert_not_nil assigns(:issue_count_by_group)
255 end
255 end
256
256
257 def test_index_with_query_id_and_project_id_should_set_session_query
257 def test_index_with_query_id_and_project_id_should_set_session_query
258 get :index, :project_id => 1, :query_id => 4
258 get :index, :project_id => 1, :query_id => 4
259 assert_response :success
259 assert_response :success
260 assert_kind_of Hash, session[:query]
260 assert_kind_of Hash, session[:query]
261 assert_equal 4, session[:query][:id]
261 assert_equal 4, session[:query][:id]
262 assert_equal 1, session[:query][:project_id]
262 assert_equal 1, session[:query][:project_id]
263 end
263 end
264
264
265 def test_index_with_cross_project_query_in_session_should_show_project_issues
265 def test_index_with_cross_project_query_in_session_should_show_project_issues
266 q = Query.create!(:name => "test", :user_id => 2, :is_public => false, :project => nil)
266 q = Query.create!(:name => "test", :user_id => 2, :is_public => false, :project => nil)
267 @request.session[:query] = {:id => q.id, :project_id => 1}
267 @request.session[:query] = {:id => q.id, :project_id => 1}
268
268
269 with_settings :display_subprojects_issues => '0' do
269 with_settings :display_subprojects_issues => '0' do
270 get :index, :project_id => 1
270 get :index, :project_id => 1
271 end
271 end
272 assert_response :success
272 assert_response :success
273 assert_not_nil assigns(:query)
273 assert_not_nil assigns(:query)
274 assert_equal q.id, assigns(:query).id
274 assert_equal q.id, assigns(:query).id
275 assert_equal 1, assigns(:query).project_id
275 assert_equal 1, assigns(:query).project_id
276 assert_equal [1], assigns(:issues).map(&:project_id).uniq
276 assert_equal [1], assigns(:issues).map(&:project_id).uniq
277 end
277 end
278
278
279 def test_private_query_should_not_be_available_to_other_users
279 def test_private_query_should_not_be_available_to_other_users
280 q = Query.create!(:name => "private", :user => User.find(2), :is_public => false, :project => nil)
280 q = Query.create!(:name => "private", :user => User.find(2), :is_public => false, :project => nil)
281 @request.session[:user_id] = 3
281 @request.session[:user_id] = 3
282
282
283 get :index, :query_id => q.id
283 get :index, :query_id => q.id
284 assert_response 403
284 assert_response 403
285 end
285 end
286
286
287 def test_private_query_should_be_available_to_its_user
287 def test_private_query_should_be_available_to_its_user
288 q = Query.create!(:name => "private", :user => User.find(2), :is_public => false, :project => nil)
288 q = Query.create!(:name => "private", :user => User.find(2), :is_public => false, :project => nil)
289 @request.session[:user_id] = 2
289 @request.session[:user_id] = 2
290
290
291 get :index, :query_id => q.id
291 get :index, :query_id => q.id
292 assert_response :success
292 assert_response :success
293 end
293 end
294
294
295 def test_public_query_should_be_available_to_other_users
295 def test_public_query_should_be_available_to_other_users
296 q = Query.create!(:name => "private", :user => User.find(2), :is_public => true, :project => nil)
296 q = Query.create!(:name => "private", :user => User.find(2), :is_public => true, :project => nil)
297 @request.session[:user_id] = 3
297 @request.session[:user_id] = 3
298
298
299 get :index, :query_id => q.id
299 get :index, :query_id => q.id
300 assert_response :success
300 assert_response :success
301 end
301 end
302
302
303 def test_index_csv
303 def test_index_csv
304 get :index, :format => 'csv'
304 get :index, :format => 'csv'
305 assert_response :success
305 assert_response :success
306 assert_not_nil assigns(:issues)
306 assert_not_nil assigns(:issues)
307 assert_equal 'text/csv', @response.content_type
307 assert_equal 'text/csv', @response.content_type
308 assert @response.body.starts_with?("#,")
308 assert @response.body.starts_with?("#,")
309 lines = @response.body.chomp.split("\n")
309 lines = @response.body.chomp.split("\n")
310 assert_equal assigns(:query).columns.size + 1, lines[0].split(',').size
310 assert_equal assigns(:query).columns.size + 1, lines[0].split(',').size
311 end
311 end
312
312
313 def test_index_csv_with_project
313 def test_index_csv_with_project
314 get :index, :project_id => 1, :format => 'csv'
314 get :index, :project_id => 1, :format => 'csv'
315 assert_response :success
315 assert_response :success
316 assert_not_nil assigns(:issues)
316 assert_not_nil assigns(:issues)
317 assert_equal 'text/csv', @response.content_type
317 assert_equal 'text/csv', @response.content_type
318 end
318 end
319
319
320 def test_index_csv_with_description
320 def test_index_csv_with_description
321 get :index, :format => 'csv', :description => '1'
321 get :index, :format => 'csv', :description => '1'
322 assert_response :success
322 assert_response :success
323 assert_not_nil assigns(:issues)
323 assert_not_nil assigns(:issues)
324 assert_equal 'text/csv', @response.content_type
324 assert_equal 'text/csv', @response.content_type
325 assert @response.body.starts_with?("#,")
325 assert @response.body.starts_with?("#,")
326 lines = @response.body.chomp.split("\n")
326 lines = @response.body.chomp.split("\n")
327 assert_equal assigns(:query).columns.size + 2, lines[0].split(',').size
327 assert_equal assigns(:query).columns.size + 2, lines[0].split(',').size
328 end
328 end
329
329
330 def test_index_csv_with_all_columns
330 def test_index_csv_with_all_columns
331 get :index, :format => 'csv', :columns => 'all'
331 get :index, :format => 'csv', :columns => 'all'
332 assert_response :success
332 assert_response :success
333 assert_not_nil assigns(:issues)
333 assert_not_nil assigns(:issues)
334 assert_equal 'text/csv', @response.content_type
334 assert_equal 'text/csv', @response.content_type
335 assert @response.body.starts_with?("#,")
335 assert @response.body.starts_with?("#,")
336 lines = @response.body.chomp.split("\n")
336 lines = @response.body.chomp.split("\n")
337 assert_equal assigns(:query).available_columns.size + 1, lines[0].split(',').size
337 assert_equal assigns(:query).available_columns.size + 1, lines[0].split(',').size
338 end
338 end
339
339
340 def test_index_csv_with_multi_column_field
341 CustomField.find(1).update_attribute :multiple, true
342 issue = Issue.find(1)
343 issue.custom_field_values = {1 => ['MySQL', 'Oracle']}
344 issue.save!
345
346 get :index, :format => 'csv', :columns => 'all'
347 assert_response :success
348 lines = @response.body.chomp.split("\n")
349 assert lines.detect {|line| line.include?('"MySQL, Oracle"')}
350 end
351
340 def test_index_csv_big_5
352 def test_index_csv_big_5
341 with_settings :default_language => "zh-TW" do
353 with_settings :default_language => "zh-TW" do
342 str_utf8 = "\xe4\xb8\x80\xe6\x9c\x88"
354 str_utf8 = "\xe4\xb8\x80\xe6\x9c\x88"
343 str_big5 = "\xa4@\xa4\xeb"
355 str_big5 = "\xa4@\xa4\xeb"
344 if str_utf8.respond_to?(:force_encoding)
356 if str_utf8.respond_to?(:force_encoding)
345 str_utf8.force_encoding('UTF-8')
357 str_utf8.force_encoding('UTF-8')
346 str_big5.force_encoding('Big5')
358 str_big5.force_encoding('Big5')
347 end
359 end
348 issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 3,
360 issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 3,
349 :status_id => 1, :priority => IssuePriority.all.first,
361 :status_id => 1, :priority => IssuePriority.all.first,
350 :subject => str_utf8)
362 :subject => str_utf8)
351 assert issue.save
363 assert issue.save
352
364
353 get :index, :project_id => 1,
365 get :index, :project_id => 1,
354 :f => ['subject'],
366 :f => ['subject'],
355 :op => '=', :values => [str_utf8],
367 :op => '=', :values => [str_utf8],
356 :format => 'csv'
368 :format => 'csv'
357 assert_equal 'text/csv', @response.content_type
369 assert_equal 'text/csv', @response.content_type
358 lines = @response.body.chomp.split("\n")
370 lines = @response.body.chomp.split("\n")
359 s1 = "\xaa\xac\xbaA"
371 s1 = "\xaa\xac\xbaA"
360 if str_utf8.respond_to?(:force_encoding)
372 if str_utf8.respond_to?(:force_encoding)
361 s1.force_encoding('Big5')
373 s1.force_encoding('Big5')
362 end
374 end
363 assert lines[0].include?(s1)
375 assert lines[0].include?(s1)
364 assert lines[1].include?(str_big5)
376 assert lines[1].include?(str_big5)
365 end
377 end
366 end
378 end
367
379
368 def test_index_csv_cannot_convert_should_be_replaced_big_5
380 def test_index_csv_cannot_convert_should_be_replaced_big_5
369 with_settings :default_language => "zh-TW" do
381 with_settings :default_language => "zh-TW" do
370 str_utf8 = "\xe4\xbb\xa5\xe5\x86\x85"
382 str_utf8 = "\xe4\xbb\xa5\xe5\x86\x85"
371 if str_utf8.respond_to?(:force_encoding)
383 if str_utf8.respond_to?(:force_encoding)
372 str_utf8.force_encoding('UTF-8')
384 str_utf8.force_encoding('UTF-8')
373 end
385 end
374 issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 3,
386 issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 3,
375 :status_id => 1, :priority => IssuePriority.all.first,
387 :status_id => 1, :priority => IssuePriority.all.first,
376 :subject => str_utf8)
388 :subject => str_utf8)
377 assert issue.save
389 assert issue.save
378
390
379 get :index, :project_id => 1,
391 get :index, :project_id => 1,
380 :f => ['subject'],
392 :f => ['subject'],
381 :op => '=', :values => [str_utf8],
393 :op => '=', :values => [str_utf8],
382 :c => ['status', 'subject'],
394 :c => ['status', 'subject'],
383 :format => 'csv',
395 :format => 'csv',
384 :set_filter => 1
396 :set_filter => 1
385 assert_equal 'text/csv', @response.content_type
397 assert_equal 'text/csv', @response.content_type
386 lines = @response.body.chomp.split("\n")
398 lines = @response.body.chomp.split("\n")
387 s1 = "\xaa\xac\xbaA" # status
399 s1 = "\xaa\xac\xbaA" # status
388 if str_utf8.respond_to?(:force_encoding)
400 if str_utf8.respond_to?(:force_encoding)
389 s1.force_encoding('Big5')
401 s1.force_encoding('Big5')
390 end
402 end
391 assert lines[0].include?(s1)
403 assert lines[0].include?(s1)
392 s2 = lines[1].split(",")[2]
404 s2 = lines[1].split(",")[2]
393 if s1.respond_to?(:force_encoding)
405 if s1.respond_to?(:force_encoding)
394 s3 = "\xa5H?" # subject
406 s3 = "\xa5H?" # subject
395 s3.force_encoding('Big5')
407 s3.force_encoding('Big5')
396 assert_equal s3, s2
408 assert_equal s3, s2
397 elsif RUBY_PLATFORM == 'java'
409 elsif RUBY_PLATFORM == 'java'
398 assert_equal "??", s2
410 assert_equal "??", s2
399 else
411 else
400 assert_equal "\xa5H???", s2
412 assert_equal "\xa5H???", s2
401 end
413 end
402 end
414 end
403 end
415 end
404
416
405 def test_index_csv_tw
417 def test_index_csv_tw
406 with_settings :default_language => "zh-TW" do
418 with_settings :default_language => "zh-TW" do
407 str1 = "test_index_csv_tw"
419 str1 = "test_index_csv_tw"
408 issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 3,
420 issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 3,
409 :status_id => 1, :priority => IssuePriority.all.first,
421 :status_id => 1, :priority => IssuePriority.all.first,
410 :subject => str1, :estimated_hours => '1234.5')
422 :subject => str1, :estimated_hours => '1234.5')
411 assert issue.save
423 assert issue.save
412 assert_equal 1234.5, issue.estimated_hours
424 assert_equal 1234.5, issue.estimated_hours
413
425
414 get :index, :project_id => 1,
426 get :index, :project_id => 1,
415 :f => ['subject'],
427 :f => ['subject'],
416 :op => '=', :values => [str1],
428 :op => '=', :values => [str1],
417 :c => ['estimated_hours', 'subject'],
429 :c => ['estimated_hours', 'subject'],
418 :format => 'csv',
430 :format => 'csv',
419 :set_filter => 1
431 :set_filter => 1
420 assert_equal 'text/csv', @response.content_type
432 assert_equal 'text/csv', @response.content_type
421 lines = @response.body.chomp.split("\n")
433 lines = @response.body.chomp.split("\n")
422 assert_equal "#{issue.id},1234.5,#{str1}", lines[1]
434 assert_equal "#{issue.id},1234.5,#{str1}", lines[1]
423
435
424 str_tw = "Traditional Chinese (\xe7\xb9\x81\xe9\xab\x94\xe4\xb8\xad\xe6\x96\x87)"
436 str_tw = "Traditional Chinese (\xe7\xb9\x81\xe9\xab\x94\xe4\xb8\xad\xe6\x96\x87)"
425 if str_tw.respond_to?(:force_encoding)
437 if str_tw.respond_to?(:force_encoding)
426 str_tw.force_encoding('UTF-8')
438 str_tw.force_encoding('UTF-8')
427 end
439 end
428 assert_equal str_tw, l(:general_lang_name)
440 assert_equal str_tw, l(:general_lang_name)
429 assert_equal ',', l(:general_csv_separator)
441 assert_equal ',', l(:general_csv_separator)
430 assert_equal '.', l(:general_csv_decimal_separator)
442 assert_equal '.', l(:general_csv_decimal_separator)
431 end
443 end
432 end
444 end
433
445
434 def test_index_csv_fr
446 def test_index_csv_fr
435 with_settings :default_language => "fr" do
447 with_settings :default_language => "fr" do
436 str1 = "test_index_csv_fr"
448 str1 = "test_index_csv_fr"
437 issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 3,
449 issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 3,
438 :status_id => 1, :priority => IssuePriority.all.first,
450 :status_id => 1, :priority => IssuePriority.all.first,
439 :subject => str1, :estimated_hours => '1234.5')
451 :subject => str1, :estimated_hours => '1234.5')
440 assert issue.save
452 assert issue.save
441 assert_equal 1234.5, issue.estimated_hours
453 assert_equal 1234.5, issue.estimated_hours
442
454
443 get :index, :project_id => 1,
455 get :index, :project_id => 1,
444 :f => ['subject'],
456 :f => ['subject'],
445 :op => '=', :values => [str1],
457 :op => '=', :values => [str1],
446 :c => ['estimated_hours', 'subject'],
458 :c => ['estimated_hours', 'subject'],
447 :format => 'csv',
459 :format => 'csv',
448 :set_filter => 1
460 :set_filter => 1
449 assert_equal 'text/csv', @response.content_type
461 assert_equal 'text/csv', @response.content_type
450 lines = @response.body.chomp.split("\n")
462 lines = @response.body.chomp.split("\n")
451 assert_equal "#{issue.id};1234,5;#{str1}", lines[1]
463 assert_equal "#{issue.id};1234,5;#{str1}", lines[1]
452
464
453 str_fr = "Fran\xc3\xa7ais"
465 str_fr = "Fran\xc3\xa7ais"
454 if str_fr.respond_to?(:force_encoding)
466 if str_fr.respond_to?(:force_encoding)
455 str_fr.force_encoding('UTF-8')
467 str_fr.force_encoding('UTF-8')
456 end
468 end
457 assert_equal str_fr, l(:general_lang_name)
469 assert_equal str_fr, l(:general_lang_name)
458 assert_equal ';', l(:general_csv_separator)
470 assert_equal ';', l(:general_csv_separator)
459 assert_equal ',', l(:general_csv_decimal_separator)
471 assert_equal ',', l(:general_csv_decimal_separator)
460 end
472 end
461 end
473 end
462
474
463 def test_index_pdf
475 def test_index_pdf
464 ["en", "zh", "zh-TW", "ja", "ko"].each do |lang|
476 ["en", "zh", "zh-TW", "ja", "ko"].each do |lang|
465 with_settings :default_language => lang do
477 with_settings :default_language => lang do
466
478
467 get :index
479 get :index
468 assert_response :success
480 assert_response :success
469 assert_template 'index'
481 assert_template 'index'
470
482
471 if lang == "ja"
483 if lang == "ja"
472 if RUBY_PLATFORM != 'java'
484 if RUBY_PLATFORM != 'java'
473 assert_equal "CP932", l(:general_pdf_encoding)
485 assert_equal "CP932", l(:general_pdf_encoding)
474 end
486 end
475 if RUBY_PLATFORM == 'java' && l(:general_pdf_encoding) == "CP932"
487 if RUBY_PLATFORM == 'java' && l(:general_pdf_encoding) == "CP932"
476 next
488 next
477 end
489 end
478 end
490 end
479
491
480 get :index, :format => 'pdf'
492 get :index, :format => 'pdf'
481 assert_response :success
493 assert_response :success
482 assert_not_nil assigns(:issues)
494 assert_not_nil assigns(:issues)
483 assert_equal 'application/pdf', @response.content_type
495 assert_equal 'application/pdf', @response.content_type
484
496
485 get :index, :project_id => 1, :format => 'pdf'
497 get :index, :project_id => 1, :format => 'pdf'
486 assert_response :success
498 assert_response :success
487 assert_not_nil assigns(:issues)
499 assert_not_nil assigns(:issues)
488 assert_equal 'application/pdf', @response.content_type
500 assert_equal 'application/pdf', @response.content_type
489
501
490 get :index, :project_id => 1, :query_id => 6, :format => 'pdf'
502 get :index, :project_id => 1, :query_id => 6, :format => 'pdf'
491 assert_response :success
503 assert_response :success
492 assert_not_nil assigns(:issues)
504 assert_not_nil assigns(:issues)
493 assert_equal 'application/pdf', @response.content_type
505 assert_equal 'application/pdf', @response.content_type
494 end
506 end
495 end
507 end
496 end
508 end
497
509
498 def test_index_pdf_with_query_grouped_by_list_custom_field
510 def test_index_pdf_with_query_grouped_by_list_custom_field
499 get :index, :project_id => 1, :query_id => 9, :format => 'pdf'
511 get :index, :project_id => 1, :query_id => 9, :format => 'pdf'
500 assert_response :success
512 assert_response :success
501 assert_not_nil assigns(:issues)
513 assert_not_nil assigns(:issues)
502 assert_not_nil assigns(:issue_count_by_group)
514 assert_not_nil assigns(:issue_count_by_group)
503 assert_equal 'application/pdf', @response.content_type
515 assert_equal 'application/pdf', @response.content_type
504 end
516 end
505
517
506 def test_index_sort
518 def test_index_sort
507 get :index, :sort => 'tracker,id:desc'
519 get :index, :sort => 'tracker,id:desc'
508 assert_response :success
520 assert_response :success
509
521
510 sort_params = @request.session['issues_index_sort']
522 sort_params = @request.session['issues_index_sort']
511 assert sort_params.is_a?(String)
523 assert sort_params.is_a?(String)
512 assert_equal 'tracker,id:desc', sort_params
524 assert_equal 'tracker,id:desc', sort_params
513
525
514 issues = assigns(:issues)
526 issues = assigns(:issues)
515 assert_not_nil issues
527 assert_not_nil issues
516 assert !issues.empty?
528 assert !issues.empty?
517 assert_equal issues.sort {|a,b| a.tracker == b.tracker ? b.id <=> a.id : a.tracker <=> b.tracker }.collect(&:id), issues.collect(&:id)
529 assert_equal issues.sort {|a,b| a.tracker == b.tracker ? b.id <=> a.id : a.tracker <=> b.tracker }.collect(&:id), issues.collect(&:id)
518 end
530 end
519
531
520 def test_index_sort_by_field_not_included_in_columns
532 def test_index_sort_by_field_not_included_in_columns
521 Setting.issue_list_default_columns = %w(subject author)
533 Setting.issue_list_default_columns = %w(subject author)
522 get :index, :sort => 'tracker'
534 get :index, :sort => 'tracker'
523 end
535 end
524
536
525 def test_index_sort_by_assigned_to
537 def test_index_sort_by_assigned_to
526 get :index, :sort => 'assigned_to'
538 get :index, :sort => 'assigned_to'
527 assert_response :success
539 assert_response :success
528 assignees = assigns(:issues).collect(&:assigned_to).compact
540 assignees = assigns(:issues).collect(&:assigned_to).compact
529 assert_equal assignees.sort, assignees
541 assert_equal assignees.sort, assignees
530 end
542 end
531
543
532 def test_index_sort_by_assigned_to_desc
544 def test_index_sort_by_assigned_to_desc
533 get :index, :sort => 'assigned_to:desc'
545 get :index, :sort => 'assigned_to:desc'
534 assert_response :success
546 assert_response :success
535 assignees = assigns(:issues).collect(&:assigned_to).compact
547 assignees = assigns(:issues).collect(&:assigned_to).compact
536 assert_equal assignees.sort.reverse, assignees
548 assert_equal assignees.sort.reverse, assignees
537 end
549 end
538
550
539 def test_index_group_by_assigned_to
551 def test_index_group_by_assigned_to
540 get :index, :group_by => 'assigned_to', :sort => 'priority'
552 get :index, :group_by => 'assigned_to', :sort => 'priority'
541 assert_response :success
553 assert_response :success
542 end
554 end
543
555
544 def test_index_sort_by_author
556 def test_index_sort_by_author
545 get :index, :sort => 'author'
557 get :index, :sort => 'author'
546 assert_response :success
558 assert_response :success
547 authors = assigns(:issues).collect(&:author)
559 authors = assigns(:issues).collect(&:author)
548 assert_equal authors.sort, authors
560 assert_equal authors.sort, authors
549 end
561 end
550
562
551 def test_index_sort_by_author_desc
563 def test_index_sort_by_author_desc
552 get :index, :sort => 'author:desc'
564 get :index, :sort => 'author:desc'
553 assert_response :success
565 assert_response :success
554 authors = assigns(:issues).collect(&:author)
566 authors = assigns(:issues).collect(&:author)
555 assert_equal authors.sort.reverse, authors
567 assert_equal authors.sort.reverse, authors
556 end
568 end
557
569
558 def test_index_group_by_author
570 def test_index_group_by_author
559 get :index, :group_by => 'author', :sort => 'priority'
571 get :index, :group_by => 'author', :sort => 'priority'
560 assert_response :success
572 assert_response :success
561 end
573 end
562
574
563 def test_index_sort_by_spent_hours
575 def test_index_sort_by_spent_hours
564 get :index, :sort => 'spent_hours:desc'
576 get :index, :sort => 'spent_hours:desc'
565 assert_response :success
577 assert_response :success
566 hours = assigns(:issues).collect(&:spent_hours)
578 hours = assigns(:issues).collect(&:spent_hours)
567 assert_equal hours.sort.reverse, hours
579 assert_equal hours.sort.reverse, hours
568 end
580 end
569
581
570 def test_index_with_columns
582 def test_index_with_columns
571 columns = ['tracker', 'subject', 'assigned_to']
583 columns = ['tracker', 'subject', 'assigned_to']
572 get :index, :set_filter => 1, :c => columns
584 get :index, :set_filter => 1, :c => columns
573 assert_response :success
585 assert_response :success
574
586
575 # query should use specified columns
587 # query should use specified columns
576 query = assigns(:query)
588 query = assigns(:query)
577 assert_kind_of Query, query
589 assert_kind_of Query, query
578 assert_equal columns, query.column_names.map(&:to_s)
590 assert_equal columns, query.column_names.map(&:to_s)
579
591
580 # columns should be stored in session
592 # columns should be stored in session
581 assert_kind_of Hash, session[:query]
593 assert_kind_of Hash, session[:query]
582 assert_kind_of Array, session[:query][:column_names]
594 assert_kind_of Array, session[:query][:column_names]
583 assert_equal columns, session[:query][:column_names].map(&:to_s)
595 assert_equal columns, session[:query][:column_names].map(&:to_s)
584
596
585 # ensure only these columns are kept in the selected columns list
597 # ensure only these columns are kept in the selected columns list
586 assert_tag :tag => 'select', :attributes => { :id => 'selected_columns' },
598 assert_tag :tag => 'select', :attributes => { :id => 'selected_columns' },
587 :children => { :count => 3 }
599 :children => { :count => 3 }
588 assert_no_tag :tag => 'option', :attributes => { :value => 'project' },
600 assert_no_tag :tag => 'option', :attributes => { :value => 'project' },
589 :parent => { :tag => 'select', :attributes => { :id => "selected_columns" } }
601 :parent => { :tag => 'select', :attributes => { :id => "selected_columns" } }
590 end
602 end
591
603
592 def test_index_without_project_should_implicitly_add_project_column_to_default_columns
604 def test_index_without_project_should_implicitly_add_project_column_to_default_columns
593 Setting.issue_list_default_columns = ['tracker', 'subject', 'assigned_to']
605 Setting.issue_list_default_columns = ['tracker', 'subject', 'assigned_to']
594 get :index, :set_filter => 1
606 get :index, :set_filter => 1
595
607
596 # query should use specified columns
608 # query should use specified columns
597 query = assigns(:query)
609 query = assigns(:query)
598 assert_kind_of Query, query
610 assert_kind_of Query, query
599 assert_equal [:project, :tracker, :subject, :assigned_to], query.columns.map(&:name)
611 assert_equal [:project, :tracker, :subject, :assigned_to], query.columns.map(&:name)
600 end
612 end
601
613
602 def test_index_without_project_and_explicit_default_columns_should_not_add_project_column
614 def test_index_without_project_and_explicit_default_columns_should_not_add_project_column
603 Setting.issue_list_default_columns = ['tracker', 'subject', 'assigned_to']
615 Setting.issue_list_default_columns = ['tracker', 'subject', 'assigned_to']
604 columns = ['tracker', 'subject', 'assigned_to']
616 columns = ['tracker', 'subject', 'assigned_to']
605 get :index, :set_filter => 1, :c => columns
617 get :index, :set_filter => 1, :c => columns
606
618
607 # query should use specified columns
619 # query should use specified columns
608 query = assigns(:query)
620 query = assigns(:query)
609 assert_kind_of Query, query
621 assert_kind_of Query, query
610 assert_equal columns.map(&:to_sym), query.columns.map(&:name)
622 assert_equal columns.map(&:to_sym), query.columns.map(&:name)
611 end
623 end
612
624
613 def test_index_with_custom_field_column
625 def test_index_with_custom_field_column
614 columns = %w(tracker subject cf_2)
626 columns = %w(tracker subject cf_2)
615 get :index, :set_filter => 1, :c => columns
627 get :index, :set_filter => 1, :c => columns
616 assert_response :success
628 assert_response :success
617
629
618 # query should use specified columns
630 # query should use specified columns
619 query = assigns(:query)
631 query = assigns(:query)
620 assert_kind_of Query, query
632 assert_kind_of Query, query
621 assert_equal columns, query.column_names.map(&:to_s)
633 assert_equal columns, query.column_names.map(&:to_s)
622
634
623 assert_tag :td,
635 assert_tag :td,
624 :attributes => {:class => 'cf_2 string'},
636 :attributes => {:class => 'cf_2 string'},
625 :ancestor => {:tag => 'table', :attributes => {:class => /issues/}}
637 :ancestor => {:tag => 'table', :attributes => {:class => /issues/}}
626 end
638 end
627
639
628 def test_index_with_multi_custom_field_column
640 def test_index_with_multi_custom_field_column
629 field = CustomField.find(1)
641 field = CustomField.find(1)
630 field.update_attribute :multiple, true
642 field.update_attribute :multiple, true
631 issue = Issue.find(1)
643 issue = Issue.find(1)
632 issue.custom_field_values = {1 => ['MySQL', 'Oracle']}
644 issue.custom_field_values = {1 => ['MySQL', 'Oracle']}
633 issue.save!
645 issue.save!
634
646
635 get :index, :set_filter => 1, :c => %w(tracker subject cf_1)
647 get :index, :set_filter => 1, :c => %w(tracker subject cf_1)
636 assert_response :success
648 assert_response :success
637
649
638 assert_tag :td,
650 assert_tag :td,
639 :attributes => {:class => /cf_1/},
651 :attributes => {:class => /cf_1/},
640 :content => 'MySQL, Oracle'
652 :content => 'MySQL, Oracle'
641 end
653 end
642
654
643 def test_index_with_multi_user_custom_field_column
655 def test_index_with_multi_user_custom_field_column
644 field = IssueCustomField.create!(:name => 'Multi user', :field_format => 'user', :multiple => true,
656 field = IssueCustomField.create!(:name => 'Multi user', :field_format => 'user', :multiple => true,
645 :tracker_ids => [1], :is_for_all => true)
657 :tracker_ids => [1], :is_for_all => true)
646 issue = Issue.find(1)
658 issue = Issue.find(1)
647 issue.custom_field_values = {field.id => ['2', '3']}
659 issue.custom_field_values = {field.id => ['2', '3']}
648 issue.save!
660 issue.save!
649
661
650 get :index, :set_filter => 1, :c => ['tracker', 'subject', "cf_#{field.id}"]
662 get :index, :set_filter => 1, :c => ['tracker', 'subject', "cf_#{field.id}"]
651 assert_response :success
663 assert_response :success
652
664
653 assert_tag :td,
665 assert_tag :td,
654 :attributes => {:class => /cf_#{field.id}/},
666 :attributes => {:class => /cf_#{field.id}/},
655 :child => {:tag => 'a', :content => 'John Smith'}
667 :child => {:tag => 'a', :content => 'John Smith'}
656 end
668 end
657
669
658 def test_index_with_date_column
670 def test_index_with_date_column
659 Issue.find(1).update_attribute :start_date, '1987-08-24'
671 Issue.find(1).update_attribute :start_date, '1987-08-24'
660
672
661 with_settings :date_format => '%d/%m/%Y' do
673 with_settings :date_format => '%d/%m/%Y' do
662 get :index, :set_filter => 1, :c => %w(start_date)
674 get :index, :set_filter => 1, :c => %w(start_date)
663 assert_tag 'td', :attributes => {:class => /start_date/}, :content => '24/08/1987'
675 assert_tag 'td', :attributes => {:class => /start_date/}, :content => '24/08/1987'
664 end
676 end
665 end
677 end
666
678
667 def test_index_with_done_ratio
679 def test_index_with_done_ratio
668 Issue.find(1).update_attribute :done_ratio, 40
680 Issue.find(1).update_attribute :done_ratio, 40
669
681
670 get :index, :set_filter => 1, :c => %w(done_ratio)
682 get :index, :set_filter => 1, :c => %w(done_ratio)
671 assert_tag 'td', :attributes => {:class => /done_ratio/},
683 assert_tag 'td', :attributes => {:class => /done_ratio/},
672 :child => {:tag => 'table', :attributes => {:class => 'progress'},
684 :child => {:tag => 'table', :attributes => {:class => 'progress'},
673 :descendant => {:tag => 'td', :attributes => {:class => 'closed', :style => 'width: 40%;'}}
685 :descendant => {:tag => 'td', :attributes => {:class => 'closed', :style => 'width: 40%;'}}
674 }
686 }
675 end
687 end
676
688
677 def test_index_with_spent_hours_column
689 def test_index_with_spent_hours_column
678 get :index, :set_filter => 1, :c => %w(subject spent_hours)
690 get :index, :set_filter => 1, :c => %w(subject spent_hours)
679
691
680 assert_tag 'tr', :attributes => {:id => 'issue-3'},
692 assert_tag 'tr', :attributes => {:id => 'issue-3'},
681 :child => {
693 :child => {
682 :tag => 'td', :attributes => {:class => /spent_hours/}, :content => '1.00'
694 :tag => 'td', :attributes => {:class => /spent_hours/}, :content => '1.00'
683 }
695 }
684 end
696 end
685
697
686 def test_index_should_not_show_spent_hours_column_without_permission
698 def test_index_should_not_show_spent_hours_column_without_permission
687 Role.anonymous.remove_permission! :view_time_entries
699 Role.anonymous.remove_permission! :view_time_entries
688 get :index, :set_filter => 1, :c => %w(subject spent_hours)
700 get :index, :set_filter => 1, :c => %w(subject spent_hours)
689
701
690 assert_no_tag 'td', :attributes => {:class => /spent_hours/}
702 assert_no_tag 'td', :attributes => {:class => /spent_hours/}
691 end
703 end
692
704
693 def test_index_with_fixed_version
705 def test_index_with_fixed_version
694 get :index, :set_filter => 1, :c => %w(fixed_version)
706 get :index, :set_filter => 1, :c => %w(fixed_version)
695 assert_tag 'td', :attributes => {:class => /fixed_version/},
707 assert_tag 'td', :attributes => {:class => /fixed_version/},
696 :child => {:tag => 'a', :content => '1.0', :attributes => {:href => '/versions/2'}}
708 :child => {:tag => 'a', :content => '1.0', :attributes => {:href => '/versions/2'}}
697 end
709 end
698
710
699 def test_index_send_html_if_query_is_invalid
711 def test_index_send_html_if_query_is_invalid
700 get :index, :f => ['start_date'], :op => {:start_date => '='}
712 get :index, :f => ['start_date'], :op => {:start_date => '='}
701 assert_equal 'text/html', @response.content_type
713 assert_equal 'text/html', @response.content_type
702 assert_template 'index'
714 assert_template 'index'
703 end
715 end
704
716
705 def test_index_send_nothing_if_query_is_invalid
717 def test_index_send_nothing_if_query_is_invalid
706 get :index, :f => ['start_date'], :op => {:start_date => '='}, :format => 'csv'
718 get :index, :f => ['start_date'], :op => {:start_date => '='}, :format => 'csv'
707 assert_equal 'text/csv', @response.content_type
719 assert_equal 'text/csv', @response.content_type
708 assert @response.body.blank?
720 assert @response.body.blank?
709 end
721 end
710
722
711 def test_show_by_anonymous
723 def test_show_by_anonymous
712 get :show, :id => 1
724 get :show, :id => 1
713 assert_response :success
725 assert_response :success
714 assert_template 'show'
726 assert_template 'show'
715 assert_not_nil assigns(:issue)
727 assert_not_nil assigns(:issue)
716 assert_equal Issue.find(1), assigns(:issue)
728 assert_equal Issue.find(1), assigns(:issue)
717
729
718 # anonymous role is allowed to add a note
730 # anonymous role is allowed to add a note
719 assert_tag :tag => 'form',
731 assert_tag :tag => 'form',
720 :descendant => { :tag => 'fieldset',
732 :descendant => { :tag => 'fieldset',
721 :child => { :tag => 'legend',
733 :child => { :tag => 'legend',
722 :content => /Notes/ } }
734 :content => /Notes/ } }
723 assert_tag :tag => 'title',
735 assert_tag :tag => 'title',
724 :content => "Bug #1: Can't print recipes - eCookbook - Redmine"
736 :content => "Bug #1: Can't print recipes - eCookbook - Redmine"
725 end
737 end
726
738
727 def test_show_by_manager
739 def test_show_by_manager
728 @request.session[:user_id] = 2
740 @request.session[:user_id] = 2
729 get :show, :id => 1
741 get :show, :id => 1
730 assert_response :success
742 assert_response :success
731
743
732 assert_tag :tag => 'a',
744 assert_tag :tag => 'a',
733 :content => /Quote/
745 :content => /Quote/
734
746
735 assert_tag :tag => 'form',
747 assert_tag :tag => 'form',
736 :descendant => { :tag => 'fieldset',
748 :descendant => { :tag => 'fieldset',
737 :child => { :tag => 'legend',
749 :child => { :tag => 'legend',
738 :content => /Change properties/ } },
750 :content => /Change properties/ } },
739 :descendant => { :tag => 'fieldset',
751 :descendant => { :tag => 'fieldset',
740 :child => { :tag => 'legend',
752 :child => { :tag => 'legend',
741 :content => /Log time/ } },
753 :content => /Log time/ } },
742 :descendant => { :tag => 'fieldset',
754 :descendant => { :tag => 'fieldset',
743 :child => { :tag => 'legend',
755 :child => { :tag => 'legend',
744 :content => /Notes/ } }
756 :content => /Notes/ } }
745 end
757 end
746
758
747 def test_show_should_display_update_form
759 def test_show_should_display_update_form
748 @request.session[:user_id] = 2
760 @request.session[:user_id] = 2
749 get :show, :id => 1
761 get :show, :id => 1
750 assert_response :success
762 assert_response :success
751
763
752 assert_tag 'form', :attributes => {:id => 'issue-form'}
764 assert_tag 'form', :attributes => {:id => 'issue-form'}
753 assert_tag 'input', :attributes => {:name => 'issue[is_private]'}
765 assert_tag 'input', :attributes => {:name => 'issue[is_private]'}
754 assert_tag 'select', :attributes => {:name => 'issue[project_id]'}
766 assert_tag 'select', :attributes => {:name => 'issue[project_id]'}
755 assert_tag 'select', :attributes => {:name => 'issue[tracker_id]'}
767 assert_tag 'select', :attributes => {:name => 'issue[tracker_id]'}
756 assert_tag 'input', :attributes => {:name => 'issue[subject]'}
768 assert_tag 'input', :attributes => {:name => 'issue[subject]'}
757 assert_tag 'textarea', :attributes => {:name => 'issue[description]'}
769 assert_tag 'textarea', :attributes => {:name => 'issue[description]'}
758 assert_tag 'select', :attributes => {:name => 'issue[status_id]'}
770 assert_tag 'select', :attributes => {:name => 'issue[status_id]'}
759 assert_tag 'select', :attributes => {:name => 'issue[priority_id]'}
771 assert_tag 'select', :attributes => {:name => 'issue[priority_id]'}
760 assert_tag 'select', :attributes => {:name => 'issue[assigned_to_id]'}
772 assert_tag 'select', :attributes => {:name => 'issue[assigned_to_id]'}
761 assert_tag 'select', :attributes => {:name => 'issue[category_id]'}
773 assert_tag 'select', :attributes => {:name => 'issue[category_id]'}
762 assert_tag 'select', :attributes => {:name => 'issue[fixed_version_id]'}
774 assert_tag 'select', :attributes => {:name => 'issue[fixed_version_id]'}
763 assert_tag 'input', :attributes => {:name => 'issue[parent_issue_id]'}
775 assert_tag 'input', :attributes => {:name => 'issue[parent_issue_id]'}
764 assert_tag 'input', :attributes => {:name => 'issue[start_date]'}
776 assert_tag 'input', :attributes => {:name => 'issue[start_date]'}
765 assert_tag 'input', :attributes => {:name => 'issue[due_date]'}
777 assert_tag 'input', :attributes => {:name => 'issue[due_date]'}
766 assert_tag 'select', :attributes => {:name => 'issue[done_ratio]'}
778 assert_tag 'select', :attributes => {:name => 'issue[done_ratio]'}
767 assert_tag 'input', :attributes => { :name => 'issue[custom_field_values][2]' }
779 assert_tag 'input', :attributes => { :name => 'issue[custom_field_values][2]' }
768 assert_no_tag 'input', :attributes => {:name => 'issue[watcher_user_ids][]'}
780 assert_no_tag 'input', :attributes => {:name => 'issue[watcher_user_ids][]'}
769 assert_tag 'textarea', :attributes => {:name => 'notes'}
781 assert_tag 'textarea', :attributes => {:name => 'notes'}
770 end
782 end
771
783
772 def test_show_should_display_update_form_with_minimal_permissions
784 def test_show_should_display_update_form_with_minimal_permissions
773 Role.find(1).update_attribute :permissions, [:view_issues, :add_issue_notes]
785 Role.find(1).update_attribute :permissions, [:view_issues, :add_issue_notes]
774 Workflow.delete_all :role_id => 1
786 Workflow.delete_all :role_id => 1
775
787
776 @request.session[:user_id] = 2
788 @request.session[:user_id] = 2
777 get :show, :id => 1
789 get :show, :id => 1
778 assert_response :success
790 assert_response :success
779
791
780 assert_tag 'form', :attributes => {:id => 'issue-form'}
792 assert_tag 'form', :attributes => {:id => 'issue-form'}
781 assert_no_tag 'input', :attributes => {:name => 'issue[is_private]'}
793 assert_no_tag 'input', :attributes => {:name => 'issue[is_private]'}
782 assert_no_tag 'select', :attributes => {:name => 'issue[project_id]'}
794 assert_no_tag 'select', :attributes => {:name => 'issue[project_id]'}
783 assert_no_tag 'select', :attributes => {:name => 'issue[tracker_id]'}
795 assert_no_tag 'select', :attributes => {:name => 'issue[tracker_id]'}
784 assert_no_tag 'input', :attributes => {:name => 'issue[subject]'}
796 assert_no_tag 'input', :attributes => {:name => 'issue[subject]'}
785 assert_no_tag 'textarea', :attributes => {:name => 'issue[description]'}
797 assert_no_tag 'textarea', :attributes => {:name => 'issue[description]'}
786 assert_no_tag 'select', :attributes => {:name => 'issue[status_id]'}
798 assert_no_tag 'select', :attributes => {:name => 'issue[status_id]'}
787 assert_no_tag 'select', :attributes => {:name => 'issue[priority_id]'}
799 assert_no_tag 'select', :attributes => {:name => 'issue[priority_id]'}
788 assert_no_tag 'select', :attributes => {:name => 'issue[assigned_to_id]'}
800 assert_no_tag 'select', :attributes => {:name => 'issue[assigned_to_id]'}
789 assert_no_tag 'select', :attributes => {:name => 'issue[category_id]'}
801 assert_no_tag 'select', :attributes => {:name => 'issue[category_id]'}
790 assert_no_tag 'select', :attributes => {:name => 'issue[fixed_version_id]'}
802 assert_no_tag 'select', :attributes => {:name => 'issue[fixed_version_id]'}
791 assert_no_tag 'input', :attributes => {:name => 'issue[parent_issue_id]'}
803 assert_no_tag 'input', :attributes => {:name => 'issue[parent_issue_id]'}
792 assert_no_tag 'input', :attributes => {:name => 'issue[start_date]'}
804 assert_no_tag 'input', :attributes => {:name => 'issue[start_date]'}
793 assert_no_tag 'input', :attributes => {:name => 'issue[due_date]'}
805 assert_no_tag 'input', :attributes => {:name => 'issue[due_date]'}
794 assert_no_tag 'select', :attributes => {:name => 'issue[done_ratio]'}
806 assert_no_tag 'select', :attributes => {:name => 'issue[done_ratio]'}
795 assert_no_tag 'input', :attributes => { :name => 'issue[custom_field_values][2]' }
807 assert_no_tag 'input', :attributes => { :name => 'issue[custom_field_values][2]' }
796 assert_no_tag 'input', :attributes => {:name => 'issue[watcher_user_ids][]'}
808 assert_no_tag 'input', :attributes => {:name => 'issue[watcher_user_ids][]'}
797 assert_tag 'textarea', :attributes => {:name => 'notes'}
809 assert_tag 'textarea', :attributes => {:name => 'notes'}
798 end
810 end
799
811
800 def test_show_should_display_update_form_with_workflow_permissions
812 def test_show_should_display_update_form_with_workflow_permissions
801 Role.find(1).update_attribute :permissions, [:view_issues, :add_issue_notes]
813 Role.find(1).update_attribute :permissions, [:view_issues, :add_issue_notes]
802
814
803 @request.session[:user_id] = 2
815 @request.session[:user_id] = 2
804 get :show, :id => 1
816 get :show, :id => 1
805 assert_response :success
817 assert_response :success
806
818
807 assert_tag 'form', :attributes => {:id => 'issue-form'}
819 assert_tag 'form', :attributes => {:id => 'issue-form'}
808 assert_no_tag 'input', :attributes => {:name => 'issue[is_private]'}
820 assert_no_tag 'input', :attributes => {:name => 'issue[is_private]'}
809 assert_no_tag 'select', :attributes => {:name => 'issue[project_id]'}
821 assert_no_tag 'select', :attributes => {:name => 'issue[project_id]'}
810 assert_no_tag 'select', :attributes => {:name => 'issue[tracker_id]'}
822 assert_no_tag 'select', :attributes => {:name => 'issue[tracker_id]'}
811 assert_no_tag 'input', :attributes => {:name => 'issue[subject]'}
823 assert_no_tag 'input', :attributes => {:name => 'issue[subject]'}
812 assert_no_tag 'textarea', :attributes => {:name => 'issue[description]'}
824 assert_no_tag 'textarea', :attributes => {:name => 'issue[description]'}
813 assert_tag 'select', :attributes => {:name => 'issue[status_id]'}
825 assert_tag 'select', :attributes => {:name => 'issue[status_id]'}
814 assert_no_tag 'select', :attributes => {:name => 'issue[priority_id]'}
826 assert_no_tag 'select', :attributes => {:name => 'issue[priority_id]'}
815 assert_tag 'select', :attributes => {:name => 'issue[assigned_to_id]'}
827 assert_tag 'select', :attributes => {:name => 'issue[assigned_to_id]'}
816 assert_no_tag 'select', :attributes => {:name => 'issue[category_id]'}
828 assert_no_tag 'select', :attributes => {:name => 'issue[category_id]'}
817 assert_tag 'select', :attributes => {:name => 'issue[fixed_version_id]'}
829 assert_tag 'select', :attributes => {:name => 'issue[fixed_version_id]'}
818 assert_no_tag 'input', :attributes => {:name => 'issue[parent_issue_id]'}
830 assert_no_tag 'input', :attributes => {:name => 'issue[parent_issue_id]'}
819 assert_no_tag 'input', :attributes => {:name => 'issue[start_date]'}
831 assert_no_tag 'input', :attributes => {:name => 'issue[start_date]'}
820 assert_no_tag 'input', :attributes => {:name => 'issue[due_date]'}
832 assert_no_tag 'input', :attributes => {:name => 'issue[due_date]'}
821 assert_tag 'select', :attributes => {:name => 'issue[done_ratio]'}
833 assert_tag 'select', :attributes => {:name => 'issue[done_ratio]'}
822 assert_no_tag 'input', :attributes => { :name => 'issue[custom_field_values][2]' }
834 assert_no_tag 'input', :attributes => { :name => 'issue[custom_field_values][2]' }
823 assert_no_tag 'input', :attributes => {:name => 'issue[watcher_user_ids][]'}
835 assert_no_tag 'input', :attributes => {:name => 'issue[watcher_user_ids][]'}
824 assert_tag 'textarea', :attributes => {:name => 'notes'}
836 assert_tag 'textarea', :attributes => {:name => 'notes'}
825 end
837 end
826
838
827 def test_show_should_not_display_update_form_without_permissions
839 def test_show_should_not_display_update_form_without_permissions
828 Role.find(1).update_attribute :permissions, [:view_issues]
840 Role.find(1).update_attribute :permissions, [:view_issues]
829
841
830 @request.session[:user_id] = 2
842 @request.session[:user_id] = 2
831 get :show, :id => 1
843 get :show, :id => 1
832 assert_response :success
844 assert_response :success
833
845
834 assert_no_tag 'form', :attributes => {:id => 'issue-form'}
846 assert_no_tag 'form', :attributes => {:id => 'issue-form'}
835 end
847 end
836
848
837 def test_update_form_should_not_display_inactive_enumerations
849 def test_update_form_should_not_display_inactive_enumerations
838 @request.session[:user_id] = 2
850 @request.session[:user_id] = 2
839 get :show, :id => 1
851 get :show, :id => 1
840 assert_response :success
852 assert_response :success
841
853
842 assert ! IssuePriority.find(15).active?
854 assert ! IssuePriority.find(15).active?
843 assert_no_tag :option, :attributes => {:value => '15'},
855 assert_no_tag :option, :attributes => {:value => '15'},
844 :parent => {:tag => 'select', :attributes => {:id => 'issue_priority_id'} }
856 :parent => {:tag => 'select', :attributes => {:id => 'issue_priority_id'} }
845 end
857 end
846
858
847 def test_update_form_should_allow_attachment_upload
859 def test_update_form_should_allow_attachment_upload
848 @request.session[:user_id] = 2
860 @request.session[:user_id] = 2
849 get :show, :id => 1
861 get :show, :id => 1
850
862
851 assert_tag :tag => 'form',
863 assert_tag :tag => 'form',
852 :attributes => {:id => 'issue-form', :method => 'post', :enctype => 'multipart/form-data'},
864 :attributes => {:id => 'issue-form', :method => 'post', :enctype => 'multipart/form-data'},
853 :descendant => {
865 :descendant => {
854 :tag => 'input',
866 :tag => 'input',
855 :attributes => {:type => 'file', :name => 'attachments[1][file]'}
867 :attributes => {:type => 'file', :name => 'attachments[1][file]'}
856 }
868 }
857 end
869 end
858
870
859 def test_show_should_deny_anonymous_access_without_permission
871 def test_show_should_deny_anonymous_access_without_permission
860 Role.anonymous.remove_permission!(:view_issues)
872 Role.anonymous.remove_permission!(:view_issues)
861 get :show, :id => 1
873 get :show, :id => 1
862 assert_response :redirect
874 assert_response :redirect
863 end
875 end
864
876
865 def test_show_should_deny_anonymous_access_to_private_issue
877 def test_show_should_deny_anonymous_access_to_private_issue
866 Issue.update_all(["is_private = ?", true], "id = 1")
878 Issue.update_all(["is_private = ?", true], "id = 1")
867 get :show, :id => 1
879 get :show, :id => 1
868 assert_response :redirect
880 assert_response :redirect
869 end
881 end
870
882
871 def test_show_should_deny_non_member_access_without_permission
883 def test_show_should_deny_non_member_access_without_permission
872 Role.non_member.remove_permission!(:view_issues)
884 Role.non_member.remove_permission!(:view_issues)
873 @request.session[:user_id] = 9
885 @request.session[:user_id] = 9
874 get :show, :id => 1
886 get :show, :id => 1
875 assert_response 403
887 assert_response 403
876 end
888 end
877
889
878 def test_show_should_deny_non_member_access_to_private_issue
890 def test_show_should_deny_non_member_access_to_private_issue
879 Issue.update_all(["is_private = ?", true], "id = 1")
891 Issue.update_all(["is_private = ?", true], "id = 1")
880 @request.session[:user_id] = 9
892 @request.session[:user_id] = 9
881 get :show, :id => 1
893 get :show, :id => 1
882 assert_response 403
894 assert_response 403
883 end
895 end
884
896
885 def test_show_should_deny_member_access_without_permission
897 def test_show_should_deny_member_access_without_permission
886 Role.find(1).remove_permission!(:view_issues)
898 Role.find(1).remove_permission!(:view_issues)
887 @request.session[:user_id] = 2
899 @request.session[:user_id] = 2
888 get :show, :id => 1
900 get :show, :id => 1
889 assert_response 403
901 assert_response 403
890 end
902 end
891
903
892 def test_show_should_deny_member_access_to_private_issue_without_permission
904 def test_show_should_deny_member_access_to_private_issue_without_permission
893 Issue.update_all(["is_private = ?", true], "id = 1")
905 Issue.update_all(["is_private = ?", true], "id = 1")
894 @request.session[:user_id] = 3
906 @request.session[:user_id] = 3
895 get :show, :id => 1
907 get :show, :id => 1
896 assert_response 403
908 assert_response 403
897 end
909 end
898
910
899 def test_show_should_allow_author_access_to_private_issue
911 def test_show_should_allow_author_access_to_private_issue
900 Issue.update_all(["is_private = ?, author_id = 3", true], "id = 1")
912 Issue.update_all(["is_private = ?, author_id = 3", true], "id = 1")
901 @request.session[:user_id] = 3
913 @request.session[:user_id] = 3
902 get :show, :id => 1
914 get :show, :id => 1
903 assert_response :success
915 assert_response :success
904 end
916 end
905
917
906 def test_show_should_allow_assignee_access_to_private_issue
918 def test_show_should_allow_assignee_access_to_private_issue
907 Issue.update_all(["is_private = ?, assigned_to_id = 3", true], "id = 1")
919 Issue.update_all(["is_private = ?, assigned_to_id = 3", true], "id = 1")
908 @request.session[:user_id] = 3
920 @request.session[:user_id] = 3
909 get :show, :id => 1
921 get :show, :id => 1
910 assert_response :success
922 assert_response :success
911 end
923 end
912
924
913 def test_show_should_allow_member_access_to_private_issue_with_permission
925 def test_show_should_allow_member_access_to_private_issue_with_permission
914 Issue.update_all(["is_private = ?", true], "id = 1")
926 Issue.update_all(["is_private = ?", true], "id = 1")
915 User.find(3).roles_for_project(Project.find(1)).first.update_attribute :issues_visibility, 'all'
927 User.find(3).roles_for_project(Project.find(1)).first.update_attribute :issues_visibility, 'all'
916 @request.session[:user_id] = 3
928 @request.session[:user_id] = 3
917 get :show, :id => 1
929 get :show, :id => 1
918 assert_response :success
930 assert_response :success
919 end
931 end
920
932
921 def test_show_should_not_disclose_relations_to_invisible_issues
933 def test_show_should_not_disclose_relations_to_invisible_issues
922 Setting.cross_project_issue_relations = '1'
934 Setting.cross_project_issue_relations = '1'
923 IssueRelation.create!(:issue_from => Issue.find(1), :issue_to => Issue.find(2), :relation_type => 'relates')
935 IssueRelation.create!(:issue_from => Issue.find(1), :issue_to => Issue.find(2), :relation_type => 'relates')
924 # Relation to a private project issue
936 # Relation to a private project issue
925 IssueRelation.create!(:issue_from => Issue.find(1), :issue_to => Issue.find(4), :relation_type => 'relates')
937 IssueRelation.create!(:issue_from => Issue.find(1), :issue_to => Issue.find(4), :relation_type => 'relates')
926
938
927 get :show, :id => 1
939 get :show, :id => 1
928 assert_response :success
940 assert_response :success
929
941
930 assert_tag :div, :attributes => { :id => 'relations' },
942 assert_tag :div, :attributes => { :id => 'relations' },
931 :descendant => { :tag => 'a', :content => /#2$/ }
943 :descendant => { :tag => 'a', :content => /#2$/ }
932 assert_no_tag :div, :attributes => { :id => 'relations' },
944 assert_no_tag :div, :attributes => { :id => 'relations' },
933 :descendant => { :tag => 'a', :content => /#4$/ }
945 :descendant => { :tag => 'a', :content => /#4$/ }
934 end
946 end
935
947
936 def test_show_should_list_subtasks
948 def test_show_should_list_subtasks
937 Issue.generate!(:project_id => 1, :author_id => 1, :tracker_id => 1, :parent_issue_id => 1, :subject => 'Child Issue')
949 Issue.generate!(:project_id => 1, :author_id => 1, :tracker_id => 1, :parent_issue_id => 1, :subject => 'Child Issue')
938
950
939 get :show, :id => 1
951 get :show, :id => 1
940 assert_response :success
952 assert_response :success
941 assert_tag 'div', :attributes => {:id => 'issue_tree'},
953 assert_tag 'div', :attributes => {:id => 'issue_tree'},
942 :descendant => {:tag => 'td', :content => /Child Issue/, :attributes => {:class => /subject/}}
954 :descendant => {:tag => 'td', :content => /Child Issue/, :attributes => {:class => /subject/}}
943 end
955 end
944
956
945 def test_show_should_list_parents
957 def test_show_should_list_parents
946 issue = Issue.generate!(:project_id => 1, :author_id => 1, :tracker_id => 1, :parent_issue_id => 1, :subject => 'Child Issue')
958 issue = Issue.generate!(:project_id => 1, :author_id => 1, :tracker_id => 1, :parent_issue_id => 1, :subject => 'Child Issue')
947
959
948 get :show, :id => issue.id
960 get :show, :id => issue.id
949 assert_response :success
961 assert_response :success
950 assert_tag 'div', :attributes => {:class => 'subject'},
962 assert_tag 'div', :attributes => {:class => 'subject'},
951 :descendant => {:tag => 'h3', :content => 'Child Issue'}
963 :descendant => {:tag => 'h3', :content => 'Child Issue'}
952 assert_tag 'div', :attributes => {:class => 'subject'},
964 assert_tag 'div', :attributes => {:class => 'subject'},
953 :descendant => {:tag => 'a', :attributes => {:href => '/issues/1'}}
965 :descendant => {:tag => 'a', :attributes => {:href => '/issues/1'}}
954 end
966 end
955
967
956 def test_show_should_not_display_prev_next_links_without_query_in_session
968 def test_show_should_not_display_prev_next_links_without_query_in_session
957 get :show, :id => 1
969 get :show, :id => 1
958 assert_response :success
970 assert_response :success
959 assert_nil assigns(:prev_issue_id)
971 assert_nil assigns(:prev_issue_id)
960 assert_nil assigns(:next_issue_id)
972 assert_nil assigns(:next_issue_id)
961
973
962 assert_no_tag 'div', :attributes => {:class => /next-prev-links/}
974 assert_no_tag 'div', :attributes => {:class => /next-prev-links/}
963 end
975 end
964
976
965 def test_show_should_display_prev_next_links_with_query_in_session
977 def test_show_should_display_prev_next_links_with_query_in_session
966 @request.session[:query] = {:filters => {'status_id' => {:values => [''], :operator => 'o'}}, :project_id => nil}
978 @request.session[:query] = {:filters => {'status_id' => {:values => [''], :operator => 'o'}}, :project_id => nil}
967 @request.session['issues_index_sort'] = 'id'
979 @request.session['issues_index_sort'] = 'id'
968
980
969 with_settings :display_subprojects_issues => '0' do
981 with_settings :display_subprojects_issues => '0' do
970 get :show, :id => 3
982 get :show, :id => 3
971 end
983 end
972
984
973 assert_response :success
985 assert_response :success
974 # Previous and next issues for all projects
986 # Previous and next issues for all projects
975 assert_equal 2, assigns(:prev_issue_id)
987 assert_equal 2, assigns(:prev_issue_id)
976 assert_equal 5, assigns(:next_issue_id)
988 assert_equal 5, assigns(:next_issue_id)
977
989
978 assert_tag 'div', :attributes => {:class => /next-prev-links/}
990 assert_tag 'div', :attributes => {:class => /next-prev-links/}
979 assert_tag 'a', :attributes => {:href => '/issues/2'}, :content => /Previous/
991 assert_tag 'a', :attributes => {:href => '/issues/2'}, :content => /Previous/
980 assert_tag 'a', :attributes => {:href => '/issues/5'}, :content => /Next/
992 assert_tag 'a', :attributes => {:href => '/issues/5'}, :content => /Next/
981
993
982 count = Issue.open.visible.count
994 count = Issue.open.visible.count
983 assert_tag 'span', :attributes => {:class => 'position'}, :content => "3 of #{count}"
995 assert_tag 'span', :attributes => {:class => 'position'}, :content => "3 of #{count}"
984 end
996 end
985
997
986 def test_show_should_display_prev_next_links_with_saved_query_in_session
998 def test_show_should_display_prev_next_links_with_saved_query_in_session
987 query = Query.create!(:name => 'test', :is_public => true, :user_id => 1,
999 query = Query.create!(:name => 'test', :is_public => true, :user_id => 1,
988 :filters => {'status_id' => {:values => ['5'], :operator => '='}},
1000 :filters => {'status_id' => {:values => ['5'], :operator => '='}},
989 :sort_criteria => [['id', 'asc']])
1001 :sort_criteria => [['id', 'asc']])
990 @request.session[:query] = {:id => query.id, :project_id => nil}
1002 @request.session[:query] = {:id => query.id, :project_id => nil}
991
1003
992 get :show, :id => 11
1004 get :show, :id => 11
993
1005
994 assert_response :success
1006 assert_response :success
995 assert_equal query, assigns(:query)
1007 assert_equal query, assigns(:query)
996 # Previous and next issues for all projects
1008 # Previous and next issues for all projects
997 assert_equal 8, assigns(:prev_issue_id)
1009 assert_equal 8, assigns(:prev_issue_id)
998 assert_equal 12, assigns(:next_issue_id)
1010 assert_equal 12, assigns(:next_issue_id)
999
1011
1000 assert_tag 'a', :attributes => {:href => '/issues/8'}, :content => /Previous/
1012 assert_tag 'a', :attributes => {:href => '/issues/8'}, :content => /Previous/
1001 assert_tag 'a', :attributes => {:href => '/issues/12'}, :content => /Next/
1013 assert_tag 'a', :attributes => {:href => '/issues/12'}, :content => /Next/
1002 end
1014 end
1003
1015
1004 def test_show_should_display_prev_next_links_with_query_and_sort_on_association
1016 def test_show_should_display_prev_next_links_with_query_and_sort_on_association
1005 @request.session[:query] = {:filters => {'status_id' => {:values => [''], :operator => 'o'}}, :project_id => nil}
1017 @request.session[:query] = {:filters => {'status_id' => {:values => [''], :operator => 'o'}}, :project_id => nil}
1006
1018
1007 %w(project tracker status priority author assigned_to category fixed_version).each do |assoc_sort|
1019 %w(project tracker status priority author assigned_to category fixed_version).each do |assoc_sort|
1008 @request.session['issues_index_sort'] = assoc_sort
1020 @request.session['issues_index_sort'] = assoc_sort
1009
1021
1010 get :show, :id => 3
1022 get :show, :id => 3
1011 assert_response :success, "Wrong response status for #{assoc_sort} sort"
1023 assert_response :success, "Wrong response status for #{assoc_sort} sort"
1012
1024
1013 assert_tag 'a', :content => /Previous/
1025 assert_tag 'a', :content => /Previous/
1014 assert_tag 'a', :content => /Next/
1026 assert_tag 'a', :content => /Next/
1015 end
1027 end
1016 end
1028 end
1017
1029
1018 def test_show_should_display_prev_next_links_with_project_query_in_session
1030 def test_show_should_display_prev_next_links_with_project_query_in_session
1019 @request.session[:query] = {:filters => {'status_id' => {:values => [''], :operator => 'o'}}, :project_id => 1}
1031 @request.session[:query] = {:filters => {'status_id' => {:values => [''], :operator => 'o'}}, :project_id => 1}
1020 @request.session['issues_index_sort'] = 'id'
1032 @request.session['issues_index_sort'] = 'id'
1021
1033
1022 with_settings :display_subprojects_issues => '0' do
1034 with_settings :display_subprojects_issues => '0' do
1023 get :show, :id => 3
1035 get :show, :id => 3
1024 end
1036 end
1025
1037
1026 assert_response :success
1038 assert_response :success
1027 # Previous and next issues inside project
1039 # Previous and next issues inside project
1028 assert_equal 2, assigns(:prev_issue_id)
1040 assert_equal 2, assigns(:prev_issue_id)
1029 assert_equal 7, assigns(:next_issue_id)
1041 assert_equal 7, assigns(:next_issue_id)
1030
1042
1031 assert_tag 'a', :attributes => {:href => '/issues/2'}, :content => /Previous/
1043 assert_tag 'a', :attributes => {:href => '/issues/2'}, :content => /Previous/
1032 assert_tag 'a', :attributes => {:href => '/issues/7'}, :content => /Next/
1044 assert_tag 'a', :attributes => {:href => '/issues/7'}, :content => /Next/
1033 end
1045 end
1034
1046
1035 def test_show_should_not_display_prev_link_for_first_issue
1047 def test_show_should_not_display_prev_link_for_first_issue
1036 @request.session[:query] = {:filters => {'status_id' => {:values => [''], :operator => 'o'}}, :project_id => 1}
1048 @request.session[:query] = {:filters => {'status_id' => {:values => [''], :operator => 'o'}}, :project_id => 1}
1037 @request.session['issues_index_sort'] = 'id'
1049 @request.session['issues_index_sort'] = 'id'
1038
1050
1039 with_settings :display_subprojects_issues => '0' do
1051 with_settings :display_subprojects_issues => '0' do
1040 get :show, :id => 1
1052 get :show, :id => 1
1041 end
1053 end
1042
1054
1043 assert_response :success
1055 assert_response :success
1044 assert_nil assigns(:prev_issue_id)
1056 assert_nil assigns(:prev_issue_id)
1045 assert_equal 2, assigns(:next_issue_id)
1057 assert_equal 2, assigns(:next_issue_id)
1046
1058
1047 assert_no_tag 'a', :content => /Previous/
1059 assert_no_tag 'a', :content => /Previous/
1048 assert_tag 'a', :attributes => {:href => '/issues/2'}, :content => /Next/
1060 assert_tag 'a', :attributes => {:href => '/issues/2'}, :content => /Next/
1049 end
1061 end
1050
1062
1051 def test_show_should_not_display_prev_next_links_for_issue_not_in_query_results
1063 def test_show_should_not_display_prev_next_links_for_issue_not_in_query_results
1052 @request.session[:query] = {:filters => {'status_id' => {:values => [''], :operator => 'c'}}, :project_id => 1}
1064 @request.session[:query] = {:filters => {'status_id' => {:values => [''], :operator => 'c'}}, :project_id => 1}
1053 @request.session['issues_index_sort'] = 'id'
1065 @request.session['issues_index_sort'] = 'id'
1054
1066
1055 get :show, :id => 1
1067 get :show, :id => 1
1056
1068
1057 assert_response :success
1069 assert_response :success
1058 assert_nil assigns(:prev_issue_id)
1070 assert_nil assigns(:prev_issue_id)
1059 assert_nil assigns(:next_issue_id)
1071 assert_nil assigns(:next_issue_id)
1060
1072
1061 assert_no_tag 'a', :content => /Previous/
1073 assert_no_tag 'a', :content => /Previous/
1062 assert_no_tag 'a', :content => /Next/
1074 assert_no_tag 'a', :content => /Next/
1063 end
1075 end
1064
1076
1065 def test_show_with_multi_custom_field
1077 def test_show_with_multi_custom_field
1066 field = CustomField.find(1)
1078 field = CustomField.find(1)
1067 field.update_attribute :multiple, true
1079 field.update_attribute :multiple, true
1068 issue = Issue.find(1)
1080 issue = Issue.find(1)
1069 issue.custom_field_values = {1 => ['MySQL', 'Oracle']}
1081 issue.custom_field_values = {1 => ['MySQL', 'Oracle']}
1070 issue.save!
1082 issue.save!
1071
1083
1072 get :show, :id => 1
1084 get :show, :id => 1
1073 assert_response :success
1085 assert_response :success
1074
1086
1075 assert_tag :td, :content => 'MySQL, Oracle'
1087 assert_tag :td, :content => 'MySQL, Oracle'
1076 end
1088 end
1077
1089
1078 def test_show_with_multi_user_custom_field
1090 def test_show_with_multi_user_custom_field
1079 field = IssueCustomField.create!(:name => 'Multi user', :field_format => 'user', :multiple => true,
1091 field = IssueCustomField.create!(:name => 'Multi user', :field_format => 'user', :multiple => true,
1080 :tracker_ids => [1], :is_for_all => true)
1092 :tracker_ids => [1], :is_for_all => true)
1081 issue = Issue.find(1)
1093 issue = Issue.find(1)
1082 issue.custom_field_values = {field.id => ['2', '3']}
1094 issue.custom_field_values = {field.id => ['2', '3']}
1083 issue.save!
1095 issue.save!
1084
1096
1085 get :show, :id => 1
1097 get :show, :id => 1
1086 assert_response :success
1098 assert_response :success
1087
1099
1088 # TODO: should display links
1100 # TODO: should display links
1089 assert_tag :td, :content => 'John Smith, Dave Lopper'
1101 assert_tag :td, :content => 'Dave Lopper, John Smith'
1090 end
1102 end
1091
1103
1092 def test_show_atom
1104 def test_show_atom
1093 get :show, :id => 2, :format => 'atom'
1105 get :show, :id => 2, :format => 'atom'
1094 assert_response :success
1106 assert_response :success
1095 assert_template 'journals/index'
1107 assert_template 'journals/index'
1096 # Inline image
1108 # Inline image
1097 assert_select 'content', :text => Regexp.new(Regexp.quote('http://test.host/attachments/download/10'))
1109 assert_select 'content', :text => Regexp.new(Regexp.quote('http://test.host/attachments/download/10'))
1098 end
1110 end
1099
1111
1100 def test_show_export_to_pdf
1112 def test_show_export_to_pdf
1101 get :show, :id => 3, :format => 'pdf'
1113 get :show, :id => 3, :format => 'pdf'
1102 assert_response :success
1114 assert_response :success
1103 assert_equal 'application/pdf', @response.content_type
1115 assert_equal 'application/pdf', @response.content_type
1104 assert @response.body.starts_with?('%PDF')
1116 assert @response.body.starts_with?('%PDF')
1105 assert_not_nil assigns(:issue)
1117 assert_not_nil assigns(:issue)
1106 end
1118 end
1107
1119
1108 def test_get_new
1120 def test_get_new
1109 @request.session[:user_id] = 2
1121 @request.session[:user_id] = 2
1110 get :new, :project_id => 1, :tracker_id => 1
1122 get :new, :project_id => 1, :tracker_id => 1
1111 assert_response :success
1123 assert_response :success
1112 assert_template 'new'
1124 assert_template 'new'
1113
1125
1114 assert_tag 'input', :attributes => {:name => 'issue[is_private]'}
1126 assert_tag 'input', :attributes => {:name => 'issue[is_private]'}
1115 assert_no_tag 'select', :attributes => {:name => 'issue[project_id]'}
1127 assert_no_tag 'select', :attributes => {:name => 'issue[project_id]'}
1116 assert_tag 'select', :attributes => {:name => 'issue[tracker_id]'}
1128 assert_tag 'select', :attributes => {:name => 'issue[tracker_id]'}
1117 assert_tag 'input', :attributes => {:name => 'issue[subject]'}
1129 assert_tag 'input', :attributes => {:name => 'issue[subject]'}
1118 assert_tag 'textarea', :attributes => {:name => 'issue[description]'}
1130 assert_tag 'textarea', :attributes => {:name => 'issue[description]'}
1119 assert_tag 'select', :attributes => {:name => 'issue[status_id]'}
1131 assert_tag 'select', :attributes => {:name => 'issue[status_id]'}
1120 assert_tag 'select', :attributes => {:name => 'issue[priority_id]'}
1132 assert_tag 'select', :attributes => {:name => 'issue[priority_id]'}
1121 assert_tag 'select', :attributes => {:name => 'issue[assigned_to_id]'}
1133 assert_tag 'select', :attributes => {:name => 'issue[assigned_to_id]'}
1122 assert_tag 'select', :attributes => {:name => 'issue[category_id]'}
1134 assert_tag 'select', :attributes => {:name => 'issue[category_id]'}
1123 assert_tag 'select', :attributes => {:name => 'issue[fixed_version_id]'}
1135 assert_tag 'select', :attributes => {:name => 'issue[fixed_version_id]'}
1124 assert_tag 'input', :attributes => {:name => 'issue[parent_issue_id]'}
1136 assert_tag 'input', :attributes => {:name => 'issue[parent_issue_id]'}
1125 assert_tag 'input', :attributes => {:name => 'issue[start_date]'}
1137 assert_tag 'input', :attributes => {:name => 'issue[start_date]'}
1126 assert_tag 'input', :attributes => {:name => 'issue[due_date]'}
1138 assert_tag 'input', :attributes => {:name => 'issue[due_date]'}
1127 assert_tag 'select', :attributes => {:name => 'issue[done_ratio]'}
1139 assert_tag 'select', :attributes => {:name => 'issue[done_ratio]'}
1128 assert_tag 'input', :attributes => { :name => 'issue[custom_field_values][2]', :value => 'Default string' }
1140 assert_tag 'input', :attributes => { :name => 'issue[custom_field_values][2]', :value => 'Default string' }
1129 assert_tag 'input', :attributes => {:name => 'issue[watcher_user_ids][]'}
1141 assert_tag 'input', :attributes => {:name => 'issue[watcher_user_ids][]'}
1130
1142
1131 # Be sure we don't display inactive IssuePriorities
1143 # Be sure we don't display inactive IssuePriorities
1132 assert ! IssuePriority.find(15).active?
1144 assert ! IssuePriority.find(15).active?
1133 assert_no_tag :option, :attributes => {:value => '15'},
1145 assert_no_tag :option, :attributes => {:value => '15'},
1134 :parent => {:tag => 'select', :attributes => {:id => 'issue_priority_id'} }
1146 :parent => {:tag => 'select', :attributes => {:id => 'issue_priority_id'} }
1135 end
1147 end
1136
1148
1137 def test_get_new_with_minimal_permissions
1149 def test_get_new_with_minimal_permissions
1138 Role.find(1).update_attribute :permissions, [:add_issues]
1150 Role.find(1).update_attribute :permissions, [:add_issues]
1139 Workflow.delete_all :role_id => 1
1151 Workflow.delete_all :role_id => 1
1140
1152
1141 @request.session[:user_id] = 2
1153 @request.session[:user_id] = 2
1142 get :new, :project_id => 1, :tracker_id => 1
1154 get :new, :project_id => 1, :tracker_id => 1
1143 assert_response :success
1155 assert_response :success
1144 assert_template 'new'
1156 assert_template 'new'
1145
1157
1146 assert_no_tag 'input', :attributes => {:name => 'issue[is_private]'}
1158 assert_no_tag 'input', :attributes => {:name => 'issue[is_private]'}
1147 assert_no_tag 'select', :attributes => {:name => 'issue[project_id]'}
1159 assert_no_tag 'select', :attributes => {:name => 'issue[project_id]'}
1148 assert_tag 'select', :attributes => {:name => 'issue[tracker_id]'}
1160 assert_tag 'select', :attributes => {:name => 'issue[tracker_id]'}
1149 assert_tag 'input', :attributes => {:name => 'issue[subject]'}
1161 assert_tag 'input', :attributes => {:name => 'issue[subject]'}
1150 assert_tag 'textarea', :attributes => {:name => 'issue[description]'}
1162 assert_tag 'textarea', :attributes => {:name => 'issue[description]'}
1151 assert_tag 'select', :attributes => {:name => 'issue[status_id]'}
1163 assert_tag 'select', :attributes => {:name => 'issue[status_id]'}
1152 assert_tag 'select', :attributes => {:name => 'issue[priority_id]'}
1164 assert_tag 'select', :attributes => {:name => 'issue[priority_id]'}
1153 assert_tag 'select', :attributes => {:name => 'issue[assigned_to_id]'}
1165 assert_tag 'select', :attributes => {:name => 'issue[assigned_to_id]'}
1154 assert_tag 'select', :attributes => {:name => 'issue[category_id]'}
1166 assert_tag 'select', :attributes => {:name => 'issue[category_id]'}
1155 assert_tag 'select', :attributes => {:name => 'issue[fixed_version_id]'}
1167 assert_tag 'select', :attributes => {:name => 'issue[fixed_version_id]'}
1156 assert_no_tag 'input', :attributes => {:name => 'issue[parent_issue_id]'}
1168 assert_no_tag 'input', :attributes => {:name => 'issue[parent_issue_id]'}
1157 assert_tag 'input', :attributes => {:name => 'issue[start_date]'}
1169 assert_tag 'input', :attributes => {:name => 'issue[start_date]'}
1158 assert_tag 'input', :attributes => {:name => 'issue[due_date]'}
1170 assert_tag 'input', :attributes => {:name => 'issue[due_date]'}
1159 assert_tag 'select', :attributes => {:name => 'issue[done_ratio]'}
1171 assert_tag 'select', :attributes => {:name => 'issue[done_ratio]'}
1160 assert_tag 'input', :attributes => { :name => 'issue[custom_field_values][2]', :value => 'Default string' }
1172 assert_tag 'input', :attributes => { :name => 'issue[custom_field_values][2]', :value => 'Default string' }
1161 assert_no_tag 'input', :attributes => {:name => 'issue[watcher_user_ids][]'}
1173 assert_no_tag 'input', :attributes => {:name => 'issue[watcher_user_ids][]'}
1162 end
1174 end
1163
1175
1164 def test_get_new_with_multi_custom_field
1176 def test_get_new_with_multi_custom_field
1165 field = IssueCustomField.find(1)
1177 field = IssueCustomField.find(1)
1166 field.update_attribute :multiple, true
1178 field.update_attribute :multiple, true
1167
1179
1168 @request.session[:user_id] = 2
1180 @request.session[:user_id] = 2
1169 get :new, :project_id => 1, :tracker_id => 1
1181 get :new, :project_id => 1, :tracker_id => 1
1170 assert_response :success
1182 assert_response :success
1171 assert_template 'new'
1183 assert_template 'new'
1172
1184
1173 assert_tag 'select',
1185 assert_tag 'select',
1174 :attributes => {:name => 'issue[custom_field_values][1][]', :multiple => 'multiple'},
1186 :attributes => {:name => 'issue[custom_field_values][1][]', :multiple => 'multiple'},
1175 :children => {:count => 3},
1187 :children => {:count => 3},
1176 :child => {:tag => 'option', :attributes => {:value => 'MySQL'}, :content => 'MySQL'}
1188 :child => {:tag => 'option', :attributes => {:value => 'MySQL'}, :content => 'MySQL'}
1177 assert_tag 'input',
1189 assert_tag 'input',
1178 :attributes => {:name => 'issue[custom_field_values][1][]', :value => ''}
1190 :attributes => {:name => 'issue[custom_field_values][1][]', :value => ''}
1179 end
1191 end
1180
1192
1181 def test_get_new_with_multi_user_custom_field
1193 def test_get_new_with_multi_user_custom_field
1182 field = IssueCustomField.create!(:name => 'Multi user', :field_format => 'user', :multiple => true,
1194 field = IssueCustomField.create!(:name => 'Multi user', :field_format => 'user', :multiple => true,
1183 :tracker_ids => [1], :is_for_all => true)
1195 :tracker_ids => [1], :is_for_all => true)
1184
1196
1185 @request.session[:user_id] = 2
1197 @request.session[:user_id] = 2
1186 get :new, :project_id => 1, :tracker_id => 1
1198 get :new, :project_id => 1, :tracker_id => 1
1187 assert_response :success
1199 assert_response :success
1188 assert_template 'new'
1200 assert_template 'new'
1189
1201
1190 assert_tag 'select',
1202 assert_tag 'select',
1191 :attributes => {:name => "issue[custom_field_values][#{field.id}][]", :multiple => 'multiple'},
1203 :attributes => {:name => "issue[custom_field_values][#{field.id}][]", :multiple => 'multiple'},
1192 :children => {:count => Project.find(1).users.count},
1204 :children => {:count => Project.find(1).users.count},
1193 :child => {:tag => 'option', :attributes => {:value => '2'}, :content => 'John Smith'}
1205 :child => {:tag => 'option', :attributes => {:value => '2'}, :content => 'John Smith'}
1194 assert_tag 'input',
1206 assert_tag 'input',
1195 :attributes => {:name => "issue[custom_field_values][#{field.id}][]", :value => ''}
1207 :attributes => {:name => "issue[custom_field_values][#{field.id}][]", :value => ''}
1196 end
1208 end
1197
1209
1198 def test_get_new_without_default_start_date_is_creation_date
1210 def test_get_new_without_default_start_date_is_creation_date
1199 Setting.default_issue_start_date_to_creation_date = 0
1211 Setting.default_issue_start_date_to_creation_date = 0
1200
1212
1201 @request.session[:user_id] = 2
1213 @request.session[:user_id] = 2
1202 get :new, :project_id => 1, :tracker_id => 1
1214 get :new, :project_id => 1, :tracker_id => 1
1203 assert_response :success
1215 assert_response :success
1204 assert_template 'new'
1216 assert_template 'new'
1205
1217
1206 assert_tag :tag => 'input', :attributes => { :name => 'issue[start_date]',
1218 assert_tag :tag => 'input', :attributes => { :name => 'issue[start_date]',
1207 :value => nil }
1219 :value => nil }
1208 end
1220 end
1209
1221
1210 def test_get_new_with_default_start_date_is_creation_date
1222 def test_get_new_with_default_start_date_is_creation_date
1211 Setting.default_issue_start_date_to_creation_date = 1
1223 Setting.default_issue_start_date_to_creation_date = 1
1212
1224
1213 @request.session[:user_id] = 2
1225 @request.session[:user_id] = 2
1214 get :new, :project_id => 1, :tracker_id => 1
1226 get :new, :project_id => 1, :tracker_id => 1
1215 assert_response :success
1227 assert_response :success
1216 assert_template 'new'
1228 assert_template 'new'
1217
1229
1218 assert_tag :tag => 'input', :attributes => { :name => 'issue[start_date]',
1230 assert_tag :tag => 'input', :attributes => { :name => 'issue[start_date]',
1219 :value => Date.today.to_s }
1231 :value => Date.today.to_s }
1220 end
1232 end
1221
1233
1222 def test_get_new_form_should_allow_attachment_upload
1234 def test_get_new_form_should_allow_attachment_upload
1223 @request.session[:user_id] = 2
1235 @request.session[:user_id] = 2
1224 get :new, :project_id => 1, :tracker_id => 1
1236 get :new, :project_id => 1, :tracker_id => 1
1225
1237
1226 assert_tag :tag => 'form',
1238 assert_tag :tag => 'form',
1227 :attributes => {:id => 'issue-form', :method => 'post', :enctype => 'multipart/form-data'},
1239 :attributes => {:id => 'issue-form', :method => 'post', :enctype => 'multipart/form-data'},
1228 :descendant => {
1240 :descendant => {
1229 :tag => 'input',
1241 :tag => 'input',
1230 :attributes => {:type => 'file', :name => 'attachments[1][file]'}
1242 :attributes => {:type => 'file', :name => 'attachments[1][file]'}
1231 }
1243 }
1232 end
1244 end
1233
1245
1234 def test_get_new_without_tracker_id
1246 def test_get_new_without_tracker_id
1235 @request.session[:user_id] = 2
1247 @request.session[:user_id] = 2
1236 get :new, :project_id => 1
1248 get :new, :project_id => 1
1237 assert_response :success
1249 assert_response :success
1238 assert_template 'new'
1250 assert_template 'new'
1239
1251
1240 issue = assigns(:issue)
1252 issue = assigns(:issue)
1241 assert_not_nil issue
1253 assert_not_nil issue
1242 assert_equal Project.find(1).trackers.first, issue.tracker
1254 assert_equal Project.find(1).trackers.first, issue.tracker
1243 end
1255 end
1244
1256
1245 def test_get_new_with_no_default_status_should_display_an_error
1257 def test_get_new_with_no_default_status_should_display_an_error
1246 @request.session[:user_id] = 2
1258 @request.session[:user_id] = 2
1247 IssueStatus.delete_all
1259 IssueStatus.delete_all
1248
1260
1249 get :new, :project_id => 1
1261 get :new, :project_id => 1
1250 assert_response 500
1262 assert_response 500
1251 assert_error_tag :content => /No default issue/
1263 assert_error_tag :content => /No default issue/
1252 end
1264 end
1253
1265
1254 def test_get_new_with_no_tracker_should_display_an_error
1266 def test_get_new_with_no_tracker_should_display_an_error
1255 @request.session[:user_id] = 2
1267 @request.session[:user_id] = 2
1256 Tracker.delete_all
1268 Tracker.delete_all
1257
1269
1258 get :new, :project_id => 1
1270 get :new, :project_id => 1
1259 assert_response 500
1271 assert_response 500
1260 assert_error_tag :content => /No tracker/
1272 assert_error_tag :content => /No tracker/
1261 end
1273 end
1262
1274
1263 def test_update_new_form
1275 def test_update_new_form
1264 @request.session[:user_id] = 2
1276 @request.session[:user_id] = 2
1265 xhr :post, :new, :project_id => 1,
1277 xhr :post, :new, :project_id => 1,
1266 :issue => {:tracker_id => 2,
1278 :issue => {:tracker_id => 2,
1267 :subject => 'This is the test_new issue',
1279 :subject => 'This is the test_new issue',
1268 :description => 'This is the description',
1280 :description => 'This is the description',
1269 :priority_id => 5}
1281 :priority_id => 5}
1270 assert_response :success
1282 assert_response :success
1271 assert_template 'attributes'
1283 assert_template 'attributes'
1272
1284
1273 issue = assigns(:issue)
1285 issue = assigns(:issue)
1274 assert_kind_of Issue, issue
1286 assert_kind_of Issue, issue
1275 assert_equal 1, issue.project_id
1287 assert_equal 1, issue.project_id
1276 assert_equal 2, issue.tracker_id
1288 assert_equal 2, issue.tracker_id
1277 assert_equal 'This is the test_new issue', issue.subject
1289 assert_equal 'This is the test_new issue', issue.subject
1278 end
1290 end
1279
1291
1280 def test_post_create
1292 def test_post_create
1281 @request.session[:user_id] = 2
1293 @request.session[:user_id] = 2
1282 assert_difference 'Issue.count' do
1294 assert_difference 'Issue.count' do
1283 post :create, :project_id => 1,
1295 post :create, :project_id => 1,
1284 :issue => {:tracker_id => 3,
1296 :issue => {:tracker_id => 3,
1285 :status_id => 2,
1297 :status_id => 2,
1286 :subject => 'This is the test_new issue',
1298 :subject => 'This is the test_new issue',
1287 :description => 'This is the description',
1299 :description => 'This is the description',
1288 :priority_id => 5,
1300 :priority_id => 5,
1289 :start_date => '2010-11-07',
1301 :start_date => '2010-11-07',
1290 :estimated_hours => '',
1302 :estimated_hours => '',
1291 :custom_field_values => {'2' => 'Value for field 2'}}
1303 :custom_field_values => {'2' => 'Value for field 2'}}
1292 end
1304 end
1293 assert_redirected_to :controller => 'issues', :action => 'show', :id => Issue.last.id
1305 assert_redirected_to :controller => 'issues', :action => 'show', :id => Issue.last.id
1294
1306
1295 issue = Issue.find_by_subject('This is the test_new issue')
1307 issue = Issue.find_by_subject('This is the test_new issue')
1296 assert_not_nil issue
1308 assert_not_nil issue
1297 assert_equal 2, issue.author_id
1309 assert_equal 2, issue.author_id
1298 assert_equal 3, issue.tracker_id
1310 assert_equal 3, issue.tracker_id
1299 assert_equal 2, issue.status_id
1311 assert_equal 2, issue.status_id
1300 assert_equal Date.parse('2010-11-07'), issue.start_date
1312 assert_equal Date.parse('2010-11-07'), issue.start_date
1301 assert_nil issue.estimated_hours
1313 assert_nil issue.estimated_hours
1302 v = issue.custom_values.find(:first, :conditions => {:custom_field_id => 2})
1314 v = issue.custom_values.find(:first, :conditions => {:custom_field_id => 2})
1303 assert_not_nil v
1315 assert_not_nil v
1304 assert_equal 'Value for field 2', v.value
1316 assert_equal 'Value for field 2', v.value
1305 end
1317 end
1306
1318
1307 def test_post_new_with_group_assignment
1319 def test_post_new_with_group_assignment
1308 group = Group.find(11)
1320 group = Group.find(11)
1309 project = Project.find(1)
1321 project = Project.find(1)
1310 project.members << Member.new(:principal => group, :roles => [Role.first])
1322 project.members << Member.new(:principal => group, :roles => [Role.first])
1311
1323
1312 with_settings :issue_group_assignment => '1' do
1324 with_settings :issue_group_assignment => '1' do
1313 @request.session[:user_id] = 2
1325 @request.session[:user_id] = 2
1314 assert_difference 'Issue.count' do
1326 assert_difference 'Issue.count' do
1315 post :create, :project_id => project.id,
1327 post :create, :project_id => project.id,
1316 :issue => {:tracker_id => 3,
1328 :issue => {:tracker_id => 3,
1317 :status_id => 1,
1329 :status_id => 1,
1318 :subject => 'This is the test_new_with_group_assignment issue',
1330 :subject => 'This is the test_new_with_group_assignment issue',
1319 :assigned_to_id => group.id}
1331 :assigned_to_id => group.id}
1320 end
1332 end
1321 end
1333 end
1322 assert_redirected_to :controller => 'issues', :action => 'show', :id => Issue.last.id
1334 assert_redirected_to :controller => 'issues', :action => 'show', :id => Issue.last.id
1323
1335
1324 issue = Issue.find_by_subject('This is the test_new_with_group_assignment issue')
1336 issue = Issue.find_by_subject('This is the test_new_with_group_assignment issue')
1325 assert_not_nil issue
1337 assert_not_nil issue
1326 assert_equal group, issue.assigned_to
1338 assert_equal group, issue.assigned_to
1327 end
1339 end
1328
1340
1329 def test_post_create_without_start_date_and_default_start_date_is_not_creation_date
1341 def test_post_create_without_start_date_and_default_start_date_is_not_creation_date
1330 Setting.default_issue_start_date_to_creation_date = 0
1342 Setting.default_issue_start_date_to_creation_date = 0
1331
1343
1332 @request.session[:user_id] = 2
1344 @request.session[:user_id] = 2
1333 assert_difference 'Issue.count' do
1345 assert_difference 'Issue.count' do
1334 post :create, :project_id => 1,
1346 post :create, :project_id => 1,
1335 :issue => {:tracker_id => 3,
1347 :issue => {:tracker_id => 3,
1336 :status_id => 2,
1348 :status_id => 2,
1337 :subject => 'This is the test_new issue',
1349 :subject => 'This is the test_new issue',
1338 :description => 'This is the description',
1350 :description => 'This is the description',
1339 :priority_id => 5,
1351 :priority_id => 5,
1340 :estimated_hours => '',
1352 :estimated_hours => '',
1341 :custom_field_values => {'2' => 'Value for field 2'}}
1353 :custom_field_values => {'2' => 'Value for field 2'}}
1342 end
1354 end
1343 assert_redirected_to :controller => 'issues', :action => 'show', :id => Issue.last.id
1355 assert_redirected_to :controller => 'issues', :action => 'show', :id => Issue.last.id
1344
1356
1345 issue = Issue.find_by_subject('This is the test_new issue')
1357 issue = Issue.find_by_subject('This is the test_new issue')
1346 assert_not_nil issue
1358 assert_not_nil issue
1347 assert_nil issue.start_date
1359 assert_nil issue.start_date
1348 end
1360 end
1349
1361
1350 def test_post_create_without_start_date_and_default_start_date_is_creation_date
1362 def test_post_create_without_start_date_and_default_start_date_is_creation_date
1351 Setting.default_issue_start_date_to_creation_date = 1
1363 Setting.default_issue_start_date_to_creation_date = 1
1352
1364
1353 @request.session[:user_id] = 2
1365 @request.session[:user_id] = 2
1354 assert_difference 'Issue.count' do
1366 assert_difference 'Issue.count' do
1355 post :create, :project_id => 1,
1367 post :create, :project_id => 1,
1356 :issue => {:tracker_id => 3,
1368 :issue => {:tracker_id => 3,
1357 :status_id => 2,
1369 :status_id => 2,
1358 :subject => 'This is the test_new issue',
1370 :subject => 'This is the test_new issue',
1359 :description => 'This is the description',
1371 :description => 'This is the description',
1360 :priority_id => 5,
1372 :priority_id => 5,
1361 :estimated_hours => '',
1373 :estimated_hours => '',
1362 :custom_field_values => {'2' => 'Value for field 2'}}
1374 :custom_field_values => {'2' => 'Value for field 2'}}
1363 end
1375 end
1364 assert_redirected_to :controller => 'issues', :action => 'show', :id => Issue.last.id
1376 assert_redirected_to :controller => 'issues', :action => 'show', :id => Issue.last.id
1365
1377
1366 issue = Issue.find_by_subject('This is the test_new issue')
1378 issue = Issue.find_by_subject('This is the test_new issue')
1367 assert_not_nil issue
1379 assert_not_nil issue
1368 assert_equal Date.today, issue.start_date
1380 assert_equal Date.today, issue.start_date
1369 end
1381 end
1370
1382
1371 def test_post_create_and_continue
1383 def test_post_create_and_continue
1372 @request.session[:user_id] = 2
1384 @request.session[:user_id] = 2
1373 assert_difference 'Issue.count' do
1385 assert_difference 'Issue.count' do
1374 post :create, :project_id => 1,
1386 post :create, :project_id => 1,
1375 :issue => {:tracker_id => 3, :subject => 'This is first issue', :priority_id => 5},
1387 :issue => {:tracker_id => 3, :subject => 'This is first issue', :priority_id => 5},
1376 :continue => ''
1388 :continue => ''
1377 end
1389 end
1378
1390
1379 issue = Issue.first(:order => 'id DESC')
1391 issue = Issue.first(:order => 'id DESC')
1380 assert_redirected_to :controller => 'issues', :action => 'new', :project_id => 'ecookbook', :issue => {:tracker_id => 3}
1392 assert_redirected_to :controller => 'issues', :action => 'new', :project_id => 'ecookbook', :issue => {:tracker_id => 3}
1381 assert_not_nil flash[:notice], "flash was not set"
1393 assert_not_nil flash[:notice], "flash was not set"
1382 assert flash[:notice].include?("<a href='/issues/#{issue.id}'>##{issue.id}</a>"), "issue link not found in flash: #{flash[:notice]}"
1394 assert flash[:notice].include?("<a href='/issues/#{issue.id}'>##{issue.id}</a>"), "issue link not found in flash: #{flash[:notice]}"
1383 end
1395 end
1384
1396
1385 def test_post_create_without_custom_fields_param
1397 def test_post_create_without_custom_fields_param
1386 @request.session[:user_id] = 2
1398 @request.session[:user_id] = 2
1387 assert_difference 'Issue.count' do
1399 assert_difference 'Issue.count' do
1388 post :create, :project_id => 1,
1400 post :create, :project_id => 1,
1389 :issue => {:tracker_id => 1,
1401 :issue => {:tracker_id => 1,
1390 :subject => 'This is the test_new issue',
1402 :subject => 'This is the test_new issue',
1391 :description => 'This is the description',
1403 :description => 'This is the description',
1392 :priority_id => 5}
1404 :priority_id => 5}
1393 end
1405 end
1394 assert_redirected_to :controller => 'issues', :action => 'show', :id => Issue.last.id
1406 assert_redirected_to :controller => 'issues', :action => 'show', :id => Issue.last.id
1395 end
1407 end
1396
1408
1397 def test_post_create_with_multi_custom_field
1409 def test_post_create_with_multi_custom_field
1398 field = IssueCustomField.find_by_name('Database')
1410 field = IssueCustomField.find_by_name('Database')
1399 field.update_attribute(:multiple, true)
1411 field.update_attribute(:multiple, true)
1400
1412
1401 @request.session[:user_id] = 2
1413 @request.session[:user_id] = 2
1402 assert_difference 'Issue.count' do
1414 assert_difference 'Issue.count' do
1403 post :create, :project_id => 1,
1415 post :create, :project_id => 1,
1404 :issue => {:tracker_id => 1,
1416 :issue => {:tracker_id => 1,
1405 :subject => 'This is the test_new issue',
1417 :subject => 'This is the test_new issue',
1406 :description => 'This is the description',
1418 :description => 'This is the description',
1407 :priority_id => 5,
1419 :priority_id => 5,
1408 :custom_field_values => {'1' => ['', 'MySQL', 'Oracle']}}
1420 :custom_field_values => {'1' => ['', 'MySQL', 'Oracle']}}
1409 end
1421 end
1410 assert_response 302
1422 assert_response 302
1411 issue = Issue.first(:order => 'id DESC')
1423 issue = Issue.first(:order => 'id DESC')
1412 assert_equal ['MySQL', 'Oracle'], issue.custom_field_value(1).sort
1424 assert_equal ['MySQL', 'Oracle'], issue.custom_field_value(1).sort
1413 end
1425 end
1414
1426
1415 def test_post_create_with_empty_multi_custom_field
1427 def test_post_create_with_empty_multi_custom_field
1416 field = IssueCustomField.find_by_name('Database')
1428 field = IssueCustomField.find_by_name('Database')
1417 field.update_attribute(:multiple, true)
1429 field.update_attribute(:multiple, true)
1418
1430
1419 @request.session[:user_id] = 2
1431 @request.session[:user_id] = 2
1420 assert_difference 'Issue.count' do
1432 assert_difference 'Issue.count' do
1421 post :create, :project_id => 1,
1433 post :create, :project_id => 1,
1422 :issue => {:tracker_id => 1,
1434 :issue => {:tracker_id => 1,
1423 :subject => 'This is the test_new issue',
1435 :subject => 'This is the test_new issue',
1424 :description => 'This is the description',
1436 :description => 'This is the description',
1425 :priority_id => 5,
1437 :priority_id => 5,
1426 :custom_field_values => {'1' => ['']}}
1438 :custom_field_values => {'1' => ['']}}
1427 end
1439 end
1428 assert_response 302
1440 assert_response 302
1429 issue = Issue.first(:order => 'id DESC')
1441 issue = Issue.first(:order => 'id DESC')
1430 assert_equal [''], issue.custom_field_value(1).sort
1442 assert_equal [''], issue.custom_field_value(1).sort
1431 end
1443 end
1432
1444
1433 def test_post_create_with_multi_user_custom_field
1445 def test_post_create_with_multi_user_custom_field
1434 field = IssueCustomField.create!(:name => 'Multi user', :field_format => 'user', :multiple => true,
1446 field = IssueCustomField.create!(:name => 'Multi user', :field_format => 'user', :multiple => true,
1435 :tracker_ids => [1], :is_for_all => true)
1447 :tracker_ids => [1], :is_for_all => true)
1436
1448
1437 @request.session[:user_id] = 2
1449 @request.session[:user_id] = 2
1438 assert_difference 'Issue.count' do
1450 assert_difference 'Issue.count' do
1439 post :create, :project_id => 1,
1451 post :create, :project_id => 1,
1440 :issue => {:tracker_id => 1,
1452 :issue => {:tracker_id => 1,
1441 :subject => 'This is the test_new issue',
1453 :subject => 'This is the test_new issue',
1442 :description => 'This is the description',
1454 :description => 'This is the description',
1443 :priority_id => 5,
1455 :priority_id => 5,
1444 :custom_field_values => {field.id.to_s => ['', '2', '3']}}
1456 :custom_field_values => {field.id.to_s => ['', '2', '3']}}
1445 end
1457 end
1446 assert_response 302
1458 assert_response 302
1447 issue = Issue.first(:order => 'id DESC')
1459 issue = Issue.first(:order => 'id DESC')
1448 assert_equal ['2', '3'], issue.custom_field_value(field).sort
1460 assert_equal ['2', '3'], issue.custom_field_value(field).sort
1449 end
1461 end
1450
1462
1451 def test_post_create_with_required_custom_field_and_without_custom_fields_param
1463 def test_post_create_with_required_custom_field_and_without_custom_fields_param
1452 field = IssueCustomField.find_by_name('Database')
1464 field = IssueCustomField.find_by_name('Database')
1453 field.update_attribute(:is_required, true)
1465 field.update_attribute(:is_required, true)
1454
1466
1455 @request.session[:user_id] = 2
1467 @request.session[:user_id] = 2
1456 assert_no_difference 'Issue.count' do
1468 assert_no_difference 'Issue.count' do
1457 post :create, :project_id => 1,
1469 post :create, :project_id => 1,
1458 :issue => {:tracker_id => 1,
1470 :issue => {:tracker_id => 1,
1459 :subject => 'This is the test_new issue',
1471 :subject => 'This is the test_new issue',
1460 :description => 'This is the description',
1472 :description => 'This is the description',
1461 :priority_id => 5}
1473 :priority_id => 5}
1462 end
1474 end
1463 assert_response :success
1475 assert_response :success
1464 assert_template 'new'
1476 assert_template 'new'
1465 issue = assigns(:issue)
1477 issue = assigns(:issue)
1466 assert_not_nil issue
1478 assert_not_nil issue
1467 assert_error_tag :content => /Database can't be blank/
1479 assert_error_tag :content => /Database can't be blank/
1468 end
1480 end
1469
1481
1470 def test_post_create_with_watchers
1482 def test_post_create_with_watchers
1471 @request.session[:user_id] = 2
1483 @request.session[:user_id] = 2
1472 ActionMailer::Base.deliveries.clear
1484 ActionMailer::Base.deliveries.clear
1473
1485
1474 assert_difference 'Watcher.count', 2 do
1486 assert_difference 'Watcher.count', 2 do
1475 post :create, :project_id => 1,
1487 post :create, :project_id => 1,
1476 :issue => {:tracker_id => 1,
1488 :issue => {:tracker_id => 1,
1477 :subject => 'This is a new issue with watchers',
1489 :subject => 'This is a new issue with watchers',
1478 :description => 'This is the description',
1490 :description => 'This is the description',
1479 :priority_id => 5,
1491 :priority_id => 5,
1480 :watcher_user_ids => ['2', '3']}
1492 :watcher_user_ids => ['2', '3']}
1481 end
1493 end
1482 issue = Issue.find_by_subject('This is a new issue with watchers')
1494 issue = Issue.find_by_subject('This is a new issue with watchers')
1483 assert_not_nil issue
1495 assert_not_nil issue
1484 assert_redirected_to :controller => 'issues', :action => 'show', :id => issue
1496 assert_redirected_to :controller => 'issues', :action => 'show', :id => issue
1485
1497
1486 # Watchers added
1498 # Watchers added
1487 assert_equal [2, 3], issue.watcher_user_ids.sort
1499 assert_equal [2, 3], issue.watcher_user_ids.sort
1488 assert issue.watched_by?(User.find(3))
1500 assert issue.watched_by?(User.find(3))
1489 # Watchers notified
1501 # Watchers notified
1490 mail = ActionMailer::Base.deliveries.last
1502 mail = ActionMailer::Base.deliveries.last
1491 assert_kind_of TMail::Mail, mail
1503 assert_kind_of TMail::Mail, mail
1492 assert [mail.bcc, mail.cc].flatten.include?(User.find(3).mail)
1504 assert [mail.bcc, mail.cc].flatten.include?(User.find(3).mail)
1493 end
1505 end
1494
1506
1495 def test_post_create_subissue
1507 def test_post_create_subissue
1496 @request.session[:user_id] = 2
1508 @request.session[:user_id] = 2
1497
1509
1498 assert_difference 'Issue.count' do
1510 assert_difference 'Issue.count' do
1499 post :create, :project_id => 1,
1511 post :create, :project_id => 1,
1500 :issue => {:tracker_id => 1,
1512 :issue => {:tracker_id => 1,
1501 :subject => 'This is a child issue',
1513 :subject => 'This is a child issue',
1502 :parent_issue_id => 2}
1514 :parent_issue_id => 2}
1503 end
1515 end
1504 issue = Issue.find_by_subject('This is a child issue')
1516 issue = Issue.find_by_subject('This is a child issue')
1505 assert_not_nil issue
1517 assert_not_nil issue
1506 assert_equal Issue.find(2), issue.parent
1518 assert_equal Issue.find(2), issue.parent
1507 end
1519 end
1508
1520
1509 def test_post_create_subissue_with_non_numeric_parent_id
1521 def test_post_create_subissue_with_non_numeric_parent_id
1510 @request.session[:user_id] = 2
1522 @request.session[:user_id] = 2
1511
1523
1512 assert_difference 'Issue.count' do
1524 assert_difference 'Issue.count' do
1513 post :create, :project_id => 1,
1525 post :create, :project_id => 1,
1514 :issue => {:tracker_id => 1,
1526 :issue => {:tracker_id => 1,
1515 :subject => 'This is a child issue',
1527 :subject => 'This is a child issue',
1516 :parent_issue_id => 'ABC'}
1528 :parent_issue_id => 'ABC'}
1517 end
1529 end
1518 issue = Issue.find_by_subject('This is a child issue')
1530 issue = Issue.find_by_subject('This is a child issue')
1519 assert_not_nil issue
1531 assert_not_nil issue
1520 assert_nil issue.parent
1532 assert_nil issue.parent
1521 end
1533 end
1522
1534
1523 def test_post_create_private
1535 def test_post_create_private
1524 @request.session[:user_id] = 2
1536 @request.session[:user_id] = 2
1525
1537
1526 assert_difference 'Issue.count' do
1538 assert_difference 'Issue.count' do
1527 post :create, :project_id => 1,
1539 post :create, :project_id => 1,
1528 :issue => {:tracker_id => 1,
1540 :issue => {:tracker_id => 1,
1529 :subject => 'This is a private issue',
1541 :subject => 'This is a private issue',
1530 :is_private => '1'}
1542 :is_private => '1'}
1531 end
1543 end
1532 issue = Issue.first(:order => 'id DESC')
1544 issue = Issue.first(:order => 'id DESC')
1533 assert issue.is_private?
1545 assert issue.is_private?
1534 end
1546 end
1535
1547
1536 def test_post_create_private_with_set_own_issues_private_permission
1548 def test_post_create_private_with_set_own_issues_private_permission
1537 role = Role.find(1)
1549 role = Role.find(1)
1538 role.remove_permission! :set_issues_private
1550 role.remove_permission! :set_issues_private
1539 role.add_permission! :set_own_issues_private
1551 role.add_permission! :set_own_issues_private
1540
1552
1541 @request.session[:user_id] = 2
1553 @request.session[:user_id] = 2
1542
1554
1543 assert_difference 'Issue.count' do
1555 assert_difference 'Issue.count' do
1544 post :create, :project_id => 1,
1556 post :create, :project_id => 1,
1545 :issue => {:tracker_id => 1,
1557 :issue => {:tracker_id => 1,
1546 :subject => 'This is a private issue',
1558 :subject => 'This is a private issue',
1547 :is_private => '1'}
1559 :is_private => '1'}
1548 end
1560 end
1549 issue = Issue.first(:order => 'id DESC')
1561 issue = Issue.first(:order => 'id DESC')
1550 assert issue.is_private?
1562 assert issue.is_private?
1551 end
1563 end
1552
1564
1553 def test_post_create_should_send_a_notification
1565 def test_post_create_should_send_a_notification
1554 ActionMailer::Base.deliveries.clear
1566 ActionMailer::Base.deliveries.clear
1555 @request.session[:user_id] = 2
1567 @request.session[:user_id] = 2
1556 assert_difference 'Issue.count' do
1568 assert_difference 'Issue.count' do
1557 post :create, :project_id => 1,
1569 post :create, :project_id => 1,
1558 :issue => {:tracker_id => 3,
1570 :issue => {:tracker_id => 3,
1559 :subject => 'This is the test_new issue',
1571 :subject => 'This is the test_new issue',
1560 :description => 'This is the description',
1572 :description => 'This is the description',
1561 :priority_id => 5,
1573 :priority_id => 5,
1562 :estimated_hours => '',
1574 :estimated_hours => '',
1563 :custom_field_values => {'2' => 'Value for field 2'}}
1575 :custom_field_values => {'2' => 'Value for field 2'}}
1564 end
1576 end
1565 assert_redirected_to :controller => 'issues', :action => 'show', :id => Issue.last.id
1577 assert_redirected_to :controller => 'issues', :action => 'show', :id => Issue.last.id
1566
1578
1567 assert_equal 1, ActionMailer::Base.deliveries.size
1579 assert_equal 1, ActionMailer::Base.deliveries.size
1568 end
1580 end
1569
1581
1570 def test_post_create_should_preserve_fields_values_on_validation_failure
1582 def test_post_create_should_preserve_fields_values_on_validation_failure
1571 @request.session[:user_id] = 2
1583 @request.session[:user_id] = 2
1572 post :create, :project_id => 1,
1584 post :create, :project_id => 1,
1573 :issue => {:tracker_id => 1,
1585 :issue => {:tracker_id => 1,
1574 # empty subject
1586 # empty subject
1575 :subject => '',
1587 :subject => '',
1576 :description => 'This is a description',
1588 :description => 'This is a description',
1577 :priority_id => 6,
1589 :priority_id => 6,
1578 :custom_field_values => {'1' => 'Oracle', '2' => 'Value for field 2'}}
1590 :custom_field_values => {'1' => 'Oracle', '2' => 'Value for field 2'}}
1579 assert_response :success
1591 assert_response :success
1580 assert_template 'new'
1592 assert_template 'new'
1581
1593
1582 assert_tag :textarea, :attributes => { :name => 'issue[description]' },
1594 assert_tag :textarea, :attributes => { :name => 'issue[description]' },
1583 :content => 'This is a description'
1595 :content => 'This is a description'
1584 assert_tag :select, :attributes => { :name => 'issue[priority_id]' },
1596 assert_tag :select, :attributes => { :name => 'issue[priority_id]' },
1585 :child => { :tag => 'option', :attributes => { :selected => 'selected',
1597 :child => { :tag => 'option', :attributes => { :selected => 'selected',
1586 :value => '6' },
1598 :value => '6' },
1587 :content => 'High' }
1599 :content => 'High' }
1588 # Custom fields
1600 # Custom fields
1589 assert_tag :select, :attributes => { :name => 'issue[custom_field_values][1]' },
1601 assert_tag :select, :attributes => { :name => 'issue[custom_field_values][1]' },
1590 :child => { :tag => 'option', :attributes => { :selected => 'selected',
1602 :child => { :tag => 'option', :attributes => { :selected => 'selected',
1591 :value => 'Oracle' },
1603 :value => 'Oracle' },
1592 :content => 'Oracle' }
1604 :content => 'Oracle' }
1593 assert_tag :input, :attributes => { :name => 'issue[custom_field_values][2]',
1605 assert_tag :input, :attributes => { :name => 'issue[custom_field_values][2]',
1594 :value => 'Value for field 2'}
1606 :value => 'Value for field 2'}
1595 end
1607 end
1596
1608
1597 def test_post_create_should_ignore_non_safe_attributes
1609 def test_post_create_should_ignore_non_safe_attributes
1598 @request.session[:user_id] = 2
1610 @request.session[:user_id] = 2
1599 assert_nothing_raised do
1611 assert_nothing_raised do
1600 post :create, :project_id => 1, :issue => { :tracker => "A param can not be a Tracker" }
1612 post :create, :project_id => 1, :issue => { :tracker => "A param can not be a Tracker" }
1601 end
1613 end
1602 end
1614 end
1603
1615
1604 def test_post_create_with_attachment
1616 def test_post_create_with_attachment
1605 set_tmp_attachments_directory
1617 set_tmp_attachments_directory
1606 @request.session[:user_id] = 2
1618 @request.session[:user_id] = 2
1607
1619
1608 assert_difference 'Issue.count' do
1620 assert_difference 'Issue.count' do
1609 assert_difference 'Attachment.count' do
1621 assert_difference 'Attachment.count' do
1610 post :create, :project_id => 1,
1622 post :create, :project_id => 1,
1611 :issue => { :tracker_id => '1', :subject => 'With attachment' },
1623 :issue => { :tracker_id => '1', :subject => 'With attachment' },
1612 :attachments => {'1' => {'file' => uploaded_test_file('testfile.txt', 'text/plain'), 'description' => 'test file'}}
1624 :attachments => {'1' => {'file' => uploaded_test_file('testfile.txt', 'text/plain'), 'description' => 'test file'}}
1613 end
1625 end
1614 end
1626 end
1615
1627
1616 issue = Issue.first(:order => 'id DESC')
1628 issue = Issue.first(:order => 'id DESC')
1617 attachment = Attachment.first(:order => 'id DESC')
1629 attachment = Attachment.first(:order => 'id DESC')
1618
1630
1619 assert_equal issue, attachment.container
1631 assert_equal issue, attachment.container
1620 assert_equal 2, attachment.author_id
1632 assert_equal 2, attachment.author_id
1621 assert_equal 'testfile.txt', attachment.filename
1633 assert_equal 'testfile.txt', attachment.filename
1622 assert_equal 'text/plain', attachment.content_type
1634 assert_equal 'text/plain', attachment.content_type
1623 assert_equal 'test file', attachment.description
1635 assert_equal 'test file', attachment.description
1624 assert_equal 59, attachment.filesize
1636 assert_equal 59, attachment.filesize
1625 assert File.exists?(attachment.diskfile)
1637 assert File.exists?(attachment.diskfile)
1626 assert_equal 59, File.size(attachment.diskfile)
1638 assert_equal 59, File.size(attachment.diskfile)
1627 end
1639 end
1628
1640
1629 context "without workflow privilege" do
1641 context "without workflow privilege" do
1630 setup do
1642 setup do
1631 Workflow.delete_all(["role_id = ?", Role.anonymous.id])
1643 Workflow.delete_all(["role_id = ?", Role.anonymous.id])
1632 Role.anonymous.add_permission! :add_issues, :add_issue_notes
1644 Role.anonymous.add_permission! :add_issues, :add_issue_notes
1633 end
1645 end
1634
1646
1635 context "#new" do
1647 context "#new" do
1636 should "propose default status only" do
1648 should "propose default status only" do
1637 get :new, :project_id => 1
1649 get :new, :project_id => 1
1638 assert_response :success
1650 assert_response :success
1639 assert_template 'new'
1651 assert_template 'new'
1640 assert_tag :tag => 'select',
1652 assert_tag :tag => 'select',
1641 :attributes => {:name => 'issue[status_id]'},
1653 :attributes => {:name => 'issue[status_id]'},
1642 :children => {:count => 1},
1654 :children => {:count => 1},
1643 :child => {:tag => 'option', :attributes => {:value => IssueStatus.default.id.to_s}}
1655 :child => {:tag => 'option', :attributes => {:value => IssueStatus.default.id.to_s}}
1644 end
1656 end
1645
1657
1646 should "accept default status" do
1658 should "accept default status" do
1647 assert_difference 'Issue.count' do
1659 assert_difference 'Issue.count' do
1648 post :create, :project_id => 1,
1660 post :create, :project_id => 1,
1649 :issue => {:tracker_id => 1,
1661 :issue => {:tracker_id => 1,
1650 :subject => 'This is an issue',
1662 :subject => 'This is an issue',
1651 :status_id => 1}
1663 :status_id => 1}
1652 end
1664 end
1653 issue = Issue.last(:order => 'id')
1665 issue = Issue.last(:order => 'id')
1654 assert_equal IssueStatus.default, issue.status
1666 assert_equal IssueStatus.default, issue.status
1655 end
1667 end
1656
1668
1657 should "ignore unauthorized status" do
1669 should "ignore unauthorized status" do
1658 assert_difference 'Issue.count' do
1670 assert_difference 'Issue.count' do
1659 post :create, :project_id => 1,
1671 post :create, :project_id => 1,
1660 :issue => {:tracker_id => 1,
1672 :issue => {:tracker_id => 1,
1661 :subject => 'This is an issue',
1673 :subject => 'This is an issue',
1662 :status_id => 3}
1674 :status_id => 3}
1663 end
1675 end
1664 issue = Issue.last(:order => 'id')
1676 issue = Issue.last(:order => 'id')
1665 assert_equal IssueStatus.default, issue.status
1677 assert_equal IssueStatus.default, issue.status
1666 end
1678 end
1667 end
1679 end
1668
1680
1669 context "#update" do
1681 context "#update" do
1670 should "ignore status change" do
1682 should "ignore status change" do
1671 assert_difference 'Journal.count' do
1683 assert_difference 'Journal.count' do
1672 put :update, :id => 1, :notes => 'just trying', :issue => {:status_id => 3}
1684 put :update, :id => 1, :notes => 'just trying', :issue => {:status_id => 3}
1673 end
1685 end
1674 assert_equal 1, Issue.find(1).status_id
1686 assert_equal 1, Issue.find(1).status_id
1675 end
1687 end
1676
1688
1677 should "ignore attributes changes" do
1689 should "ignore attributes changes" do
1678 assert_difference 'Journal.count' do
1690 assert_difference 'Journal.count' do
1679 put :update, :id => 1, :notes => 'just trying', :issue => {:subject => 'changed', :assigned_to_id => 2}
1691 put :update, :id => 1, :notes => 'just trying', :issue => {:subject => 'changed', :assigned_to_id => 2}
1680 end
1692 end
1681 issue = Issue.find(1)
1693 issue = Issue.find(1)
1682 assert_equal "Can't print recipes", issue.subject
1694 assert_equal "Can't print recipes", issue.subject
1683 assert_nil issue.assigned_to
1695 assert_nil issue.assigned_to
1684 end
1696 end
1685 end
1697 end
1686 end
1698 end
1687
1699
1688 context "with workflow privilege" do
1700 context "with workflow privilege" do
1689 setup do
1701 setup do
1690 Workflow.delete_all(["role_id = ?", Role.anonymous.id])
1702 Workflow.delete_all(["role_id = ?", Role.anonymous.id])
1691 Workflow.create!(:role => Role.anonymous, :tracker_id => 1, :old_status_id => 1, :new_status_id => 3)
1703 Workflow.create!(:role => Role.anonymous, :tracker_id => 1, :old_status_id => 1, :new_status_id => 3)
1692 Workflow.create!(:role => Role.anonymous, :tracker_id => 1, :old_status_id => 1, :new_status_id => 4)
1704 Workflow.create!(:role => Role.anonymous, :tracker_id => 1, :old_status_id => 1, :new_status_id => 4)
1693 Role.anonymous.add_permission! :add_issues, :add_issue_notes
1705 Role.anonymous.add_permission! :add_issues, :add_issue_notes
1694 end
1706 end
1695
1707
1696 context "#update" do
1708 context "#update" do
1697 should "accept authorized status" do
1709 should "accept authorized status" do
1698 assert_difference 'Journal.count' do
1710 assert_difference 'Journal.count' do
1699 put :update, :id => 1, :notes => 'just trying', :issue => {:status_id => 3}
1711 put :update, :id => 1, :notes => 'just trying', :issue => {:status_id => 3}
1700 end
1712 end
1701 assert_equal 3, Issue.find(1).status_id
1713 assert_equal 3, Issue.find(1).status_id
1702 end
1714 end
1703
1715
1704 should "ignore unauthorized status" do
1716 should "ignore unauthorized status" do
1705 assert_difference 'Journal.count' do
1717 assert_difference 'Journal.count' do
1706 put :update, :id => 1, :notes => 'just trying', :issue => {:status_id => 2}
1718 put :update, :id => 1, :notes => 'just trying', :issue => {:status_id => 2}
1707 end
1719 end
1708 assert_equal 1, Issue.find(1).status_id
1720 assert_equal 1, Issue.find(1).status_id
1709 end
1721 end
1710
1722
1711 should "accept authorized attributes changes" do
1723 should "accept authorized attributes changes" do
1712 assert_difference 'Journal.count' do
1724 assert_difference 'Journal.count' do
1713 put :update, :id => 1, :notes => 'just trying', :issue => {:assigned_to_id => 2}
1725 put :update, :id => 1, :notes => 'just trying', :issue => {:assigned_to_id => 2}
1714 end
1726 end
1715 issue = Issue.find(1)
1727 issue = Issue.find(1)
1716 assert_equal 2, issue.assigned_to_id
1728 assert_equal 2, issue.assigned_to_id
1717 end
1729 end
1718
1730
1719 should "ignore unauthorized attributes changes" do
1731 should "ignore unauthorized attributes changes" do
1720 assert_difference 'Journal.count' do
1732 assert_difference 'Journal.count' do
1721 put :update, :id => 1, :notes => 'just trying', :issue => {:subject => 'changed'}
1733 put :update, :id => 1, :notes => 'just trying', :issue => {:subject => 'changed'}
1722 end
1734 end
1723 issue = Issue.find(1)
1735 issue = Issue.find(1)
1724 assert_equal "Can't print recipes", issue.subject
1736 assert_equal "Can't print recipes", issue.subject
1725 end
1737 end
1726 end
1738 end
1727
1739
1728 context "and :edit_issues permission" do
1740 context "and :edit_issues permission" do
1729 setup do
1741 setup do
1730 Role.anonymous.add_permission! :add_issues, :edit_issues
1742 Role.anonymous.add_permission! :add_issues, :edit_issues
1731 end
1743 end
1732
1744
1733 should "accept authorized status" do
1745 should "accept authorized status" do
1734 assert_difference 'Journal.count' do
1746 assert_difference 'Journal.count' do
1735 put :update, :id => 1, :notes => 'just trying', :issue => {:status_id => 3}
1747 put :update, :id => 1, :notes => 'just trying', :issue => {:status_id => 3}
1736 end
1748 end
1737 assert_equal 3, Issue.find(1).status_id
1749 assert_equal 3, Issue.find(1).status_id
1738 end
1750 end
1739
1751
1740 should "ignore unauthorized status" do
1752 should "ignore unauthorized status" do
1741 assert_difference 'Journal.count' do
1753 assert_difference 'Journal.count' do
1742 put :update, :id => 1, :notes => 'just trying', :issue => {:status_id => 2}
1754 put :update, :id => 1, :notes => 'just trying', :issue => {:status_id => 2}
1743 end
1755 end
1744 assert_equal 1, Issue.find(1).status_id
1756 assert_equal 1, Issue.find(1).status_id
1745 end
1757 end
1746
1758
1747 should "accept authorized attributes changes" do
1759 should "accept authorized attributes changes" do
1748 assert_difference 'Journal.count' do
1760 assert_difference 'Journal.count' do
1749 put :update, :id => 1, :notes => 'just trying', :issue => {:subject => 'changed', :assigned_to_id => 2}
1761 put :update, :id => 1, :notes => 'just trying', :issue => {:subject => 'changed', :assigned_to_id => 2}
1750 end
1762 end
1751 issue = Issue.find(1)
1763 issue = Issue.find(1)
1752 assert_equal "changed", issue.subject
1764 assert_equal "changed", issue.subject
1753 assert_equal 2, issue.assigned_to_id
1765 assert_equal 2, issue.assigned_to_id
1754 end
1766 end
1755 end
1767 end
1756 end
1768 end
1757
1769
1758 def test_new_as_copy
1770 def test_new_as_copy
1759 @request.session[:user_id] = 2
1771 @request.session[:user_id] = 2
1760 get :new, :project_id => 1, :copy_from => 1
1772 get :new, :project_id => 1, :copy_from => 1
1761
1773
1762 assert_response :success
1774 assert_response :success
1763 assert_template 'new'
1775 assert_template 'new'
1764
1776
1765 assert_not_nil assigns(:issue)
1777 assert_not_nil assigns(:issue)
1766 orig = Issue.find(1)
1778 orig = Issue.find(1)
1767 assert_equal 1, assigns(:issue).project_id
1779 assert_equal 1, assigns(:issue).project_id
1768 assert_equal orig.subject, assigns(:issue).subject
1780 assert_equal orig.subject, assigns(:issue).subject
1769 assert assigns(:issue).copy?
1781 assert assigns(:issue).copy?
1770
1782
1771 assert_tag 'form', :attributes => {:id => 'issue-form', :action => '/projects/ecookbook/issues'}
1783 assert_tag 'form', :attributes => {:id => 'issue-form', :action => '/projects/ecookbook/issues'}
1772 assert_tag 'select', :attributes => {:name => 'issue[project_id]'}
1784 assert_tag 'select', :attributes => {:name => 'issue[project_id]'}
1773 assert_tag 'select', :attributes => {:name => 'issue[project_id]'},
1785 assert_tag 'select', :attributes => {:name => 'issue[project_id]'},
1774 :child => {:tag => 'option', :attributes => {:value => '1', :selected => 'selected'}, :content => 'eCookbook'}
1786 :child => {:tag => 'option', :attributes => {:value => '1', :selected => 'selected'}, :content => 'eCookbook'}
1775 assert_tag 'select', :attributes => {:name => 'issue[project_id]'},
1787 assert_tag 'select', :attributes => {:name => 'issue[project_id]'},
1776 :child => {:tag => 'option', :attributes => {:value => '2', :selected => nil}, :content => 'OnlineStore'}
1788 :child => {:tag => 'option', :attributes => {:value => '2', :selected => nil}, :content => 'OnlineStore'}
1777 assert_tag 'input', :attributes => {:name => 'copy_from', :value => '1'}
1789 assert_tag 'input', :attributes => {:name => 'copy_from', :value => '1'}
1778 end
1790 end
1779
1791
1780 def test_new_as_copy_with_attachments_should_show_copy_attachments_checkbox
1792 def test_new_as_copy_with_attachments_should_show_copy_attachments_checkbox
1781 @request.session[:user_id] = 2
1793 @request.session[:user_id] = 2
1782 issue = Issue.find(3)
1794 issue = Issue.find(3)
1783 assert issue.attachments.count > 0
1795 assert issue.attachments.count > 0
1784 get :new, :project_id => 1, :copy_from => 3
1796 get :new, :project_id => 1, :copy_from => 3
1785
1797
1786 assert_tag 'input', :attributes => {:name => 'copy_attachments', :type => 'checkbox', :checked => 'checked', :value => '1'}
1798 assert_tag 'input', :attributes => {:name => 'copy_attachments', :type => 'checkbox', :checked => 'checked', :value => '1'}
1787 end
1799 end
1788
1800
1789 def test_new_as_copy_without_attachments_should_not_show_copy_attachments_checkbox
1801 def test_new_as_copy_without_attachments_should_not_show_copy_attachments_checkbox
1790 @request.session[:user_id] = 2
1802 @request.session[:user_id] = 2
1791 issue = Issue.find(3)
1803 issue = Issue.find(3)
1792 issue.attachments.delete_all
1804 issue.attachments.delete_all
1793 get :new, :project_id => 1, :copy_from => 3
1805 get :new, :project_id => 1, :copy_from => 3
1794
1806
1795 assert_no_tag 'input', :attributes => {:name => 'copy_attachments', :type => 'checkbox', :checked => 'checked', :value => '1'}
1807 assert_no_tag 'input', :attributes => {:name => 'copy_attachments', :type => 'checkbox', :checked => 'checked', :value => '1'}
1796 end
1808 end
1797
1809
1798 def test_new_as_copy_with_invalid_issue_should_respond_with_404
1810 def test_new_as_copy_with_invalid_issue_should_respond_with_404
1799 @request.session[:user_id] = 2
1811 @request.session[:user_id] = 2
1800 get :new, :project_id => 1, :copy_from => 99999
1812 get :new, :project_id => 1, :copy_from => 99999
1801 assert_response 404
1813 assert_response 404
1802 end
1814 end
1803
1815
1804 def test_create_as_copy_on_different_project
1816 def test_create_as_copy_on_different_project
1805 @request.session[:user_id] = 2
1817 @request.session[:user_id] = 2
1806 assert_difference 'Issue.count' do
1818 assert_difference 'Issue.count' do
1807 post :create, :project_id => 1, :copy_from => 1,
1819 post :create, :project_id => 1, :copy_from => 1,
1808 :issue => {:project_id => '2', :tracker_id => '3', :status_id => '1', :subject => 'Copy'}
1820 :issue => {:project_id => '2', :tracker_id => '3', :status_id => '1', :subject => 'Copy'}
1809
1821
1810 assert_not_nil assigns(:issue)
1822 assert_not_nil assigns(:issue)
1811 assert assigns(:issue).copy?
1823 assert assigns(:issue).copy?
1812 end
1824 end
1813 issue = Issue.first(:order => 'id DESC')
1825 issue = Issue.first(:order => 'id DESC')
1814 assert_redirected_to "/issues/#{issue.id}"
1826 assert_redirected_to "/issues/#{issue.id}"
1815
1827
1816 assert_equal 2, issue.project_id
1828 assert_equal 2, issue.project_id
1817 assert_equal 3, issue.tracker_id
1829 assert_equal 3, issue.tracker_id
1818 assert_equal 'Copy', issue.subject
1830 assert_equal 'Copy', issue.subject
1819 end
1831 end
1820
1832
1821 def test_create_as_copy_should_copy_attachments
1833 def test_create_as_copy_should_copy_attachments
1822 @request.session[:user_id] = 2
1834 @request.session[:user_id] = 2
1823 issue = Issue.find(3)
1835 issue = Issue.find(3)
1824 count = issue.attachments.count
1836 count = issue.attachments.count
1825 assert count > 0
1837 assert count > 0
1826
1838
1827 assert_difference 'Issue.count' do
1839 assert_difference 'Issue.count' do
1828 assert_difference 'Attachment.count', count do
1840 assert_difference 'Attachment.count', count do
1829 assert_no_difference 'Journal.count' do
1841 assert_no_difference 'Journal.count' do
1830 post :create, :project_id => 1, :copy_from => 3,
1842 post :create, :project_id => 1, :copy_from => 3,
1831 :issue => {:project_id => '1', :tracker_id => '3', :status_id => '1', :subject => 'Copy with attachments'},
1843 :issue => {:project_id => '1', :tracker_id => '3', :status_id => '1', :subject => 'Copy with attachments'},
1832 :copy_attachments => '1'
1844 :copy_attachments => '1'
1833 end
1845 end
1834 end
1846 end
1835 end
1847 end
1836 copy = Issue.first(:order => 'id DESC')
1848 copy = Issue.first(:order => 'id DESC')
1837 assert_equal count, copy.attachments.count
1849 assert_equal count, copy.attachments.count
1838 assert_equal issue.attachments.map(&:filename).sort, copy.attachments.map(&:filename).sort
1850 assert_equal issue.attachments.map(&:filename).sort, copy.attachments.map(&:filename).sort
1839 end
1851 end
1840
1852
1841 def test_create_as_copy_without_copy_attachments_option_should_not_copy_attachments
1853 def test_create_as_copy_without_copy_attachments_option_should_not_copy_attachments
1842 @request.session[:user_id] = 2
1854 @request.session[:user_id] = 2
1843 issue = Issue.find(3)
1855 issue = Issue.find(3)
1844 count = issue.attachments.count
1856 count = issue.attachments.count
1845 assert count > 0
1857 assert count > 0
1846
1858
1847 assert_difference 'Issue.count' do
1859 assert_difference 'Issue.count' do
1848 assert_no_difference 'Attachment.count' do
1860 assert_no_difference 'Attachment.count' do
1849 assert_no_difference 'Journal.count' do
1861 assert_no_difference 'Journal.count' do
1850 post :create, :project_id => 1, :copy_from => 3,
1862 post :create, :project_id => 1, :copy_from => 3,
1851 :issue => {:project_id => '1', :tracker_id => '3', :status_id => '1', :subject => 'Copy with attachments'}
1863 :issue => {:project_id => '1', :tracker_id => '3', :status_id => '1', :subject => 'Copy with attachments'}
1852 end
1864 end
1853 end
1865 end
1854 end
1866 end
1855 copy = Issue.first(:order => 'id DESC')
1867 copy = Issue.first(:order => 'id DESC')
1856 assert_equal 0, copy.attachments.count
1868 assert_equal 0, copy.attachments.count
1857 end
1869 end
1858
1870
1859 def test_create_as_copy_with_attachments_should_add_new_files
1871 def test_create_as_copy_with_attachments_should_add_new_files
1860 @request.session[:user_id] = 2
1872 @request.session[:user_id] = 2
1861 issue = Issue.find(3)
1873 issue = Issue.find(3)
1862 count = issue.attachments.count
1874 count = issue.attachments.count
1863 assert count > 0
1875 assert count > 0
1864
1876
1865 assert_difference 'Issue.count' do
1877 assert_difference 'Issue.count' do
1866 assert_difference 'Attachment.count', count + 1 do
1878 assert_difference 'Attachment.count', count + 1 do
1867 assert_no_difference 'Journal.count' do
1879 assert_no_difference 'Journal.count' do
1868 post :create, :project_id => 1, :copy_from => 3,
1880 post :create, :project_id => 1, :copy_from => 3,
1869 :issue => {:project_id => '1', :tracker_id => '3', :status_id => '1', :subject => 'Copy with attachments'},
1881 :issue => {:project_id => '1', :tracker_id => '3', :status_id => '1', :subject => 'Copy with attachments'},
1870 :copy_attachments => '1',
1882 :copy_attachments => '1',
1871 :attachments => {'1' => {'file' => uploaded_test_file('testfile.txt', 'text/plain'), 'description' => 'test file'}}
1883 :attachments => {'1' => {'file' => uploaded_test_file('testfile.txt', 'text/plain'), 'description' => 'test file'}}
1872 end
1884 end
1873 end
1885 end
1874 end
1886 end
1875 copy = Issue.first(:order => 'id DESC')
1887 copy = Issue.first(:order => 'id DESC')
1876 assert_equal count + 1, copy.attachments.count
1888 assert_equal count + 1, copy.attachments.count
1877 end
1889 end
1878
1890
1879 def test_create_as_copy_with_failure
1891 def test_create_as_copy_with_failure
1880 @request.session[:user_id] = 2
1892 @request.session[:user_id] = 2
1881 post :create, :project_id => 1, :copy_from => 1,
1893 post :create, :project_id => 1, :copy_from => 1,
1882 :issue => {:project_id => '2', :tracker_id => '3', :status_id => '1', :subject => ''}
1894 :issue => {:project_id => '2', :tracker_id => '3', :status_id => '1', :subject => ''}
1883
1895
1884 assert_response :success
1896 assert_response :success
1885 assert_template 'new'
1897 assert_template 'new'
1886
1898
1887 assert_not_nil assigns(:issue)
1899 assert_not_nil assigns(:issue)
1888 assert assigns(:issue).copy?
1900 assert assigns(:issue).copy?
1889
1901
1890 assert_tag 'form', :attributes => {:id => 'issue-form', :action => '/projects/ecookbook/issues'}
1902 assert_tag 'form', :attributes => {:id => 'issue-form', :action => '/projects/ecookbook/issues'}
1891 assert_tag 'select', :attributes => {:name => 'issue[project_id]'}
1903 assert_tag 'select', :attributes => {:name => 'issue[project_id]'}
1892 assert_tag 'select', :attributes => {:name => 'issue[project_id]'},
1904 assert_tag 'select', :attributes => {:name => 'issue[project_id]'},
1893 :child => {:tag => 'option', :attributes => {:value => '1', :selected => nil}, :content => 'eCookbook'}
1905 :child => {:tag => 'option', :attributes => {:value => '1', :selected => nil}, :content => 'eCookbook'}
1894 assert_tag 'select', :attributes => {:name => 'issue[project_id]'},
1906 assert_tag 'select', :attributes => {:name => 'issue[project_id]'},
1895 :child => {:tag => 'option', :attributes => {:value => '2', :selected => 'selected'}, :content => 'OnlineStore'}
1907 :child => {:tag => 'option', :attributes => {:value => '2', :selected => 'selected'}, :content => 'OnlineStore'}
1896 assert_tag 'input', :attributes => {:name => 'copy_from', :value => '1'}
1908 assert_tag 'input', :attributes => {:name => 'copy_from', :value => '1'}
1897 end
1909 end
1898
1910
1899 def test_create_as_copy_on_project_without_permission_should_ignore_target_project
1911 def test_create_as_copy_on_project_without_permission_should_ignore_target_project
1900 @request.session[:user_id] = 2
1912 @request.session[:user_id] = 2
1901 assert !User.find(2).member_of?(Project.find(4))
1913 assert !User.find(2).member_of?(Project.find(4))
1902
1914
1903 assert_difference 'Issue.count' do
1915 assert_difference 'Issue.count' do
1904 post :create, :project_id => 1, :copy_from => 1,
1916 post :create, :project_id => 1, :copy_from => 1,
1905 :issue => {:project_id => '4', :tracker_id => '3', :status_id => '1', :subject => 'Copy'}
1917 :issue => {:project_id => '4', :tracker_id => '3', :status_id => '1', :subject => 'Copy'}
1906 end
1918 end
1907 issue = Issue.first(:order => 'id DESC')
1919 issue = Issue.first(:order => 'id DESC')
1908 assert_equal 1, issue.project_id
1920 assert_equal 1, issue.project_id
1909 end
1921 end
1910
1922
1911 def test_get_edit
1923 def test_get_edit
1912 @request.session[:user_id] = 2
1924 @request.session[:user_id] = 2
1913 get :edit, :id => 1
1925 get :edit, :id => 1
1914 assert_response :success
1926 assert_response :success
1915 assert_template 'edit'
1927 assert_template 'edit'
1916 assert_not_nil assigns(:issue)
1928 assert_not_nil assigns(:issue)
1917 assert_equal Issue.find(1), assigns(:issue)
1929 assert_equal Issue.find(1), assigns(:issue)
1918
1930
1919 # Be sure we don't display inactive IssuePriorities
1931 # Be sure we don't display inactive IssuePriorities
1920 assert ! IssuePriority.find(15).active?
1932 assert ! IssuePriority.find(15).active?
1921 assert_no_tag :option, :attributes => {:value => '15'},
1933 assert_no_tag :option, :attributes => {:value => '15'},
1922 :parent => {:tag => 'select', :attributes => {:id => 'issue_priority_id'} }
1934 :parent => {:tag => 'select', :attributes => {:id => 'issue_priority_id'} }
1923 end
1935 end
1924
1936
1925 def test_get_edit_should_display_the_time_entry_form_with_log_time_permission
1937 def test_get_edit_should_display_the_time_entry_form_with_log_time_permission
1926 @request.session[:user_id] = 2
1938 @request.session[:user_id] = 2
1927 Role.find_by_name('Manager').update_attribute :permissions, [:view_issues, :edit_issues, :log_time]
1939 Role.find_by_name('Manager').update_attribute :permissions, [:view_issues, :edit_issues, :log_time]
1928
1940
1929 get :edit, :id => 1
1941 get :edit, :id => 1
1930 assert_tag 'input', :attributes => {:name => 'time_entry[hours]'}
1942 assert_tag 'input', :attributes => {:name => 'time_entry[hours]'}
1931 end
1943 end
1932
1944
1933 def test_get_edit_should_not_display_the_time_entry_form_without_log_time_permission
1945 def test_get_edit_should_not_display_the_time_entry_form_without_log_time_permission
1934 @request.session[:user_id] = 2
1946 @request.session[:user_id] = 2
1935 Role.find_by_name('Manager').remove_permission! :log_time
1947 Role.find_by_name('Manager').remove_permission! :log_time
1936
1948
1937 get :edit, :id => 1
1949 get :edit, :id => 1
1938 assert_no_tag 'input', :attributes => {:name => 'time_entry[hours]'}
1950 assert_no_tag 'input', :attributes => {:name => 'time_entry[hours]'}
1939 end
1951 end
1940
1952
1941 def test_get_edit_with_params
1953 def test_get_edit_with_params
1942 @request.session[:user_id] = 2
1954 @request.session[:user_id] = 2
1943 get :edit, :id => 1, :issue => { :status_id => 5, :priority_id => 7 },
1955 get :edit, :id => 1, :issue => { :status_id => 5, :priority_id => 7 },
1944 :time_entry => { :hours => '2.5', :comments => 'test_get_edit_with_params', :activity_id => TimeEntryActivity.first.id }
1956 :time_entry => { :hours => '2.5', :comments => 'test_get_edit_with_params', :activity_id => TimeEntryActivity.first.id }
1945 assert_response :success
1957 assert_response :success
1946 assert_template 'edit'
1958 assert_template 'edit'
1947
1959
1948 issue = assigns(:issue)
1960 issue = assigns(:issue)
1949 assert_not_nil issue
1961 assert_not_nil issue
1950
1962
1951 assert_equal 5, issue.status_id
1963 assert_equal 5, issue.status_id
1952 assert_tag :select, :attributes => { :name => 'issue[status_id]' },
1964 assert_tag :select, :attributes => { :name => 'issue[status_id]' },
1953 :child => { :tag => 'option',
1965 :child => { :tag => 'option',
1954 :content => 'Closed',
1966 :content => 'Closed',
1955 :attributes => { :selected => 'selected' } }
1967 :attributes => { :selected => 'selected' } }
1956
1968
1957 assert_equal 7, issue.priority_id
1969 assert_equal 7, issue.priority_id
1958 assert_tag :select, :attributes => { :name => 'issue[priority_id]' },
1970 assert_tag :select, :attributes => { :name => 'issue[priority_id]' },
1959 :child => { :tag => 'option',
1971 :child => { :tag => 'option',
1960 :content => 'Urgent',
1972 :content => 'Urgent',
1961 :attributes => { :selected => 'selected' } }
1973 :attributes => { :selected => 'selected' } }
1962
1974
1963 assert_tag :input, :attributes => { :name => 'time_entry[hours]', :value => '2.5' }
1975 assert_tag :input, :attributes => { :name => 'time_entry[hours]', :value => '2.5' }
1964 assert_tag :select, :attributes => { :name => 'time_entry[activity_id]' },
1976 assert_tag :select, :attributes => { :name => 'time_entry[activity_id]' },
1965 :child => { :tag => 'option',
1977 :child => { :tag => 'option',
1966 :attributes => { :selected => 'selected', :value => TimeEntryActivity.first.id } }
1978 :attributes => { :selected => 'selected', :value => TimeEntryActivity.first.id } }
1967 assert_tag :input, :attributes => { :name => 'time_entry[comments]', :value => 'test_get_edit_with_params' }
1979 assert_tag :input, :attributes => { :name => 'time_entry[comments]', :value => 'test_get_edit_with_params' }
1968 end
1980 end
1969
1981
1970 def test_get_edit_with_multi_custom_field
1982 def test_get_edit_with_multi_custom_field
1971 field = CustomField.find(1)
1983 field = CustomField.find(1)
1972 field.update_attribute :multiple, true
1984 field.update_attribute :multiple, true
1973 issue = Issue.find(1)
1985 issue = Issue.find(1)
1974 issue.custom_field_values = {1 => ['MySQL', 'Oracle']}
1986 issue.custom_field_values = {1 => ['MySQL', 'Oracle']}
1975 issue.save!
1987 issue.save!
1976
1988
1977 @request.session[:user_id] = 2
1989 @request.session[:user_id] = 2
1978 get :edit, :id => 1
1990 get :edit, :id => 1
1979 assert_response :success
1991 assert_response :success
1980 assert_template 'edit'
1992 assert_template 'edit'
1981
1993
1982 assert_tag 'select', :attributes => {:name => 'issue[custom_field_values][1][]', :multiple => 'multiple'}
1994 assert_tag 'select', :attributes => {:name => 'issue[custom_field_values][1][]', :multiple => 'multiple'}
1983 assert_tag 'select', :attributes => {:name => 'issue[custom_field_values][1][]'},
1995 assert_tag 'select', :attributes => {:name => 'issue[custom_field_values][1][]'},
1984 :child => {:tag => 'option', :attributes => {:value => 'MySQL', :selected => 'selected'}}
1996 :child => {:tag => 'option', :attributes => {:value => 'MySQL', :selected => 'selected'}}
1985 assert_tag 'select', :attributes => {:name => 'issue[custom_field_values][1][]'},
1997 assert_tag 'select', :attributes => {:name => 'issue[custom_field_values][1][]'},
1986 :child => {:tag => 'option', :attributes => {:value => 'PostgreSQL', :selected => nil}}
1998 :child => {:tag => 'option', :attributes => {:value => 'PostgreSQL', :selected => nil}}
1987 assert_tag 'select', :attributes => {:name => 'issue[custom_field_values][1][]'},
1999 assert_tag 'select', :attributes => {:name => 'issue[custom_field_values][1][]'},
1988 :child => {:tag => 'option', :attributes => {:value => 'Oracle', :selected => 'selected'}}
2000 :child => {:tag => 'option', :attributes => {:value => 'Oracle', :selected => 'selected'}}
1989 end
2001 end
1990
2002
1991 def test_update_edit_form
2003 def test_update_edit_form
1992 @request.session[:user_id] = 2
2004 @request.session[:user_id] = 2
1993 xhr :put, :new, :project_id => 1,
2005 xhr :put, :new, :project_id => 1,
1994 :id => 1,
2006 :id => 1,
1995 :issue => {:tracker_id => 2,
2007 :issue => {:tracker_id => 2,
1996 :subject => 'This is the test_new issue',
2008 :subject => 'This is the test_new issue',
1997 :description => 'This is the description',
2009 :description => 'This is the description',
1998 :priority_id => 5}
2010 :priority_id => 5}
1999 assert_response :success
2011 assert_response :success
2000 assert_template 'attributes'
2012 assert_template 'attributes'
2001
2013
2002 issue = assigns(:issue)
2014 issue = assigns(:issue)
2003 assert_kind_of Issue, issue
2015 assert_kind_of Issue, issue
2004 assert_equal 1, issue.id
2016 assert_equal 1, issue.id
2005 assert_equal 1, issue.project_id
2017 assert_equal 1, issue.project_id
2006 assert_equal 2, issue.tracker_id
2018 assert_equal 2, issue.tracker_id
2007 assert_equal 'This is the test_new issue', issue.subject
2019 assert_equal 'This is the test_new issue', issue.subject
2008 end
2020 end
2009
2021
2010 def test_update_edit_form_with_project_change
2022 def test_update_edit_form_with_project_change
2011 @request.session[:user_id] = 2
2023 @request.session[:user_id] = 2
2012 xhr :put, :new, :project_id => 1,
2024 xhr :put, :new, :project_id => 1,
2013 :id => 1,
2025 :id => 1,
2014 :project_change => '1',
2026 :project_change => '1',
2015 :issue => {:project_id => 2,
2027 :issue => {:project_id => 2,
2016 :tracker_id => 2,
2028 :tracker_id => 2,
2017 :subject => 'This is the test_new issue',
2029 :subject => 'This is the test_new issue',
2018 :description => 'This is the description',
2030 :description => 'This is the description',
2019 :priority_id => 5}
2031 :priority_id => 5}
2020 assert_response :success
2032 assert_response :success
2021 assert_template 'form'
2033 assert_template 'form'
2022
2034
2023 issue = assigns(:issue)
2035 issue = assigns(:issue)
2024 assert_kind_of Issue, issue
2036 assert_kind_of Issue, issue
2025 assert_equal 1, issue.id
2037 assert_equal 1, issue.id
2026 assert_equal 2, issue.project_id
2038 assert_equal 2, issue.project_id
2027 assert_equal 2, issue.tracker_id
2039 assert_equal 2, issue.tracker_id
2028 assert_equal 'This is the test_new issue', issue.subject
2040 assert_equal 'This is the test_new issue', issue.subject
2029 end
2041 end
2030
2042
2031 def test_update_using_invalid_http_verbs
2043 def test_update_using_invalid_http_verbs
2032 @request.session[:user_id] = 2
2044 @request.session[:user_id] = 2
2033 subject = 'Updated by an invalid http verb'
2045 subject = 'Updated by an invalid http verb'
2034
2046
2035 get :update, :id => 1, :issue => {:subject => subject}
2047 get :update, :id => 1, :issue => {:subject => subject}
2036 assert_not_equal subject, Issue.find(1).subject
2048 assert_not_equal subject, Issue.find(1).subject
2037
2049
2038 post :update, :id => 1, :issue => {:subject => subject}
2050 post :update, :id => 1, :issue => {:subject => subject}
2039 assert_not_equal subject, Issue.find(1).subject
2051 assert_not_equal subject, Issue.find(1).subject
2040
2052
2041 delete :update, :id => 1, :issue => {:subject => subject}
2053 delete :update, :id => 1, :issue => {:subject => subject}
2042 assert_not_equal subject, Issue.find(1).subject
2054 assert_not_equal subject, Issue.find(1).subject
2043 end
2055 end
2044
2056
2045 def test_put_update_without_custom_fields_param
2057 def test_put_update_without_custom_fields_param
2046 @request.session[:user_id] = 2
2058 @request.session[:user_id] = 2
2047 ActionMailer::Base.deliveries.clear
2059 ActionMailer::Base.deliveries.clear
2048
2060
2049 issue = Issue.find(1)
2061 issue = Issue.find(1)
2050 assert_equal '125', issue.custom_value_for(2).value
2062 assert_equal '125', issue.custom_value_for(2).value
2051 old_subject = issue.subject
2063 old_subject = issue.subject
2052 new_subject = 'Subject modified by IssuesControllerTest#test_post_edit'
2064 new_subject = 'Subject modified by IssuesControllerTest#test_post_edit'
2053
2065
2054 assert_difference('Journal.count') do
2066 assert_difference('Journal.count') do
2055 assert_difference('JournalDetail.count', 2) do
2067 assert_difference('JournalDetail.count', 2) do
2056 put :update, :id => 1, :issue => {:subject => new_subject,
2068 put :update, :id => 1, :issue => {:subject => new_subject,
2057 :priority_id => '6',
2069 :priority_id => '6',
2058 :category_id => '1' # no change
2070 :category_id => '1' # no change
2059 }
2071 }
2060 end
2072 end
2061 end
2073 end
2062 assert_redirected_to :action => 'show', :id => '1'
2074 assert_redirected_to :action => 'show', :id => '1'
2063 issue.reload
2075 issue.reload
2064 assert_equal new_subject, issue.subject
2076 assert_equal new_subject, issue.subject
2065 # Make sure custom fields were not cleared
2077 # Make sure custom fields were not cleared
2066 assert_equal '125', issue.custom_value_for(2).value
2078 assert_equal '125', issue.custom_value_for(2).value
2067
2079
2068 mail = ActionMailer::Base.deliveries.last
2080 mail = ActionMailer::Base.deliveries.last
2069 assert_kind_of TMail::Mail, mail
2081 assert_kind_of TMail::Mail, mail
2070 assert mail.subject.starts_with?("[#{issue.project.name} - #{issue.tracker.name} ##{issue.id}]")
2082 assert mail.subject.starts_with?("[#{issue.project.name} - #{issue.tracker.name} ##{issue.id}]")
2071 assert mail.body.include?("Subject changed from #{old_subject} to #{new_subject}")
2083 assert mail.body.include?("Subject changed from #{old_subject} to #{new_subject}")
2072 end
2084 end
2073
2085
2074 def test_put_update_with_project_change
2086 def test_put_update_with_project_change
2075 @request.session[:user_id] = 2
2087 @request.session[:user_id] = 2
2076 ActionMailer::Base.deliveries.clear
2088 ActionMailer::Base.deliveries.clear
2077
2089
2078 assert_difference('Journal.count') do
2090 assert_difference('Journal.count') do
2079 assert_difference('JournalDetail.count', 3) do
2091 assert_difference('JournalDetail.count', 3) do
2080 put :update, :id => 1, :issue => {:project_id => '2',
2092 put :update, :id => 1, :issue => {:project_id => '2',
2081 :tracker_id => '1', # no change
2093 :tracker_id => '1', # no change
2082 :priority_id => '6',
2094 :priority_id => '6',
2083 :category_id => '3'
2095 :category_id => '3'
2084 }
2096 }
2085 end
2097 end
2086 end
2098 end
2087 assert_redirected_to :action => 'show', :id => '1'
2099 assert_redirected_to :action => 'show', :id => '1'
2088 issue = Issue.find(1)
2100 issue = Issue.find(1)
2089 assert_equal 2, issue.project_id
2101 assert_equal 2, issue.project_id
2090 assert_equal 1, issue.tracker_id
2102 assert_equal 1, issue.tracker_id
2091 assert_equal 6, issue.priority_id
2103 assert_equal 6, issue.priority_id
2092 assert_equal 3, issue.category_id
2104 assert_equal 3, issue.category_id
2093
2105
2094 mail = ActionMailer::Base.deliveries.last
2106 mail = ActionMailer::Base.deliveries.last
2095 assert_not_nil mail
2107 assert_not_nil mail
2096 assert mail.subject.starts_with?("[#{issue.project.name} - #{issue.tracker.name} ##{issue.id}]")
2108 assert mail.subject.starts_with?("[#{issue.project.name} - #{issue.tracker.name} ##{issue.id}]")
2097 assert mail.body.include?("Project changed from eCookbook to OnlineStore")
2109 assert mail.body.include?("Project changed from eCookbook to OnlineStore")
2098 end
2110 end
2099
2111
2100 def test_put_update_with_tracker_change
2112 def test_put_update_with_tracker_change
2101 @request.session[:user_id] = 2
2113 @request.session[:user_id] = 2
2102 ActionMailer::Base.deliveries.clear
2114 ActionMailer::Base.deliveries.clear
2103
2115
2104 assert_difference('Journal.count') do
2116 assert_difference('Journal.count') do
2105 assert_difference('JournalDetail.count', 2) do
2117 assert_difference('JournalDetail.count', 2) do
2106 put :update, :id => 1, :issue => {:project_id => '1',
2118 put :update, :id => 1, :issue => {:project_id => '1',
2107 :tracker_id => '2',
2119 :tracker_id => '2',
2108 :priority_id => '6'
2120 :priority_id => '6'
2109 }
2121 }
2110 end
2122 end
2111 end
2123 end
2112 assert_redirected_to :action => 'show', :id => '1'
2124 assert_redirected_to :action => 'show', :id => '1'
2113 issue = Issue.find(1)
2125 issue = Issue.find(1)
2114 assert_equal 1, issue.project_id
2126 assert_equal 1, issue.project_id
2115 assert_equal 2, issue.tracker_id
2127 assert_equal 2, issue.tracker_id
2116 assert_equal 6, issue.priority_id
2128 assert_equal 6, issue.priority_id
2117 assert_equal 1, issue.category_id
2129 assert_equal 1, issue.category_id
2118
2130
2119 mail = ActionMailer::Base.deliveries.last
2131 mail = ActionMailer::Base.deliveries.last
2120 assert_not_nil mail
2132 assert_not_nil mail
2121 assert mail.subject.starts_with?("[#{issue.project.name} - #{issue.tracker.name} ##{issue.id}]")
2133 assert mail.subject.starts_with?("[#{issue.project.name} - #{issue.tracker.name} ##{issue.id}]")
2122 assert mail.body.include?("Tracker changed from Bug to Feature request")
2134 assert mail.body.include?("Tracker changed from Bug to Feature request")
2123 end
2135 end
2124
2136
2125 def test_put_update_with_custom_field_change
2137 def test_put_update_with_custom_field_change
2126 @request.session[:user_id] = 2
2138 @request.session[:user_id] = 2
2127 issue = Issue.find(1)
2139 issue = Issue.find(1)
2128 assert_equal '125', issue.custom_value_for(2).value
2140 assert_equal '125', issue.custom_value_for(2).value
2129
2141
2130 assert_difference('Journal.count') do
2142 assert_difference('Journal.count') do
2131 assert_difference('JournalDetail.count', 3) do
2143 assert_difference('JournalDetail.count', 3) do
2132 put :update, :id => 1, :issue => {:subject => 'Custom field change',
2144 put :update, :id => 1, :issue => {:subject => 'Custom field change',
2133 :priority_id => '6',
2145 :priority_id => '6',
2134 :category_id => '1', # no change
2146 :category_id => '1', # no change
2135 :custom_field_values => { '2' => 'New custom value' }
2147 :custom_field_values => { '2' => 'New custom value' }
2136 }
2148 }
2137 end
2149 end
2138 end
2150 end
2139 assert_redirected_to :action => 'show', :id => '1'
2151 assert_redirected_to :action => 'show', :id => '1'
2140 issue.reload
2152 issue.reload
2141 assert_equal 'New custom value', issue.custom_value_for(2).value
2153 assert_equal 'New custom value', issue.custom_value_for(2).value
2142
2154
2143 mail = ActionMailer::Base.deliveries.last
2155 mail = ActionMailer::Base.deliveries.last
2144 assert_kind_of TMail::Mail, mail
2156 assert_kind_of TMail::Mail, mail
2145 assert mail.body.include?("Searchable field changed from 125 to New custom value")
2157 assert mail.body.include?("Searchable field changed from 125 to New custom value")
2146 end
2158 end
2147
2159
2148 def test_put_update_with_multi_custom_field_change
2160 def test_put_update_with_multi_custom_field_change
2149 field = CustomField.find(1)
2161 field = CustomField.find(1)
2150 field.update_attribute :multiple, true
2162 field.update_attribute :multiple, true
2151 issue = Issue.find(1)
2163 issue = Issue.find(1)
2152 issue.custom_field_values = {1 => ['MySQL', 'Oracle']}
2164 issue.custom_field_values = {1 => ['MySQL', 'Oracle']}
2153 issue.save!
2165 issue.save!
2154
2166
2155 @request.session[:user_id] = 2
2167 @request.session[:user_id] = 2
2156 assert_difference('Journal.count') do
2168 assert_difference('Journal.count') do
2157 assert_difference('JournalDetail.count', 3) do
2169 assert_difference('JournalDetail.count', 3) do
2158 put :update, :id => 1,
2170 put :update, :id => 1,
2159 :issue => {
2171 :issue => {
2160 :subject => 'Custom field change',
2172 :subject => 'Custom field change',
2161 :custom_field_values => { '1' => ['', 'Oracle', 'PostgreSQL'] }
2173 :custom_field_values => { '1' => ['', 'Oracle', 'PostgreSQL'] }
2162 }
2174 }
2163 end
2175 end
2164 end
2176 end
2165 assert_redirected_to :action => 'show', :id => '1'
2177 assert_redirected_to :action => 'show', :id => '1'
2166 assert_equal ['Oracle', 'PostgreSQL'], Issue.find(1).custom_field_value(1).sort
2178 assert_equal ['Oracle', 'PostgreSQL'], Issue.find(1).custom_field_value(1).sort
2167 end
2179 end
2168
2180
2169 def test_put_update_with_status_and_assignee_change
2181 def test_put_update_with_status_and_assignee_change
2170 issue = Issue.find(1)
2182 issue = Issue.find(1)
2171 assert_equal 1, issue.status_id
2183 assert_equal 1, issue.status_id
2172 @request.session[:user_id] = 2
2184 @request.session[:user_id] = 2
2173 assert_difference('TimeEntry.count', 0) do
2185 assert_difference('TimeEntry.count', 0) do
2174 put :update,
2186 put :update,
2175 :id => 1,
2187 :id => 1,
2176 :issue => { :status_id => 2, :assigned_to_id => 3 },
2188 :issue => { :status_id => 2, :assigned_to_id => 3 },
2177 :notes => 'Assigned to dlopper',
2189 :notes => 'Assigned to dlopper',
2178 :time_entry => { :hours => '', :comments => '', :activity_id => TimeEntryActivity.first }
2190 :time_entry => { :hours => '', :comments => '', :activity_id => TimeEntryActivity.first }
2179 end
2191 end
2180 assert_redirected_to :action => 'show', :id => '1'
2192 assert_redirected_to :action => 'show', :id => '1'
2181 issue.reload
2193 issue.reload
2182 assert_equal 2, issue.status_id
2194 assert_equal 2, issue.status_id
2183 j = Journal.find(:first, :order => 'id DESC')
2195 j = Journal.find(:first, :order => 'id DESC')
2184 assert_equal 'Assigned to dlopper', j.notes
2196 assert_equal 'Assigned to dlopper', j.notes
2185 assert_equal 2, j.details.size
2197 assert_equal 2, j.details.size
2186
2198
2187 mail = ActionMailer::Base.deliveries.last
2199 mail = ActionMailer::Base.deliveries.last
2188 assert mail.body.include?("Status changed from New to Assigned")
2200 assert mail.body.include?("Status changed from New to Assigned")
2189 # subject should contain the new status
2201 # subject should contain the new status
2190 assert mail.subject.include?("(#{ IssueStatus.find(2).name })")
2202 assert mail.subject.include?("(#{ IssueStatus.find(2).name })")
2191 end
2203 end
2192
2204
2193 def test_put_update_with_note_only
2205 def test_put_update_with_note_only
2194 notes = 'Note added by IssuesControllerTest#test_update_with_note_only'
2206 notes = 'Note added by IssuesControllerTest#test_update_with_note_only'
2195 # anonymous user
2207 # anonymous user
2196 put :update,
2208 put :update,
2197 :id => 1,
2209 :id => 1,
2198 :notes => notes
2210 :notes => notes
2199 assert_redirected_to :action => 'show', :id => '1'
2211 assert_redirected_to :action => 'show', :id => '1'
2200 j = Journal.find(:first, :order => 'id DESC')
2212 j = Journal.find(:first, :order => 'id DESC')
2201 assert_equal notes, j.notes
2213 assert_equal notes, j.notes
2202 assert_equal 0, j.details.size
2214 assert_equal 0, j.details.size
2203 assert_equal User.anonymous, j.user
2215 assert_equal User.anonymous, j.user
2204
2216
2205 mail = ActionMailer::Base.deliveries.last
2217 mail = ActionMailer::Base.deliveries.last
2206 assert mail.body.include?(notes)
2218 assert mail.body.include?(notes)
2207 end
2219 end
2208
2220
2209 def test_put_update_with_note_and_spent_time
2221 def test_put_update_with_note_and_spent_time
2210 @request.session[:user_id] = 2
2222 @request.session[:user_id] = 2
2211 spent_hours_before = Issue.find(1).spent_hours
2223 spent_hours_before = Issue.find(1).spent_hours
2212 assert_difference('TimeEntry.count') do
2224 assert_difference('TimeEntry.count') do
2213 put :update,
2225 put :update,
2214 :id => 1,
2226 :id => 1,
2215 :notes => '2.5 hours added',
2227 :notes => '2.5 hours added',
2216 :time_entry => { :hours => '2.5', :comments => 'test_put_update_with_note_and_spent_time', :activity_id => TimeEntryActivity.first.id }
2228 :time_entry => { :hours => '2.5', :comments => 'test_put_update_with_note_and_spent_time', :activity_id => TimeEntryActivity.first.id }
2217 end
2229 end
2218 assert_redirected_to :action => 'show', :id => '1'
2230 assert_redirected_to :action => 'show', :id => '1'
2219
2231
2220 issue = Issue.find(1)
2232 issue = Issue.find(1)
2221
2233
2222 j = Journal.find(:first, :order => 'id DESC')
2234 j = Journal.find(:first, :order => 'id DESC')
2223 assert_equal '2.5 hours added', j.notes
2235 assert_equal '2.5 hours added', j.notes
2224 assert_equal 0, j.details.size
2236 assert_equal 0, j.details.size
2225
2237
2226 t = issue.time_entries.find_by_comments('test_put_update_with_note_and_spent_time')
2238 t = issue.time_entries.find_by_comments('test_put_update_with_note_and_spent_time')
2227 assert_not_nil t
2239 assert_not_nil t
2228 assert_equal 2.5, t.hours
2240 assert_equal 2.5, t.hours
2229 assert_equal spent_hours_before + 2.5, issue.spent_hours
2241 assert_equal spent_hours_before + 2.5, issue.spent_hours
2230 end
2242 end
2231
2243
2232 def test_put_update_with_attachment_only
2244 def test_put_update_with_attachment_only
2233 set_tmp_attachments_directory
2245 set_tmp_attachments_directory
2234
2246
2235 # Delete all fixtured journals, a race condition can occur causing the wrong
2247 # Delete all fixtured journals, a race condition can occur causing the wrong
2236 # journal to get fetched in the next find.
2248 # journal to get fetched in the next find.
2237 Journal.delete_all
2249 Journal.delete_all
2238
2250
2239 # anonymous user
2251 # anonymous user
2240 assert_difference 'Attachment.count' do
2252 assert_difference 'Attachment.count' do
2241 put :update, :id => 1,
2253 put :update, :id => 1,
2242 :notes => '',
2254 :notes => '',
2243 :attachments => {'1' => {'file' => uploaded_test_file('testfile.txt', 'text/plain'), 'description' => 'test file'}}
2255 :attachments => {'1' => {'file' => uploaded_test_file('testfile.txt', 'text/plain'), 'description' => 'test file'}}
2244 end
2256 end
2245
2257
2246 assert_redirected_to :action => 'show', :id => '1'
2258 assert_redirected_to :action => 'show', :id => '1'
2247 j = Issue.find(1).journals.find(:first, :order => 'id DESC')
2259 j = Issue.find(1).journals.find(:first, :order => 'id DESC')
2248 assert j.notes.blank?
2260 assert j.notes.blank?
2249 assert_equal 1, j.details.size
2261 assert_equal 1, j.details.size
2250 assert_equal 'testfile.txt', j.details.first.value
2262 assert_equal 'testfile.txt', j.details.first.value
2251 assert_equal User.anonymous, j.user
2263 assert_equal User.anonymous, j.user
2252
2264
2253 attachment = Attachment.first(:order => 'id DESC')
2265 attachment = Attachment.first(:order => 'id DESC')
2254 assert_equal Issue.find(1), attachment.container
2266 assert_equal Issue.find(1), attachment.container
2255 assert_equal User.anonymous, attachment.author
2267 assert_equal User.anonymous, attachment.author
2256 assert_equal 'testfile.txt', attachment.filename
2268 assert_equal 'testfile.txt', attachment.filename
2257 assert_equal 'text/plain', attachment.content_type
2269 assert_equal 'text/plain', attachment.content_type
2258 assert_equal 'test file', attachment.description
2270 assert_equal 'test file', attachment.description
2259 assert_equal 59, attachment.filesize
2271 assert_equal 59, attachment.filesize
2260 assert File.exists?(attachment.diskfile)
2272 assert File.exists?(attachment.diskfile)
2261 assert_equal 59, File.size(attachment.diskfile)
2273 assert_equal 59, File.size(attachment.diskfile)
2262
2274
2263 mail = ActionMailer::Base.deliveries.last
2275 mail = ActionMailer::Base.deliveries.last
2264 assert mail.body.include?('testfile.txt')
2276 assert mail.body.include?('testfile.txt')
2265 end
2277 end
2266
2278
2267 def test_put_update_with_attachment_that_fails_to_save
2279 def test_put_update_with_attachment_that_fails_to_save
2268 set_tmp_attachments_directory
2280 set_tmp_attachments_directory
2269
2281
2270 # Delete all fixtured journals, a race condition can occur causing the wrong
2282 # Delete all fixtured journals, a race condition can occur causing the wrong
2271 # journal to get fetched in the next find.
2283 # journal to get fetched in the next find.
2272 Journal.delete_all
2284 Journal.delete_all
2273
2285
2274 # Mock out the unsaved attachment
2286 # Mock out the unsaved attachment
2275 Attachment.any_instance.stubs(:create).returns(Attachment.new)
2287 Attachment.any_instance.stubs(:create).returns(Attachment.new)
2276
2288
2277 # anonymous user
2289 # anonymous user
2278 put :update,
2290 put :update,
2279 :id => 1,
2291 :id => 1,
2280 :notes => '',
2292 :notes => '',
2281 :attachments => {'1' => {'file' => uploaded_test_file('testfile.txt', 'text/plain')}}
2293 :attachments => {'1' => {'file' => uploaded_test_file('testfile.txt', 'text/plain')}}
2282 assert_redirected_to :action => 'show', :id => '1'
2294 assert_redirected_to :action => 'show', :id => '1'
2283 assert_equal '1 file(s) could not be saved.', flash[:warning]
2295 assert_equal '1 file(s) could not be saved.', flash[:warning]
2284
2296
2285 end if Object.const_defined?(:Mocha)
2297 end if Object.const_defined?(:Mocha)
2286
2298
2287 def test_put_update_with_no_change
2299 def test_put_update_with_no_change
2288 issue = Issue.find(1)
2300 issue = Issue.find(1)
2289 issue.journals.clear
2301 issue.journals.clear
2290 ActionMailer::Base.deliveries.clear
2302 ActionMailer::Base.deliveries.clear
2291
2303
2292 put :update,
2304 put :update,
2293 :id => 1,
2305 :id => 1,
2294 :notes => ''
2306 :notes => ''
2295 assert_redirected_to :action => 'show', :id => '1'
2307 assert_redirected_to :action => 'show', :id => '1'
2296
2308
2297 issue.reload
2309 issue.reload
2298 assert issue.journals.empty?
2310 assert issue.journals.empty?
2299 # No email should be sent
2311 # No email should be sent
2300 assert ActionMailer::Base.deliveries.empty?
2312 assert ActionMailer::Base.deliveries.empty?
2301 end
2313 end
2302
2314
2303 def test_put_update_should_send_a_notification
2315 def test_put_update_should_send_a_notification
2304 @request.session[:user_id] = 2
2316 @request.session[:user_id] = 2
2305 ActionMailer::Base.deliveries.clear
2317 ActionMailer::Base.deliveries.clear
2306 issue = Issue.find(1)
2318 issue = Issue.find(1)
2307 old_subject = issue.subject
2319 old_subject = issue.subject
2308 new_subject = 'Subject modified by IssuesControllerTest#test_post_edit'
2320 new_subject = 'Subject modified by IssuesControllerTest#test_post_edit'
2309
2321
2310 put :update, :id => 1, :issue => {:subject => new_subject,
2322 put :update, :id => 1, :issue => {:subject => new_subject,
2311 :priority_id => '6',
2323 :priority_id => '6',
2312 :category_id => '1' # no change
2324 :category_id => '1' # no change
2313 }
2325 }
2314 assert_equal 1, ActionMailer::Base.deliveries.size
2326 assert_equal 1, ActionMailer::Base.deliveries.size
2315 end
2327 end
2316
2328
2317 def test_put_update_with_invalid_spent_time_hours_only
2329 def test_put_update_with_invalid_spent_time_hours_only
2318 @request.session[:user_id] = 2
2330 @request.session[:user_id] = 2
2319 notes = 'Note added by IssuesControllerTest#test_post_edit_with_invalid_spent_time'
2331 notes = 'Note added by IssuesControllerTest#test_post_edit_with_invalid_spent_time'
2320
2332
2321 assert_no_difference('Journal.count') do
2333 assert_no_difference('Journal.count') do
2322 put :update,
2334 put :update,
2323 :id => 1,
2335 :id => 1,
2324 :notes => notes,
2336 :notes => notes,
2325 :time_entry => {"comments"=>"", "activity_id"=>"", "hours"=>"2z"}
2337 :time_entry => {"comments"=>"", "activity_id"=>"", "hours"=>"2z"}
2326 end
2338 end
2327 assert_response :success
2339 assert_response :success
2328 assert_template 'edit'
2340 assert_template 'edit'
2329
2341
2330 assert_error_tag :descendant => {:content => /Activity can't be blank/}
2342 assert_error_tag :descendant => {:content => /Activity can't be blank/}
2331 assert_tag :textarea, :attributes => { :name => 'notes' }, :content => notes
2343 assert_tag :textarea, :attributes => { :name => 'notes' }, :content => notes
2332 assert_tag :input, :attributes => { :name => 'time_entry[hours]', :value => "2z" }
2344 assert_tag :input, :attributes => { :name => 'time_entry[hours]', :value => "2z" }
2333 end
2345 end
2334
2346
2335 def test_put_update_with_invalid_spent_time_comments_only
2347 def test_put_update_with_invalid_spent_time_comments_only
2336 @request.session[:user_id] = 2
2348 @request.session[:user_id] = 2
2337 notes = 'Note added by IssuesControllerTest#test_post_edit_with_invalid_spent_time'
2349 notes = 'Note added by IssuesControllerTest#test_post_edit_with_invalid_spent_time'
2338
2350
2339 assert_no_difference('Journal.count') do
2351 assert_no_difference('Journal.count') do
2340 put :update,
2352 put :update,
2341 :id => 1,
2353 :id => 1,
2342 :notes => notes,
2354 :notes => notes,
2343 :time_entry => {"comments"=>"this is my comment", "activity_id"=>"", "hours"=>""}
2355 :time_entry => {"comments"=>"this is my comment", "activity_id"=>"", "hours"=>""}
2344 end
2356 end
2345 assert_response :success
2357 assert_response :success
2346 assert_template 'edit'
2358 assert_template 'edit'
2347
2359
2348 assert_error_tag :descendant => {:content => /Activity can't be blank/}
2360 assert_error_tag :descendant => {:content => /Activity can't be blank/}
2349 assert_error_tag :descendant => {:content => /Hours can't be blank/}
2361 assert_error_tag :descendant => {:content => /Hours can't be blank/}
2350 assert_tag :textarea, :attributes => { :name => 'notes' }, :content => notes
2362 assert_tag :textarea, :attributes => { :name => 'notes' }, :content => notes
2351 assert_tag :input, :attributes => { :name => 'time_entry[comments]', :value => "this is my comment" }
2363 assert_tag :input, :attributes => { :name => 'time_entry[comments]', :value => "this is my comment" }
2352 end
2364 end
2353
2365
2354 def test_put_update_should_allow_fixed_version_to_be_set_to_a_subproject
2366 def test_put_update_should_allow_fixed_version_to_be_set_to_a_subproject
2355 issue = Issue.find(2)
2367 issue = Issue.find(2)
2356 @request.session[:user_id] = 2
2368 @request.session[:user_id] = 2
2357
2369
2358 put :update,
2370 put :update,
2359 :id => issue.id,
2371 :id => issue.id,
2360 :issue => {
2372 :issue => {
2361 :fixed_version_id => 4
2373 :fixed_version_id => 4
2362 }
2374 }
2363
2375
2364 assert_response :redirect
2376 assert_response :redirect
2365 issue.reload
2377 issue.reload
2366 assert_equal 4, issue.fixed_version_id
2378 assert_equal 4, issue.fixed_version_id
2367 assert_not_equal issue.project_id, issue.fixed_version.project_id
2379 assert_not_equal issue.project_id, issue.fixed_version.project_id
2368 end
2380 end
2369
2381
2370 def test_put_update_should_redirect_back_using_the_back_url_parameter
2382 def test_put_update_should_redirect_back_using_the_back_url_parameter
2371 issue = Issue.find(2)
2383 issue = Issue.find(2)
2372 @request.session[:user_id] = 2
2384 @request.session[:user_id] = 2
2373
2385
2374 put :update,
2386 put :update,
2375 :id => issue.id,
2387 :id => issue.id,
2376 :issue => {
2388 :issue => {
2377 :fixed_version_id => 4
2389 :fixed_version_id => 4
2378 },
2390 },
2379 :back_url => '/issues'
2391 :back_url => '/issues'
2380
2392
2381 assert_response :redirect
2393 assert_response :redirect
2382 assert_redirected_to '/issues'
2394 assert_redirected_to '/issues'
2383 end
2395 end
2384
2396
2385 def test_put_update_should_not_redirect_back_using_the_back_url_parameter_off_the_host
2397 def test_put_update_should_not_redirect_back_using_the_back_url_parameter_off_the_host
2386 issue = Issue.find(2)
2398 issue = Issue.find(2)
2387 @request.session[:user_id] = 2
2399 @request.session[:user_id] = 2
2388
2400
2389 put :update,
2401 put :update,
2390 :id => issue.id,
2402 :id => issue.id,
2391 :issue => {
2403 :issue => {
2392 :fixed_version_id => 4
2404 :fixed_version_id => 4
2393 },
2405 },
2394 :back_url => 'http://google.com'
2406 :back_url => 'http://google.com'
2395
2407
2396 assert_response :redirect
2408 assert_response :redirect
2397 assert_redirected_to :controller => 'issues', :action => 'show', :id => issue.id
2409 assert_redirected_to :controller => 'issues', :action => 'show', :id => issue.id
2398 end
2410 end
2399
2411
2400 def test_get_bulk_edit
2412 def test_get_bulk_edit
2401 @request.session[:user_id] = 2
2413 @request.session[:user_id] = 2
2402 get :bulk_edit, :ids => [1, 2]
2414 get :bulk_edit, :ids => [1, 2]
2403 assert_response :success
2415 assert_response :success
2404 assert_template 'bulk_edit'
2416 assert_template 'bulk_edit'
2405
2417
2406 assert_tag :select, :attributes => {:name => 'issue[project_id]'}
2418 assert_tag :select, :attributes => {:name => 'issue[project_id]'}
2407 assert_tag :input, :attributes => {:name => 'issue[parent_issue_id]'}
2419 assert_tag :input, :attributes => {:name => 'issue[parent_issue_id]'}
2408
2420
2409 # Project specific custom field, date type
2421 # Project specific custom field, date type
2410 field = CustomField.find(9)
2422 field = CustomField.find(9)
2411 assert !field.is_for_all?
2423 assert !field.is_for_all?
2412 assert_equal 'date', field.field_format
2424 assert_equal 'date', field.field_format
2413 assert_tag :input, :attributes => {:name => 'issue[custom_field_values][9]'}
2425 assert_tag :input, :attributes => {:name => 'issue[custom_field_values][9]'}
2414
2426
2415 # System wide custom field
2427 # System wide custom field
2416 assert CustomField.find(1).is_for_all?
2428 assert CustomField.find(1).is_for_all?
2417 assert_tag :select, :attributes => {:name => 'issue[custom_field_values][1]'}
2429 assert_tag :select, :attributes => {:name => 'issue[custom_field_values][1]'}
2418
2430
2419 # Be sure we don't display inactive IssuePriorities
2431 # Be sure we don't display inactive IssuePriorities
2420 assert ! IssuePriority.find(15).active?
2432 assert ! IssuePriority.find(15).active?
2421 assert_no_tag :option, :attributes => {:value => '15'},
2433 assert_no_tag :option, :attributes => {:value => '15'},
2422 :parent => {:tag => 'select', :attributes => {:id => 'issue_priority_id'} }
2434 :parent => {:tag => 'select', :attributes => {:id => 'issue_priority_id'} }
2423 end
2435 end
2424
2436
2425 def test_get_bulk_edit_on_different_projects
2437 def test_get_bulk_edit_on_different_projects
2426 @request.session[:user_id] = 2
2438 @request.session[:user_id] = 2
2427 get :bulk_edit, :ids => [1, 2, 6]
2439 get :bulk_edit, :ids => [1, 2, 6]
2428 assert_response :success
2440 assert_response :success
2429 assert_template 'bulk_edit'
2441 assert_template 'bulk_edit'
2430
2442
2431 # Can not set issues from different projects as children of an issue
2443 # Can not set issues from different projects as children of an issue
2432 assert_no_tag :input, :attributes => {:name => 'issue[parent_issue_id]'}
2444 assert_no_tag :input, :attributes => {:name => 'issue[parent_issue_id]'}
2433
2445
2434 # Project specific custom field, date type
2446 # Project specific custom field, date type
2435 field = CustomField.find(9)
2447 field = CustomField.find(9)
2436 assert !field.is_for_all?
2448 assert !field.is_for_all?
2437 assert !field.project_ids.include?(Issue.find(6).project_id)
2449 assert !field.project_ids.include?(Issue.find(6).project_id)
2438 assert_no_tag :input, :attributes => {:name => 'issue[custom_field_values][9]'}
2450 assert_no_tag :input, :attributes => {:name => 'issue[custom_field_values][9]'}
2439 end
2451 end
2440
2452
2441 def test_get_bulk_edit_with_user_custom_field
2453 def test_get_bulk_edit_with_user_custom_field
2442 field = IssueCustomField.create!(:name => 'Tester', :field_format => 'user', :is_for_all => true)
2454 field = IssueCustomField.create!(:name => 'Tester', :field_format => 'user', :is_for_all => true)
2443
2455
2444 @request.session[:user_id] = 2
2456 @request.session[:user_id] = 2
2445 get :bulk_edit, :ids => [1, 2]
2457 get :bulk_edit, :ids => [1, 2]
2446 assert_response :success
2458 assert_response :success
2447 assert_template 'bulk_edit'
2459 assert_template 'bulk_edit'
2448
2460
2449 assert_tag :select,
2461 assert_tag :select,
2450 :attributes => {:name => "issue[custom_field_values][#{field.id}]"},
2462 :attributes => {:name => "issue[custom_field_values][#{field.id}]"},
2451 :children => {
2463 :children => {
2452 :only => {:tag => 'option'},
2464 :only => {:tag => 'option'},
2453 :count => Project.find(1).users.count + 1
2465 :count => Project.find(1).users.count + 1
2454 }
2466 }
2455 end
2467 end
2456
2468
2457 def test_get_bulk_edit_with_version_custom_field
2469 def test_get_bulk_edit_with_version_custom_field
2458 field = IssueCustomField.create!(:name => 'Affected version', :field_format => 'version', :is_for_all => true)
2470 field = IssueCustomField.create!(:name => 'Affected version', :field_format => 'version', :is_for_all => true)
2459
2471
2460 @request.session[:user_id] = 2
2472 @request.session[:user_id] = 2
2461 get :bulk_edit, :ids => [1, 2]
2473 get :bulk_edit, :ids => [1, 2]
2462 assert_response :success
2474 assert_response :success
2463 assert_template 'bulk_edit'
2475 assert_template 'bulk_edit'
2464
2476
2465 assert_tag :select,
2477 assert_tag :select,
2466 :attributes => {:name => "issue[custom_field_values][#{field.id}]"},
2478 :attributes => {:name => "issue[custom_field_values][#{field.id}]"},
2467 :children => {
2479 :children => {
2468 :only => {:tag => 'option'},
2480 :only => {:tag => 'option'},
2469 :count => Project.find(1).shared_versions.count + 1
2481 :count => Project.find(1).shared_versions.count + 1
2470 }
2482 }
2471 end
2483 end
2472
2484
2473 def test_get_bulk_edit_with_multi_custom_field
2485 def test_get_bulk_edit_with_multi_custom_field
2474 field = CustomField.find(1)
2486 field = CustomField.find(1)
2475 field.update_attribute :multiple, true
2487 field.update_attribute :multiple, true
2476
2488
2477 @request.session[:user_id] = 2
2489 @request.session[:user_id] = 2
2478 get :bulk_edit, :ids => [1, 2]
2490 get :bulk_edit, :ids => [1, 2]
2479 assert_response :success
2491 assert_response :success
2480 assert_template 'bulk_edit'
2492 assert_template 'bulk_edit'
2481
2493
2482 assert_tag :select,
2494 assert_tag :select,
2483 :attributes => {:name => "issue[custom_field_values][1][]"},
2495 :attributes => {:name => "issue[custom_field_values][1][]"},
2484 :children => {
2496 :children => {
2485 :only => {:tag => 'option'},
2497 :only => {:tag => 'option'},
2486 :count => 3
2498 :count => 3
2487 }
2499 }
2488 end
2500 end
2489
2501
2490 def test_bulk_update
2502 def test_bulk_update
2491 @request.session[:user_id] = 2
2503 @request.session[:user_id] = 2
2492 # update issues priority
2504 # update issues priority
2493 post :bulk_update, :ids => [1, 2], :notes => 'Bulk editing',
2505 post :bulk_update, :ids => [1, 2], :notes => 'Bulk editing',
2494 :issue => {:priority_id => 7,
2506 :issue => {:priority_id => 7,
2495 :assigned_to_id => '',
2507 :assigned_to_id => '',
2496 :custom_field_values => {'2' => ''}}
2508 :custom_field_values => {'2' => ''}}
2497
2509
2498 assert_response 302
2510 assert_response 302
2499 # check that the issues were updated
2511 # check that the issues were updated
2500 assert_equal [7, 7], Issue.find_all_by_id([1, 2]).collect {|i| i.priority.id}
2512 assert_equal [7, 7], Issue.find_all_by_id([1, 2]).collect {|i| i.priority.id}
2501
2513
2502 issue = Issue.find(1)
2514 issue = Issue.find(1)
2503 journal = issue.journals.find(:first, :order => 'created_on DESC')
2515 journal = issue.journals.find(:first, :order => 'created_on DESC')
2504 assert_equal '125', issue.custom_value_for(2).value
2516 assert_equal '125', issue.custom_value_for(2).value
2505 assert_equal 'Bulk editing', journal.notes
2517 assert_equal 'Bulk editing', journal.notes
2506 assert_equal 1, journal.details.size
2518 assert_equal 1, journal.details.size
2507 end
2519 end
2508
2520
2509 def test_bulk_update_with_group_assignee
2521 def test_bulk_update_with_group_assignee
2510 group = Group.find(11)
2522 group = Group.find(11)
2511 project = Project.find(1)
2523 project = Project.find(1)
2512 project.members << Member.new(:principal => group, :roles => [Role.first])
2524 project.members << Member.new(:principal => group, :roles => [Role.first])
2513
2525
2514 @request.session[:user_id] = 2
2526 @request.session[:user_id] = 2
2515 # update issues assignee
2527 # update issues assignee
2516 post :bulk_update, :ids => [1, 2], :notes => 'Bulk editing',
2528 post :bulk_update, :ids => [1, 2], :notes => 'Bulk editing',
2517 :issue => {:priority_id => '',
2529 :issue => {:priority_id => '',
2518 :assigned_to_id => group.id,
2530 :assigned_to_id => group.id,
2519 :custom_field_values => {'2' => ''}}
2531 :custom_field_values => {'2' => ''}}
2520
2532
2521 assert_response 302
2533 assert_response 302
2522 assert_equal [group, group], Issue.find_all_by_id([1, 2]).collect {|i| i.assigned_to}
2534 assert_equal [group, group], Issue.find_all_by_id([1, 2]).collect {|i| i.assigned_to}
2523 end
2535 end
2524
2536
2525 def test_bulk_update_on_different_projects
2537 def test_bulk_update_on_different_projects
2526 @request.session[:user_id] = 2
2538 @request.session[:user_id] = 2
2527 # update issues priority
2539 # update issues priority
2528 post :bulk_update, :ids => [1, 2, 6], :notes => 'Bulk editing',
2540 post :bulk_update, :ids => [1, 2, 6], :notes => 'Bulk editing',
2529 :issue => {:priority_id => 7,
2541 :issue => {:priority_id => 7,
2530 :assigned_to_id => '',
2542 :assigned_to_id => '',
2531 :custom_field_values => {'2' => ''}}
2543 :custom_field_values => {'2' => ''}}
2532
2544
2533 assert_response 302
2545 assert_response 302
2534 # check that the issues were updated
2546 # check that the issues were updated
2535 assert_equal [7, 7, 7], Issue.find([1,2,6]).map(&:priority_id)
2547 assert_equal [7, 7, 7], Issue.find([1,2,6]).map(&:priority_id)
2536
2548
2537 issue = Issue.find(1)
2549 issue = Issue.find(1)
2538 journal = issue.journals.find(:first, :order => 'created_on DESC')
2550 journal = issue.journals.find(:first, :order => 'created_on DESC')
2539 assert_equal '125', issue.custom_value_for(2).value
2551 assert_equal '125', issue.custom_value_for(2).value
2540 assert_equal 'Bulk editing', journal.notes
2552 assert_equal 'Bulk editing', journal.notes
2541 assert_equal 1, journal.details.size
2553 assert_equal 1, journal.details.size
2542 end
2554 end
2543
2555
2544 def test_bulk_update_on_different_projects_without_rights
2556 def test_bulk_update_on_different_projects_without_rights
2545 @request.session[:user_id] = 3
2557 @request.session[:user_id] = 3
2546 user = User.find(3)
2558 user = User.find(3)
2547 action = { :controller => "issues", :action => "bulk_update" }
2559 action = { :controller => "issues", :action => "bulk_update" }
2548 assert user.allowed_to?(action, Issue.find(1).project)
2560 assert user.allowed_to?(action, Issue.find(1).project)
2549 assert ! user.allowed_to?(action, Issue.find(6).project)
2561 assert ! user.allowed_to?(action, Issue.find(6).project)
2550 post :bulk_update, :ids => [1, 6], :notes => 'Bulk should fail',
2562 post :bulk_update, :ids => [1, 6], :notes => 'Bulk should fail',
2551 :issue => {:priority_id => 7,
2563 :issue => {:priority_id => 7,
2552 :assigned_to_id => '',
2564 :assigned_to_id => '',
2553 :custom_field_values => {'2' => ''}}
2565 :custom_field_values => {'2' => ''}}
2554 assert_response 403
2566 assert_response 403
2555 assert_not_equal "Bulk should fail", Journal.last.notes
2567 assert_not_equal "Bulk should fail", Journal.last.notes
2556 end
2568 end
2557
2569
2558 def test_bullk_update_should_send_a_notification
2570 def test_bullk_update_should_send_a_notification
2559 @request.session[:user_id] = 2
2571 @request.session[:user_id] = 2
2560 ActionMailer::Base.deliveries.clear
2572 ActionMailer::Base.deliveries.clear
2561 post(:bulk_update,
2573 post(:bulk_update,
2562 {
2574 {
2563 :ids => [1, 2],
2575 :ids => [1, 2],
2564 :notes => 'Bulk editing',
2576 :notes => 'Bulk editing',
2565 :issue => {
2577 :issue => {
2566 :priority_id => 7,
2578 :priority_id => 7,
2567 :assigned_to_id => '',
2579 :assigned_to_id => '',
2568 :custom_field_values => {'2' => ''}
2580 :custom_field_values => {'2' => ''}
2569 }
2581 }
2570 })
2582 })
2571
2583
2572 assert_response 302
2584 assert_response 302
2573 assert_equal 2, ActionMailer::Base.deliveries.size
2585 assert_equal 2, ActionMailer::Base.deliveries.size
2574 end
2586 end
2575
2587
2576 def test_bulk_update_project
2588 def test_bulk_update_project
2577 @request.session[:user_id] = 2
2589 @request.session[:user_id] = 2
2578 post :bulk_update, :ids => [1, 2], :issue => {:project_id => '2'}
2590 post :bulk_update, :ids => [1, 2], :issue => {:project_id => '2'}
2579 assert_redirected_to :controller => 'issues', :action => 'index', :project_id => 'ecookbook'
2591 assert_redirected_to :controller => 'issues', :action => 'index', :project_id => 'ecookbook'
2580 # Issues moved to project 2
2592 # Issues moved to project 2
2581 assert_equal 2, Issue.find(1).project_id
2593 assert_equal 2, Issue.find(1).project_id
2582 assert_equal 2, Issue.find(2).project_id
2594 assert_equal 2, Issue.find(2).project_id
2583 # No tracker change
2595 # No tracker change
2584 assert_equal 1, Issue.find(1).tracker_id
2596 assert_equal 1, Issue.find(1).tracker_id
2585 assert_equal 2, Issue.find(2).tracker_id
2597 assert_equal 2, Issue.find(2).tracker_id
2586 end
2598 end
2587
2599
2588 def test_bulk_update_project_on_single_issue_should_follow_when_needed
2600 def test_bulk_update_project_on_single_issue_should_follow_when_needed
2589 @request.session[:user_id] = 2
2601 @request.session[:user_id] = 2
2590 post :bulk_update, :id => 1, :issue => {:project_id => '2'}, :follow => '1'
2602 post :bulk_update, :id => 1, :issue => {:project_id => '2'}, :follow => '1'
2591 assert_redirected_to '/issues/1'
2603 assert_redirected_to '/issues/1'
2592 end
2604 end
2593
2605
2594 def test_bulk_update_project_on_multiple_issues_should_follow_when_needed
2606 def test_bulk_update_project_on_multiple_issues_should_follow_when_needed
2595 @request.session[:user_id] = 2
2607 @request.session[:user_id] = 2
2596 post :bulk_update, :id => [1, 2], :issue => {:project_id => '2'}, :follow => '1'
2608 post :bulk_update, :id => [1, 2], :issue => {:project_id => '2'}, :follow => '1'
2597 assert_redirected_to '/projects/onlinestore/issues'
2609 assert_redirected_to '/projects/onlinestore/issues'
2598 end
2610 end
2599
2611
2600 def test_bulk_update_tracker
2612 def test_bulk_update_tracker
2601 @request.session[:user_id] = 2
2613 @request.session[:user_id] = 2
2602 post :bulk_update, :ids => [1, 2], :issue => {:tracker_id => '2'}
2614 post :bulk_update, :ids => [1, 2], :issue => {:tracker_id => '2'}
2603 assert_redirected_to :controller => 'issues', :action => 'index', :project_id => 'ecookbook'
2615 assert_redirected_to :controller => 'issues', :action => 'index', :project_id => 'ecookbook'
2604 assert_equal 2, Issue.find(1).tracker_id
2616 assert_equal 2, Issue.find(1).tracker_id
2605 assert_equal 2, Issue.find(2).tracker_id
2617 assert_equal 2, Issue.find(2).tracker_id
2606 end
2618 end
2607
2619
2608 def test_bulk_update_status
2620 def test_bulk_update_status
2609 @request.session[:user_id] = 2
2621 @request.session[:user_id] = 2
2610 # update issues priority
2622 # update issues priority
2611 post :bulk_update, :ids => [1, 2], :notes => 'Bulk editing status',
2623 post :bulk_update, :ids => [1, 2], :notes => 'Bulk editing status',
2612 :issue => {:priority_id => '',
2624 :issue => {:priority_id => '',
2613 :assigned_to_id => '',
2625 :assigned_to_id => '',
2614 :status_id => '5'}
2626 :status_id => '5'}
2615
2627
2616 assert_response 302
2628 assert_response 302
2617 issue = Issue.find(1)
2629 issue = Issue.find(1)
2618 assert issue.closed?
2630 assert issue.closed?
2619 end
2631 end
2620
2632
2621 def test_bulk_update_priority
2633 def test_bulk_update_priority
2622 @request.session[:user_id] = 2
2634 @request.session[:user_id] = 2
2623 post :bulk_update, :ids => [1, 2], :issue => {:priority_id => 6}
2635 post :bulk_update, :ids => [1, 2], :issue => {:priority_id => 6}
2624
2636
2625 assert_redirected_to :controller => 'issues', :action => 'index', :project_id => 'ecookbook'
2637 assert_redirected_to :controller => 'issues', :action => 'index', :project_id => 'ecookbook'
2626 assert_equal 6, Issue.find(1).priority_id
2638 assert_equal 6, Issue.find(1).priority_id
2627 assert_equal 6, Issue.find(2).priority_id
2639 assert_equal 6, Issue.find(2).priority_id
2628 end
2640 end
2629
2641
2630 def test_bulk_update_with_notes
2642 def test_bulk_update_with_notes
2631 @request.session[:user_id] = 2
2643 @request.session[:user_id] = 2
2632 post :bulk_update, :ids => [1, 2], :notes => 'Moving two issues'
2644 post :bulk_update, :ids => [1, 2], :notes => 'Moving two issues'
2633
2645
2634 assert_redirected_to :controller => 'issues', :action => 'index', :project_id => 'ecookbook'
2646 assert_redirected_to :controller => 'issues', :action => 'index', :project_id => 'ecookbook'
2635 assert_equal 'Moving two issues', Issue.find(1).journals.sort_by(&:id).last.notes
2647 assert_equal 'Moving two issues', Issue.find(1).journals.sort_by(&:id).last.notes
2636 assert_equal 'Moving two issues', Issue.find(2).journals.sort_by(&:id).last.notes
2648 assert_equal 'Moving two issues', Issue.find(2).journals.sort_by(&:id).last.notes
2637 end
2649 end
2638
2650
2639 def test_bulk_update_parent_id
2651 def test_bulk_update_parent_id
2640 @request.session[:user_id] = 2
2652 @request.session[:user_id] = 2
2641 post :bulk_update, :ids => [1, 3],
2653 post :bulk_update, :ids => [1, 3],
2642 :notes => 'Bulk editing parent',
2654 :notes => 'Bulk editing parent',
2643 :issue => {:priority_id => '', :assigned_to_id => '', :status_id => '', :parent_issue_id => '2'}
2655 :issue => {:priority_id => '', :assigned_to_id => '', :status_id => '', :parent_issue_id => '2'}
2644
2656
2645 assert_response 302
2657 assert_response 302
2646 parent = Issue.find(2)
2658 parent = Issue.find(2)
2647 assert_equal parent.id, Issue.find(1).parent_id
2659 assert_equal parent.id, Issue.find(1).parent_id
2648 assert_equal parent.id, Issue.find(3).parent_id
2660 assert_equal parent.id, Issue.find(3).parent_id
2649 assert_equal [1, 3], parent.children.collect(&:id).sort
2661 assert_equal [1, 3], parent.children.collect(&:id).sort
2650 end
2662 end
2651
2663
2652 def test_bulk_update_custom_field
2664 def test_bulk_update_custom_field
2653 @request.session[:user_id] = 2
2665 @request.session[:user_id] = 2
2654 # update issues priority
2666 # update issues priority
2655 post :bulk_update, :ids => [1, 2], :notes => 'Bulk editing custom field',
2667 post :bulk_update, :ids => [1, 2], :notes => 'Bulk editing custom field',
2656 :issue => {:priority_id => '',
2668 :issue => {:priority_id => '',
2657 :assigned_to_id => '',
2669 :assigned_to_id => '',
2658 :custom_field_values => {'2' => '777'}}
2670 :custom_field_values => {'2' => '777'}}
2659
2671
2660 assert_response 302
2672 assert_response 302
2661
2673
2662 issue = Issue.find(1)
2674 issue = Issue.find(1)
2663 journal = issue.journals.find(:first, :order => 'created_on DESC')
2675 journal = issue.journals.find(:first, :order => 'created_on DESC')
2664 assert_equal '777', issue.custom_value_for(2).value
2676 assert_equal '777', issue.custom_value_for(2).value
2665 assert_equal 1, journal.details.size
2677 assert_equal 1, journal.details.size
2666 assert_equal '125', journal.details.first.old_value
2678 assert_equal '125', journal.details.first.old_value
2667 assert_equal '777', journal.details.first.value
2679 assert_equal '777', journal.details.first.value
2668 end
2680 end
2669
2681
2670 def test_bulk_update_multi_custom_field
2682 def test_bulk_update_multi_custom_field
2671 field = CustomField.find(1)
2683 field = CustomField.find(1)
2672 field.update_attribute :multiple, true
2684 field.update_attribute :multiple, true
2673
2685
2674 @request.session[:user_id] = 2
2686 @request.session[:user_id] = 2
2675 post :bulk_update, :ids => [1, 2, 3], :notes => 'Bulk editing multi custom field',
2687 post :bulk_update, :ids => [1, 2, 3], :notes => 'Bulk editing multi custom field',
2676 :issue => {:priority_id => '',
2688 :issue => {:priority_id => '',
2677 :assigned_to_id => '',
2689 :assigned_to_id => '',
2678 :custom_field_values => {'1' => ['MySQL', 'Oracle']}}
2690 :custom_field_values => {'1' => ['MySQL', 'Oracle']}}
2679
2691
2680 assert_response 302
2692 assert_response 302
2681
2693
2682 assert_equal ['MySQL', 'Oracle'], Issue.find(1).custom_field_value(1).sort
2694 assert_equal ['MySQL', 'Oracle'], Issue.find(1).custom_field_value(1).sort
2683 assert_equal ['MySQL', 'Oracle'], Issue.find(3).custom_field_value(1).sort
2695 assert_equal ['MySQL', 'Oracle'], Issue.find(3).custom_field_value(1).sort
2684 # the custom field is not associated with the issue tracker
2696 # the custom field is not associated with the issue tracker
2685 assert_nil Issue.find(2).custom_field_value(1)
2697 assert_nil Issue.find(2).custom_field_value(1)
2686 end
2698 end
2687
2699
2688 def test_bulk_update_unassign
2700 def test_bulk_update_unassign
2689 assert_not_nil Issue.find(2).assigned_to
2701 assert_not_nil Issue.find(2).assigned_to
2690 @request.session[:user_id] = 2
2702 @request.session[:user_id] = 2
2691 # unassign issues
2703 # unassign issues
2692 post :bulk_update, :ids => [1, 2], :notes => 'Bulk unassigning', :issue => {:assigned_to_id => 'none'}
2704 post :bulk_update, :ids => [1, 2], :notes => 'Bulk unassigning', :issue => {:assigned_to_id => 'none'}
2693 assert_response 302
2705 assert_response 302
2694 # check that the issues were updated
2706 # check that the issues were updated
2695 assert_nil Issue.find(2).assigned_to
2707 assert_nil Issue.find(2).assigned_to
2696 end
2708 end
2697
2709
2698 def test_post_bulk_update_should_allow_fixed_version_to_be_set_to_a_subproject
2710 def test_post_bulk_update_should_allow_fixed_version_to_be_set_to_a_subproject
2699 @request.session[:user_id] = 2
2711 @request.session[:user_id] = 2
2700
2712
2701 post :bulk_update, :ids => [1,2], :issue => {:fixed_version_id => 4}
2713 post :bulk_update, :ids => [1,2], :issue => {:fixed_version_id => 4}
2702
2714
2703 assert_response :redirect
2715 assert_response :redirect
2704 issues = Issue.find([1,2])
2716 issues = Issue.find([1,2])
2705 issues.each do |issue|
2717 issues.each do |issue|
2706 assert_equal 4, issue.fixed_version_id
2718 assert_equal 4, issue.fixed_version_id
2707 assert_not_equal issue.project_id, issue.fixed_version.project_id
2719 assert_not_equal issue.project_id, issue.fixed_version.project_id
2708 end
2720 end
2709 end
2721 end
2710
2722
2711 def test_post_bulk_update_should_redirect_back_using_the_back_url_parameter
2723 def test_post_bulk_update_should_redirect_back_using_the_back_url_parameter
2712 @request.session[:user_id] = 2
2724 @request.session[:user_id] = 2
2713 post :bulk_update, :ids => [1,2], :back_url => '/issues'
2725 post :bulk_update, :ids => [1,2], :back_url => '/issues'
2714
2726
2715 assert_response :redirect
2727 assert_response :redirect
2716 assert_redirected_to '/issues'
2728 assert_redirected_to '/issues'
2717 end
2729 end
2718
2730
2719 def test_post_bulk_update_should_not_redirect_back_using_the_back_url_parameter_off_the_host
2731 def test_post_bulk_update_should_not_redirect_back_using_the_back_url_parameter_off_the_host
2720 @request.session[:user_id] = 2
2732 @request.session[:user_id] = 2
2721 post :bulk_update, :ids => [1,2], :back_url => 'http://google.com'
2733 post :bulk_update, :ids => [1,2], :back_url => 'http://google.com'
2722
2734
2723 assert_response :redirect
2735 assert_response :redirect
2724 assert_redirected_to :controller => 'issues', :action => 'index', :project_id => Project.find(1).identifier
2736 assert_redirected_to :controller => 'issues', :action => 'index', :project_id => Project.find(1).identifier
2725 end
2737 end
2726
2738
2727 def test_bulk_copy_to_another_project
2739 def test_bulk_copy_to_another_project
2728 @request.session[:user_id] = 2
2740 @request.session[:user_id] = 2
2729 assert_difference 'Issue.count', 2 do
2741 assert_difference 'Issue.count', 2 do
2730 assert_no_difference 'Project.find(1).issues.count' do
2742 assert_no_difference 'Project.find(1).issues.count' do
2731 post :bulk_update, :ids => [1, 2], :issue => {:project_id => '2'}, :copy => '1'
2743 post :bulk_update, :ids => [1, 2], :issue => {:project_id => '2'}, :copy => '1'
2732 end
2744 end
2733 end
2745 end
2734 assert_redirected_to '/projects/ecookbook/issues'
2746 assert_redirected_to '/projects/ecookbook/issues'
2735 end
2747 end
2736
2748
2737 def test_bulk_copy_should_allow_not_changing_the_issue_attributes
2749 def test_bulk_copy_should_allow_not_changing_the_issue_attributes
2738 @request.session[:user_id] = 2
2750 @request.session[:user_id] = 2
2739 issue_before_move = Issue.find(1)
2751 issue_before_move = Issue.find(1)
2740 assert_difference 'Issue.count', 1 do
2752 assert_difference 'Issue.count', 1 do
2741 assert_no_difference 'Project.find(1).issues.count' do
2753 assert_no_difference 'Project.find(1).issues.count' do
2742 post :bulk_update, :ids => [1], :copy => '1',
2754 post :bulk_update, :ids => [1], :copy => '1',
2743 :issue => {
2755 :issue => {
2744 :project_id => '2', :tracker_id => '', :assigned_to_id => '',
2756 :project_id => '2', :tracker_id => '', :assigned_to_id => '',
2745 :status_id => '', :start_date => '', :due_date => ''
2757 :status_id => '', :start_date => '', :due_date => ''
2746 }
2758 }
2747 end
2759 end
2748 end
2760 end
2749 issue_after_move = Issue.first(:order => 'id desc', :conditions => {:project_id => 2})
2761 issue_after_move = Issue.first(:order => 'id desc', :conditions => {:project_id => 2})
2750 assert_equal issue_before_move.tracker_id, issue_after_move.tracker_id
2762 assert_equal issue_before_move.tracker_id, issue_after_move.tracker_id
2751 assert_equal issue_before_move.status_id, issue_after_move.status_id
2763 assert_equal issue_before_move.status_id, issue_after_move.status_id
2752 assert_equal issue_before_move.assigned_to_id, issue_after_move.assigned_to_id
2764 assert_equal issue_before_move.assigned_to_id, issue_after_move.assigned_to_id
2753 end
2765 end
2754
2766
2755 def test_bulk_copy_should_allow_changing_the_issue_attributes
2767 def test_bulk_copy_should_allow_changing_the_issue_attributes
2756 # Fixes random test failure with Mysql
2768 # Fixes random test failure with Mysql
2757 # where Issue.all(:limit => 2, :order => 'id desc', :conditions => {:project_id => 2})
2769 # where Issue.all(:limit => 2, :order => 'id desc', :conditions => {:project_id => 2})
2758 # doesn't return the expected results
2770 # doesn't return the expected results
2759 Issue.delete_all("project_id=2")
2771 Issue.delete_all("project_id=2")
2760
2772
2761 @request.session[:user_id] = 2
2773 @request.session[:user_id] = 2
2762 assert_difference 'Issue.count', 2 do
2774 assert_difference 'Issue.count', 2 do
2763 assert_no_difference 'Project.find(1).issues.count' do
2775 assert_no_difference 'Project.find(1).issues.count' do
2764 post :bulk_update, :ids => [1, 2], :copy => '1',
2776 post :bulk_update, :ids => [1, 2], :copy => '1',
2765 :issue => {
2777 :issue => {
2766 :project_id => '2', :tracker_id => '', :assigned_to_id => '4',
2778 :project_id => '2', :tracker_id => '', :assigned_to_id => '4',
2767 :status_id => '3', :start_date => '2009-12-01', :due_date => '2009-12-31'
2779 :status_id => '3', :start_date => '2009-12-01', :due_date => '2009-12-31'
2768 }
2780 }
2769 end
2781 end
2770 end
2782 end
2771
2783
2772 copied_issues = Issue.all(:limit => 2, :order => 'id desc', :conditions => {:project_id => 2})
2784 copied_issues = Issue.all(:limit => 2, :order => 'id desc', :conditions => {:project_id => 2})
2773 assert_equal 2, copied_issues.size
2785 assert_equal 2, copied_issues.size
2774 copied_issues.each do |issue|
2786 copied_issues.each do |issue|
2775 assert_equal 2, issue.project_id, "Project is incorrect"
2787 assert_equal 2, issue.project_id, "Project is incorrect"
2776 assert_equal 4, issue.assigned_to_id, "Assigned to is incorrect"
2788 assert_equal 4, issue.assigned_to_id, "Assigned to is incorrect"
2777 assert_equal 3, issue.status_id, "Status is incorrect"
2789 assert_equal 3, issue.status_id, "Status is incorrect"
2778 assert_equal '2009-12-01', issue.start_date.to_s, "Start date is incorrect"
2790 assert_equal '2009-12-01', issue.start_date.to_s, "Start date is incorrect"
2779 assert_equal '2009-12-31', issue.due_date.to_s, "Due date is incorrect"
2791 assert_equal '2009-12-31', issue.due_date.to_s, "Due date is incorrect"
2780 end
2792 end
2781 end
2793 end
2782
2794
2783 def test_bulk_copy_should_allow_adding_a_note
2795 def test_bulk_copy_should_allow_adding_a_note
2784 @request.session[:user_id] = 2
2796 @request.session[:user_id] = 2
2785 assert_difference 'Issue.count', 1 do
2797 assert_difference 'Issue.count', 1 do
2786 post :bulk_update, :ids => [1], :copy => '1',
2798 post :bulk_update, :ids => [1], :copy => '1',
2787 :notes => 'Copying one issue',
2799 :notes => 'Copying one issue',
2788 :issue => {
2800 :issue => {
2789 :project_id => '', :tracker_id => '', :assigned_to_id => '4',
2801 :project_id => '', :tracker_id => '', :assigned_to_id => '4',
2790 :status_id => '3', :start_date => '2009-12-01', :due_date => '2009-12-31'
2802 :status_id => '3', :start_date => '2009-12-01', :due_date => '2009-12-31'
2791 }
2803 }
2792 end
2804 end
2793
2805
2794 issue = Issue.first(:order => 'id DESC')
2806 issue = Issue.first(:order => 'id DESC')
2795 assert_equal 1, issue.journals.size
2807 assert_equal 1, issue.journals.size
2796 journal = issue.journals.first
2808 journal = issue.journals.first
2797 assert_equal 0, journal.details.size
2809 assert_equal 0, journal.details.size
2798 assert_equal 'Copying one issue', journal.notes
2810 assert_equal 'Copying one issue', journal.notes
2799 end
2811 end
2800
2812
2801 def test_bulk_copy_to_another_project_should_follow_when_needed
2813 def test_bulk_copy_to_another_project_should_follow_when_needed
2802 @request.session[:user_id] = 2
2814 @request.session[:user_id] = 2
2803 post :bulk_update, :ids => [1], :copy => '1', :issue => {:project_id => 2}, :follow => '1'
2815 post :bulk_update, :ids => [1], :copy => '1', :issue => {:project_id => 2}, :follow => '1'
2804 issue = Issue.first(:order => 'id DESC')
2816 issue = Issue.first(:order => 'id DESC')
2805 assert_redirected_to :controller => 'issues', :action => 'show', :id => issue
2817 assert_redirected_to :controller => 'issues', :action => 'show', :id => issue
2806 end
2818 end
2807
2819
2808 def test_destroy_issue_with_no_time_entries
2820 def test_destroy_issue_with_no_time_entries
2809 assert_nil TimeEntry.find_by_issue_id(2)
2821 assert_nil TimeEntry.find_by_issue_id(2)
2810 @request.session[:user_id] = 2
2822 @request.session[:user_id] = 2
2811
2823
2812 assert_difference 'Issue.count', -1 do
2824 assert_difference 'Issue.count', -1 do
2813 delete :destroy, :id => 2
2825 delete :destroy, :id => 2
2814 end
2826 end
2815 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
2827 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
2816 assert_nil Issue.find_by_id(2)
2828 assert_nil Issue.find_by_id(2)
2817 end
2829 end
2818
2830
2819 def test_destroy_issues_with_time_entries
2831 def test_destroy_issues_with_time_entries
2820 @request.session[:user_id] = 2
2832 @request.session[:user_id] = 2
2821
2833
2822 assert_no_difference 'Issue.count' do
2834 assert_no_difference 'Issue.count' do
2823 delete :destroy, :ids => [1, 3]
2835 delete :destroy, :ids => [1, 3]
2824 end
2836 end
2825 assert_response :success
2837 assert_response :success
2826 assert_template 'destroy'
2838 assert_template 'destroy'
2827 assert_not_nil assigns(:hours)
2839 assert_not_nil assigns(:hours)
2828 assert Issue.find_by_id(1) && Issue.find_by_id(3)
2840 assert Issue.find_by_id(1) && Issue.find_by_id(3)
2829 assert_tag 'form',
2841 assert_tag 'form',
2830 :descendant => {:tag => 'input', :attributes => {:name => '_method', :value => 'delete'}}
2842 :descendant => {:tag => 'input', :attributes => {:name => '_method', :value => 'delete'}}
2831 end
2843 end
2832
2844
2833 def test_destroy_issues_and_destroy_time_entries
2845 def test_destroy_issues_and_destroy_time_entries
2834 @request.session[:user_id] = 2
2846 @request.session[:user_id] = 2
2835
2847
2836 assert_difference 'Issue.count', -2 do
2848 assert_difference 'Issue.count', -2 do
2837 assert_difference 'TimeEntry.count', -3 do
2849 assert_difference 'TimeEntry.count', -3 do
2838 delete :destroy, :ids => [1, 3], :todo => 'destroy'
2850 delete :destroy, :ids => [1, 3], :todo => 'destroy'
2839 end
2851 end
2840 end
2852 end
2841 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
2853 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
2842 assert !(Issue.find_by_id(1) || Issue.find_by_id(3))
2854 assert !(Issue.find_by_id(1) || Issue.find_by_id(3))
2843 assert_nil TimeEntry.find_by_id([1, 2])
2855 assert_nil TimeEntry.find_by_id([1, 2])
2844 end
2856 end
2845
2857
2846 def test_destroy_issues_and_assign_time_entries_to_project
2858 def test_destroy_issues_and_assign_time_entries_to_project
2847 @request.session[:user_id] = 2
2859 @request.session[:user_id] = 2
2848
2860
2849 assert_difference 'Issue.count', -2 do
2861 assert_difference 'Issue.count', -2 do
2850 assert_no_difference 'TimeEntry.count' do
2862 assert_no_difference 'TimeEntry.count' do
2851 delete :destroy, :ids => [1, 3], :todo => 'nullify'
2863 delete :destroy, :ids => [1, 3], :todo => 'nullify'
2852 end
2864 end
2853 end
2865 end
2854 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
2866 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
2855 assert !(Issue.find_by_id(1) || Issue.find_by_id(3))
2867 assert !(Issue.find_by_id(1) || Issue.find_by_id(3))
2856 assert_nil TimeEntry.find(1).issue_id
2868 assert_nil TimeEntry.find(1).issue_id
2857 assert_nil TimeEntry.find(2).issue_id
2869 assert_nil TimeEntry.find(2).issue_id
2858 end
2870 end
2859
2871
2860 def test_destroy_issues_and_reassign_time_entries_to_another_issue
2872 def test_destroy_issues_and_reassign_time_entries_to_another_issue
2861 @request.session[:user_id] = 2
2873 @request.session[:user_id] = 2
2862
2874
2863 assert_difference 'Issue.count', -2 do
2875 assert_difference 'Issue.count', -2 do
2864 assert_no_difference 'TimeEntry.count' do
2876 assert_no_difference 'TimeEntry.count' do
2865 delete :destroy, :ids => [1, 3], :todo => 'reassign', :reassign_to_id => 2
2877 delete :destroy, :ids => [1, 3], :todo => 'reassign', :reassign_to_id => 2
2866 end
2878 end
2867 end
2879 end
2868 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
2880 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
2869 assert !(Issue.find_by_id(1) || Issue.find_by_id(3))
2881 assert !(Issue.find_by_id(1) || Issue.find_by_id(3))
2870 assert_equal 2, TimeEntry.find(1).issue_id
2882 assert_equal 2, TimeEntry.find(1).issue_id
2871 assert_equal 2, TimeEntry.find(2).issue_id
2883 assert_equal 2, TimeEntry.find(2).issue_id
2872 end
2884 end
2873
2885
2874 def test_destroy_issues_from_different_projects
2886 def test_destroy_issues_from_different_projects
2875 @request.session[:user_id] = 2
2887 @request.session[:user_id] = 2
2876
2888
2877 assert_difference 'Issue.count', -3 do
2889 assert_difference 'Issue.count', -3 do
2878 delete :destroy, :ids => [1, 2, 6], :todo => 'destroy'
2890 delete :destroy, :ids => [1, 2, 6], :todo => 'destroy'
2879 end
2891 end
2880 assert_redirected_to :controller => 'issues', :action => 'index'
2892 assert_redirected_to :controller => 'issues', :action => 'index'
2881 assert !(Issue.find_by_id(1) || Issue.find_by_id(2) || Issue.find_by_id(6))
2893 assert !(Issue.find_by_id(1) || Issue.find_by_id(2) || Issue.find_by_id(6))
2882 end
2894 end
2883
2895
2884 def test_destroy_parent_and_child_issues
2896 def test_destroy_parent_and_child_issues
2885 parent = Issue.generate!(:project_id => 1, :tracker_id => 1)
2897 parent = Issue.generate!(:project_id => 1, :tracker_id => 1)
2886 child = Issue.generate!(:project_id => 1, :tracker_id => 1, :parent_issue_id => parent.id)
2898 child = Issue.generate!(:project_id => 1, :tracker_id => 1, :parent_issue_id => parent.id)
2887 assert child.is_descendant_of?(parent.reload)
2899 assert child.is_descendant_of?(parent.reload)
2888
2900
2889 @request.session[:user_id] = 2
2901 @request.session[:user_id] = 2
2890 assert_difference 'Issue.count', -2 do
2902 assert_difference 'Issue.count', -2 do
2891 delete :destroy, :ids => [parent.id, child.id], :todo => 'destroy'
2903 delete :destroy, :ids => [parent.id, child.id], :todo => 'destroy'
2892 end
2904 end
2893 assert_response 302
2905 assert_response 302
2894 end
2906 end
2895
2907
2896 def test_default_search_scope
2908 def test_default_search_scope
2897 get :index
2909 get :index
2898 assert_tag :div, :attributes => {:id => 'quick-search'},
2910 assert_tag :div, :attributes => {:id => 'quick-search'},
2899 :child => {:tag => 'form',
2911 :child => {:tag => 'form',
2900 :child => {:tag => 'input', :attributes => {:name => 'issues', :type => 'hidden', :value => '1'}}}
2912 :child => {:tag => 'input', :attributes => {:name => 'issues', :type => 'hidden', :value => '1'}}}
2901 end
2913 end
2902 end
2914 end
@@ -1,718 +1,730
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 # Redmine - project management software
2 # Redmine - project management software
3 # Copyright (C) 2006-2011 Jean-Philippe Lang
3 # Copyright (C) 2006-2011 Jean-Philippe Lang
4 #
4 #
5 # This program is free software; you can redistribute it and/or
5 # This program is free software; you can redistribute it and/or
6 # modify it under the terms of the GNU General Public License
6 # modify it under the terms of the GNU General Public License
7 # as published by the Free Software Foundation; either version 2
7 # as published by the Free Software Foundation; either version 2
8 # of the License, or (at your option) any later version.
8 # of the License, or (at your option) any later version.
9 #
9 #
10 # This program is distributed in the hope that it will be useful,
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # GNU General Public License for more details.
13 # GNU General Public License for more details.
14 #
14 #
15 # You should have received a copy of the GNU General Public License
15 # You should have received a copy of the GNU General Public License
16 # along with this program; if not, write to the Free Software
16 # along with this program; if not, write to the Free Software
17 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18
18
19 require File.expand_path('../../test_helper', __FILE__)
19 require File.expand_path('../../test_helper', __FILE__)
20 require 'timelog_controller'
20 require 'timelog_controller'
21
21
22 # Re-raise errors caught by the controller.
22 # Re-raise errors caught by the controller.
23 class TimelogController; def rescue_action(e) raise e end; end
23 class TimelogController; def rescue_action(e) raise e end; end
24
24
25 class TimelogControllerTest < ActionController::TestCase
25 class TimelogControllerTest < ActionController::TestCase
26 fixtures :projects, :enabled_modules, :roles, :members,
26 fixtures :projects, :enabled_modules, :roles, :members,
27 :member_roles, :issues, :time_entries, :users,
27 :member_roles, :issues, :time_entries, :users,
28 :trackers, :enumerations, :issue_statuses,
28 :trackers, :enumerations, :issue_statuses,
29 :custom_fields, :custom_values
29 :custom_fields, :custom_values
30
30
31 include Redmine::I18n
31 include Redmine::I18n
32
32
33 def setup
33 def setup
34 @controller = TimelogController.new
34 @controller = TimelogController.new
35 @request = ActionController::TestRequest.new
35 @request = ActionController::TestRequest.new
36 @response = ActionController::TestResponse.new
36 @response = ActionController::TestResponse.new
37 end
37 end
38
38
39 def test_get_new
39 def test_get_new
40 @request.session[:user_id] = 3
40 @request.session[:user_id] = 3
41 get :new, :project_id => 1
41 get :new, :project_id => 1
42 assert_response :success
42 assert_response :success
43 assert_template 'new'
43 assert_template 'new'
44 # Default activity selected
44 # Default activity selected
45 assert_tag :tag => 'option', :attributes => { :selected => 'selected' },
45 assert_tag :tag => 'option', :attributes => { :selected => 'selected' },
46 :content => 'Development'
46 :content => 'Development'
47 end
47 end
48
48
49 def test_get_new_should_only_show_active_time_entry_activities
49 def test_get_new_should_only_show_active_time_entry_activities
50 @request.session[:user_id] = 3
50 @request.session[:user_id] = 3
51 get :new, :project_id => 1
51 get :new, :project_id => 1
52 assert_response :success
52 assert_response :success
53 assert_template 'new'
53 assert_template 'new'
54 assert_no_tag 'select', :attributes => {:name => 'time_entry[project_id]'}
54 assert_no_tag 'select', :attributes => {:name => 'time_entry[project_id]'}
55 assert_no_tag 'option', :content => 'Inactive Activity'
55 assert_no_tag 'option', :content => 'Inactive Activity'
56 end
56 end
57
57
58 def test_new_without_project
58 def test_new_without_project
59 @request.session[:user_id] = 3
59 @request.session[:user_id] = 3
60 get :new
60 get :new
61 assert_response :success
61 assert_response :success
62 assert_template 'new'
62 assert_template 'new'
63 assert_tag 'select', :attributes => {:name => 'time_entry[project_id]'}
63 assert_tag 'select', :attributes => {:name => 'time_entry[project_id]'}
64 end
64 end
65
65
66 def test_new_without_project_should_deny_without_permission
66 def test_new_without_project_should_deny_without_permission
67 Role.all.each {|role| role.remove_permission! :log_time}
67 Role.all.each {|role| role.remove_permission! :log_time}
68 @request.session[:user_id] = 3
68 @request.session[:user_id] = 3
69
69
70 get :new
70 get :new
71 assert_response 403
71 assert_response 403
72 end
72 end
73
73
74 def test_get_edit_existing_time
74 def test_get_edit_existing_time
75 @request.session[:user_id] = 2
75 @request.session[:user_id] = 2
76 get :edit, :id => 2, :project_id => nil
76 get :edit, :id => 2, :project_id => nil
77 assert_response :success
77 assert_response :success
78 assert_template 'edit'
78 assert_template 'edit'
79 # Default activity selected
79 # Default activity selected
80 assert_tag :tag => 'form', :attributes => { :action => '/projects/ecookbook/time_entries/2' }
80 assert_tag :tag => 'form', :attributes => { :action => '/projects/ecookbook/time_entries/2' }
81 end
81 end
82
82
83 def test_get_edit_with_an_existing_time_entry_with_inactive_activity
83 def test_get_edit_with_an_existing_time_entry_with_inactive_activity
84 te = TimeEntry.find(1)
84 te = TimeEntry.find(1)
85 te.activity = TimeEntryActivity.find_by_name("Inactive Activity")
85 te.activity = TimeEntryActivity.find_by_name("Inactive Activity")
86 te.save!
86 te.save!
87
87
88 @request.session[:user_id] = 1
88 @request.session[:user_id] = 1
89 get :edit, :project_id => 1, :id => 1
89 get :edit, :project_id => 1, :id => 1
90 assert_response :success
90 assert_response :success
91 assert_template 'edit'
91 assert_template 'edit'
92 # Blank option since nothing is pre-selected
92 # Blank option since nothing is pre-selected
93 assert_tag :tag => 'option', :content => '--- Please select ---'
93 assert_tag :tag => 'option', :content => '--- Please select ---'
94 end
94 end
95
95
96 def test_post_create
96 def test_post_create
97 # TODO: should POST to issues’ time log instead of project. change form
97 # TODO: should POST to issues’ time log instead of project. change form
98 # and routing
98 # and routing
99 @request.session[:user_id] = 3
99 @request.session[:user_id] = 3
100 post :create, :project_id => 1,
100 post :create, :project_id => 1,
101 :time_entry => {:comments => 'Some work on TimelogControllerTest',
101 :time_entry => {:comments => 'Some work on TimelogControllerTest',
102 # Not the default activity
102 # Not the default activity
103 :activity_id => '11',
103 :activity_id => '11',
104 :spent_on => '2008-03-14',
104 :spent_on => '2008-03-14',
105 :issue_id => '1',
105 :issue_id => '1',
106 :hours => '7.3'}
106 :hours => '7.3'}
107 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
107 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
108
108
109 i = Issue.find(1)
109 i = Issue.find(1)
110 t = TimeEntry.find_by_comments('Some work on TimelogControllerTest')
110 t = TimeEntry.find_by_comments('Some work on TimelogControllerTest')
111 assert_not_nil t
111 assert_not_nil t
112 assert_equal 11, t.activity_id
112 assert_equal 11, t.activity_id
113 assert_equal 7.3, t.hours
113 assert_equal 7.3, t.hours
114 assert_equal 3, t.user_id
114 assert_equal 3, t.user_id
115 assert_equal i, t.issue
115 assert_equal i, t.issue
116 assert_equal i.project, t.project
116 assert_equal i.project, t.project
117 end
117 end
118
118
119 def test_post_create_with_blank_issue
119 def test_post_create_with_blank_issue
120 # TODO: should POST to issues’ time log instead of project. change form
120 # TODO: should POST to issues’ time log instead of project. change form
121 # and routing
121 # and routing
122 @request.session[:user_id] = 3
122 @request.session[:user_id] = 3
123 post :create, :project_id => 1,
123 post :create, :project_id => 1,
124 :time_entry => {:comments => 'Some work on TimelogControllerTest',
124 :time_entry => {:comments => 'Some work on TimelogControllerTest',
125 # Not the default activity
125 # Not the default activity
126 :activity_id => '11',
126 :activity_id => '11',
127 :issue_id => '',
127 :issue_id => '',
128 :spent_on => '2008-03-14',
128 :spent_on => '2008-03-14',
129 :hours => '7.3'}
129 :hours => '7.3'}
130 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
130 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
131
131
132 t = TimeEntry.find_by_comments('Some work on TimelogControllerTest')
132 t = TimeEntry.find_by_comments('Some work on TimelogControllerTest')
133 assert_not_nil t
133 assert_not_nil t
134 assert_equal 11, t.activity_id
134 assert_equal 11, t.activity_id
135 assert_equal 7.3, t.hours
135 assert_equal 7.3, t.hours
136 assert_equal 3, t.user_id
136 assert_equal 3, t.user_id
137 end
137 end
138
138
139 def test_create_and_continue
139 def test_create_and_continue
140 @request.session[:user_id] = 2
140 @request.session[:user_id] = 2
141 post :create, :project_id => 1,
141 post :create, :project_id => 1,
142 :time_entry => {:activity_id => '11',
142 :time_entry => {:activity_id => '11',
143 :issue_id => '',
143 :issue_id => '',
144 :spent_on => '2008-03-14',
144 :spent_on => '2008-03-14',
145 :hours => '7.3'},
145 :hours => '7.3'},
146 :continue => '1'
146 :continue => '1'
147 assert_redirected_to '/projects/ecookbook/time_entries/new'
147 assert_redirected_to '/projects/ecookbook/time_entries/new'
148 end
148 end
149
149
150 def test_create_and_continue_with_issue_id
150 def test_create_and_continue_with_issue_id
151 @request.session[:user_id] = 2
151 @request.session[:user_id] = 2
152 post :create, :project_id => 1,
152 post :create, :project_id => 1,
153 :time_entry => {:activity_id => '11',
153 :time_entry => {:activity_id => '11',
154 :issue_id => '1',
154 :issue_id => '1',
155 :spent_on => '2008-03-14',
155 :spent_on => '2008-03-14',
156 :hours => '7.3'},
156 :hours => '7.3'},
157 :continue => '1'
157 :continue => '1'
158 assert_redirected_to '/projects/ecookbook/issues/1/time_entries/new'
158 assert_redirected_to '/projects/ecookbook/issues/1/time_entries/new'
159 end
159 end
160
160
161 def test_create_and_continue_without_project
161 def test_create_and_continue_without_project
162 @request.session[:user_id] = 2
162 @request.session[:user_id] = 2
163 post :create, :time_entry => {:project_id => '1',
163 post :create, :time_entry => {:project_id => '1',
164 :activity_id => '11',
164 :activity_id => '11',
165 :issue_id => '',
165 :issue_id => '',
166 :spent_on => '2008-03-14',
166 :spent_on => '2008-03-14',
167 :hours => '7.3'},
167 :hours => '7.3'},
168 :continue => '1'
168 :continue => '1'
169
169
170 assert_redirected_to '/time_entries/new'
170 assert_redirected_to '/time_entries/new'
171 end
171 end
172
172
173 def test_create_without_log_time_permission_should_be_denied
173 def test_create_without_log_time_permission_should_be_denied
174 @request.session[:user_id] = 2
174 @request.session[:user_id] = 2
175 Role.find_by_name('Manager').remove_permission! :log_time
175 Role.find_by_name('Manager').remove_permission! :log_time
176 post :create, :project_id => 1,
176 post :create, :project_id => 1,
177 :time_entry => {:activity_id => '11',
177 :time_entry => {:activity_id => '11',
178 :issue_id => '',
178 :issue_id => '',
179 :spent_on => '2008-03-14',
179 :spent_on => '2008-03-14',
180 :hours => '7.3'}
180 :hours => '7.3'}
181
181
182 assert_response 403
182 assert_response 403
183 end
183 end
184
184
185 def test_create_with_failure
185 def test_create_with_failure
186 @request.session[:user_id] = 2
186 @request.session[:user_id] = 2
187 post :create, :project_id => 1,
187 post :create, :project_id => 1,
188 :time_entry => {:activity_id => '',
188 :time_entry => {:activity_id => '',
189 :issue_id => '',
189 :issue_id => '',
190 :spent_on => '2008-03-14',
190 :spent_on => '2008-03-14',
191 :hours => '7.3'}
191 :hours => '7.3'}
192
192
193 assert_response :success
193 assert_response :success
194 assert_template 'new'
194 assert_template 'new'
195 end
195 end
196
196
197 def test_create_without_project
197 def test_create_without_project
198 @request.session[:user_id] = 2
198 @request.session[:user_id] = 2
199 assert_difference 'TimeEntry.count' do
199 assert_difference 'TimeEntry.count' do
200 post :create, :time_entry => {:project_id => '1',
200 post :create, :time_entry => {:project_id => '1',
201 :activity_id => '11',
201 :activity_id => '11',
202 :issue_id => '',
202 :issue_id => '',
203 :spent_on => '2008-03-14',
203 :spent_on => '2008-03-14',
204 :hours => '7.3'}
204 :hours => '7.3'}
205 end
205 end
206
206
207 assert_redirected_to '/projects/ecookbook/time_entries'
207 assert_redirected_to '/projects/ecookbook/time_entries'
208 time_entry = TimeEntry.first(:order => 'id DESC')
208 time_entry = TimeEntry.first(:order => 'id DESC')
209 assert_equal 1, time_entry.project_id
209 assert_equal 1, time_entry.project_id
210 end
210 end
211
211
212 def test_create_without_project_should_fail_with_issue_not_inside_project
212 def test_create_without_project_should_fail_with_issue_not_inside_project
213 @request.session[:user_id] = 2
213 @request.session[:user_id] = 2
214 assert_no_difference 'TimeEntry.count' do
214 assert_no_difference 'TimeEntry.count' do
215 post :create, :time_entry => {:project_id => '1',
215 post :create, :time_entry => {:project_id => '1',
216 :activity_id => '11',
216 :activity_id => '11',
217 :issue_id => '5',
217 :issue_id => '5',
218 :spent_on => '2008-03-14',
218 :spent_on => '2008-03-14',
219 :hours => '7.3'}
219 :hours => '7.3'}
220 end
220 end
221
221
222 assert_response :success
222 assert_response :success
223 assert assigns(:time_entry).errors[:issue_id].present?
223 assert assigns(:time_entry).errors[:issue_id].present?
224 end
224 end
225
225
226 def test_create_without_project_should_deny_without_permission
226 def test_create_without_project_should_deny_without_permission
227 @request.session[:user_id] = 2
227 @request.session[:user_id] = 2
228 Project.find(3).disable_module!(:time_tracking)
228 Project.find(3).disable_module!(:time_tracking)
229
229
230 assert_no_difference 'TimeEntry.count' do
230 assert_no_difference 'TimeEntry.count' do
231 post :create, :time_entry => {:project_id => '3',
231 post :create, :time_entry => {:project_id => '3',
232 :activity_id => '11',
232 :activity_id => '11',
233 :issue_id => '',
233 :issue_id => '',
234 :spent_on => '2008-03-14',
234 :spent_on => '2008-03-14',
235 :hours => '7.3'}
235 :hours => '7.3'}
236 end
236 end
237
237
238 assert_response 403
238 assert_response 403
239 end
239 end
240
240
241 def test_create_without_project_with_failure
241 def test_create_without_project_with_failure
242 @request.session[:user_id] = 2
242 @request.session[:user_id] = 2
243 assert_no_difference 'TimeEntry.count' do
243 assert_no_difference 'TimeEntry.count' do
244 post :create, :time_entry => {:project_id => '1',
244 post :create, :time_entry => {:project_id => '1',
245 :activity_id => '11',
245 :activity_id => '11',
246 :issue_id => '',
246 :issue_id => '',
247 :spent_on => '2008-03-14',
247 :spent_on => '2008-03-14',
248 :hours => ''}
248 :hours => ''}
249 end
249 end
250
250
251 assert_response :success
251 assert_response :success
252 assert_tag 'select', :attributes => {:name => 'time_entry[project_id]'},
252 assert_tag 'select', :attributes => {:name => 'time_entry[project_id]'},
253 :child => {:tag => 'option', :attributes => {:value => '1', :selected => 'selected'}}
253 :child => {:tag => 'option', :attributes => {:value => '1', :selected => 'selected'}}
254 end
254 end
255
255
256 def test_update
256 def test_update
257 entry = TimeEntry.find(1)
257 entry = TimeEntry.find(1)
258 assert_equal 1, entry.issue_id
258 assert_equal 1, entry.issue_id
259 assert_equal 2, entry.user_id
259 assert_equal 2, entry.user_id
260
260
261 @request.session[:user_id] = 1
261 @request.session[:user_id] = 1
262 put :update, :id => 1,
262 put :update, :id => 1,
263 :time_entry => {:issue_id => '2',
263 :time_entry => {:issue_id => '2',
264 :hours => '8'}
264 :hours => '8'}
265 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
265 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
266 entry.reload
266 entry.reload
267
267
268 assert_equal 8, entry.hours
268 assert_equal 8, entry.hours
269 assert_equal 2, entry.issue_id
269 assert_equal 2, entry.issue_id
270 assert_equal 2, entry.user_id
270 assert_equal 2, entry.user_id
271 end
271 end
272
272
273 def test_get_bulk_edit
273 def test_get_bulk_edit
274 @request.session[:user_id] = 2
274 @request.session[:user_id] = 2
275 get :bulk_edit, :ids => [1, 2]
275 get :bulk_edit, :ids => [1, 2]
276 assert_response :success
276 assert_response :success
277 assert_template 'bulk_edit'
277 assert_template 'bulk_edit'
278
278
279 # System wide custom field
279 # System wide custom field
280 assert_tag :select, :attributes => {:name => 'time_entry[custom_field_values][10]'}
280 assert_tag :select, :attributes => {:name => 'time_entry[custom_field_values][10]'}
281 end
281 end
282
282
283 def test_get_bulk_edit_on_different_projects
283 def test_get_bulk_edit_on_different_projects
284 @request.session[:user_id] = 2
284 @request.session[:user_id] = 2
285 get :bulk_edit, :ids => [1, 2, 6]
285 get :bulk_edit, :ids => [1, 2, 6]
286 assert_response :success
286 assert_response :success
287 assert_template 'bulk_edit'
287 assert_template 'bulk_edit'
288 end
288 end
289
289
290 def test_bulk_update
290 def test_bulk_update
291 @request.session[:user_id] = 2
291 @request.session[:user_id] = 2
292 # update time entry activity
292 # update time entry activity
293 post :bulk_update, :ids => [1, 2], :time_entry => { :activity_id => 9}
293 post :bulk_update, :ids => [1, 2], :time_entry => { :activity_id => 9}
294
294
295 assert_response 302
295 assert_response 302
296 # check that the issues were updated
296 # check that the issues were updated
297 assert_equal [9, 9], TimeEntry.find_all_by_id([1, 2]).collect {|i| i.activity_id}
297 assert_equal [9, 9], TimeEntry.find_all_by_id([1, 2]).collect {|i| i.activity_id}
298 end
298 end
299
299
300 def test_bulk_update_with_failure
300 def test_bulk_update_with_failure
301 @request.session[:user_id] = 2
301 @request.session[:user_id] = 2
302 post :bulk_update, :ids => [1, 2], :time_entry => { :hours => 'A'}
302 post :bulk_update, :ids => [1, 2], :time_entry => { :hours => 'A'}
303
303
304 assert_response 302
304 assert_response 302
305 assert_match /Failed to save 2 time entrie/, flash[:error]
305 assert_match /Failed to save 2 time entrie/, flash[:error]
306 end
306 end
307
307
308 def test_bulk_update_on_different_projects
308 def test_bulk_update_on_different_projects
309 @request.session[:user_id] = 2
309 @request.session[:user_id] = 2
310 # makes user a manager on the other project
310 # makes user a manager on the other project
311 Member.create!(:user_id => 2, :project_id => 3, :role_ids => [1])
311 Member.create!(:user_id => 2, :project_id => 3, :role_ids => [1])
312
312
313 # update time entry activity
313 # update time entry activity
314 post :bulk_update, :ids => [1, 2, 4], :time_entry => { :activity_id => 9 }
314 post :bulk_update, :ids => [1, 2, 4], :time_entry => { :activity_id => 9 }
315
315
316 assert_response 302
316 assert_response 302
317 # check that the issues were updated
317 # check that the issues were updated
318 assert_equal [9, 9, 9], TimeEntry.find_all_by_id([1, 2, 4]).collect {|i| i.activity_id}
318 assert_equal [9, 9, 9], TimeEntry.find_all_by_id([1, 2, 4]).collect {|i| i.activity_id}
319 end
319 end
320
320
321 def test_bulk_update_on_different_projects_without_rights
321 def test_bulk_update_on_different_projects_without_rights
322 @request.session[:user_id] = 3
322 @request.session[:user_id] = 3
323 user = User.find(3)
323 user = User.find(3)
324 action = { :controller => "timelog", :action => "bulk_update" }
324 action = { :controller => "timelog", :action => "bulk_update" }
325 assert user.allowed_to?(action, TimeEntry.find(1).project)
325 assert user.allowed_to?(action, TimeEntry.find(1).project)
326 assert ! user.allowed_to?(action, TimeEntry.find(5).project)
326 assert ! user.allowed_to?(action, TimeEntry.find(5).project)
327 post :bulk_update, :ids => [1, 5], :time_entry => { :activity_id => 9 }
327 post :bulk_update, :ids => [1, 5], :time_entry => { :activity_id => 9 }
328 assert_response 403
328 assert_response 403
329 end
329 end
330
330
331 def test_bulk_update_custom_field
331 def test_bulk_update_custom_field
332 @request.session[:user_id] = 2
332 @request.session[:user_id] = 2
333 post :bulk_update, :ids => [1, 2], :time_entry => { :custom_field_values => {'10' => '0'} }
333 post :bulk_update, :ids => [1, 2], :time_entry => { :custom_field_values => {'10' => '0'} }
334
334
335 assert_response 302
335 assert_response 302
336 assert_equal ["0", "0"], TimeEntry.find_all_by_id([1, 2]).collect {|i| i.custom_value_for(10).value}
336 assert_equal ["0", "0"], TimeEntry.find_all_by_id([1, 2]).collect {|i| i.custom_value_for(10).value}
337 end
337 end
338
338
339 def test_post_bulk_update_should_redirect_back_using_the_back_url_parameter
339 def test_post_bulk_update_should_redirect_back_using_the_back_url_parameter
340 @request.session[:user_id] = 2
340 @request.session[:user_id] = 2
341 post :bulk_update, :ids => [1,2], :back_url => '/time_entries'
341 post :bulk_update, :ids => [1,2], :back_url => '/time_entries'
342
342
343 assert_response :redirect
343 assert_response :redirect
344 assert_redirected_to '/time_entries'
344 assert_redirected_to '/time_entries'
345 end
345 end
346
346
347 def test_post_bulk_update_should_not_redirect_back_using_the_back_url_parameter_off_the_host
347 def test_post_bulk_update_should_not_redirect_back_using_the_back_url_parameter_off_the_host
348 @request.session[:user_id] = 2
348 @request.session[:user_id] = 2
349 post :bulk_update, :ids => [1,2], :back_url => 'http://google.com'
349 post :bulk_update, :ids => [1,2], :back_url => 'http://google.com'
350
350
351 assert_response :redirect
351 assert_response :redirect
352 assert_redirected_to :controller => 'timelog', :action => 'index', :project_id => Project.find(1).identifier
352 assert_redirected_to :controller => 'timelog', :action => 'index', :project_id => Project.find(1).identifier
353 end
353 end
354
354
355 def test_post_bulk_update_without_edit_permission_should_be_denied
355 def test_post_bulk_update_without_edit_permission_should_be_denied
356 @request.session[:user_id] = 2
356 @request.session[:user_id] = 2
357 Role.find_by_name('Manager').remove_permission! :edit_time_entries
357 Role.find_by_name('Manager').remove_permission! :edit_time_entries
358 post :bulk_update, :ids => [1,2]
358 post :bulk_update, :ids => [1,2]
359
359
360 assert_response 403
360 assert_response 403
361 end
361 end
362
362
363 def test_destroy
363 def test_destroy
364 @request.session[:user_id] = 2
364 @request.session[:user_id] = 2
365 delete :destroy, :id => 1
365 delete :destroy, :id => 1
366 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
366 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
367 assert_equal I18n.t(:notice_successful_delete), flash[:notice]
367 assert_equal I18n.t(:notice_successful_delete), flash[:notice]
368 assert_nil TimeEntry.find_by_id(1)
368 assert_nil TimeEntry.find_by_id(1)
369 end
369 end
370
370
371 def test_destroy_should_fail
371 def test_destroy_should_fail
372 # simulate that this fails (e.g. due to a plugin), see #5700
372 # simulate that this fails (e.g. due to a plugin), see #5700
373 TimeEntry.any_instance.expects(:destroy).returns(false)
373 TimeEntry.any_instance.expects(:destroy).returns(false)
374
374
375 @request.session[:user_id] = 2
375 @request.session[:user_id] = 2
376 delete :destroy, :id => 1
376 delete :destroy, :id => 1
377 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
377 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
378 assert_equal I18n.t(:notice_unable_delete_time_entry), flash[:error]
378 assert_equal I18n.t(:notice_unable_delete_time_entry), flash[:error]
379 assert_not_nil TimeEntry.find_by_id(1)
379 assert_not_nil TimeEntry.find_by_id(1)
380 end
380 end
381
381
382 def test_index_all_projects
382 def test_index_all_projects
383 get :index
383 get :index
384 assert_response :success
384 assert_response :success
385 assert_template 'index'
385 assert_template 'index'
386 assert_not_nil assigns(:total_hours)
386 assert_not_nil assigns(:total_hours)
387 assert_equal "162.90", "%.2f" % assigns(:total_hours)
387 assert_equal "162.90", "%.2f" % assigns(:total_hours)
388 assert_tag :form,
388 assert_tag :form,
389 :attributes => {:action => "/time_entries", :id => 'query_form'}
389 :attributes => {:action => "/time_entries", :id => 'query_form'}
390 end
390 end
391
391
392 def test_index_all_projects_should_show_log_time_link
392 def test_index_all_projects_should_show_log_time_link
393 @request.session[:user_id] = 2
393 @request.session[:user_id] = 2
394 get :index
394 get :index
395 assert_response :success
395 assert_response :success
396 assert_template 'index'
396 assert_template 'index'
397 assert_tag 'a', :attributes => {:href => '/time_entries/new'}, :content => /Log time/
397 assert_tag 'a', :attributes => {:href => '/time_entries/new'}, :content => /Log time/
398 end
398 end
399
399
400 def test_index_at_project_level
400 def test_index_at_project_level
401 get :index, :project_id => 'ecookbook'
401 get :index, :project_id => 'ecookbook'
402 assert_response :success
402 assert_response :success
403 assert_template 'index'
403 assert_template 'index'
404 assert_not_nil assigns(:entries)
404 assert_not_nil assigns(:entries)
405 assert_equal 4, assigns(:entries).size
405 assert_equal 4, assigns(:entries).size
406 # project and subproject
406 # project and subproject
407 assert_equal [1, 3], assigns(:entries).collect(&:project_id).uniq.sort
407 assert_equal [1, 3], assigns(:entries).collect(&:project_id).uniq.sort
408 assert_not_nil assigns(:total_hours)
408 assert_not_nil assigns(:total_hours)
409 assert_equal "162.90", "%.2f" % assigns(:total_hours)
409 assert_equal "162.90", "%.2f" % assigns(:total_hours)
410 # display all time by default
410 # display all time by default
411 assert_nil assigns(:from)
411 assert_nil assigns(:from)
412 assert_nil assigns(:to)
412 assert_nil assigns(:to)
413 assert_tag :form,
413 assert_tag :form,
414 :attributes => {:action => "/projects/ecookbook/time_entries", :id => 'query_form'}
414 :attributes => {:action => "/projects/ecookbook/time_entries", :id => 'query_form'}
415 end
415 end
416
416
417 def test_index_at_project_level_with_date_range
417 def test_index_at_project_level_with_date_range
418 get :index, :project_id => 'ecookbook', :from => '2007-03-20', :to => '2007-04-30'
418 get :index, :project_id => 'ecookbook', :from => '2007-03-20', :to => '2007-04-30'
419 assert_response :success
419 assert_response :success
420 assert_template 'index'
420 assert_template 'index'
421 assert_not_nil assigns(:entries)
421 assert_not_nil assigns(:entries)
422 assert_equal 3, assigns(:entries).size
422 assert_equal 3, assigns(:entries).size
423 assert_not_nil assigns(:total_hours)
423 assert_not_nil assigns(:total_hours)
424 assert_equal "12.90", "%.2f" % assigns(:total_hours)
424 assert_equal "12.90", "%.2f" % assigns(:total_hours)
425 assert_equal '2007-03-20'.to_date, assigns(:from)
425 assert_equal '2007-03-20'.to_date, assigns(:from)
426 assert_equal '2007-04-30'.to_date, assigns(:to)
426 assert_equal '2007-04-30'.to_date, assigns(:to)
427 assert_tag :form,
427 assert_tag :form,
428 :attributes => {:action => "/projects/ecookbook/time_entries", :id => 'query_form'}
428 :attributes => {:action => "/projects/ecookbook/time_entries", :id => 'query_form'}
429 end
429 end
430
430
431 def test_index_at_project_level_with_period
431 def test_index_at_project_level_with_period
432 get :index, :project_id => 'ecookbook', :period => '7_days'
432 get :index, :project_id => 'ecookbook', :period => '7_days'
433 assert_response :success
433 assert_response :success
434 assert_template 'index'
434 assert_template 'index'
435 assert_not_nil assigns(:entries)
435 assert_not_nil assigns(:entries)
436 assert_not_nil assigns(:total_hours)
436 assert_not_nil assigns(:total_hours)
437 assert_equal Date.today - 7, assigns(:from)
437 assert_equal Date.today - 7, assigns(:from)
438 assert_equal Date.today, assigns(:to)
438 assert_equal Date.today, assigns(:to)
439 assert_tag :form,
439 assert_tag :form,
440 :attributes => {:action => "/projects/ecookbook/time_entries", :id => 'query_form'}
440 :attributes => {:action => "/projects/ecookbook/time_entries", :id => 'query_form'}
441 end
441 end
442
442
443 def test_index_one_day
443 def test_index_one_day
444 get :index, :project_id => 'ecookbook', :from => "2007-03-23", :to => "2007-03-23"
444 get :index, :project_id => 'ecookbook', :from => "2007-03-23", :to => "2007-03-23"
445 assert_response :success
445 assert_response :success
446 assert_template 'index'
446 assert_template 'index'
447 assert_not_nil assigns(:total_hours)
447 assert_not_nil assigns(:total_hours)
448 assert_equal "4.25", "%.2f" % assigns(:total_hours)
448 assert_equal "4.25", "%.2f" % assigns(:total_hours)
449 assert_tag :form,
449 assert_tag :form,
450 :attributes => {:action => "/projects/ecookbook/time_entries", :id => 'query_form'}
450 :attributes => {:action => "/projects/ecookbook/time_entries", :id => 'query_form'}
451 end
451 end
452
452
453 def test_index_today
453 def test_index_today
454 Date.stubs(:today).returns('2011-12-15'.to_date)
454 Date.stubs(:today).returns('2011-12-15'.to_date)
455 get :index, :period => 'today'
455 get :index, :period => 'today'
456 assert_equal '2011-12-15'.to_date, assigns(:from)
456 assert_equal '2011-12-15'.to_date, assigns(:from)
457 assert_equal '2011-12-15'.to_date, assigns(:to)
457 assert_equal '2011-12-15'.to_date, assigns(:to)
458 end
458 end
459
459
460 def test_index_yesterday
460 def test_index_yesterday
461 Date.stubs(:today).returns('2011-12-15'.to_date)
461 Date.stubs(:today).returns('2011-12-15'.to_date)
462 get :index, :period => 'yesterday'
462 get :index, :period => 'yesterday'
463 assert_equal '2011-12-14'.to_date, assigns(:from)
463 assert_equal '2011-12-14'.to_date, assigns(:from)
464 assert_equal '2011-12-14'.to_date, assigns(:to)
464 assert_equal '2011-12-14'.to_date, assigns(:to)
465 end
465 end
466
466
467 def test_index_current_week
467 def test_index_current_week
468 Date.stubs(:today).returns('2011-12-15'.to_date)
468 Date.stubs(:today).returns('2011-12-15'.to_date)
469 get :index, :period => 'current_week'
469 get :index, :period => 'current_week'
470 assert_equal '2011-12-12'.to_date, assigns(:from)
470 assert_equal '2011-12-12'.to_date, assigns(:from)
471 assert_equal '2011-12-18'.to_date, assigns(:to)
471 assert_equal '2011-12-18'.to_date, assigns(:to)
472 end
472 end
473
473
474 def test_index_last_week
474 def test_index_last_week
475 Date.stubs(:today).returns('2011-12-15'.to_date)
475 Date.stubs(:today).returns('2011-12-15'.to_date)
476 get :index, :period => 'current_week'
476 get :index, :period => 'current_week'
477 assert_equal '2011-12-05'.to_date, assigns(:from)
477 assert_equal '2011-12-05'.to_date, assigns(:from)
478 assert_equal '2011-12-11'.to_date, assigns(:to)
478 assert_equal '2011-12-11'.to_date, assigns(:to)
479 end
479 end
480
480
481 def test_index_last_week
481 def test_index_last_week
482 Date.stubs(:today).returns('2011-12-15'.to_date)
482 Date.stubs(:today).returns('2011-12-15'.to_date)
483 get :index, :period => 'last_week'
483 get :index, :period => 'last_week'
484 assert_equal '2011-12-05'.to_date, assigns(:from)
484 assert_equal '2011-12-05'.to_date, assigns(:from)
485 assert_equal '2011-12-11'.to_date, assigns(:to)
485 assert_equal '2011-12-11'.to_date, assigns(:to)
486 end
486 end
487
487
488 def test_index_7_days
488 def test_index_7_days
489 Date.stubs(:today).returns('2011-12-15'.to_date)
489 Date.stubs(:today).returns('2011-12-15'.to_date)
490 get :index, :period => '7_days'
490 get :index, :period => '7_days'
491 assert_equal '2011-12-08'.to_date, assigns(:from)
491 assert_equal '2011-12-08'.to_date, assigns(:from)
492 assert_equal '2011-12-15'.to_date, assigns(:to)
492 assert_equal '2011-12-15'.to_date, assigns(:to)
493 end
493 end
494
494
495 def test_index_current_month
495 def test_index_current_month
496 Date.stubs(:today).returns('2011-12-15'.to_date)
496 Date.stubs(:today).returns('2011-12-15'.to_date)
497 get :index, :period => 'current_month'
497 get :index, :period => 'current_month'
498 assert_equal '2011-12-01'.to_date, assigns(:from)
498 assert_equal '2011-12-01'.to_date, assigns(:from)
499 assert_equal '2011-12-31'.to_date, assigns(:to)
499 assert_equal '2011-12-31'.to_date, assigns(:to)
500 end
500 end
501
501
502 def test_index_last_month
502 def test_index_last_month
503 Date.stubs(:today).returns('2011-12-15'.to_date)
503 Date.stubs(:today).returns('2011-12-15'.to_date)
504 get :index, :period => 'last_month'
504 get :index, :period => 'last_month'
505 assert_equal '2011-11-01'.to_date, assigns(:from)
505 assert_equal '2011-11-01'.to_date, assigns(:from)
506 assert_equal '2011-11-30'.to_date, assigns(:to)
506 assert_equal '2011-11-30'.to_date, assigns(:to)
507 end
507 end
508
508
509 def test_index_30_days
509 def test_index_30_days
510 Date.stubs(:today).returns('2011-12-15'.to_date)
510 Date.stubs(:today).returns('2011-12-15'.to_date)
511 get :index, :period => '30_days'
511 get :index, :period => '30_days'
512 assert_equal '2011-11-15'.to_date, assigns(:from)
512 assert_equal '2011-11-15'.to_date, assigns(:from)
513 assert_equal '2011-12-15'.to_date, assigns(:to)
513 assert_equal '2011-12-15'.to_date, assigns(:to)
514 end
514 end
515
515
516 def test_index_current_year
516 def test_index_current_year
517 Date.stubs(:today).returns('2011-12-15'.to_date)
517 Date.stubs(:today).returns('2011-12-15'.to_date)
518 get :index, :period => 'current_year'
518 get :index, :period => 'current_year'
519 assert_equal '2011-01-01'.to_date, assigns(:from)
519 assert_equal '2011-01-01'.to_date, assigns(:from)
520 assert_equal '2011-12-31'.to_date, assigns(:to)
520 assert_equal '2011-12-31'.to_date, assigns(:to)
521 end
521 end
522
522
523 def test_index_at_issue_level
523 def test_index_at_issue_level
524 get :index, :issue_id => 1
524 get :index, :issue_id => 1
525 assert_response :success
525 assert_response :success
526 assert_template 'index'
526 assert_template 'index'
527 assert_not_nil assigns(:entries)
527 assert_not_nil assigns(:entries)
528 assert_equal 2, assigns(:entries).size
528 assert_equal 2, assigns(:entries).size
529 assert_not_nil assigns(:total_hours)
529 assert_not_nil assigns(:total_hours)
530 assert_equal 154.25, assigns(:total_hours)
530 assert_equal 154.25, assigns(:total_hours)
531 # display all time
531 # display all time
532 assert_nil assigns(:from)
532 assert_nil assigns(:from)
533 assert_nil assigns(:to)
533 assert_nil assigns(:to)
534 # TODO: remove /projects/:project_id/issues/:issue_id/time_entries routes
534 # TODO: remove /projects/:project_id/issues/:issue_id/time_entries routes
535 # to use /issues/:issue_id/time_entries
535 # to use /issues/:issue_id/time_entries
536 assert_tag :form,
536 assert_tag :form,
537 :attributes => {:action => "/projects/ecookbook/issues/1/time_entries", :id => 'query_form'}
537 :attributes => {:action => "/projects/ecookbook/issues/1/time_entries", :id => 'query_form'}
538 end
538 end
539
539
540 def test_index_atom_feed
540 def test_index_atom_feed
541 get :index, :project_id => 1, :format => 'atom'
541 get :index, :project_id => 1, :format => 'atom'
542 assert_response :success
542 assert_response :success
543 assert_equal 'application/atom+xml', @response.content_type
543 assert_equal 'application/atom+xml', @response.content_type
544 assert_not_nil assigns(:items)
544 assert_not_nil assigns(:items)
545 assert assigns(:items).first.is_a?(TimeEntry)
545 assert assigns(:items).first.is_a?(TimeEntry)
546 end
546 end
547
547
548 def test_index_all_projects_csv_export
548 def test_index_all_projects_csv_export
549 Setting.date_format = '%m/%d/%Y'
549 Setting.date_format = '%m/%d/%Y'
550 get :index, :format => 'csv'
550 get :index, :format => 'csv'
551 assert_response :success
551 assert_response :success
552 assert_equal 'text/csv', @response.content_type
552 assert_equal 'text/csv', @response.content_type
553 assert @response.body.include?("Date,User,Activity,Project,Issue,Tracker,Subject,Hours,Comment,Overtime\n")
553 assert @response.body.include?("Date,User,Activity,Project,Issue,Tracker,Subject,Hours,Comment,Overtime\n")
554 assert @response.body.include?("\n04/21/2007,redMine Admin,Design,eCookbook,3,Bug,Error 281 when updating a recipe,1.0,\"\",\"\"\n")
554 assert @response.body.include?("\n04/21/2007,redMine Admin,Design,eCookbook,3,Bug,Error 281 when updating a recipe,1.0,\"\",\"\"\n")
555 end
555 end
556
556
557 def test_index_csv_export
557 def test_index_csv_export
558 Setting.date_format = '%m/%d/%Y'
558 Setting.date_format = '%m/%d/%Y'
559 get :index, :project_id => 1, :format => 'csv'
559 get :index, :project_id => 1, :format => 'csv'
560 assert_response :success
560 assert_response :success
561 assert_equal 'text/csv', @response.content_type
561 assert_equal 'text/csv', @response.content_type
562 assert @response.body.include?("Date,User,Activity,Project,Issue,Tracker,Subject,Hours,Comment,Overtime\n")
562 assert @response.body.include?("Date,User,Activity,Project,Issue,Tracker,Subject,Hours,Comment,Overtime\n")
563 assert @response.body.include?("\n04/21/2007,redMine Admin,Design,eCookbook,3,Bug,Error 281 when updating a recipe,1.0,\"\",\"\"\n")
563 assert @response.body.include?("\n04/21/2007,redMine Admin,Design,eCookbook,3,Bug,Error 281 when updating a recipe,1.0,\"\",\"\"\n")
564 end
564 end
565
565
566 def test_index_csv_export_with_multi_custom_field
567 field = TimeEntryCustomField.create!(:name => 'Test', :field_format => 'list',
568 :multiple => true, :possible_values => ['value1', 'value2'])
569 entry = TimeEntry.find(1)
570 entry.custom_field_values = {field.id => ['value1', 'value2']}
571 entry.save!
572
573 get :index, :project_id => 1, :format => 'csv'
574 assert_response :success
575 assert_include '"value1, value2"', @response.body
576 end
577
566 def test_csv_big_5
578 def test_csv_big_5
567 user = User.find_by_id(3)
579 user = User.find_by_id(3)
568 user.language = "zh-TW"
580 user.language = "zh-TW"
569 assert user.save
581 assert user.save
570 str_utf8 = "\xe4\xb8\x80\xe6\x9c\x88"
582 str_utf8 = "\xe4\xb8\x80\xe6\x9c\x88"
571 str_big5 = "\xa4@\xa4\xeb"
583 str_big5 = "\xa4@\xa4\xeb"
572 if str_utf8.respond_to?(:force_encoding)
584 if str_utf8.respond_to?(:force_encoding)
573 str_utf8.force_encoding('UTF-8')
585 str_utf8.force_encoding('UTF-8')
574 str_big5.force_encoding('Big5')
586 str_big5.force_encoding('Big5')
575 end
587 end
576 @request.session[:user_id] = 3
588 @request.session[:user_id] = 3
577 post :create, :project_id => 1,
589 post :create, :project_id => 1,
578 :time_entry => {:comments => str_utf8,
590 :time_entry => {:comments => str_utf8,
579 # Not the default activity
591 # Not the default activity
580 :activity_id => '11',
592 :activity_id => '11',
581 :issue_id => '',
593 :issue_id => '',
582 :spent_on => '2011-11-10',
594 :spent_on => '2011-11-10',
583 :hours => '7.3'}
595 :hours => '7.3'}
584 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
596 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
585
597
586 t = TimeEntry.find_by_comments(str_utf8)
598 t = TimeEntry.find_by_comments(str_utf8)
587 assert_not_nil t
599 assert_not_nil t
588 assert_equal 11, t.activity_id
600 assert_equal 11, t.activity_id
589 assert_equal 7.3, t.hours
601 assert_equal 7.3, t.hours
590 assert_equal 3, t.user_id
602 assert_equal 3, t.user_id
591
603
592 get :index, :project_id => 1, :format => 'csv',
604 get :index, :project_id => 1, :format => 'csv',
593 :from => '2011-11-10', :to => '2011-11-10'
605 :from => '2011-11-10', :to => '2011-11-10'
594 assert_response :success
606 assert_response :success
595 assert_equal 'text/csv', @response.content_type
607 assert_equal 'text/csv', @response.content_type
596 ar = @response.body.chomp.split("\n")
608 ar = @response.body.chomp.split("\n")
597 s1 = "\xa4\xe9\xb4\xc1"
609 s1 = "\xa4\xe9\xb4\xc1"
598 if str_utf8.respond_to?(:force_encoding)
610 if str_utf8.respond_to?(:force_encoding)
599 s1.force_encoding('Big5')
611 s1.force_encoding('Big5')
600 end
612 end
601 assert ar[0].include?(s1)
613 assert ar[0].include?(s1)
602 assert ar[1].include?(str_big5)
614 assert ar[1].include?(str_big5)
603 end
615 end
604
616
605 def test_csv_cannot_convert_should_be_replaced_big_5
617 def test_csv_cannot_convert_should_be_replaced_big_5
606 user = User.find_by_id(3)
618 user = User.find_by_id(3)
607 user.language = "zh-TW"
619 user.language = "zh-TW"
608 assert user.save
620 assert user.save
609 str_utf8 = "\xe4\xbb\xa5\xe5\x86\x85"
621 str_utf8 = "\xe4\xbb\xa5\xe5\x86\x85"
610 if str_utf8.respond_to?(:force_encoding)
622 if str_utf8.respond_to?(:force_encoding)
611 str_utf8.force_encoding('UTF-8')
623 str_utf8.force_encoding('UTF-8')
612 end
624 end
613 @request.session[:user_id] = 3
625 @request.session[:user_id] = 3
614 post :create, :project_id => 1,
626 post :create, :project_id => 1,
615 :time_entry => {:comments => str_utf8,
627 :time_entry => {:comments => str_utf8,
616 # Not the default activity
628 # Not the default activity
617 :activity_id => '11',
629 :activity_id => '11',
618 :issue_id => '',
630 :issue_id => '',
619 :spent_on => '2011-11-10',
631 :spent_on => '2011-11-10',
620 :hours => '7.3'}
632 :hours => '7.3'}
621 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
633 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
622
634
623 t = TimeEntry.find_by_comments(str_utf8)
635 t = TimeEntry.find_by_comments(str_utf8)
624 assert_not_nil t
636 assert_not_nil t
625 assert_equal 11, t.activity_id
637 assert_equal 11, t.activity_id
626 assert_equal 7.3, t.hours
638 assert_equal 7.3, t.hours
627 assert_equal 3, t.user_id
639 assert_equal 3, t.user_id
628
640
629 get :index, :project_id => 1, :format => 'csv',
641 get :index, :project_id => 1, :format => 'csv',
630 :from => '2011-11-10', :to => '2011-11-10'
642 :from => '2011-11-10', :to => '2011-11-10'
631 assert_response :success
643 assert_response :success
632 assert_equal 'text/csv', @response.content_type
644 assert_equal 'text/csv', @response.content_type
633 ar = @response.body.chomp.split("\n")
645 ar = @response.body.chomp.split("\n")
634 s1 = "\xa4\xe9\xb4\xc1"
646 s1 = "\xa4\xe9\xb4\xc1"
635 if str_utf8.respond_to?(:force_encoding)
647 if str_utf8.respond_to?(:force_encoding)
636 s1.force_encoding('Big5')
648 s1.force_encoding('Big5')
637 end
649 end
638 assert ar[0].include?(s1)
650 assert ar[0].include?(s1)
639 s2 = ar[1].split(",")[8]
651 s2 = ar[1].split(",")[8]
640 if s2.respond_to?(:force_encoding)
652 if s2.respond_to?(:force_encoding)
641 s3 = "\xa5H?"
653 s3 = "\xa5H?"
642 s3.force_encoding('Big5')
654 s3.force_encoding('Big5')
643 assert_equal s3, s2
655 assert_equal s3, s2
644 elsif RUBY_PLATFORM == 'java'
656 elsif RUBY_PLATFORM == 'java'
645 assert_equal "??", s2
657 assert_equal "??", s2
646 else
658 else
647 assert_equal "\xa5H???", s2
659 assert_equal "\xa5H???", s2
648 end
660 end
649 end
661 end
650
662
651 def test_csv_tw
663 def test_csv_tw
652 with_settings :default_language => "zh-TW" do
664 with_settings :default_language => "zh-TW" do
653 str1 = "test_csv_tw"
665 str1 = "test_csv_tw"
654 user = User.find_by_id(3)
666 user = User.find_by_id(3)
655 te1 = TimeEntry.create(:spent_on => '2011-11-10',
667 te1 = TimeEntry.create(:spent_on => '2011-11-10',
656 :hours => 999.9,
668 :hours => 999.9,
657 :project => Project.find(1),
669 :project => Project.find(1),
658 :user => user,
670 :user => user,
659 :activity => TimeEntryActivity.find_by_name('Design'),
671 :activity => TimeEntryActivity.find_by_name('Design'),
660 :comments => str1)
672 :comments => str1)
661 te2 = TimeEntry.find_by_comments(str1)
673 te2 = TimeEntry.find_by_comments(str1)
662 assert_not_nil te2
674 assert_not_nil te2
663 assert_equal 999.9, te2.hours
675 assert_equal 999.9, te2.hours
664 assert_equal 3, te2.user_id
676 assert_equal 3, te2.user_id
665
677
666 get :index, :project_id => 1, :format => 'csv',
678 get :index, :project_id => 1, :format => 'csv',
667 :from => '2011-11-10', :to => '2011-11-10'
679 :from => '2011-11-10', :to => '2011-11-10'
668 assert_response :success
680 assert_response :success
669 assert_equal 'text/csv', @response.content_type
681 assert_equal 'text/csv', @response.content_type
670
682
671 ar = @response.body.chomp.split("\n")
683 ar = @response.body.chomp.split("\n")
672 s2 = ar[1].split(",")[7]
684 s2 = ar[1].split(",")[7]
673 assert_equal '999.9', s2
685 assert_equal '999.9', s2
674
686
675 str_tw = "Traditional Chinese (\xe7\xb9\x81\xe9\xab\x94\xe4\xb8\xad\xe6\x96\x87)"
687 str_tw = "Traditional Chinese (\xe7\xb9\x81\xe9\xab\x94\xe4\xb8\xad\xe6\x96\x87)"
676 if str_tw.respond_to?(:force_encoding)
688 if str_tw.respond_to?(:force_encoding)
677 str_tw.force_encoding('UTF-8')
689 str_tw.force_encoding('UTF-8')
678 end
690 end
679 assert_equal str_tw, l(:general_lang_name)
691 assert_equal str_tw, l(:general_lang_name)
680 assert_equal ',', l(:general_csv_separator)
692 assert_equal ',', l(:general_csv_separator)
681 assert_equal '.', l(:general_csv_decimal_separator)
693 assert_equal '.', l(:general_csv_decimal_separator)
682 end
694 end
683 end
695 end
684
696
685 def test_csv_fr
697 def test_csv_fr
686 with_settings :default_language => "fr" do
698 with_settings :default_language => "fr" do
687 str1 = "test_csv_fr"
699 str1 = "test_csv_fr"
688 user = User.find_by_id(3)
700 user = User.find_by_id(3)
689 te1 = TimeEntry.create(:spent_on => '2011-11-10',
701 te1 = TimeEntry.create(:spent_on => '2011-11-10',
690 :hours => 999.9,
702 :hours => 999.9,
691 :project => Project.find(1),
703 :project => Project.find(1),
692 :user => user,
704 :user => user,
693 :activity => TimeEntryActivity.find_by_name('Design'),
705 :activity => TimeEntryActivity.find_by_name('Design'),
694 :comments => str1)
706 :comments => str1)
695 te2 = TimeEntry.find_by_comments(str1)
707 te2 = TimeEntry.find_by_comments(str1)
696 assert_not_nil te2
708 assert_not_nil te2
697 assert_equal 999.9, te2.hours
709 assert_equal 999.9, te2.hours
698 assert_equal 3, te2.user_id
710 assert_equal 3, te2.user_id
699
711
700 get :index, :project_id => 1, :format => 'csv',
712 get :index, :project_id => 1, :format => 'csv',
701 :from => '2011-11-10', :to => '2011-11-10'
713 :from => '2011-11-10', :to => '2011-11-10'
702 assert_response :success
714 assert_response :success
703 assert_equal 'text/csv', @response.content_type
715 assert_equal 'text/csv', @response.content_type
704
716
705 ar = @response.body.chomp.split("\n")
717 ar = @response.body.chomp.split("\n")
706 s2 = ar[1].split(";")[7]
718 s2 = ar[1].split(";")[7]
707 assert_equal '999,9', s2
719 assert_equal '999,9', s2
708
720
709 str_fr = "Fran\xc3\xa7ais"
721 str_fr = "Fran\xc3\xa7ais"
710 if str_fr.respond_to?(:force_encoding)
722 if str_fr.respond_to?(:force_encoding)
711 str_fr.force_encoding('UTF-8')
723 str_fr.force_encoding('UTF-8')
712 end
724 end
713 assert_equal str_fr, l(:general_lang_name)
725 assert_equal str_fr, l(:general_lang_name)
714 assert_equal ';', l(:general_csv_separator)
726 assert_equal ';', l(:general_csv_separator)
715 assert_equal ',', l(:general_csv_decimal_separator)
727 assert_equal ',', l(:general_csv_decimal_separator)
716 end
728 end
717 end
729 end
718 end
730 end
General Comments 0
You need to be logged in to leave comments. Login now