##// END OF EJS Templates
Use regular edit/update actions and named routes for JournalsController....
Jean-Philippe Lang -
r14692:6bb1ea8ae8a1
parent child
Show More
@@ -1,109 +1,110
1 # Redmine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006-2015 Jean-Philippe Lang
2 # Copyright (C) 2006-2015 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 class JournalsController < ApplicationController
18 class JournalsController < ApplicationController
19 before_filter :find_journal, :only => [:edit, :diff]
19 before_filter :find_journal, :only => [:edit, :update, :diff]
20 before_filter :find_issue, :only => [:new]
20 before_filter :find_issue, :only => [:new]
21 before_filter :find_optional_project, :only => [:index]
21 before_filter :find_optional_project, :only => [:index]
22 before_filter :authorize, :only => [:new, :edit, :diff]
22 before_filter :authorize, :only => [:new, :edit, :update, :diff]
23 accept_rss_auth :index
23 accept_rss_auth :index
24 menu_item :issues
24 menu_item :issues
25
25
26 helper :issues
26 helper :issues
27 helper :custom_fields
27 helper :custom_fields
28 helper :queries
28 helper :queries
29 include QueriesHelper
29 include QueriesHelper
30 helper :sort
30 helper :sort
31 include SortHelper
31 include SortHelper
32
32
33 def index
33 def index
34 retrieve_query
34 retrieve_query
35 sort_init 'id', 'desc'
35 sort_init 'id', 'desc'
36 sort_update(@query.sortable_columns)
36 sort_update(@query.sortable_columns)
37 if @query.valid?
37 if @query.valid?
38 @journals = @query.journals(:order => "#{Journal.table_name}.created_on DESC",
38 @journals = @query.journals(:order => "#{Journal.table_name}.created_on DESC",
39 :limit => 25)
39 :limit => 25)
40 end
40 end
41 @title = (@project ? @project.name : Setting.app_title) + ": " + (@query.new_record? ? l(:label_changes_details) : @query.name)
41 @title = (@project ? @project.name : Setting.app_title) + ": " + (@query.new_record? ? l(:label_changes_details) : @query.name)
42 render :layout => false, :content_type => 'application/atom+xml'
42 render :layout => false, :content_type => 'application/atom+xml'
43 rescue ActiveRecord::RecordNotFound
43 rescue ActiveRecord::RecordNotFound
44 render_404
44 render_404
45 end
45 end
46
46
47 def diff
47 def diff
48 @issue = @journal.issue
48 @issue = @journal.issue
49 if params[:detail_id].present?
49 if params[:detail_id].present?
50 @detail = @journal.details.find_by_id(params[:detail_id])
50 @detail = @journal.details.find_by_id(params[:detail_id])
51 else
51 else
52 @detail = @journal.details.detect {|d| d.property == 'attr' && d.prop_key == 'description'}
52 @detail = @journal.details.detect {|d| d.property == 'attr' && d.prop_key == 'description'}
53 end
53 end
54 unless @issue && @detail
54 unless @issue && @detail
55 render_404
55 render_404
56 return false
56 return false
57 end
57 end
58 if @detail.property == 'cf'
58 if @detail.property == 'cf'
59 unless @detail.custom_field && @detail.custom_field.visible_by?(@issue.project, User.current)
59 unless @detail.custom_field && @detail.custom_field.visible_by?(@issue.project, User.current)
60 raise ::Unauthorized
60 raise ::Unauthorized
61 end
61 end
62 end
62 end
63 @diff = Redmine::Helpers::Diff.new(@detail.value, @detail.old_value)
63 @diff = Redmine::Helpers::Diff.new(@detail.value, @detail.old_value)
64 end
64 end
65
65
66 def new
66 def new
67 @journal = Journal.visible.find(params[:journal_id]) if params[:journal_id]
67 @journal = Journal.visible.find(params[:journal_id]) if params[:journal_id]
68 if @journal
68 if @journal
69 user = @journal.user
69 user = @journal.user
70 text = @journal.notes
70 text = @journal.notes
71 else
71 else
72 user = @issue.author
72 user = @issue.author
73 text = @issue.description
73 text = @issue.description
74 end
74 end
75 # Replaces pre blocks with [...]
75 # Replaces pre blocks with [...]
76 text = text.to_s.strip.gsub(%r{<pre>(.*?)</pre>}m, '[...]')
76 text = text.to_s.strip.gsub(%r{<pre>(.*?)</pre>}m, '[...]')
77 @content = "#{ll(Setting.default_language, :text_user_wrote, user)}\n> "
77 @content = "#{ll(Setting.default_language, :text_user_wrote, user)}\n> "
78 @content << text.gsub(/(\r?\n|\r\n?)/, "\n> ") + "\n\n"
78 @content << text.gsub(/(\r?\n|\r\n?)/, "\n> ") + "\n\n"
79 rescue ActiveRecord::RecordNotFound
79 rescue ActiveRecord::RecordNotFound
80 render_404
80 render_404
81 end
81 end
82
82
83 def edit
83 def edit
84 (render_403; return false) unless @journal.editable_by?(User.current)
84 (render_403; return false) unless @journal.editable_by?(User.current)
85 if request.post?
85 respond_to do |format|
86 @journal.update_attributes(:notes => params[:notes]) if params[:notes]
86 # TODO: implement non-JS journal update
87 @journal.destroy if @journal.details.empty? && @journal.notes.blank?
87 format.js
88 call_hook(:controller_journals_edit_post, { :journal => @journal, :params => params})
88 end
89 respond_to do |format|
89 end
90 format.html { redirect_to issue_path(@journal.journalized) }
90
91 format.js { render :action => 'update' }
91 def update
92 end
92 (render_403; return false) unless @journal.editable_by?(User.current)
93 else
93 @journal.update_attributes(:notes => params[:notes]) if params[:notes]
94 respond_to do |format|
94 @journal.destroy if @journal.details.empty? && @journal.notes.blank?
95 # TODO: implement non-JS journal update
95 call_hook(:controller_journals_edit_post, { :journal => @journal, :params => params})
96 format.js
96 respond_to do |format|
97 end
97 format.html { redirect_to issue_path(@journal.journalized) }
98 format.js
98 end
99 end
99 end
100 end
100
101
101 private
102 private
102
103
103 def find_journal
104 def find_journal
104 @journal = Journal.visible.find(params[:id])
105 @journal = Journal.visible.find(params[:id])
105 @project = @journal.journalized.project
106 @project = @journal.journalized.project
106 rescue ActiveRecord::RecordNotFound
107 rescue ActiveRecord::RecordNotFound
107 render_404
108 render_404
108 end
109 end
109 end
110 end
@@ -1,515 +1,514
1 # encoding: utf-8
1 # encoding: utf-8
2 #
2 #
3 # Redmine - project management software
3 # Redmine - project management software
4 # Copyright (C) 2006-2015 Jean-Philippe Lang
4 # Copyright (C) 2006-2015 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 include Redmine::Export::PDF::IssuesPdfHelper
22 include Redmine::Export::PDF::IssuesPdfHelper
23
23
24 def issue_list(issues, &block)
24 def issue_list(issues, &block)
25 ancestors = []
25 ancestors = []
26 issues.each do |issue|
26 issues.each do |issue|
27 while (ancestors.any? && !issue.is_descendant_of?(ancestors.last))
27 while (ancestors.any? && !issue.is_descendant_of?(ancestors.last))
28 ancestors.pop
28 ancestors.pop
29 end
29 end
30 yield issue, ancestors.size
30 yield issue, ancestors.size
31 ancestors << issue unless issue.leaf?
31 ancestors << issue unless issue.leaf?
32 end
32 end
33 end
33 end
34
34
35 def grouped_issue_list(issues, query, issue_count_by_group, &block)
35 def grouped_issue_list(issues, query, issue_count_by_group, &block)
36 previous_group, first = false, true
36 previous_group, first = false, true
37 totals_by_group = query.totalable_columns.inject({}) do |h, column|
37 totals_by_group = query.totalable_columns.inject({}) do |h, column|
38 h[column] = query.total_by_group_for(column)
38 h[column] = query.total_by_group_for(column)
39 h
39 h
40 end
40 end
41 issue_list(issues) do |issue, level|
41 issue_list(issues) do |issue, level|
42 group_name = group_count = nil
42 group_name = group_count = nil
43 if query.grouped?
43 if query.grouped?
44 group = query.group_by_column.value(issue)
44 group = query.group_by_column.value(issue)
45 if first || group != previous_group
45 if first || group != previous_group
46 if group.blank? && group != false
46 if group.blank? && group != false
47 group_name = "(#{l(:label_blank_value)})"
47 group_name = "(#{l(:label_blank_value)})"
48 else
48 else
49 group_name = format_object(group)
49 group_name = format_object(group)
50 end
50 end
51 group_name ||= ""
51 group_name ||= ""
52 group_count = issue_count_by_group[group]
52 group_count = issue_count_by_group[group]
53 group_totals = totals_by_group.map {|column, t| total_tag(column, t[group] || 0)}.join(" ").html_safe
53 group_totals = totals_by_group.map {|column, t| total_tag(column, t[group] || 0)}.join(" ").html_safe
54 end
54 end
55 end
55 end
56 yield issue, level, group_name, group_count, group_totals
56 yield issue, level, group_name, group_count, group_totals
57 previous_group, first = group, false
57 previous_group, first = group, false
58 end
58 end
59 end
59 end
60
60
61 # Renders a HTML/CSS tooltip
61 # Renders a HTML/CSS tooltip
62 #
62 #
63 # To use, a trigger div is needed. This is a div with the class of "tooltip"
63 # To use, a trigger div is needed. This is a div with the class of "tooltip"
64 # that contains this method wrapped in a span with the class of "tip"
64 # that contains this method wrapped in a span with the class of "tip"
65 #
65 #
66 # <div class="tooltip"><%= link_to_issue(issue) %>
66 # <div class="tooltip"><%= link_to_issue(issue) %>
67 # <span class="tip"><%= render_issue_tooltip(issue) %></span>
67 # <span class="tip"><%= render_issue_tooltip(issue) %></span>
68 # </div>
68 # </div>
69 #
69 #
70 def render_issue_tooltip(issue)
70 def render_issue_tooltip(issue)
71 @cached_label_status ||= l(:field_status)
71 @cached_label_status ||= l(:field_status)
72 @cached_label_start_date ||= l(:field_start_date)
72 @cached_label_start_date ||= l(:field_start_date)
73 @cached_label_due_date ||= l(:field_due_date)
73 @cached_label_due_date ||= l(:field_due_date)
74 @cached_label_assigned_to ||= l(:field_assigned_to)
74 @cached_label_assigned_to ||= l(:field_assigned_to)
75 @cached_label_priority ||= l(:field_priority)
75 @cached_label_priority ||= l(:field_priority)
76 @cached_label_project ||= l(:field_project)
76 @cached_label_project ||= l(:field_project)
77
77
78 link_to_issue(issue) + "<br /><br />".html_safe +
78 link_to_issue(issue) + "<br /><br />".html_safe +
79 "<strong>#{@cached_label_project}</strong>: #{link_to_project(issue.project)}<br />".html_safe +
79 "<strong>#{@cached_label_project}</strong>: #{link_to_project(issue.project)}<br />".html_safe +
80 "<strong>#{@cached_label_status}</strong>: #{h(issue.status.name)}<br />".html_safe +
80 "<strong>#{@cached_label_status}</strong>: #{h(issue.status.name)}<br />".html_safe +
81 "<strong>#{@cached_label_start_date}</strong>: #{format_date(issue.start_date)}<br />".html_safe +
81 "<strong>#{@cached_label_start_date}</strong>: #{format_date(issue.start_date)}<br />".html_safe +
82 "<strong>#{@cached_label_due_date}</strong>: #{format_date(issue.due_date)}<br />".html_safe +
82 "<strong>#{@cached_label_due_date}</strong>: #{format_date(issue.due_date)}<br />".html_safe +
83 "<strong>#{@cached_label_assigned_to}</strong>: #{h(issue.assigned_to)}<br />".html_safe +
83 "<strong>#{@cached_label_assigned_to}</strong>: #{h(issue.assigned_to)}<br />".html_safe +
84 "<strong>#{@cached_label_priority}</strong>: #{h(issue.priority.name)}".html_safe
84 "<strong>#{@cached_label_priority}</strong>: #{h(issue.priority.name)}".html_safe
85 end
85 end
86
86
87 def issue_heading(issue)
87 def issue_heading(issue)
88 h("#{issue.tracker} ##{issue.id}")
88 h("#{issue.tracker} ##{issue.id}")
89 end
89 end
90
90
91 def render_issue_subject_with_tree(issue)
91 def render_issue_subject_with_tree(issue)
92 s = ''
92 s = ''
93 ancestors = issue.root? ? [] : issue.ancestors.visible.to_a
93 ancestors = issue.root? ? [] : issue.ancestors.visible.to_a
94 ancestors.each do |ancestor|
94 ancestors.each do |ancestor|
95 s << '<div>' + content_tag('p', link_to_issue(ancestor, :project => (issue.project_id != ancestor.project_id)))
95 s << '<div>' + content_tag('p', link_to_issue(ancestor, :project => (issue.project_id != ancestor.project_id)))
96 end
96 end
97 s << '<div>'
97 s << '<div>'
98 subject = h(issue.subject)
98 subject = h(issue.subject)
99 if issue.is_private?
99 if issue.is_private?
100 subject = content_tag('span', l(:field_is_private), :class => 'private') + ' ' + subject
100 subject = content_tag('span', l(:field_is_private), :class => 'private') + ' ' + subject
101 end
101 end
102 s << content_tag('h3', subject)
102 s << content_tag('h3', subject)
103 s << '</div>' * (ancestors.size + 1)
103 s << '</div>' * (ancestors.size + 1)
104 s.html_safe
104 s.html_safe
105 end
105 end
106
106
107 def render_descendants_tree(issue)
107 def render_descendants_tree(issue)
108 s = '<form><table class="list issues">'
108 s = '<form><table class="list issues">'
109 issue_list(issue.descendants.visible.preload(:status, :priority, :tracker).sort_by(&:lft)) do |child, level|
109 issue_list(issue.descendants.visible.preload(:status, :priority, :tracker).sort_by(&:lft)) do |child, level|
110 css = "issue issue-#{child.id} hascontextmenu #{issue.css_classes}"
110 css = "issue issue-#{child.id} hascontextmenu #{issue.css_classes}"
111 css << " idnt idnt-#{level}" if level > 0
111 css << " idnt idnt-#{level}" if level > 0
112 s << content_tag('tr',
112 s << content_tag('tr',
113 content_tag('td', check_box_tag("ids[]", child.id, false, :id => nil), :class => 'checkbox') +
113 content_tag('td', check_box_tag("ids[]", child.id, false, :id => nil), :class => 'checkbox') +
114 content_tag('td', link_to_issue(child, :project => (issue.project_id != child.project_id)), :class => 'subject', :style => 'width: 50%') +
114 content_tag('td', link_to_issue(child, :project => (issue.project_id != child.project_id)), :class => 'subject', :style => 'width: 50%') +
115 content_tag('td', h(child.status)) +
115 content_tag('td', h(child.status)) +
116 content_tag('td', link_to_user(child.assigned_to)) +
116 content_tag('td', link_to_user(child.assigned_to)) +
117 content_tag('td', progress_bar(child.done_ratio)),
117 content_tag('td', progress_bar(child.done_ratio)),
118 :class => css)
118 :class => css)
119 end
119 end
120 s << '</table></form>'
120 s << '</table></form>'
121 s.html_safe
121 s.html_safe
122 end
122 end
123
123
124 def issue_estimated_hours_details(issue)
124 def issue_estimated_hours_details(issue)
125 if issue.total_estimated_hours.present?
125 if issue.total_estimated_hours.present?
126 if issue.total_estimated_hours == issue.estimated_hours
126 if issue.total_estimated_hours == issue.estimated_hours
127 l_hours_short(issue.estimated_hours)
127 l_hours_short(issue.estimated_hours)
128 else
128 else
129 s = issue.estimated_hours.present? ? l_hours_short(issue.estimated_hours) : ""
129 s = issue.estimated_hours.present? ? l_hours_short(issue.estimated_hours) : ""
130 s << " (#{l(:label_total)}: #{l_hours_short(issue.total_estimated_hours)})"
130 s << " (#{l(:label_total)}: #{l_hours_short(issue.total_estimated_hours)})"
131 s.html_safe
131 s.html_safe
132 end
132 end
133 end
133 end
134 end
134 end
135
135
136 def issue_spent_hours_details(issue)
136 def issue_spent_hours_details(issue)
137 if issue.total_spent_hours > 0
137 if issue.total_spent_hours > 0
138 if issue.total_spent_hours == issue.spent_hours
138 if issue.total_spent_hours == issue.spent_hours
139 link_to(l_hours_short(issue.spent_hours), issue_time_entries_path(issue))
139 link_to(l_hours_short(issue.spent_hours), issue_time_entries_path(issue))
140 else
140 else
141 s = issue.spent_hours > 0 ? l_hours_short(issue.spent_hours) : ""
141 s = issue.spent_hours > 0 ? l_hours_short(issue.spent_hours) : ""
142 s << " (#{l(:label_total)}: #{link_to l_hours_short(issue.total_spent_hours), issue_time_entries_path(issue)})"
142 s << " (#{l(:label_total)}: #{link_to l_hours_short(issue.total_spent_hours), issue_time_entries_path(issue)})"
143 s.html_safe
143 s.html_safe
144 end
144 end
145 end
145 end
146 end
146 end
147
147
148 # Returns an array of error messages for bulk edited issues
148 # Returns an array of error messages for bulk edited issues
149 def bulk_edit_error_messages(issues)
149 def bulk_edit_error_messages(issues)
150 messages = {}
150 messages = {}
151 issues.each do |issue|
151 issues.each do |issue|
152 issue.errors.full_messages.each do |message|
152 issue.errors.full_messages.each do |message|
153 messages[message] ||= []
153 messages[message] ||= []
154 messages[message] << issue
154 messages[message] << issue
155 end
155 end
156 end
156 end
157 messages.map { |message, issues|
157 messages.map { |message, issues|
158 "#{message}: " + issues.map {|i| "##{i.id}"}.join(', ')
158 "#{message}: " + issues.map {|i| "##{i.id}"}.join(', ')
159 }
159 }
160 end
160 end
161
161
162 # Returns a link for adding a new subtask to the given issue
162 # Returns a link for adding a new subtask to the given issue
163 def link_to_new_subtask(issue)
163 def link_to_new_subtask(issue)
164 attrs = {
164 attrs = {
165 :tracker_id => issue.tracker,
165 :tracker_id => issue.tracker,
166 :parent_issue_id => issue
166 :parent_issue_id => issue
167 }
167 }
168 link_to(l(:button_add), new_project_issue_path(issue.project, :issue => attrs))
168 link_to(l(:button_add), new_project_issue_path(issue.project, :issue => attrs))
169 end
169 end
170
170
171 class IssueFieldsRows
171 class IssueFieldsRows
172 include ActionView::Helpers::TagHelper
172 include ActionView::Helpers::TagHelper
173
173
174 def initialize
174 def initialize
175 @left = []
175 @left = []
176 @right = []
176 @right = []
177 end
177 end
178
178
179 def left(*args)
179 def left(*args)
180 args.any? ? @left << cells(*args) : @left
180 args.any? ? @left << cells(*args) : @left
181 end
181 end
182
182
183 def right(*args)
183 def right(*args)
184 args.any? ? @right << cells(*args) : @right
184 args.any? ? @right << cells(*args) : @right
185 end
185 end
186
186
187 def size
187 def size
188 @left.size > @right.size ? @left.size : @right.size
188 @left.size > @right.size ? @left.size : @right.size
189 end
189 end
190
190
191 def to_html
191 def to_html
192 content =
192 content =
193 content_tag('div', @left.reduce(&:+), :class => 'splitcontentleft') +
193 content_tag('div', @left.reduce(&:+), :class => 'splitcontentleft') +
194 content_tag('div', @right.reduce(&:+), :class => 'splitcontentleft')
194 content_tag('div', @right.reduce(&:+), :class => 'splitcontentleft')
195
195
196 content_tag('div', content, :class => 'splitcontent')
196 content_tag('div', content, :class => 'splitcontent')
197 end
197 end
198
198
199 def cells(label, text, options={})
199 def cells(label, text, options={})
200 options[:class] = [options[:class] || "", 'attribute'].join(' ')
200 options[:class] = [options[:class] || "", 'attribute'].join(' ')
201 content_tag 'div',
201 content_tag 'div',
202 content_tag('div', label + ":", :class => 'label') + content_tag('div', text, :class => 'value'),
202 content_tag('div', label + ":", :class => 'label') + content_tag('div', text, :class => 'value'),
203 options
203 options
204 end
204 end
205 end
205 end
206
206
207 def issue_fields_rows
207 def issue_fields_rows
208 r = IssueFieldsRows.new
208 r = IssueFieldsRows.new
209 yield r
209 yield r
210 r.to_html
210 r.to_html
211 end
211 end
212
212
213 def render_custom_fields_rows(issue)
213 def render_custom_fields_rows(issue)
214 values = issue.visible_custom_field_values
214 values = issue.visible_custom_field_values
215 return if values.empty?
215 return if values.empty?
216 half = (values.size / 2.0).ceil
216 half = (values.size / 2.0).ceil
217 issue_fields_rows do |rows|
217 issue_fields_rows do |rows|
218 values.each_with_index do |value, i|
218 values.each_with_index do |value, i|
219 css = "cf_#{value.custom_field.id}"
219 css = "cf_#{value.custom_field.id}"
220 m = (i < half ? :left : :right)
220 m = (i < half ? :left : :right)
221 rows.send m, custom_field_name_tag(value.custom_field), show_value(value), :class => css
221 rows.send m, custom_field_name_tag(value.custom_field), show_value(value), :class => css
222 end
222 end
223 end
223 end
224 end
224 end
225
225
226 # Returns the path for updating the issue form
226 # Returns the path for updating the issue form
227 # with project as the current project
227 # with project as the current project
228 def update_issue_form_path(project, issue)
228 def update_issue_form_path(project, issue)
229 options = {:format => 'js'}
229 options = {:format => 'js'}
230 if issue.new_record?
230 if issue.new_record?
231 if project
231 if project
232 new_project_issue_path(project, options)
232 new_project_issue_path(project, options)
233 else
233 else
234 new_issue_path(options)
234 new_issue_path(options)
235 end
235 end
236 else
236 else
237 edit_issue_path(issue, options)
237 edit_issue_path(issue, options)
238 end
238 end
239 end
239 end
240
240
241 # Returns the number of descendants for an array of issues
241 # Returns the number of descendants for an array of issues
242 def issues_descendant_count(issues)
242 def issues_descendant_count(issues)
243 ids = issues.reject(&:leaf?).map {|issue| issue.descendants.ids}.flatten.uniq
243 ids = issues.reject(&:leaf?).map {|issue| issue.descendants.ids}.flatten.uniq
244 ids -= issues.map(&:id)
244 ids -= issues.map(&:id)
245 ids.size
245 ids.size
246 end
246 end
247
247
248 def issues_destroy_confirmation_message(issues)
248 def issues_destroy_confirmation_message(issues)
249 issues = [issues] unless issues.is_a?(Array)
249 issues = [issues] unless issues.is_a?(Array)
250 message = l(:text_issues_destroy_confirmation)
250 message = l(:text_issues_destroy_confirmation)
251
251
252 descendant_count = issues_descendant_count(issues)
252 descendant_count = issues_descendant_count(issues)
253 if descendant_count > 0
253 if descendant_count > 0
254 message << "\n" + l(:text_issues_destroy_descendants_confirmation, :count => descendant_count)
254 message << "\n" + l(:text_issues_destroy_descendants_confirmation, :count => descendant_count)
255 end
255 end
256 message
256 message
257 end
257 end
258
258
259 # Returns an array of users that are proposed as watchers
259 # Returns an array of users that are proposed as watchers
260 # on the new issue form
260 # on the new issue form
261 def users_for_new_issue_watchers(issue)
261 def users_for_new_issue_watchers(issue)
262 users = issue.watcher_users
262 users = issue.watcher_users
263 if issue.project.users.count <= 20
263 if issue.project.users.count <= 20
264 users = (users + issue.project.users.sort).uniq
264 users = (users + issue.project.users.sort).uniq
265 end
265 end
266 users
266 users
267 end
267 end
268
268
269 def sidebar_queries
269 def sidebar_queries
270 unless @sidebar_queries
270 unless @sidebar_queries
271 @sidebar_queries = IssueQuery.visible.
271 @sidebar_queries = IssueQuery.visible.
272 order("#{Query.table_name}.name ASC").
272 order("#{Query.table_name}.name ASC").
273 # Project specific queries and global queries
273 # Project specific queries and global queries
274 where(@project.nil? ? ["project_id IS NULL"] : ["project_id IS NULL OR project_id = ?", @project.id]).
274 where(@project.nil? ? ["project_id IS NULL"] : ["project_id IS NULL OR project_id = ?", @project.id]).
275 to_a
275 to_a
276 end
276 end
277 @sidebar_queries
277 @sidebar_queries
278 end
278 end
279
279
280 def query_links(title, queries)
280 def query_links(title, queries)
281 return '' if queries.empty?
281 return '' if queries.empty?
282 # links to #index on issues/show
282 # links to #index on issues/show
283 url_params = controller_name == 'issues' ? {:controller => 'issues', :action => 'index', :project_id => @project} : params
283 url_params = controller_name == 'issues' ? {:controller => 'issues', :action => 'index', :project_id => @project} : params
284
284
285 content_tag('h3', title) + "\n" +
285 content_tag('h3', title) + "\n" +
286 content_tag('ul',
286 content_tag('ul',
287 queries.collect {|query|
287 queries.collect {|query|
288 css = 'query'
288 css = 'query'
289 css << ' selected' if query == @query
289 css << ' selected' if query == @query
290 content_tag('li', link_to(query.name, url_params.merge(:query_id => query), :class => css))
290 content_tag('li', link_to(query.name, url_params.merge(:query_id => query), :class => css))
291 }.join("\n").html_safe,
291 }.join("\n").html_safe,
292 :class => 'queries'
292 :class => 'queries'
293 ) + "\n"
293 ) + "\n"
294 end
294 end
295
295
296 def render_sidebar_queries
296 def render_sidebar_queries
297 out = ''.html_safe
297 out = ''.html_safe
298 out << query_links(l(:label_my_queries), sidebar_queries.select(&:is_private?))
298 out << query_links(l(:label_my_queries), sidebar_queries.select(&:is_private?))
299 out << query_links(l(:label_query_plural), sidebar_queries.reject(&:is_private?))
299 out << query_links(l(:label_query_plural), sidebar_queries.reject(&:is_private?))
300 out
300 out
301 end
301 end
302
302
303 def email_issue_attributes(issue, user)
303 def email_issue_attributes(issue, user)
304 items = []
304 items = []
305 %w(author status priority assigned_to category fixed_version).each do |attribute|
305 %w(author status priority assigned_to category fixed_version).each do |attribute|
306 unless issue.disabled_core_fields.include?(attribute+"_id")
306 unless issue.disabled_core_fields.include?(attribute+"_id")
307 items << "#{l("field_#{attribute}")}: #{issue.send attribute}"
307 items << "#{l("field_#{attribute}")}: #{issue.send attribute}"
308 end
308 end
309 end
309 end
310 issue.visible_custom_field_values(user).each do |value|
310 issue.visible_custom_field_values(user).each do |value|
311 items << "#{value.custom_field.name}: #{show_value(value, false)}"
311 items << "#{value.custom_field.name}: #{show_value(value, false)}"
312 end
312 end
313 items
313 items
314 end
314 end
315
315
316 def render_email_issue_attributes(issue, user, html=false)
316 def render_email_issue_attributes(issue, user, html=false)
317 items = email_issue_attributes(issue, user)
317 items = email_issue_attributes(issue, user)
318 if html
318 if html
319 content_tag('ul', items.map{|s| content_tag('li', s)}.join("\n").html_safe)
319 content_tag('ul', items.map{|s| content_tag('li', s)}.join("\n").html_safe)
320 else
320 else
321 items.map{|s| "* #{s}"}.join("\n")
321 items.map{|s| "* #{s}"}.join("\n")
322 end
322 end
323 end
323 end
324
324
325 # Returns the textual representation of a journal details
325 # Returns the textual representation of a journal details
326 # as an array of strings
326 # as an array of strings
327 def details_to_strings(details, no_html=false, options={})
327 def details_to_strings(details, no_html=false, options={})
328 options[:only_path] = (options[:only_path] == false ? false : true)
328 options[:only_path] = (options[:only_path] == false ? false : true)
329 strings = []
329 strings = []
330 values_by_field = {}
330 values_by_field = {}
331 details.each do |detail|
331 details.each do |detail|
332 if detail.property == 'cf'
332 if detail.property == 'cf'
333 field = detail.custom_field
333 field = detail.custom_field
334 if field && field.multiple?
334 if field && field.multiple?
335 values_by_field[field] ||= {:added => [], :deleted => []}
335 values_by_field[field] ||= {:added => [], :deleted => []}
336 if detail.old_value
336 if detail.old_value
337 values_by_field[field][:deleted] << detail.old_value
337 values_by_field[field][:deleted] << detail.old_value
338 end
338 end
339 if detail.value
339 if detail.value
340 values_by_field[field][:added] << detail.value
340 values_by_field[field][:added] << detail.value
341 end
341 end
342 next
342 next
343 end
343 end
344 end
344 end
345 strings << show_detail(detail, no_html, options)
345 strings << show_detail(detail, no_html, options)
346 end
346 end
347 if values_by_field.present?
347 if values_by_field.present?
348 multiple_values_detail = Struct.new(:property, :prop_key, :custom_field, :old_value, :value)
348 multiple_values_detail = Struct.new(:property, :prop_key, :custom_field, :old_value, :value)
349 values_by_field.each do |field, changes|
349 values_by_field.each do |field, changes|
350 if changes[:added].any?
350 if changes[:added].any?
351 detail = multiple_values_detail.new('cf', field.id.to_s, field)
351 detail = multiple_values_detail.new('cf', field.id.to_s, field)
352 detail.value = changes[:added]
352 detail.value = changes[:added]
353 strings << show_detail(detail, no_html, options)
353 strings << show_detail(detail, no_html, options)
354 end
354 end
355 if changes[:deleted].any?
355 if changes[:deleted].any?
356 detail = multiple_values_detail.new('cf', field.id.to_s, field)
356 detail = multiple_values_detail.new('cf', field.id.to_s, field)
357 detail.old_value = changes[:deleted]
357 detail.old_value = changes[:deleted]
358 strings << show_detail(detail, no_html, options)
358 strings << show_detail(detail, no_html, options)
359 end
359 end
360 end
360 end
361 end
361 end
362 strings
362 strings
363 end
363 end
364
364
365 # Returns the textual representation of a single journal detail
365 # Returns the textual representation of a single journal detail
366 def show_detail(detail, no_html=false, options={})
366 def show_detail(detail, no_html=false, options={})
367 multiple = false
367 multiple = false
368 show_diff = false
368 show_diff = false
369
369
370 case detail.property
370 case detail.property
371 when 'attr'
371 when 'attr'
372 field = detail.prop_key.to_s.gsub(/\_id$/, "")
372 field = detail.prop_key.to_s.gsub(/\_id$/, "")
373 label = l(("field_" + field).to_sym)
373 label = l(("field_" + field).to_sym)
374 case detail.prop_key
374 case detail.prop_key
375 when 'due_date', 'start_date'
375 when 'due_date', 'start_date'
376 value = format_date(detail.value.to_date) if detail.value
376 value = format_date(detail.value.to_date) if detail.value
377 old_value = format_date(detail.old_value.to_date) if detail.old_value
377 old_value = format_date(detail.old_value.to_date) if detail.old_value
378
378
379 when 'project_id', 'status_id', 'tracker_id', 'assigned_to_id',
379 when 'project_id', 'status_id', 'tracker_id', 'assigned_to_id',
380 'priority_id', 'category_id', 'fixed_version_id'
380 'priority_id', 'category_id', 'fixed_version_id'
381 value = find_name_by_reflection(field, detail.value)
381 value = find_name_by_reflection(field, detail.value)
382 old_value = find_name_by_reflection(field, detail.old_value)
382 old_value = find_name_by_reflection(field, detail.old_value)
383
383
384 when 'estimated_hours'
384 when 'estimated_hours'
385 value = "%0.02f" % detail.value.to_f unless detail.value.blank?
385 value = "%0.02f" % detail.value.to_f unless detail.value.blank?
386 old_value = "%0.02f" % detail.old_value.to_f unless detail.old_value.blank?
386 old_value = "%0.02f" % detail.old_value.to_f unless detail.old_value.blank?
387
387
388 when 'parent_id'
388 when 'parent_id'
389 label = l(:field_parent_issue)
389 label = l(:field_parent_issue)
390 value = "##{detail.value}" unless detail.value.blank?
390 value = "##{detail.value}" unless detail.value.blank?
391 old_value = "##{detail.old_value}" unless detail.old_value.blank?
391 old_value = "##{detail.old_value}" unless detail.old_value.blank?
392
392
393 when 'is_private'
393 when 'is_private'
394 value = l(detail.value == "0" ? :general_text_No : :general_text_Yes) unless detail.value.blank?
394 value = l(detail.value == "0" ? :general_text_No : :general_text_Yes) unless detail.value.blank?
395 old_value = l(detail.old_value == "0" ? :general_text_No : :general_text_Yes) unless detail.old_value.blank?
395 old_value = l(detail.old_value == "0" ? :general_text_No : :general_text_Yes) unless detail.old_value.blank?
396
396
397 when 'description'
397 when 'description'
398 show_diff = true
398 show_diff = true
399 end
399 end
400 when 'cf'
400 when 'cf'
401 custom_field = detail.custom_field
401 custom_field = detail.custom_field
402 if custom_field
402 if custom_field
403 label = custom_field.name
403 label = custom_field.name
404 if custom_field.format.class.change_as_diff
404 if custom_field.format.class.change_as_diff
405 show_diff = true
405 show_diff = true
406 else
406 else
407 multiple = custom_field.multiple?
407 multiple = custom_field.multiple?
408 value = format_value(detail.value, custom_field) if detail.value
408 value = format_value(detail.value, custom_field) if detail.value
409 old_value = format_value(detail.old_value, custom_field) if detail.old_value
409 old_value = format_value(detail.old_value, custom_field) if detail.old_value
410 end
410 end
411 end
411 end
412 when 'attachment'
412 when 'attachment'
413 label = l(:label_attachment)
413 label = l(:label_attachment)
414 when 'relation'
414 when 'relation'
415 if detail.value && !detail.old_value
415 if detail.value && !detail.old_value
416 rel_issue = Issue.visible.find_by_id(detail.value)
416 rel_issue = Issue.visible.find_by_id(detail.value)
417 value = rel_issue.nil? ? "#{l(:label_issue)} ##{detail.value}" :
417 value = rel_issue.nil? ? "#{l(:label_issue)} ##{detail.value}" :
418 (no_html ? rel_issue : link_to_issue(rel_issue, :only_path => options[:only_path]))
418 (no_html ? rel_issue : link_to_issue(rel_issue, :only_path => options[:only_path]))
419 elsif detail.old_value && !detail.value
419 elsif detail.old_value && !detail.value
420 rel_issue = Issue.visible.find_by_id(detail.old_value)
420 rel_issue = Issue.visible.find_by_id(detail.old_value)
421 old_value = rel_issue.nil? ? "#{l(:label_issue)} ##{detail.old_value}" :
421 old_value = rel_issue.nil? ? "#{l(:label_issue)} ##{detail.old_value}" :
422 (no_html ? rel_issue : link_to_issue(rel_issue, :only_path => options[:only_path]))
422 (no_html ? rel_issue : link_to_issue(rel_issue, :only_path => options[:only_path]))
423 end
423 end
424 relation_type = IssueRelation::TYPES[detail.prop_key]
424 relation_type = IssueRelation::TYPES[detail.prop_key]
425 label = l(relation_type[:name]) if relation_type
425 label = l(relation_type[:name]) if relation_type
426 end
426 end
427 call_hook(:helper_issues_show_detail_after_setting,
427 call_hook(:helper_issues_show_detail_after_setting,
428 {:detail => detail, :label => label, :value => value, :old_value => old_value })
428 {:detail => detail, :label => label, :value => value, :old_value => old_value })
429
429
430 label ||= detail.prop_key
430 label ||= detail.prop_key
431 value ||= detail.value
431 value ||= detail.value
432 old_value ||= detail.old_value
432 old_value ||= detail.old_value
433
433
434 unless no_html
434 unless no_html
435 label = content_tag('strong', label)
435 label = content_tag('strong', label)
436 old_value = content_tag("i", h(old_value)) if detail.old_value
436 old_value = content_tag("i", h(old_value)) if detail.old_value
437 if detail.old_value && detail.value.blank? && detail.property != 'relation'
437 if detail.old_value && detail.value.blank? && detail.property != 'relation'
438 old_value = content_tag("del", old_value)
438 old_value = content_tag("del", old_value)
439 end
439 end
440 if detail.property == 'attachment' && value.present? &&
440 if detail.property == 'attachment' && value.present? &&
441 atta = detail.journal.journalized.attachments.detect {|a| a.id == detail.prop_key.to_i}
441 atta = detail.journal.journalized.attachments.detect {|a| a.id == detail.prop_key.to_i}
442 # Link to the attachment if it has not been removed
442 # Link to the attachment if it has not been removed
443 value = link_to_attachment(atta, :download => true, :only_path => options[:only_path])
443 value = link_to_attachment(atta, :download => true, :only_path => options[:only_path])
444 if options[:only_path] != false && atta.is_text?
444 if options[:only_path] != false && atta.is_text?
445 value += link_to('',
445 value += link_to('',
446 { :controller => 'attachments', :action => 'show',
446 { :controller => 'attachments', :action => 'show',
447 :id => atta, :filename => atta.filename },
447 :id => atta, :filename => atta.filename },
448 :class => 'icon icon-magnifier')
448 :class => 'icon icon-magnifier')
449 end
449 end
450 else
450 else
451 value = content_tag("i", h(value)) if value
451 value = content_tag("i", h(value)) if value
452 end
452 end
453 end
453 end
454
454
455 if show_diff
455 if show_diff
456 s = l(:text_journal_changed_no_detail, :label => label)
456 s = l(:text_journal_changed_no_detail, :label => label)
457 unless no_html
457 unless no_html
458 diff_link = link_to 'diff',
458 diff_link = link_to 'diff',
459 {:controller => 'journals', :action => 'diff', :id => detail.journal_id,
459 diff_journal_url(detail.journal_id, :detail_id => detail.id, :only_path => options[:only_path]),
460 :detail_id => detail.id, :only_path => options[:only_path]},
461 :title => l(:label_view_diff)
460 :title => l(:label_view_diff)
462 s << " (#{ diff_link })"
461 s << " (#{ diff_link })"
463 end
462 end
464 s.html_safe
463 s.html_safe
465 elsif detail.value.present?
464 elsif detail.value.present?
466 case detail.property
465 case detail.property
467 when 'attr', 'cf'
466 when 'attr', 'cf'
468 if detail.old_value.present?
467 if detail.old_value.present?
469 l(:text_journal_changed, :label => label, :old => old_value, :new => value).html_safe
468 l(:text_journal_changed, :label => label, :old => old_value, :new => value).html_safe
470 elsif multiple
469 elsif multiple
471 l(:text_journal_added, :label => label, :value => value).html_safe
470 l(:text_journal_added, :label => label, :value => value).html_safe
472 else
471 else
473 l(:text_journal_set_to, :label => label, :value => value).html_safe
472 l(:text_journal_set_to, :label => label, :value => value).html_safe
474 end
473 end
475 when 'attachment', 'relation'
474 when 'attachment', 'relation'
476 l(:text_journal_added, :label => label, :value => value).html_safe
475 l(:text_journal_added, :label => label, :value => value).html_safe
477 end
476 end
478 else
477 else
479 l(:text_journal_deleted, :label => label, :old => old_value).html_safe
478 l(:text_journal_deleted, :label => label, :old => old_value).html_safe
480 end
479 end
481 end
480 end
482
481
483 # Find the name of an associated record stored in the field attribute
482 # Find the name of an associated record stored in the field attribute
484 def find_name_by_reflection(field, id)
483 def find_name_by_reflection(field, id)
485 unless id.present?
484 unless id.present?
486 return nil
485 return nil
487 end
486 end
488 @detail_value_name_by_reflection ||= Hash.new do |hash, key|
487 @detail_value_name_by_reflection ||= Hash.new do |hash, key|
489 association = Issue.reflect_on_association(key.first.to_sym)
488 association = Issue.reflect_on_association(key.first.to_sym)
490 name = nil
489 name = nil
491 if association
490 if association
492 record = association.klass.find_by_id(key.last)
491 record = association.klass.find_by_id(key.last)
493 if record
492 if record
494 name = record.name.force_encoding('UTF-8')
493 name = record.name.force_encoding('UTF-8')
495 end
494 end
496 end
495 end
497 hash[key] = name
496 hash[key] = name
498 end
497 end
499 @detail_value_name_by_reflection[[field, id]]
498 @detail_value_name_by_reflection[[field, id]]
500 end
499 end
501
500
502 # Renders issue children recursively
501 # Renders issue children recursively
503 def render_api_issue_children(issue, api)
502 def render_api_issue_children(issue, api)
504 return if issue.leaf?
503 return if issue.leaf?
505 api.array :children do
504 api.array :children do
506 issue.children.each do |child|
505 issue.children.each do |child|
507 api.issue(:id => child.id) do
506 api.issue(:id => child.id) do
508 api.tracker(:id => child.tracker_id, :name => child.tracker.name) unless child.tracker.nil?
507 api.tracker(:id => child.tracker_id, :name => child.tracker.name) unless child.tracker.nil?
509 api.subject child.subject
508 api.subject child.subject
510 render_api_issue_children(child, api)
509 render_api_issue_children(child, api)
511 end
510 end
512 end
511 end
513 end
512 end
514 end
513 end
515 end
514 end
@@ -1,61 +1,61
1 # encoding: utf-8
1 # encoding: utf-8
2 #
2 #
3 # Redmine - project management software
3 # Redmine - project management software
4 # Copyright (C) 2006-2015 Jean-Philippe Lang
4 # Copyright (C) 2006-2015 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 JournalsHelper
20 module JournalsHelper
21
21
22 # Returns the attachments of a journal that are displayed as thumbnails
22 # Returns the attachments of a journal that are displayed as thumbnails
23 def journal_thumbnail_attachments(journal)
23 def journal_thumbnail_attachments(journal)
24 ids = journal.details.select {|d| d.property == 'attachment' && d.value.present?}.map(&:prop_key)
24 ids = journal.details.select {|d| d.property == 'attachment' && d.value.present?}.map(&:prop_key)
25 ids.any? ? Attachment.where(:id => ids).select(&:thumbnailable?) : []
25 ids.any? ? Attachment.where(:id => ids).select(&:thumbnailable?) : []
26 end
26 end
27
27
28 def render_notes(issue, journal, options={})
28 def render_notes(issue, journal, options={})
29 content = ''
29 content = ''
30 editable = User.current.logged? && (User.current.allowed_to?(:edit_issue_notes, issue.project) || (journal.user == User.current && User.current.allowed_to?(:edit_own_issue_notes, issue.project)))
30 editable = User.current.logged? && (User.current.allowed_to?(:edit_issue_notes, issue.project) || (journal.user == User.current && User.current.allowed_to?(:edit_own_issue_notes, issue.project)))
31 links = []
31 links = []
32 if !journal.notes.blank?
32 if !journal.notes.blank?
33 links << link_to('',
33 links << link_to('',
34 {:controller => 'journals', :action => 'new', :id => issue, :journal_id => journal},
34 quoted_issue_path(issue, :journal_id => journal),
35 :remote => true,
35 :remote => true,
36 :method => 'post',
36 :method => 'post',
37 :title => l(:button_quote),
37 :title => l(:button_quote),
38 :class => 'icon-only icon-comment'
38 :class => 'icon-only icon-comment'
39 ) if options[:reply_links]
39 ) if options[:reply_links]
40 links << link_to('',
40 links << link_to('',
41 {:controller => 'journals', :action => 'edit', :id => journal},
41 edit_journal_path(journal),
42 :remote => true,
42 :remote => true,
43 :method => 'get',
43 :method => 'get',
44 :title => l(:button_edit),
44 :title => l(:button_edit),
45 :class => 'icon-only icon-edit'
45 :class => 'icon-only icon-edit'
46 ) if editable
46 ) if editable
47 links << link_to('',
47 links << link_to('',
48 {:controller => 'journals', :action => 'edit', :id => journal, :notes => ""},
48 journal_path(journal, :notes => ""),
49 :remote => true,
49 :remote => true,
50 :method => :post, :data => {:confirm => l(:text_are_you_sure)},
50 :method => 'put', :data => {:confirm => l(:text_are_you_sure)},
51 :title => l(:button_delete),
51 :title => l(:button_delete),
52 :class => 'icon-only icon-del'
52 :class => 'icon-only icon-del'
53 ) if editable
53 ) if editable
54 end
54 end
55 content << content_tag('div', links.join(' ').html_safe, :class => 'contextual') unless links.empty?
55 content << content_tag('div', links.join(' ').html_safe, :class => 'contextual') unless links.empty?
56 content << textilizable(journal, :notes)
56 content << textilizable(journal, :notes)
57 css_classes = "wiki"
57 css_classes = "wiki"
58 css_classes << " editable" if editable
58 css_classes << " editable" if editable
59 content_tag('div', content.html_safe, :id => "journal-#{journal.id}-notes", :class => css_classes)
59 content_tag('div', content.html_safe, :id => "journal-#{journal.id}-notes", :class => css_classes)
60 end
60 end
61 end
61 end
@@ -1,18 +1,19
1 <%= form_tag({:controller => 'journals', :action => 'edit', :id => @journal},
1 <%= form_tag(journal_path(@journal),
2 :remote => true,
2 :remote => true,
3 :method => 'put',
3 :id => "journal-#{@journal.id}-form") do %>
4 :id => "journal-#{@journal.id}-form") do %>
4 <%= label_tag "notes", l(:description_notes), :class => "hidden-for-sighted" %>
5 <%= label_tag "notes", l(:description_notes), :class => "hidden-for-sighted" %>
5 <%= text_area_tag :notes, @journal.notes,
6 <%= text_area_tag :notes, @journal.notes,
6 :id => "journal_#{@journal.id}_notes",
7 :id => "journal_#{@journal.id}_notes",
7 :class => 'wiki-edit',
8 :class => 'wiki-edit',
8 :rows => (@journal.notes.blank? ? 10 : [[10, @journal.notes.length / 50].max, 100].min) %>
9 :rows => (@journal.notes.blank? ? 10 : [[10, @journal.notes.length / 50].max, 100].min) %>
9 <%= call_hook(:view_journals_notes_form_after_notes, { :journal => @journal}) %>
10 <%= call_hook(:view_journals_notes_form_after_notes, { :journal => @journal}) %>
10 <p><%= submit_tag l(:button_save) %>
11 <p><%= submit_tag l(:button_save) %>
11 <%= preview_link preview_edit_issue_path(:project_id => @project, :id => @journal.issue),
12 <%= preview_link preview_edit_issue_path(:project_id => @project, :id => @journal.issue),
12 "journal-#{@journal.id}-form",
13 "journal-#{@journal.id}-form",
13 "journal_#{@journal.id}_preview" %> |
14 "journal_#{@journal.id}_preview" %> |
14 <%= link_to l(:button_cancel), '#', :onclick => "$('#journal-#{@journal.id}-form').remove(); $('#journal-#{@journal.id}-notes').show(); return false;" %></p>
15 <%= link_to l(:button_cancel), '#', :onclick => "$('#journal-#{@journal.id}-form').remove(); $('#journal-#{@journal.id}-notes').show(); return false;" %></p>
15
16
16 <div id="journal_<%= @journal.id %>_preview" class="wiki"></div>
17 <div id="journal_<%= @journal.id %>_preview" class="wiki"></div>
17 <% end %>
18 <% end %>
18 <%= wikitoolbar_for "journal_#{@journal.id}_notes" %>
19 <%= wikitoolbar_for "journal_#{@journal.id}_notes" %>
@@ -1,376 +1,379
1 # Redmine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006-2015 Jean-Philippe Lang
2 # Copyright (C) 2006-2015 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 Rails.application.routes.draw do
18 Rails.application.routes.draw do
19 root :to => 'welcome#index', :as => 'home'
19 root :to => 'welcome#index', :as => 'home'
20
20
21 match 'login', :to => 'account#login', :as => 'signin', :via => [:get, :post]
21 match 'login', :to => 'account#login', :as => 'signin', :via => [:get, :post]
22 match 'logout', :to => 'account#logout', :as => 'signout', :via => [:get, :post]
22 match 'logout', :to => 'account#logout', :as => 'signout', :via => [:get, :post]
23 match 'account/register', :to => 'account#register', :via => [:get, :post], :as => 'register'
23 match 'account/register', :to => 'account#register', :via => [:get, :post], :as => 'register'
24 match 'account/lost_password', :to => 'account#lost_password', :via => [:get, :post], :as => 'lost_password'
24 match 'account/lost_password', :to => 'account#lost_password', :via => [:get, :post], :as => 'lost_password'
25 match 'account/activate', :to => 'account#activate', :via => :get
25 match 'account/activate', :to => 'account#activate', :via => :get
26 get 'account/activation_email', :to => 'account#activation_email', :as => 'activation_email'
26 get 'account/activation_email', :to => 'account#activation_email', :as => 'activation_email'
27
27
28 match '/news/preview', :controller => 'previews', :action => 'news', :as => 'preview_news', :via => [:get, :post, :put, :patch]
28 match '/news/preview', :controller => 'previews', :action => 'news', :as => 'preview_news', :via => [:get, :post, :put, :patch]
29 match '/issues/preview/new/:project_id', :to => 'previews#issue', :as => 'preview_new_issue', :via => [:get, :post, :put, :patch]
29 match '/issues/preview/new/:project_id', :to => 'previews#issue', :as => 'preview_new_issue', :via => [:get, :post, :put, :patch]
30 match '/issues/preview/edit/:id', :to => 'previews#issue', :as => 'preview_edit_issue', :via => [:get, :post, :put, :patch]
30 match '/issues/preview/edit/:id', :to => 'previews#issue', :as => 'preview_edit_issue', :via => [:get, :post, :put, :patch]
31 match '/issues/preview', :to => 'previews#issue', :as => 'preview_issue', :via => [:get, :post, :put, :patch]
31 match '/issues/preview', :to => 'previews#issue', :as => 'preview_issue', :via => [:get, :post, :put, :patch]
32
32
33 match 'projects/:id/wiki', :to => 'wikis#edit', :via => :post
33 match 'projects/:id/wiki', :to => 'wikis#edit', :via => :post
34 match 'projects/:id/wiki/destroy', :to => 'wikis#destroy', :via => [:get, :post]
34 match 'projects/:id/wiki/destroy', :to => 'wikis#destroy', :via => [:get, :post]
35
35
36 match 'boards/:board_id/topics/new', :to => 'messages#new', :via => [:get, :post], :as => 'new_board_message'
36 match 'boards/:board_id/topics/new', :to => 'messages#new', :via => [:get, :post], :as => 'new_board_message'
37 get 'boards/:board_id/topics/:id', :to => 'messages#show', :as => 'board_message'
37 get 'boards/:board_id/topics/:id', :to => 'messages#show', :as => 'board_message'
38 match 'boards/:board_id/topics/quote/:id', :to => 'messages#quote', :via => [:get, :post]
38 match 'boards/:board_id/topics/quote/:id', :to => 'messages#quote', :via => [:get, :post]
39 get 'boards/:board_id/topics/:id/edit', :to => 'messages#edit'
39 get 'boards/:board_id/topics/:id/edit', :to => 'messages#edit'
40
40
41 post 'boards/:board_id/topics/preview', :to => 'messages#preview', :as => 'preview_board_message'
41 post 'boards/:board_id/topics/preview', :to => 'messages#preview', :as => 'preview_board_message'
42 post 'boards/:board_id/topics/:id/replies', :to => 'messages#reply'
42 post 'boards/:board_id/topics/:id/replies', :to => 'messages#reply'
43 post 'boards/:board_id/topics/:id/edit', :to => 'messages#edit'
43 post 'boards/:board_id/topics/:id/edit', :to => 'messages#edit'
44 post 'boards/:board_id/topics/:id/destroy', :to => 'messages#destroy'
44 post 'boards/:board_id/topics/:id/destroy', :to => 'messages#destroy'
45
45
46 # Misc issue routes. TODO: move into resources
46 # Misc issue routes. TODO: move into resources
47 match '/issues/auto_complete', :to => 'auto_completes#issues', :via => :get, :as => 'auto_complete_issues'
47 match '/issues/auto_complete', :to => 'auto_completes#issues', :via => :get, :as => 'auto_complete_issues'
48 match '/issues/context_menu', :to => 'context_menus#issues', :as => 'issues_context_menu', :via => [:get, :post]
48 match '/issues/context_menu', :to => 'context_menus#issues', :as => 'issues_context_menu', :via => [:get, :post]
49 match '/issues/changes', :to => 'journals#index', :as => 'issue_changes', :via => :get
49 match '/issues/changes', :to => 'journals#index', :as => 'issue_changes', :via => :get
50 match '/issues/:id/quoted', :to => 'journals#new', :id => /\d+/, :via => :post, :as => 'quoted_issue'
50 match '/issues/:id/quoted', :to => 'journals#new', :id => /\d+/, :via => :post, :as => 'quoted_issue'
51
51
52 match '/journals/diff/:id', :to => 'journals#diff', :id => /\d+/, :via => :get
52 resources :journals, :only => [:edit, :update] do
53 match '/journals/edit/:id', :to => 'journals#edit', :id => /\d+/, :via => [:get, :post]
53 member do
54 get 'diff'
55 end
56 end
54
57
55 get '/projects/:project_id/issues/gantt', :to => 'gantts#show', :as => 'project_gantt'
58 get '/projects/:project_id/issues/gantt', :to => 'gantts#show', :as => 'project_gantt'
56 get '/issues/gantt', :to => 'gantts#show'
59 get '/issues/gantt', :to => 'gantts#show'
57
60
58 get '/projects/:project_id/issues/calendar', :to => 'calendars#show', :as => 'project_calendar'
61 get '/projects/:project_id/issues/calendar', :to => 'calendars#show', :as => 'project_calendar'
59 get '/issues/calendar', :to => 'calendars#show'
62 get '/issues/calendar', :to => 'calendars#show'
60
63
61 get 'projects/:id/issues/report', :to => 'reports#issue_report', :as => 'project_issues_report'
64 get 'projects/:id/issues/report', :to => 'reports#issue_report', :as => 'project_issues_report'
62 get 'projects/:id/issues/report/:detail', :to => 'reports#issue_report_details', :as => 'project_issues_report_details'
65 get 'projects/:id/issues/report/:detail', :to => 'reports#issue_report_details', :as => 'project_issues_report_details'
63
66
64 get '/issues/imports/new', :to => 'imports#new', :as => 'new_issues_import'
67 get '/issues/imports/new', :to => 'imports#new', :as => 'new_issues_import'
65 post '/imports', :to => 'imports#create', :as => 'imports'
68 post '/imports', :to => 'imports#create', :as => 'imports'
66 get '/imports/:id', :to => 'imports#show', :as => 'import'
69 get '/imports/:id', :to => 'imports#show', :as => 'import'
67 match '/imports/:id/settings', :to => 'imports#settings', :via => [:get, :post], :as => 'import_settings'
70 match '/imports/:id/settings', :to => 'imports#settings', :via => [:get, :post], :as => 'import_settings'
68 match '/imports/:id/mapping', :to => 'imports#mapping', :via => [:get, :post], :as => 'import_mapping'
71 match '/imports/:id/mapping', :to => 'imports#mapping', :via => [:get, :post], :as => 'import_mapping'
69 match '/imports/:id/run', :to => 'imports#run', :via => [:get, :post], :as => 'import_run'
72 match '/imports/:id/run', :to => 'imports#run', :via => [:get, :post], :as => 'import_run'
70
73
71 match 'my/account', :controller => 'my', :action => 'account', :via => [:get, :post]
74 match 'my/account', :controller => 'my', :action => 'account', :via => [:get, :post]
72 match 'my/account/destroy', :controller => 'my', :action => 'destroy', :via => [:get, :post]
75 match 'my/account/destroy', :controller => 'my', :action => 'destroy', :via => [:get, :post]
73 match 'my/page', :controller => 'my', :action => 'page', :via => :get
76 match 'my/page', :controller => 'my', :action => 'page', :via => :get
74 match 'my', :controller => 'my', :action => 'index', :via => :get # Redirects to my/page
77 match 'my', :controller => 'my', :action => 'index', :via => :get # Redirects to my/page
75 get 'my/api_key', :to => 'my#show_api_key', :as => 'my_api_key'
78 get 'my/api_key', :to => 'my#show_api_key', :as => 'my_api_key'
76 post 'my/api_key', :to => 'my#reset_api_key'
79 post 'my/api_key', :to => 'my#reset_api_key'
77 post 'my/rss_key', :to => 'my#reset_rss_key', :as => 'my_rss_key'
80 post 'my/rss_key', :to => 'my#reset_rss_key', :as => 'my_rss_key'
78 match 'my/password', :controller => 'my', :action => 'password', :via => [:get, :post]
81 match 'my/password', :controller => 'my', :action => 'password', :via => [:get, :post]
79 match 'my/page_layout', :controller => 'my', :action => 'page_layout', :via => :get
82 match 'my/page_layout', :controller => 'my', :action => 'page_layout', :via => :get
80 match 'my/add_block', :controller => 'my', :action => 'add_block', :via => :post
83 match 'my/add_block', :controller => 'my', :action => 'add_block', :via => :post
81 match 'my/remove_block', :controller => 'my', :action => 'remove_block', :via => :post
84 match 'my/remove_block', :controller => 'my', :action => 'remove_block', :via => :post
82 match 'my/order_blocks', :controller => 'my', :action => 'order_blocks', :via => :post
85 match 'my/order_blocks', :controller => 'my', :action => 'order_blocks', :via => :post
83
86
84 resources :users do
87 resources :users do
85 resources :memberships, :controller => 'principal_memberships'
88 resources :memberships, :controller => 'principal_memberships'
86 resources :email_addresses, :only => [:index, :create, :update, :destroy]
89 resources :email_addresses, :only => [:index, :create, :update, :destroy]
87 end
90 end
88
91
89 post 'watchers/watch', :to => 'watchers#watch', :as => 'watch'
92 post 'watchers/watch', :to => 'watchers#watch', :as => 'watch'
90 delete 'watchers/watch', :to => 'watchers#unwatch'
93 delete 'watchers/watch', :to => 'watchers#unwatch'
91 get 'watchers/new', :to => 'watchers#new'
94 get 'watchers/new', :to => 'watchers#new'
92 post 'watchers', :to => 'watchers#create'
95 post 'watchers', :to => 'watchers#create'
93 post 'watchers/append', :to => 'watchers#append'
96 post 'watchers/append', :to => 'watchers#append'
94 delete 'watchers', :to => 'watchers#destroy'
97 delete 'watchers', :to => 'watchers#destroy'
95 get 'watchers/autocomplete_for_user', :to => 'watchers#autocomplete_for_user'
98 get 'watchers/autocomplete_for_user', :to => 'watchers#autocomplete_for_user'
96 # Specific routes for issue watchers API
99 # Specific routes for issue watchers API
97 post 'issues/:object_id/watchers', :to => 'watchers#create', :object_type => 'issue'
100 post 'issues/:object_id/watchers', :to => 'watchers#create', :object_type => 'issue'
98 delete 'issues/:object_id/watchers/:user_id' => 'watchers#destroy', :object_type => 'issue'
101 delete 'issues/:object_id/watchers/:user_id' => 'watchers#destroy', :object_type => 'issue'
99
102
100 resources :projects do
103 resources :projects do
101 member do
104 member do
102 get 'settings(/:tab)', :action => 'settings', :as => 'settings'
105 get 'settings(/:tab)', :action => 'settings', :as => 'settings'
103 post 'modules'
106 post 'modules'
104 post 'archive'
107 post 'archive'
105 post 'unarchive'
108 post 'unarchive'
106 post 'close'
109 post 'close'
107 post 'reopen'
110 post 'reopen'
108 match 'copy', :via => [:get, :post]
111 match 'copy', :via => [:get, :post]
109 end
112 end
110
113
111 shallow do
114 shallow do
112 resources :memberships, :controller => 'members', :only => [:index, :show, :new, :create, :update, :destroy] do
115 resources :memberships, :controller => 'members', :only => [:index, :show, :new, :create, :update, :destroy] do
113 collection do
116 collection do
114 get 'autocomplete'
117 get 'autocomplete'
115 end
118 end
116 end
119 end
117 end
120 end
118
121
119 resource :enumerations, :controller => 'project_enumerations', :only => [:update, :destroy]
122 resource :enumerations, :controller => 'project_enumerations', :only => [:update, :destroy]
120
123
121 get 'issues/:copy_from/copy', :to => 'issues#new', :as => 'copy_issue'
124 get 'issues/:copy_from/copy', :to => 'issues#new', :as => 'copy_issue'
122 resources :issues, :only => [:index, :new, :create]
125 resources :issues, :only => [:index, :new, :create]
123 # Used when updating the form of a new issue
126 # Used when updating the form of a new issue
124 post 'issues/new', :to => 'issues#new'
127 post 'issues/new', :to => 'issues#new'
125
128
126 resources :files, :only => [:index, :new, :create]
129 resources :files, :only => [:index, :new, :create]
127
130
128 resources :versions, :except => [:index, :show, :edit, :update, :destroy] do
131 resources :versions, :except => [:index, :show, :edit, :update, :destroy] do
129 collection do
132 collection do
130 put 'close_completed'
133 put 'close_completed'
131 end
134 end
132 end
135 end
133 get 'versions.:format', :to => 'versions#index'
136 get 'versions.:format', :to => 'versions#index'
134 get 'roadmap', :to => 'versions#index', :format => false
137 get 'roadmap', :to => 'versions#index', :format => false
135 get 'versions', :to => 'versions#index'
138 get 'versions', :to => 'versions#index'
136
139
137 resources :news, :except => [:show, :edit, :update, :destroy]
140 resources :news, :except => [:show, :edit, :update, :destroy]
138 resources :time_entries, :controller => 'timelog', :except => [:show, :edit, :update, :destroy] do
141 resources :time_entries, :controller => 'timelog', :except => [:show, :edit, :update, :destroy] do
139 get 'report', :on => :collection
142 get 'report', :on => :collection
140 end
143 end
141 resources :queries, :only => [:new, :create]
144 resources :queries, :only => [:new, :create]
142 shallow do
145 shallow do
143 resources :issue_categories
146 resources :issue_categories
144 end
147 end
145 resources :documents, :except => [:show, :edit, :update, :destroy]
148 resources :documents, :except => [:show, :edit, :update, :destroy]
146 resources :boards
149 resources :boards
147 shallow do
150 shallow do
148 resources :repositories, :except => [:index, :show] do
151 resources :repositories, :except => [:index, :show] do
149 member do
152 member do
150 match 'committers', :via => [:get, :post]
153 match 'committers', :via => [:get, :post]
151 end
154 end
152 end
155 end
153 end
156 end
154
157
155 match 'wiki/index', :controller => 'wiki', :action => 'index', :via => :get
158 match 'wiki/index', :controller => 'wiki', :action => 'index', :via => :get
156 resources :wiki, :except => [:index, :new, :create], :as => 'wiki_page' do
159 resources :wiki, :except => [:index, :new, :create], :as => 'wiki_page' do
157 member do
160 member do
158 get 'rename'
161 get 'rename'
159 post 'rename'
162 post 'rename'
160 get 'history'
163 get 'history'
161 get 'diff'
164 get 'diff'
162 match 'preview', :via => [:post, :put, :patch]
165 match 'preview', :via => [:post, :put, :patch]
163 post 'protect'
166 post 'protect'
164 post 'add_attachment'
167 post 'add_attachment'
165 end
168 end
166 collection do
169 collection do
167 get 'export'
170 get 'export'
168 get 'date_index'
171 get 'date_index'
169 end
172 end
170 end
173 end
171 match 'wiki', :controller => 'wiki', :action => 'show', :via => :get
174 match 'wiki', :controller => 'wiki', :action => 'show', :via => :get
172 get 'wiki/:id/:version', :to => 'wiki#show', :constraints => {:version => /\d+/}
175 get 'wiki/:id/:version', :to => 'wiki#show', :constraints => {:version => /\d+/}
173 delete 'wiki/:id/:version', :to => 'wiki#destroy_version'
176 delete 'wiki/:id/:version', :to => 'wiki#destroy_version'
174 get 'wiki/:id/:version/annotate', :to => 'wiki#annotate'
177 get 'wiki/:id/:version/annotate', :to => 'wiki#annotate'
175 get 'wiki/:id/:version/diff', :to => 'wiki#diff'
178 get 'wiki/:id/:version/diff', :to => 'wiki#diff'
176 end
179 end
177
180
178 resources :issues do
181 resources :issues do
179 member do
182 member do
180 # Used when updating the form of an existing issue
183 # Used when updating the form of an existing issue
181 patch 'edit', :to => 'issues#edit'
184 patch 'edit', :to => 'issues#edit'
182 end
185 end
183 collection do
186 collection do
184 match 'bulk_edit', :via => [:get, :post]
187 match 'bulk_edit', :via => [:get, :post]
185 post 'bulk_update'
188 post 'bulk_update'
186 end
189 end
187 resources :time_entries, :controller => 'timelog', :except => [:show, :edit, :update, :destroy] do
190 resources :time_entries, :controller => 'timelog', :except => [:show, :edit, :update, :destroy] do
188 collection do
191 collection do
189 get 'report'
192 get 'report'
190 end
193 end
191 end
194 end
192 shallow do
195 shallow do
193 resources :relations, :controller => 'issue_relations', :only => [:index, :show, :create, :destroy]
196 resources :relations, :controller => 'issue_relations', :only => [:index, :show, :create, :destroy]
194 end
197 end
195 end
198 end
196 # Used when updating the form of a new issue outside a project
199 # Used when updating the form of a new issue outside a project
197 post '/issues/new', :to => 'issues#new'
200 post '/issues/new', :to => 'issues#new'
198 match '/issues', :controller => 'issues', :action => 'destroy', :via => :delete
201 match '/issues', :controller => 'issues', :action => 'destroy', :via => :delete
199
202
200 resources :queries, :except => [:show]
203 resources :queries, :except => [:show]
201
204
202 resources :news, :only => [:index, :show, :edit, :update, :destroy]
205 resources :news, :only => [:index, :show, :edit, :update, :destroy]
203 match '/news/:id/comments', :to => 'comments#create', :via => :post
206 match '/news/:id/comments', :to => 'comments#create', :via => :post
204 match '/news/:id/comments/:comment_id', :to => 'comments#destroy', :via => :delete
207 match '/news/:id/comments/:comment_id', :to => 'comments#destroy', :via => :delete
205
208
206 resources :versions, :only => [:show, :edit, :update, :destroy] do
209 resources :versions, :only => [:show, :edit, :update, :destroy] do
207 post 'status_by', :on => :member
210 post 'status_by', :on => :member
208 end
211 end
209
212
210 resources :documents, :only => [:show, :edit, :update, :destroy] do
213 resources :documents, :only => [:show, :edit, :update, :destroy] do
211 post 'add_attachment', :on => :member
214 post 'add_attachment', :on => :member
212 end
215 end
213
216
214 match '/time_entries/context_menu', :to => 'context_menus#time_entries', :as => :time_entries_context_menu, :via => [:get, :post]
217 match '/time_entries/context_menu', :to => 'context_menus#time_entries', :as => :time_entries_context_menu, :via => [:get, :post]
215
218
216 resources :time_entries, :controller => 'timelog', :except => :destroy do
219 resources :time_entries, :controller => 'timelog', :except => :destroy do
217 collection do
220 collection do
218 get 'report'
221 get 'report'
219 get 'bulk_edit'
222 get 'bulk_edit'
220 post 'bulk_update'
223 post 'bulk_update'
221 end
224 end
222 end
225 end
223 match '/time_entries/:id', :to => 'timelog#destroy', :via => :delete, :id => /\d+/
226 match '/time_entries/:id', :to => 'timelog#destroy', :via => :delete, :id => /\d+/
224 # TODO: delete /time_entries for bulk deletion
227 # TODO: delete /time_entries for bulk deletion
225 match '/time_entries/destroy', :to => 'timelog#destroy', :via => :delete
228 match '/time_entries/destroy', :to => 'timelog#destroy', :via => :delete
226 # Used to update the new time entry form
229 # Used to update the new time entry form
227 post '/time_entries/new', :to => 'timelog#new'
230 post '/time_entries/new', :to => 'timelog#new'
228
231
229 get 'projects/:id/activity', :to => 'activities#index', :as => :project_activity
232 get 'projects/:id/activity', :to => 'activities#index', :as => :project_activity
230 get 'activity', :to => 'activities#index'
233 get 'activity', :to => 'activities#index'
231
234
232 # repositories routes
235 # repositories routes
233 get 'projects/:id/repository/:repository_id/statistics', :to => 'repositories#stats'
236 get 'projects/:id/repository/:repository_id/statistics', :to => 'repositories#stats'
234 get 'projects/:id/repository/:repository_id/graph', :to => 'repositories#graph'
237 get 'projects/:id/repository/:repository_id/graph', :to => 'repositories#graph'
235
238
236 get 'projects/:id/repository/:repository_id/changes(/*path)',
239 get 'projects/:id/repository/:repository_id/changes(/*path)',
237 :to => 'repositories#changes',
240 :to => 'repositories#changes',
238 :format => false
241 :format => false
239
242
240 get 'projects/:id/repository/:repository_id/revisions/:rev', :to => 'repositories#revision'
243 get 'projects/:id/repository/:repository_id/revisions/:rev', :to => 'repositories#revision'
241 get 'projects/:id/repository/:repository_id/revision', :to => 'repositories#revision'
244 get 'projects/:id/repository/:repository_id/revision', :to => 'repositories#revision'
242 post 'projects/:id/repository/:repository_id/revisions/:rev/issues', :to => 'repositories#add_related_issue'
245 post 'projects/:id/repository/:repository_id/revisions/:rev/issues', :to => 'repositories#add_related_issue'
243 delete 'projects/:id/repository/:repository_id/revisions/:rev/issues/:issue_id', :to => 'repositories#remove_related_issue'
246 delete 'projects/:id/repository/:repository_id/revisions/:rev/issues/:issue_id', :to => 'repositories#remove_related_issue'
244 get 'projects/:id/repository/:repository_id/revisions', :to => 'repositories#revisions'
247 get 'projects/:id/repository/:repository_id/revisions', :to => 'repositories#revisions'
245 get 'projects/:id/repository/:repository_id/revisions/:rev/:action(/*path)',
248 get 'projects/:id/repository/:repository_id/revisions/:rev/:action(/*path)',
246 :controller => 'repositories',
249 :controller => 'repositories',
247 :format => false,
250 :format => false,
248 :constraints => {
251 :constraints => {
249 :action => /(browse|show|entry|raw|annotate|diff)/,
252 :action => /(browse|show|entry|raw|annotate|diff)/,
250 :rev => /[a-z0-9\.\-_]+/
253 :rev => /[a-z0-9\.\-_]+/
251 }
254 }
252
255
253 get 'projects/:id/repository/statistics', :to => 'repositories#stats'
256 get 'projects/:id/repository/statistics', :to => 'repositories#stats'
254 get 'projects/:id/repository/graph', :to => 'repositories#graph'
257 get 'projects/:id/repository/graph', :to => 'repositories#graph'
255
258
256 get 'projects/:id/repository/changes(/*path)',
259 get 'projects/:id/repository/changes(/*path)',
257 :to => 'repositories#changes',
260 :to => 'repositories#changes',
258 :format => false
261 :format => false
259
262
260 get 'projects/:id/repository/revisions', :to => 'repositories#revisions'
263 get 'projects/:id/repository/revisions', :to => 'repositories#revisions'
261 get 'projects/:id/repository/revisions/:rev', :to => 'repositories#revision'
264 get 'projects/:id/repository/revisions/:rev', :to => 'repositories#revision'
262 get 'projects/:id/repository/revision', :to => 'repositories#revision'
265 get 'projects/:id/repository/revision', :to => 'repositories#revision'
263 post 'projects/:id/repository/revisions/:rev/issues', :to => 'repositories#add_related_issue'
266 post 'projects/:id/repository/revisions/:rev/issues', :to => 'repositories#add_related_issue'
264 delete 'projects/:id/repository/revisions/:rev/issues/:issue_id', :to => 'repositories#remove_related_issue'
267 delete 'projects/:id/repository/revisions/:rev/issues/:issue_id', :to => 'repositories#remove_related_issue'
265 get 'projects/:id/repository/revisions/:rev/:action(/*path)',
268 get 'projects/:id/repository/revisions/:rev/:action(/*path)',
266 :controller => 'repositories',
269 :controller => 'repositories',
267 :format => false,
270 :format => false,
268 :constraints => {
271 :constraints => {
269 :action => /(browse|show|entry|raw|annotate|diff)/,
272 :action => /(browse|show|entry|raw|annotate|diff)/,
270 :rev => /[a-z0-9\.\-_]+/
273 :rev => /[a-z0-9\.\-_]+/
271 }
274 }
272 get 'projects/:id/repository/:repository_id/:action(/*path)',
275 get 'projects/:id/repository/:repository_id/:action(/*path)',
273 :controller => 'repositories',
276 :controller => 'repositories',
274 :action => /(browse|show|entry|raw|changes|annotate|diff)/,
277 :action => /(browse|show|entry|raw|changes|annotate|diff)/,
275 :format => false
278 :format => false
276 get 'projects/:id/repository/:action(/*path)',
279 get 'projects/:id/repository/:action(/*path)',
277 :controller => 'repositories',
280 :controller => 'repositories',
278 :action => /(browse|show|entry|raw|changes|annotate|diff)/,
281 :action => /(browse|show|entry|raw|changes|annotate|diff)/,
279 :format => false
282 :format => false
280
283
281 get 'projects/:id/repository/:repository_id', :to => 'repositories#show', :path => nil
284 get 'projects/:id/repository/:repository_id', :to => 'repositories#show', :path => nil
282 get 'projects/:id/repository', :to => 'repositories#show', :path => nil
285 get 'projects/:id/repository', :to => 'repositories#show', :path => nil
283
286
284 # additional routes for having the file name at the end of url
287 # additional routes for having the file name at the end of url
285 get 'attachments/:id/:filename', :to => 'attachments#show', :id => /\d+/, :filename => /.*/, :as => 'named_attachment'
288 get 'attachments/:id/:filename', :to => 'attachments#show', :id => /\d+/, :filename => /.*/, :as => 'named_attachment'
286 get 'attachments/download/:id/:filename', :to => 'attachments#download', :id => /\d+/, :filename => /.*/, :as => 'download_named_attachment'
289 get 'attachments/download/:id/:filename', :to => 'attachments#download', :id => /\d+/, :filename => /.*/, :as => 'download_named_attachment'
287 get 'attachments/download/:id', :to => 'attachments#download', :id => /\d+/
290 get 'attachments/download/:id', :to => 'attachments#download', :id => /\d+/
288 get 'attachments/thumbnail/:id(/:size)', :to => 'attachments#thumbnail', :id => /\d+/, :size => /\d+/, :as => 'thumbnail'
291 get 'attachments/thumbnail/:id(/:size)', :to => 'attachments#thumbnail', :id => /\d+/, :size => /\d+/, :as => 'thumbnail'
289 resources :attachments, :only => [:show, :destroy]
292 resources :attachments, :only => [:show, :destroy]
290 get 'attachments/:object_type/:object_id/edit', :to => 'attachments#edit', :as => :object_attachments_edit
293 get 'attachments/:object_type/:object_id/edit', :to => 'attachments#edit', :as => :object_attachments_edit
291 patch 'attachments/:object_type/:object_id', :to => 'attachments#update', :as => :object_attachments
294 patch 'attachments/:object_type/:object_id', :to => 'attachments#update', :as => :object_attachments
292
295
293 resources :groups do
296 resources :groups do
294 resources :memberships, :controller => 'principal_memberships'
297 resources :memberships, :controller => 'principal_memberships'
295 member do
298 member do
296 get 'autocomplete_for_user'
299 get 'autocomplete_for_user'
297 end
300 end
298 end
301 end
299
302
300 get 'groups/:id/users/new', :to => 'groups#new_users', :id => /\d+/, :as => 'new_group_users'
303 get 'groups/:id/users/new', :to => 'groups#new_users', :id => /\d+/, :as => 'new_group_users'
301 post 'groups/:id/users', :to => 'groups#add_users', :id => /\d+/, :as => 'group_users'
304 post 'groups/:id/users', :to => 'groups#add_users', :id => /\d+/, :as => 'group_users'
302 delete 'groups/:id/users/:user_id', :to => 'groups#remove_user', :id => /\d+/, :as => 'group_user'
305 delete 'groups/:id/users/:user_id', :to => 'groups#remove_user', :id => /\d+/, :as => 'group_user'
303
306
304 resources :trackers, :except => :show do
307 resources :trackers, :except => :show do
305 collection do
308 collection do
306 match 'fields', :via => [:get, :post]
309 match 'fields', :via => [:get, :post]
307 end
310 end
308 end
311 end
309 resources :issue_statuses, :except => :show do
312 resources :issue_statuses, :except => :show do
310 collection do
313 collection do
311 post 'update_issue_done_ratio'
314 post 'update_issue_done_ratio'
312 end
315 end
313 end
316 end
314 resources :custom_fields, :except => :show do
317 resources :custom_fields, :except => :show do
315 resources :enumerations, :controller => 'custom_field_enumerations', :except => [:show, :new, :edit]
318 resources :enumerations, :controller => 'custom_field_enumerations', :except => [:show, :new, :edit]
316 put 'enumerations', :to => 'custom_field_enumerations#update_each'
319 put 'enumerations', :to => 'custom_field_enumerations#update_each'
317 end
320 end
318 resources :roles do
321 resources :roles do
319 collection do
322 collection do
320 match 'permissions', :via => [:get, :post]
323 match 'permissions', :via => [:get, :post]
321 end
324 end
322 end
325 end
323 resources :enumerations, :except => :show
326 resources :enumerations, :except => :show
324 match 'enumerations/:type', :to => 'enumerations#index', :via => :get
327 match 'enumerations/:type', :to => 'enumerations#index', :via => :get
325
328
326 get 'projects/:id/search', :controller => 'search', :action => 'index'
329 get 'projects/:id/search', :controller => 'search', :action => 'index'
327 get 'search', :controller => 'search', :action => 'index'
330 get 'search', :controller => 'search', :action => 'index'
328
331
329
332
330 get 'mail_handler', :to => 'mail_handler#new'
333 get 'mail_handler', :to => 'mail_handler#new'
331 post 'mail_handler', :to => 'mail_handler#index'
334 post 'mail_handler', :to => 'mail_handler#index'
332
335
333 get 'admin', :to => 'admin#index'
336 get 'admin', :to => 'admin#index'
334 get 'admin/projects', :to => 'admin#projects'
337 get 'admin/projects', :to => 'admin#projects'
335 get 'admin/plugins', :to => 'admin#plugins'
338 get 'admin/plugins', :to => 'admin#plugins'
336 get 'admin/info', :to => 'admin#info'
339 get 'admin/info', :to => 'admin#info'
337 post 'admin/test_email', :to => 'admin#test_email', :as => 'test_email'
340 post 'admin/test_email', :to => 'admin#test_email', :as => 'test_email'
338 post 'admin/default_configuration', :to => 'admin#default_configuration'
341 post 'admin/default_configuration', :to => 'admin#default_configuration'
339
342
340 resources :auth_sources do
343 resources :auth_sources do
341 member do
344 member do
342 get 'test_connection', :as => 'try_connection'
345 get 'test_connection', :as => 'try_connection'
343 end
346 end
344 collection do
347 collection do
345 get 'autocomplete_for_new_user'
348 get 'autocomplete_for_new_user'
346 end
349 end
347 end
350 end
348
351
349 match 'workflows', :controller => 'workflows', :action => 'index', :via => :get
352 match 'workflows', :controller => 'workflows', :action => 'index', :via => :get
350 match 'workflows/edit', :controller => 'workflows', :action => 'edit', :via => [:get, :post]
353 match 'workflows/edit', :controller => 'workflows', :action => 'edit', :via => [:get, :post]
351 match 'workflows/permissions', :controller => 'workflows', :action => 'permissions', :via => [:get, :post]
354 match 'workflows/permissions', :controller => 'workflows', :action => 'permissions', :via => [:get, :post]
352 match 'workflows/copy', :controller => 'workflows', :action => 'copy', :via => [:get, :post]
355 match 'workflows/copy', :controller => 'workflows', :action => 'copy', :via => [:get, :post]
353 match 'settings', :controller => 'settings', :action => 'index', :via => :get
356 match 'settings', :controller => 'settings', :action => 'index', :via => :get
354 match 'settings/edit', :controller => 'settings', :action => 'edit', :via => [:get, :post]
357 match 'settings/edit', :controller => 'settings', :action => 'edit', :via => [:get, :post]
355 match 'settings/plugin/:id', :controller => 'settings', :action => 'plugin', :via => [:get, :post], :as => 'plugin_settings'
358 match 'settings/plugin/:id', :controller => 'settings', :action => 'plugin', :via => [:get, :post], :as => 'plugin_settings'
356
359
357 match 'sys/projects', :to => 'sys#projects', :via => :get
360 match 'sys/projects', :to => 'sys#projects', :via => :get
358 match 'sys/projects/:id/repository', :to => 'sys#create_project_repository', :via => :post
361 match 'sys/projects/:id/repository', :to => 'sys#create_project_repository', :via => :post
359 match 'sys/fetch_changesets', :to => 'sys#fetch_changesets', :via => [:get, :post]
362 match 'sys/fetch_changesets', :to => 'sys#fetch_changesets', :via => [:get, :post]
360
363
361 match 'uploads', :to => 'attachments#upload', :via => :post
364 match 'uploads', :to => 'attachments#upload', :via => :post
362
365
363 get 'robots.txt', :to => 'welcome#robots'
366 get 'robots.txt', :to => 'welcome#robots'
364
367
365 Dir.glob File.expand_path("plugins/*", Rails.root) do |plugin_dir|
368 Dir.glob File.expand_path("plugins/*", Rails.root) do |plugin_dir|
366 file = File.join(plugin_dir, "config/routes.rb")
369 file = File.join(plugin_dir, "config/routes.rb")
367 if File.exists?(file)
370 if File.exists?(file)
368 begin
371 begin
369 instance_eval File.read(file)
372 instance_eval File.read(file)
370 rescue Exception => e
373 rescue Exception => e
371 puts "An error occurred while loading the routes definition of #{File.basename(plugin_dir)} plugin (#{file}): #{e.message}."
374 puts "An error occurred while loading the routes definition of #{File.basename(plugin_dir)} plugin (#{file}): #{e.message}."
372 exit 1
375 exit 1
373 end
376 end
374 end
377 end
375 end
378 end
376 end
379 end
@@ -1,276 +1,276
1 # Redmine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006-2015 Jean-Philippe Lang
2 # Copyright (C) 2006-2015 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 'redmine/core_ext'
18 require 'redmine/core_ext'
19
19
20 begin
20 begin
21 require 'rmagick' unless Object.const_defined?(:Magick)
21 require 'rmagick' unless Object.const_defined?(:Magick)
22 rescue LoadError
22 rescue LoadError
23 # RMagick is not available
23 # RMagick is not available
24 end
24 end
25 begin
25 begin
26 require 'redcarpet' unless Object.const_defined?(:Redcarpet)
26 require 'redcarpet' unless Object.const_defined?(:Redcarpet)
27 rescue LoadError
27 rescue LoadError
28 # Redcarpet is not available
28 # Redcarpet is not available
29 end
29 end
30
30
31 require 'redmine/scm/base'
31 require 'redmine/scm/base'
32 require 'redmine/access_control'
32 require 'redmine/access_control'
33 require 'redmine/access_keys'
33 require 'redmine/access_keys'
34 require 'redmine/activity'
34 require 'redmine/activity'
35 require 'redmine/activity/fetcher'
35 require 'redmine/activity/fetcher'
36 require 'redmine/ciphering'
36 require 'redmine/ciphering'
37 require 'redmine/codeset_util'
37 require 'redmine/codeset_util'
38 require 'redmine/field_format'
38 require 'redmine/field_format'
39 require 'redmine/menu_manager'
39 require 'redmine/menu_manager'
40 require 'redmine/notifiable'
40 require 'redmine/notifiable'
41 require 'redmine/platform'
41 require 'redmine/platform'
42 require 'redmine/mime_type'
42 require 'redmine/mime_type'
43 require 'redmine/notifiable'
43 require 'redmine/notifiable'
44 require 'redmine/search'
44 require 'redmine/search'
45 require 'redmine/syntax_highlighting'
45 require 'redmine/syntax_highlighting'
46 require 'redmine/thumbnail'
46 require 'redmine/thumbnail'
47 require 'redmine/unified_diff'
47 require 'redmine/unified_diff'
48 require 'redmine/utils'
48 require 'redmine/utils'
49 require 'redmine/version'
49 require 'redmine/version'
50 require 'redmine/wiki_formatting'
50 require 'redmine/wiki_formatting'
51
51
52 require 'redmine/default_data/loader'
52 require 'redmine/default_data/loader'
53 require 'redmine/helpers/calendar'
53 require 'redmine/helpers/calendar'
54 require 'redmine/helpers/diff'
54 require 'redmine/helpers/diff'
55 require 'redmine/helpers/gantt'
55 require 'redmine/helpers/gantt'
56 require 'redmine/helpers/time_report'
56 require 'redmine/helpers/time_report'
57 require 'redmine/views/other_formats_builder'
57 require 'redmine/views/other_formats_builder'
58 require 'redmine/views/labelled_form_builder'
58 require 'redmine/views/labelled_form_builder'
59 require 'redmine/views/builders'
59 require 'redmine/views/builders'
60
60
61 require 'redmine/themes'
61 require 'redmine/themes'
62 require 'redmine/hook'
62 require 'redmine/hook'
63 require 'redmine/hook/listener'
63 require 'redmine/hook/listener'
64 require 'redmine/hook/view_listener'
64 require 'redmine/hook/view_listener'
65 require 'redmine/plugin'
65 require 'redmine/plugin'
66
66
67 Redmine::Scm::Base.add "Subversion"
67 Redmine::Scm::Base.add "Subversion"
68 Redmine::Scm::Base.add "Darcs"
68 Redmine::Scm::Base.add "Darcs"
69 Redmine::Scm::Base.add "Mercurial"
69 Redmine::Scm::Base.add "Mercurial"
70 Redmine::Scm::Base.add "Cvs"
70 Redmine::Scm::Base.add "Cvs"
71 Redmine::Scm::Base.add "Bazaar"
71 Redmine::Scm::Base.add "Bazaar"
72 Redmine::Scm::Base.add "Git"
72 Redmine::Scm::Base.add "Git"
73 Redmine::Scm::Base.add "Filesystem"
73 Redmine::Scm::Base.add "Filesystem"
74
74
75 # Permissions
75 # Permissions
76 Redmine::AccessControl.map do |map|
76 Redmine::AccessControl.map do |map|
77 map.permission :view_project, {:projects => [:show], :activities => [:index]}, :public => true, :read => true
77 map.permission :view_project, {:projects => [:show], :activities => [:index]}, :public => true, :read => true
78 map.permission :search_project, {:search => :index}, :public => true, :read => true
78 map.permission :search_project, {:search => :index}, :public => true, :read => true
79 map.permission :add_project, {:projects => [:new, :create]}, :require => :loggedin
79 map.permission :add_project, {:projects => [:new, :create]}, :require => :loggedin
80 map.permission :edit_project, {:projects => [:settings, :edit, :update]}, :require => :member
80 map.permission :edit_project, {:projects => [:settings, :edit, :update]}, :require => :member
81 map.permission :close_project, {:projects => [:close, :reopen]}, :require => :member, :read => true
81 map.permission :close_project, {:projects => [:close, :reopen]}, :require => :member, :read => true
82 map.permission :select_project_modules, {:projects => :modules}, :require => :member
82 map.permission :select_project_modules, {:projects => :modules}, :require => :member
83 map.permission :view_members, {:members => [:index, :show]}, :public => true, :read => true
83 map.permission :view_members, {:members => [:index, :show]}, :public => true, :read => true
84 map.permission :manage_members, {:projects => :settings, :members => [:index, :show, :new, :create, :update, :destroy, :autocomplete]}, :require => :member
84 map.permission :manage_members, {:projects => :settings, :members => [:index, :show, :new, :create, :update, :destroy, :autocomplete]}, :require => :member
85 map.permission :manage_versions, {:projects => :settings, :versions => [:new, :create, :edit, :update, :close_completed, :destroy]}, :require => :member
85 map.permission :manage_versions, {:projects => :settings, :versions => [:new, :create, :edit, :update, :close_completed, :destroy]}, :require => :member
86 map.permission :add_subprojects, {:projects => [:new, :create]}, :require => :member
86 map.permission :add_subprojects, {:projects => [:new, :create]}, :require => :member
87
87
88 map.project_module :issue_tracking do |map|
88 map.project_module :issue_tracking do |map|
89 # Issue categories
89 # Issue categories
90 map.permission :manage_categories, {:projects => :settings, :issue_categories => [:index, :show, :new, :create, :edit, :update, :destroy]}, :require => :member
90 map.permission :manage_categories, {:projects => :settings, :issue_categories => [:index, :show, :new, :create, :edit, :update, :destroy]}, :require => :member
91 # Issues
91 # Issues
92 map.permission :view_issues, {:issues => [:index, :show],
92 map.permission :view_issues, {:issues => [:index, :show],
93 :auto_complete => [:issues],
93 :auto_complete => [:issues],
94 :context_menus => [:issues],
94 :context_menus => [:issues],
95 :versions => [:index, :show, :status_by],
95 :versions => [:index, :show, :status_by],
96 :journals => [:index, :diff],
96 :journals => [:index, :diff],
97 :queries => :index,
97 :queries => :index,
98 :reports => [:issue_report, :issue_report_details]},
98 :reports => [:issue_report, :issue_report_details]},
99 :read => true
99 :read => true
100 map.permission :add_issues, {:issues => [:new, :create], :attachments => :upload}
100 map.permission :add_issues, {:issues => [:new, :create], :attachments => :upload}
101 map.permission :edit_issues, {:issues => [:edit, :update, :bulk_edit, :bulk_update], :journals => [:new], :attachments => :upload}
101 map.permission :edit_issues, {:issues => [:edit, :update, :bulk_edit, :bulk_update], :journals => [:new], :attachments => :upload}
102 map.permission :copy_issues, {:issues => [:new, :create, :bulk_edit, :bulk_update], :attachments => :upload}
102 map.permission :copy_issues, {:issues => [:new, :create, :bulk_edit, :bulk_update], :attachments => :upload}
103 map.permission :manage_issue_relations, {:issue_relations => [:index, :show, :create, :destroy]}
103 map.permission :manage_issue_relations, {:issue_relations => [:index, :show, :create, :destroy]}
104 map.permission :manage_subtasks, {}
104 map.permission :manage_subtasks, {}
105 map.permission :set_issues_private, {}
105 map.permission :set_issues_private, {}
106 map.permission :set_own_issues_private, {}, :require => :loggedin
106 map.permission :set_own_issues_private, {}, :require => :loggedin
107 map.permission :add_issue_notes, {:issues => [:edit, :update], :journals => [:new], :attachments => :upload}
107 map.permission :add_issue_notes, {:issues => [:edit, :update], :journals => [:new], :attachments => :upload}
108 map.permission :edit_issue_notes, {:journals => :edit}, :require => :loggedin
108 map.permission :edit_issue_notes, {:journals => [:edit, :update]}, :require => :loggedin
109 map.permission :edit_own_issue_notes, {:journals => :edit}, :require => :loggedin
109 map.permission :edit_own_issue_notes, {:journals => [:edit, :update]}, :require => :loggedin
110 map.permission :view_private_notes, {}, :read => true, :require => :member
110 map.permission :view_private_notes, {}, :read => true, :require => :member
111 map.permission :set_notes_private, {}, :require => :member
111 map.permission :set_notes_private, {}, :require => :member
112 map.permission :delete_issues, {:issues => :destroy}, :require => :member
112 map.permission :delete_issues, {:issues => :destroy}, :require => :member
113 # Queries
113 # Queries
114 map.permission :manage_public_queries, {:queries => [:new, :create, :edit, :update, :destroy]}, :require => :member
114 map.permission :manage_public_queries, {:queries => [:new, :create, :edit, :update, :destroy]}, :require => :member
115 map.permission :save_queries, {:queries => [:new, :create, :edit, :update, :destroy]}, :require => :loggedin
115 map.permission :save_queries, {:queries => [:new, :create, :edit, :update, :destroy]}, :require => :loggedin
116 # Watchers
116 # Watchers
117 map.permission :view_issue_watchers, {}, :read => true
117 map.permission :view_issue_watchers, {}, :read => true
118 map.permission :add_issue_watchers, {:watchers => [:new, :create, :append, :autocomplete_for_user]}
118 map.permission :add_issue_watchers, {:watchers => [:new, :create, :append, :autocomplete_for_user]}
119 map.permission :delete_issue_watchers, {:watchers => :destroy}
119 map.permission :delete_issue_watchers, {:watchers => :destroy}
120 map.permission :import_issues, {:imports => [:new, :create, :settings, :mapping, :run, :show]}
120 map.permission :import_issues, {:imports => [:new, :create, :settings, :mapping, :run, :show]}
121 end
121 end
122
122
123 map.project_module :time_tracking do |map|
123 map.project_module :time_tracking do |map|
124 map.permission :log_time, {:timelog => [:new, :create]}, :require => :loggedin
124 map.permission :log_time, {:timelog => [:new, :create]}, :require => :loggedin
125 map.permission :view_time_entries, {:timelog => [:index, :report, :show]}, :read => true
125 map.permission :view_time_entries, {:timelog => [:index, :report, :show]}, :read => true
126 map.permission :edit_time_entries, {:timelog => [:edit, :update, :destroy, :bulk_edit, :bulk_update]}, :require => :member
126 map.permission :edit_time_entries, {:timelog => [:edit, :update, :destroy, :bulk_edit, :bulk_update]}, :require => :member
127 map.permission :edit_own_time_entries, {:timelog => [:edit, :update, :destroy,:bulk_edit, :bulk_update]}, :require => :loggedin
127 map.permission :edit_own_time_entries, {:timelog => [:edit, :update, :destroy,:bulk_edit, :bulk_update]}, :require => :loggedin
128 map.permission :manage_project_activities, {:project_enumerations => [:update, :destroy]}, :require => :member
128 map.permission :manage_project_activities, {:project_enumerations => [:update, :destroy]}, :require => :member
129 end
129 end
130
130
131 map.project_module :news do |map|
131 map.project_module :news do |map|
132 map.permission :manage_news, {:news => [:new, :create, :edit, :update, :destroy], :comments => [:destroy], :attachments => :upload}, :require => :member
132 map.permission :manage_news, {:news => [:new, :create, :edit, :update, :destroy], :comments => [:destroy], :attachments => :upload}, :require => :member
133 map.permission :view_news, {:news => [:index, :show]}, :public => true, :read => true
133 map.permission :view_news, {:news => [:index, :show]}, :public => true, :read => true
134 map.permission :comment_news, {:comments => :create}
134 map.permission :comment_news, {:comments => :create}
135 end
135 end
136
136
137 map.project_module :documents do |map|
137 map.project_module :documents do |map|
138 map.permission :add_documents, {:documents => [:new, :create, :add_attachment], :attachments => :upload}, :require => :loggedin
138 map.permission :add_documents, {:documents => [:new, :create, :add_attachment], :attachments => :upload}, :require => :loggedin
139 map.permission :edit_documents, {:documents => [:edit, :update, :add_attachment], :attachments => :upload}, :require => :loggedin
139 map.permission :edit_documents, {:documents => [:edit, :update, :add_attachment], :attachments => :upload}, :require => :loggedin
140 map.permission :delete_documents, {:documents => [:destroy]}, :require => :loggedin
140 map.permission :delete_documents, {:documents => [:destroy]}, :require => :loggedin
141 map.permission :view_documents, {:documents => [:index, :show, :download]}, :read => true
141 map.permission :view_documents, {:documents => [:index, :show, :download]}, :read => true
142 end
142 end
143
143
144 map.project_module :files do |map|
144 map.project_module :files do |map|
145 map.permission :manage_files, {:files => [:new, :create], :attachments => :upload}, :require => :loggedin
145 map.permission :manage_files, {:files => [:new, :create], :attachments => :upload}, :require => :loggedin
146 map.permission :view_files, {:files => :index, :versions => :download}, :read => true
146 map.permission :view_files, {:files => :index, :versions => :download}, :read => true
147 end
147 end
148
148
149 map.project_module :wiki do |map|
149 map.project_module :wiki do |map|
150 map.permission :manage_wiki, {:wikis => [:edit, :destroy]}, :require => :member
150 map.permission :manage_wiki, {:wikis => [:edit, :destroy]}, :require => :member
151 map.permission :rename_wiki_pages, {:wiki => :rename}, :require => :member
151 map.permission :rename_wiki_pages, {:wiki => :rename}, :require => :member
152 map.permission :delete_wiki_pages, {:wiki => [:destroy, :destroy_version]}, :require => :member
152 map.permission :delete_wiki_pages, {:wiki => [:destroy, :destroy_version]}, :require => :member
153 map.permission :view_wiki_pages, {:wiki => [:index, :show, :special, :date_index]}, :read => true
153 map.permission :view_wiki_pages, {:wiki => [:index, :show, :special, :date_index]}, :read => true
154 map.permission :export_wiki_pages, {:wiki => [:export]}, :read => true
154 map.permission :export_wiki_pages, {:wiki => [:export]}, :read => true
155 map.permission :view_wiki_edits, {:wiki => [:history, :diff, :annotate]}, :read => true
155 map.permission :view_wiki_edits, {:wiki => [:history, :diff, :annotate]}, :read => true
156 map.permission :edit_wiki_pages, :wiki => [:edit, :update, :preview, :add_attachment], :attachments => :upload
156 map.permission :edit_wiki_pages, :wiki => [:edit, :update, :preview, :add_attachment], :attachments => :upload
157 map.permission :delete_wiki_pages_attachments, {}
157 map.permission :delete_wiki_pages_attachments, {}
158 map.permission :protect_wiki_pages, {:wiki => :protect}, :require => :member
158 map.permission :protect_wiki_pages, {:wiki => :protect}, :require => :member
159 end
159 end
160
160
161 map.project_module :repository do |map|
161 map.project_module :repository do |map|
162 map.permission :manage_repository, {:repositories => [:new, :create, :edit, :update, :committers, :destroy]}, :require => :member
162 map.permission :manage_repository, {:repositories => [:new, :create, :edit, :update, :committers, :destroy]}, :require => :member
163 map.permission :browse_repository, {:repositories => [:show, :browse, :entry, :raw, :annotate, :changes, :diff, :stats, :graph]}, :read => true
163 map.permission :browse_repository, {:repositories => [:show, :browse, :entry, :raw, :annotate, :changes, :diff, :stats, :graph]}, :read => true
164 map.permission :view_changesets, {:repositories => [:show, :revisions, :revision]}, :read => true
164 map.permission :view_changesets, {:repositories => [:show, :revisions, :revision]}, :read => true
165 map.permission :commit_access, {}
165 map.permission :commit_access, {}
166 map.permission :manage_related_issues, {:repositories => [:add_related_issue, :remove_related_issue]}
166 map.permission :manage_related_issues, {:repositories => [:add_related_issue, :remove_related_issue]}
167 end
167 end
168
168
169 map.project_module :boards do |map|
169 map.project_module :boards do |map|
170 map.permission :manage_boards, {:boards => [:new, :create, :edit, :update, :destroy]}, :require => :member
170 map.permission :manage_boards, {:boards => [:new, :create, :edit, :update, :destroy]}, :require => :member
171 map.permission :view_messages, {:boards => [:index, :show], :messages => [:show]}, :public => true, :read => true
171 map.permission :view_messages, {:boards => [:index, :show], :messages => [:show]}, :public => true, :read => true
172 map.permission :add_messages, {:messages => [:new, :reply, :quote], :attachments => :upload}
172 map.permission :add_messages, {:messages => [:new, :reply, :quote], :attachments => :upload}
173 map.permission :edit_messages, {:messages => :edit, :attachments => :upload}, :require => :member
173 map.permission :edit_messages, {:messages => :edit, :attachments => :upload}, :require => :member
174 map.permission :edit_own_messages, {:messages => :edit, :attachments => :upload}, :require => :loggedin
174 map.permission :edit_own_messages, {:messages => :edit, :attachments => :upload}, :require => :loggedin
175 map.permission :delete_messages, {:messages => :destroy}, :require => :member
175 map.permission :delete_messages, {:messages => :destroy}, :require => :member
176 map.permission :delete_own_messages, {:messages => :destroy}, :require => :loggedin
176 map.permission :delete_own_messages, {:messages => :destroy}, :require => :loggedin
177 end
177 end
178
178
179 map.project_module :calendar do |map|
179 map.project_module :calendar do |map|
180 map.permission :view_calendar, {:calendars => [:show, :update]}, :read => true
180 map.permission :view_calendar, {:calendars => [:show, :update]}, :read => true
181 end
181 end
182
182
183 map.project_module :gantt do |map|
183 map.project_module :gantt do |map|
184 map.permission :view_gantt, {:gantts => [:show, :update]}, :read => true
184 map.permission :view_gantt, {:gantts => [:show, :update]}, :read => true
185 end
185 end
186 end
186 end
187
187
188 Redmine::MenuManager.map :top_menu do |menu|
188 Redmine::MenuManager.map :top_menu do |menu|
189 menu.push :home, :home_path
189 menu.push :home, :home_path
190 menu.push :my_page, { :controller => 'my', :action => 'page' }, :if => Proc.new { User.current.logged? }
190 menu.push :my_page, { :controller => 'my', :action => 'page' }, :if => Proc.new { User.current.logged? }
191 menu.push :projects, { :controller => 'projects', :action => 'index' }, :caption => :label_project_plural
191 menu.push :projects, { :controller => 'projects', :action => 'index' }, :caption => :label_project_plural
192 menu.push :administration, { :controller => 'admin', :action => 'index' }, :if => Proc.new { User.current.admin? }, :last => true
192 menu.push :administration, { :controller => 'admin', :action => 'index' }, :if => Proc.new { User.current.admin? }, :last => true
193 menu.push :help, Redmine::Info.help_url, :last => true
193 menu.push :help, Redmine::Info.help_url, :last => true
194 end
194 end
195
195
196 Redmine::MenuManager.map :account_menu do |menu|
196 Redmine::MenuManager.map :account_menu do |menu|
197 menu.push :login, :signin_path, :if => Proc.new { !User.current.logged? }
197 menu.push :login, :signin_path, :if => Proc.new { !User.current.logged? }
198 menu.push :register, :register_path, :if => Proc.new { !User.current.logged? && Setting.self_registration? }
198 menu.push :register, :register_path, :if => Proc.new { !User.current.logged? && Setting.self_registration? }
199 menu.push :my_account, { :controller => 'my', :action => 'account' }, :if => Proc.new { User.current.logged? }
199 menu.push :my_account, { :controller => 'my', :action => 'account' }, :if => Proc.new { User.current.logged? }
200 menu.push :logout, :signout_path, :html => {:method => 'post'}, :if => Proc.new { User.current.logged? }
200 menu.push :logout, :signout_path, :html => {:method => 'post'}, :if => Proc.new { User.current.logged? }
201 end
201 end
202
202
203 Redmine::MenuManager.map :application_menu do |menu|
203 Redmine::MenuManager.map :application_menu do |menu|
204 # Empty
204 # Empty
205 end
205 end
206
206
207 Redmine::MenuManager.map :admin_menu do |menu|
207 Redmine::MenuManager.map :admin_menu do |menu|
208 menu.push :projects, {:controller => 'admin', :action => 'projects'}, :caption => :label_project_plural
208 menu.push :projects, {:controller => 'admin', :action => 'projects'}, :caption => :label_project_plural
209 menu.push :users, {:controller => 'users'}, :caption => :label_user_plural
209 menu.push :users, {:controller => 'users'}, :caption => :label_user_plural
210 menu.push :groups, {:controller => 'groups'}, :caption => :label_group_plural
210 menu.push :groups, {:controller => 'groups'}, :caption => :label_group_plural
211 menu.push :roles, {:controller => 'roles'}, :caption => :label_role_and_permissions
211 menu.push :roles, {:controller => 'roles'}, :caption => :label_role_and_permissions
212 menu.push :trackers, {:controller => 'trackers'}, :caption => :label_tracker_plural
212 menu.push :trackers, {:controller => 'trackers'}, :caption => :label_tracker_plural
213 menu.push :issue_statuses, {:controller => 'issue_statuses'}, :caption => :label_issue_status_plural,
213 menu.push :issue_statuses, {:controller => 'issue_statuses'}, :caption => :label_issue_status_plural,
214 :html => {:class => 'issue_statuses'}
214 :html => {:class => 'issue_statuses'}
215 menu.push :workflows, {:controller => 'workflows', :action => 'edit'}, :caption => :label_workflow
215 menu.push :workflows, {:controller => 'workflows', :action => 'edit'}, :caption => :label_workflow
216 menu.push :custom_fields, {:controller => 'custom_fields'}, :caption => :label_custom_field_plural,
216 menu.push :custom_fields, {:controller => 'custom_fields'}, :caption => :label_custom_field_plural,
217 :html => {:class => 'custom_fields'}
217 :html => {:class => 'custom_fields'}
218 menu.push :enumerations, {:controller => 'enumerations'}
218 menu.push :enumerations, {:controller => 'enumerations'}
219 menu.push :settings, {:controller => 'settings'}
219 menu.push :settings, {:controller => 'settings'}
220 menu.push :ldap_authentication, {:controller => 'auth_sources', :action => 'index'},
220 menu.push :ldap_authentication, {:controller => 'auth_sources', :action => 'index'},
221 :html => {:class => 'server_authentication'}
221 :html => {:class => 'server_authentication'}
222 menu.push :plugins, {:controller => 'admin', :action => 'plugins'}, :last => true
222 menu.push :plugins, {:controller => 'admin', :action => 'plugins'}, :last => true
223 menu.push :info, {:controller => 'admin', :action => 'info'}, :caption => :label_information_plural, :last => true
223 menu.push :info, {:controller => 'admin', :action => 'info'}, :caption => :label_information_plural, :last => true
224 end
224 end
225
225
226 Redmine::MenuManager.map :project_menu do |menu|
226 Redmine::MenuManager.map :project_menu do |menu|
227 menu.push :overview, { :controller => 'projects', :action => 'show' }
227 menu.push :overview, { :controller => 'projects', :action => 'show' }
228 menu.push :activity, { :controller => 'activities', :action => 'index' }
228 menu.push :activity, { :controller => 'activities', :action => 'index' }
229 menu.push :roadmap, { :controller => 'versions', :action => 'index' }, :param => :project_id,
229 menu.push :roadmap, { :controller => 'versions', :action => 'index' }, :param => :project_id,
230 :if => Proc.new { |p| p.shared_versions.any? }
230 :if => Proc.new { |p| p.shared_versions.any? }
231 menu.push :issues, { :controller => 'issues', :action => 'index' }, :param => :project_id, :caption => :label_issue_plural
231 menu.push :issues, { :controller => 'issues', :action => 'index' }, :param => :project_id, :caption => :label_issue_plural
232 menu.push :new_issue, { :controller => 'issues', :action => 'new', :copy_from => nil }, :param => :project_id, :caption => :label_issue_new,
232 menu.push :new_issue, { :controller => 'issues', :action => 'new', :copy_from => nil }, :param => :project_id, :caption => :label_issue_new,
233 :html => { :accesskey => Redmine::AccessKeys.key_for(:new_issue) },
233 :html => { :accesskey => Redmine::AccessKeys.key_for(:new_issue) },
234 :if => Proc.new { |p| p.trackers.any? },
234 :if => Proc.new { |p| p.trackers.any? },
235 :permission => :add_issues
235 :permission => :add_issues
236 menu.push :gantt, { :controller => 'gantts', :action => 'show' }, :param => :project_id, :caption => :label_gantt
236 menu.push :gantt, { :controller => 'gantts', :action => 'show' }, :param => :project_id, :caption => :label_gantt
237 menu.push :calendar, { :controller => 'calendars', :action => 'show' }, :param => :project_id, :caption => :label_calendar
237 menu.push :calendar, { :controller => 'calendars', :action => 'show' }, :param => :project_id, :caption => :label_calendar
238 menu.push :news, { :controller => 'news', :action => 'index' }, :param => :project_id, :caption => :label_news_plural
238 menu.push :news, { :controller => 'news', :action => 'index' }, :param => :project_id, :caption => :label_news_plural
239 menu.push :documents, { :controller => 'documents', :action => 'index' }, :param => :project_id, :caption => :label_document_plural
239 menu.push :documents, { :controller => 'documents', :action => 'index' }, :param => :project_id, :caption => :label_document_plural
240 menu.push :wiki, { :controller => 'wiki', :action => 'show', :id => nil }, :param => :project_id,
240 menu.push :wiki, { :controller => 'wiki', :action => 'show', :id => nil }, :param => :project_id,
241 :if => Proc.new { |p| p.wiki && !p.wiki.new_record? }
241 :if => Proc.new { |p| p.wiki && !p.wiki.new_record? }
242 menu.push :boards, { :controller => 'boards', :action => 'index', :id => nil }, :param => :project_id,
242 menu.push :boards, { :controller => 'boards', :action => 'index', :id => nil }, :param => :project_id,
243 :if => Proc.new { |p| p.boards.any? }, :caption => :label_board_plural
243 :if => Proc.new { |p| p.boards.any? }, :caption => :label_board_plural
244 menu.push :files, { :controller => 'files', :action => 'index' }, :caption => :label_file_plural, :param => :project_id
244 menu.push :files, { :controller => 'files', :action => 'index' }, :caption => :label_file_plural, :param => :project_id
245 menu.push :repository, { :controller => 'repositories', :action => 'show', :repository_id => nil, :path => nil, :rev => nil },
245 menu.push :repository, { :controller => 'repositories', :action => 'show', :repository_id => nil, :path => nil, :rev => nil },
246 :if => Proc.new { |p| p.repository && !p.repository.new_record? }
246 :if => Proc.new { |p| p.repository && !p.repository.new_record? }
247 menu.push :settings, { :controller => 'projects', :action => 'settings' }, :last => true
247 menu.push :settings, { :controller => 'projects', :action => 'settings' }, :last => true
248 end
248 end
249
249
250 Redmine::Activity.map do |activity|
250 Redmine::Activity.map do |activity|
251 activity.register :issues, :class_name => %w(Issue Journal)
251 activity.register :issues, :class_name => %w(Issue Journal)
252 activity.register :changesets
252 activity.register :changesets
253 activity.register :news
253 activity.register :news
254 activity.register :documents, :class_name => %w(Document Attachment)
254 activity.register :documents, :class_name => %w(Document Attachment)
255 activity.register :files, :class_name => 'Attachment'
255 activity.register :files, :class_name => 'Attachment'
256 activity.register :wiki_edits, :class_name => 'WikiContent::Version', :default => false
256 activity.register :wiki_edits, :class_name => 'WikiContent::Version', :default => false
257 activity.register :messages, :default => false
257 activity.register :messages, :default => false
258 activity.register :time_entries, :default => false
258 activity.register :time_entries, :default => false
259 end
259 end
260
260
261 Redmine::Search.map do |search|
261 Redmine::Search.map do |search|
262 search.register :issues
262 search.register :issues
263 search.register :news
263 search.register :news
264 search.register :documents
264 search.register :documents
265 search.register :changesets
265 search.register :changesets
266 search.register :wiki_pages
266 search.register :wiki_pages
267 search.register :messages
267 search.register :messages
268 search.register :projects
268 search.register :projects
269 end
269 end
270
270
271 Redmine::WikiFormatting.map do |format|
271 Redmine::WikiFormatting.map do |format|
272 format.register :textile
272 format.register :textile
273 format.register :markdown if Object.const_defined?(:Redcarpet)
273 format.register :markdown if Object.const_defined?(:Redcarpet)
274 end
274 end
275
275
276 ActionView::Template.register_template_handler :rsb, Redmine::Views::ApiTemplateHandler
276 ActionView::Template.register_template_handler :rsb, Redmine::Views::ApiTemplateHandler
@@ -1,221 +1,221
1 # Redmine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006-2015 Jean-Philippe Lang
2 # Copyright (C) 2006-2015 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
19
20 class JournalsControllerTest < ActionController::TestCase
20 class JournalsControllerTest < ActionController::TestCase
21 fixtures :projects, :users, :members, :member_roles, :roles, :issues, :journals, :journal_details, :enabled_modules,
21 fixtures :projects, :users, :members, :member_roles, :roles, :issues, :journals, :journal_details, :enabled_modules,
22 :trackers, :issue_statuses, :enumerations, :custom_fields, :custom_values, :custom_fields_projects, :projects_trackers
22 :trackers, :issue_statuses, :enumerations, :custom_fields, :custom_values, :custom_fields_projects, :projects_trackers
23
23
24 def setup
24 def setup
25 User.current = nil
25 User.current = nil
26 end
26 end
27
27
28 def test_index
28 def test_index
29 get :index, :project_id => 1
29 get :index, :project_id => 1
30 assert_response :success
30 assert_response :success
31 assert_not_nil assigns(:journals)
31 assert_not_nil assigns(:journals)
32 assert_equal 'application/atom+xml', @response.content_type
32 assert_equal 'application/atom+xml', @response.content_type
33 end
33 end
34
34
35 def test_index_with_invalid_query_id
35 def test_index_with_invalid_query_id
36 get :index, :project_id => 1, :query_id => 999
36 get :index, :project_id => 1, :query_id => 999
37 assert_response 404
37 assert_response 404
38 end
38 end
39
39
40 def test_index_should_return_privates_notes_with_permission_only
40 def test_index_should_return_privates_notes_with_permission_only
41 journal = Journal.create!(:journalized => Issue.find(2), :notes => 'Privates notes', :private_notes => true, :user_id => 1)
41 journal = Journal.create!(:journalized => Issue.find(2), :notes => 'Privates notes', :private_notes => true, :user_id => 1)
42 @request.session[:user_id] = 2
42 @request.session[:user_id] = 2
43
43
44 get :index, :project_id => 1
44 get :index, :project_id => 1
45 assert_response :success
45 assert_response :success
46 assert_include journal, assigns(:journals)
46 assert_include journal, assigns(:journals)
47
47
48 Role.find(1).remove_permission! :view_private_notes
48 Role.find(1).remove_permission! :view_private_notes
49 get :index, :project_id => 1
49 get :index, :project_id => 1
50 assert_response :success
50 assert_response :success
51 assert_not_include journal, assigns(:journals)
51 assert_not_include journal, assigns(:journals)
52 end
52 end
53
53
54 def test_index_should_show_visible_custom_fields_only
54 def test_index_should_show_visible_custom_fields_only
55 Issue.destroy_all
55 Issue.destroy_all
56 field_attributes = {:field_format => 'string', :is_for_all => true, :is_filter => true, :trackers => Tracker.all}
56 field_attributes = {:field_format => 'string', :is_for_all => true, :is_filter => true, :trackers => Tracker.all}
57 @fields = []
57 @fields = []
58 @fields << (@field1 = IssueCustomField.create!(field_attributes.merge(:name => 'Field 1', :visible => true)))
58 @fields << (@field1 = IssueCustomField.create!(field_attributes.merge(:name => 'Field 1', :visible => true)))
59 @fields << (@field2 = IssueCustomField.create!(field_attributes.merge(:name => 'Field 2', :visible => false, :role_ids => [1, 2])))
59 @fields << (@field2 = IssueCustomField.create!(field_attributes.merge(:name => 'Field 2', :visible => false, :role_ids => [1, 2])))
60 @fields << (@field3 = IssueCustomField.create!(field_attributes.merge(:name => 'Field 3', :visible => false, :role_ids => [1, 3])))
60 @fields << (@field3 = IssueCustomField.create!(field_attributes.merge(:name => 'Field 3', :visible => false, :role_ids => [1, 3])))
61 @issue = Issue.generate!(
61 @issue = Issue.generate!(
62 :author_id => 1,
62 :author_id => 1,
63 :project_id => 1,
63 :project_id => 1,
64 :tracker_id => 1,
64 :tracker_id => 1,
65 :custom_field_values => {@field1.id => 'Value0', @field2.id => 'Value1', @field3.id => 'Value2'}
65 :custom_field_values => {@field1.id => 'Value0', @field2.id => 'Value1', @field3.id => 'Value2'}
66 )
66 )
67 @issue.init_journal(User.find(1))
67 @issue.init_journal(User.find(1))
68 @issue.update_attribute :custom_field_values, {@field1.id => 'NewValue0', @field2.id => 'NewValue1', @field3.id => 'NewValue2'}
68 @issue.update_attribute :custom_field_values, {@field1.id => 'NewValue0', @field2.id => 'NewValue1', @field3.id => 'NewValue2'}
69
69
70
70
71 user_with_role_on_other_project = User.generate!
71 user_with_role_on_other_project = User.generate!
72 User.add_to_project(user_with_role_on_other_project, Project.find(2), Role.find(3))
72 User.add_to_project(user_with_role_on_other_project, Project.find(2), Role.find(3))
73 users_to_test = {
73 users_to_test = {
74 User.find(1) => [@field1, @field2, @field3],
74 User.find(1) => [@field1, @field2, @field3],
75 User.find(3) => [@field1, @field2],
75 User.find(3) => [@field1, @field2],
76 user_with_role_on_other_project => [@field1], # should see field1 only on Project 1
76 user_with_role_on_other_project => [@field1], # should see field1 only on Project 1
77 User.generate! => [@field1],
77 User.generate! => [@field1],
78 User.anonymous => [@field1]
78 User.anonymous => [@field1]
79 }
79 }
80
80
81 users_to_test.each do |user, visible_fields|
81 users_to_test.each do |user, visible_fields|
82 get :index, :format => 'atom', :key => user.rss_key
82 get :index, :format => 'atom', :key => user.rss_key
83 @fields.each_with_index do |field, i|
83 @fields.each_with_index do |field, i|
84 if visible_fields.include?(field)
84 if visible_fields.include?(field)
85 assert_select "content[type=html]", { :text => /NewValue#{i}/, :count => 1 }, "User #{user.id} was not able to view #{field.name} in API"
85 assert_select "content[type=html]", { :text => /NewValue#{i}/, :count => 1 }, "User #{user.id} was not able to view #{field.name} in API"
86 else
86 else
87 assert_select "content[type=html]", { :text => /NewValue#{i}/, :count => 0 }, "User #{user.id} was able to view #{field.name} in API"
87 assert_select "content[type=html]", { :text => /NewValue#{i}/, :count => 0 }, "User #{user.id} was able to view #{field.name} in API"
88 end
88 end
89 end
89 end
90 end
90 end
91
91
92 end
92 end
93
93
94 def test_diff_for_description_change
94 def test_diff_for_description_change
95 get :diff, :id => 3, :detail_id => 4
95 get :diff, :id => 3, :detail_id => 4
96 assert_response :success
96 assert_response :success
97 assert_template 'diff'
97 assert_template 'diff'
98
98
99 assert_select 'span.diff_out', :text => /removed/
99 assert_select 'span.diff_out', :text => /removed/
100 assert_select 'span.diff_in', :text => /added/
100 assert_select 'span.diff_in', :text => /added/
101 end
101 end
102
102
103 def test_diff_for_custom_field
103 def test_diff_for_custom_field
104 field = IssueCustomField.create!(:name => "Long field", :field_format => 'text')
104 field = IssueCustomField.create!(:name => "Long field", :field_format => 'text')
105 journal = Journal.create!(:journalized => Issue.find(2), :notes => 'Notes', :user_id => 1)
105 journal = Journal.create!(:journalized => Issue.find(2), :notes => 'Notes', :user_id => 1)
106 detail = JournalDetail.create!(:journal => journal, :property => 'cf', :prop_key => field.id,
106 detail = JournalDetail.create!(:journal => journal, :property => 'cf', :prop_key => field.id,
107 :old_value => 'Foo', :value => 'Bar')
107 :old_value => 'Foo', :value => 'Bar')
108
108
109 get :diff, :id => journal.id, :detail_id => detail.id
109 get :diff, :id => journal.id, :detail_id => detail.id
110 assert_response :success
110 assert_response :success
111 assert_template 'diff'
111 assert_template 'diff'
112
112
113 assert_select 'span.diff_out', :text => /Foo/
113 assert_select 'span.diff_out', :text => /Foo/
114 assert_select 'span.diff_in', :text => /Bar/
114 assert_select 'span.diff_in', :text => /Bar/
115 end
115 end
116
116
117 def test_diff_for_custom_field_should_be_denied_if_custom_field_is_not_visible
117 def test_diff_for_custom_field_should_be_denied_if_custom_field_is_not_visible
118 field = IssueCustomField.create!(:name => "Long field", :field_format => 'text', :visible => false, :role_ids => [1])
118 field = IssueCustomField.create!(:name => "Long field", :field_format => 'text', :visible => false, :role_ids => [1])
119 journal = Journal.create!(:journalized => Issue.find(2), :notes => 'Notes', :user_id => 1)
119 journal = Journal.create!(:journalized => Issue.find(2), :notes => 'Notes', :user_id => 1)
120 detail = JournalDetail.create!(:journal => journal, :property => 'cf', :prop_key => field.id,
120 detail = JournalDetail.create!(:journal => journal, :property => 'cf', :prop_key => field.id,
121 :old_value => 'Foo', :value => 'Bar')
121 :old_value => 'Foo', :value => 'Bar')
122
122
123 get :diff, :id => journal.id, :detail_id => detail.id
123 get :diff, :id => journal.id, :detail_id => detail.id
124 assert_response 302
124 assert_response 302
125 end
125 end
126
126
127 def test_diff_should_default_to_description_diff
127 def test_diff_should_default_to_description_diff
128 get :diff, :id => 3
128 get :diff, :id => 3
129 assert_response :success
129 assert_response :success
130 assert_template 'diff'
130 assert_template 'diff'
131
131
132 assert_select 'span.diff_out', :text => /removed/
132 assert_select 'span.diff_out', :text => /removed/
133 assert_select 'span.diff_in', :text => /added/
133 assert_select 'span.diff_in', :text => /added/
134 end
134 end
135
135
136 def test_reply_to_issue
136 def test_reply_to_issue
137 @request.session[:user_id] = 2
137 @request.session[:user_id] = 2
138 xhr :get, :new, :id => 6
138 xhr :get, :new, :id => 6
139 assert_response :success
139 assert_response :success
140 assert_template 'new'
140 assert_template 'new'
141 assert_equal 'text/javascript', response.content_type
141 assert_equal 'text/javascript', response.content_type
142 assert_include '> This is an issue', response.body
142 assert_include '> This is an issue', response.body
143 end
143 end
144
144
145 def test_reply_to_issue_without_permission
145 def test_reply_to_issue_without_permission
146 @request.session[:user_id] = 7
146 @request.session[:user_id] = 7
147 xhr :get, :new, :id => 6
147 xhr :get, :new, :id => 6
148 assert_response 403
148 assert_response 403
149 end
149 end
150
150
151 def test_reply_to_note
151 def test_reply_to_note
152 @request.session[:user_id] = 2
152 @request.session[:user_id] = 2
153 xhr :get, :new, :id => 6, :journal_id => 4
153 xhr :get, :new, :id => 6, :journal_id => 4
154 assert_response :success
154 assert_response :success
155 assert_template 'new'
155 assert_template 'new'
156 assert_equal 'text/javascript', response.content_type
156 assert_equal 'text/javascript', response.content_type
157 assert_include '> A comment with a private version', response.body
157 assert_include '> A comment with a private version', response.body
158 end
158 end
159
159
160 def test_reply_to_private_note_should_fail_without_permission
160 def test_reply_to_private_note_should_fail_without_permission
161 journal = Journal.create!(:journalized => Issue.find(2), :notes => 'Privates notes', :private_notes => true)
161 journal = Journal.create!(:journalized => Issue.find(2), :notes => 'Privates notes', :private_notes => true)
162 @request.session[:user_id] = 2
162 @request.session[:user_id] = 2
163
163
164 xhr :get, :new, :id => 2, :journal_id => journal.id
164 xhr :get, :new, :id => 2, :journal_id => journal.id
165 assert_response :success
165 assert_response :success
166 assert_template 'new'
166 assert_template 'new'
167 assert_equal 'text/javascript', response.content_type
167 assert_equal 'text/javascript', response.content_type
168 assert_include '> Privates notes', response.body
168 assert_include '> Privates notes', response.body
169
169
170 Role.find(1).remove_permission! :view_private_notes
170 Role.find(1).remove_permission! :view_private_notes
171 xhr :get, :new, :id => 2, :journal_id => journal.id
171 xhr :get, :new, :id => 2, :journal_id => journal.id
172 assert_response 404
172 assert_response 404
173 end
173 end
174
174
175 def test_edit_xhr
175 def test_edit_xhr
176 @request.session[:user_id] = 1
176 @request.session[:user_id] = 1
177 xhr :get, :edit, :id => 2
177 xhr :get, :edit, :id => 2
178 assert_response :success
178 assert_response :success
179 assert_template 'edit'
179 assert_template 'edit'
180 assert_equal 'text/javascript', response.content_type
180 assert_equal 'text/javascript', response.content_type
181 assert_include 'textarea', response.body
181 assert_include 'textarea', response.body
182 end
182 end
183
183
184 def test_edit_private_note_should_fail_without_permission
184 def test_edit_private_note_should_fail_without_permission
185 journal = Journal.create!(:journalized => Issue.find(2), :notes => 'Privates notes', :private_notes => true)
185 journal = Journal.create!(:journalized => Issue.find(2), :notes => 'Privates notes', :private_notes => true)
186 @request.session[:user_id] = 2
186 @request.session[:user_id] = 2
187 Role.find(1).add_permission! :edit_issue_notes
187 Role.find(1).add_permission! :edit_issue_notes
188
188
189 xhr :get, :edit, :id => journal.id
189 xhr :get, :edit, :id => journal.id
190 assert_response :success
190 assert_response :success
191 assert_template 'edit'
191 assert_template 'edit'
192 assert_equal 'text/javascript', response.content_type
192 assert_equal 'text/javascript', response.content_type
193 assert_include 'textarea', response.body
193 assert_include 'textarea', response.body
194
194
195 Role.find(1).remove_permission! :view_private_notes
195 Role.find(1).remove_permission! :view_private_notes
196 xhr :get, :edit, :id => journal.id
196 xhr :get, :edit, :id => journal.id
197 assert_response 404
197 assert_response 404
198 end
198 end
199
199
200 def test_update_xhr
200 def test_update_xhr
201 @request.session[:user_id] = 1
201 @request.session[:user_id] = 1
202 xhr :post, :edit, :id => 2, :notes => 'Updated notes'
202 xhr :post, :update, :id => 2, :notes => 'Updated notes'
203 assert_response :success
203 assert_response :success
204 assert_template 'update'
204 assert_template 'update'
205 assert_equal 'text/javascript', response.content_type
205 assert_equal 'text/javascript', response.content_type
206 assert_equal 'Updated notes', Journal.find(2).notes
206 assert_equal 'Updated notes', Journal.find(2).notes
207 assert_include 'journal-2-notes', response.body
207 assert_include 'journal-2-notes', response.body
208 end
208 end
209
209
210 def test_update_xhr_with_empty_notes_should_delete_the_journal
210 def test_update_xhr_with_empty_notes_should_delete_the_journal
211 @request.session[:user_id] = 1
211 @request.session[:user_id] = 1
212 assert_difference 'Journal.count', -1 do
212 assert_difference 'Journal.count', -1 do
213 xhr :post, :edit, :id => 2, :notes => ''
213 xhr :post, :update, :id => 2, :notes => ''
214 assert_response :success
214 assert_response :success
215 assert_template 'update'
215 assert_template 'update'
216 assert_equal 'text/javascript', response.content_type
216 assert_equal 'text/javascript', response.content_type
217 end
217 end
218 assert_nil Journal.find_by_id(2)
218 assert_nil Journal.find_by_id(2)
219 assert_include 'change-2', response.body
219 assert_include 'change-2', response.body
220 end
220 end
221 end
221 end
@@ -1,29 +1,29
1 # Redmine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006-2015 Jean-Philippe Lang
2 # Copyright (C) 2006-2015 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
19
20 class RoutingJournalsTest < Redmine::RoutingTest
20 class RoutingJournalsTest < Redmine::RoutingTest
21 def test_journals
21 def test_journals
22 should_route 'POST /issues/1/quoted' => 'journals#new', :id => '1'
22 should_route 'POST /issues/1/quoted' => 'journals#new', :id => '1'
23 should_route 'GET /issues/changes' => 'journals#index'
23 should_route 'GET /issues/changes' => 'journals#index'
24 should_route 'GET /journals/diff/1' => 'journals#diff', :id => '1'
24 should_route 'GET /journals/1/diff' => 'journals#diff', :id => '1'
25
25
26 should_route 'GET /journals/edit/1' => 'journals#edit', :id => '1'
26 should_route 'GET /journals/1/edit' => 'journals#edit', :id => '1'
27 should_route 'POST /journals/edit/1' => 'journals#edit', :id => '1'
27 should_route 'PUT /journals/1' => 'journals#update', :id => '1'
28 end
28 end
29 end
29 end
@@ -1,853 +1,853
1 # Redmine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006-2015 Jean-Philippe Lang
2 # Copyright (C) 2006-2015 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
19
20 class MailerTest < ActiveSupport::TestCase
20 class MailerTest < ActiveSupport::TestCase
21 include Redmine::I18n
21 include Redmine::I18n
22 include Rails::Dom::Testing::Assertions
22 include Rails::Dom::Testing::Assertions
23 fixtures :projects, :enabled_modules, :issues, :users, :email_addresses, :members,
23 fixtures :projects, :enabled_modules, :issues, :users, :email_addresses, :members,
24 :member_roles, :roles, :documents, :attachments, :news,
24 :member_roles, :roles, :documents, :attachments, :news,
25 :tokens, :journals, :journal_details, :changesets,
25 :tokens, :journals, :journal_details, :changesets,
26 :trackers, :projects_trackers,
26 :trackers, :projects_trackers,
27 :issue_statuses, :enumerations, :messages, :boards, :repositories,
27 :issue_statuses, :enumerations, :messages, :boards, :repositories,
28 :wikis, :wiki_pages, :wiki_contents, :wiki_content_versions,
28 :wikis, :wiki_pages, :wiki_contents, :wiki_content_versions,
29 :versions,
29 :versions,
30 :comments
30 :comments
31
31
32 def setup
32 def setup
33 ActionMailer::Base.deliveries.clear
33 ActionMailer::Base.deliveries.clear
34 Setting.host_name = 'mydomain.foo'
34 Setting.host_name = 'mydomain.foo'
35 Setting.protocol = 'http'
35 Setting.protocol = 'http'
36 Setting.plain_text_mail = '0'
36 Setting.plain_text_mail = '0'
37 Setting.default_language = 'en'
37 Setting.default_language = 'en'
38 User.current = nil
38 User.current = nil
39 end
39 end
40
40
41 def test_generated_links_in_emails
41 def test_generated_links_in_emails
42 Setting.host_name = 'mydomain.foo'
42 Setting.host_name = 'mydomain.foo'
43 Setting.protocol = 'https'
43 Setting.protocol = 'https'
44
44
45 journal = Journal.find(3)
45 journal = Journal.find(3)
46 assert Mailer.deliver_issue_edit(journal)
46 assert Mailer.deliver_issue_edit(journal)
47
47
48 mail = last_email
48 mail = last_email
49 assert_not_nil mail
49 assert_not_nil mail
50
50
51 assert_select_email do
51 assert_select_email do
52 # link to the main ticket
52 # link to the main ticket
53 assert_select 'a[href=?]',
53 assert_select 'a[href=?]',
54 'https://mydomain.foo/issues/2#change-3',
54 'https://mydomain.foo/issues/2#change-3',
55 :text => 'Feature request #2: Add ingredients categories'
55 :text => 'Feature request #2: Add ingredients categories'
56 # link to a referenced ticket
56 # link to a referenced ticket
57 assert_select 'a[href=?][title=?]',
57 assert_select 'a[href=?][title=?]',
58 'https://mydomain.foo/issues/1',
58 'https://mydomain.foo/issues/1',
59 "Bug: Cannot print recipes (New)",
59 "Bug: Cannot print recipes (New)",
60 :text => '#1'
60 :text => '#1'
61 # link to a changeset
61 # link to a changeset
62 assert_select 'a[href=?][title=?]',
62 assert_select 'a[href=?][title=?]',
63 'https://mydomain.foo/projects/ecookbook/repository/revisions/2',
63 'https://mydomain.foo/projects/ecookbook/repository/revisions/2',
64 'This commit fixes #1, #2 and references #1 & #3',
64 'This commit fixes #1, #2 and references #1 & #3',
65 :text => 'r2'
65 :text => 'r2'
66 # link to a description diff
66 # link to a description diff
67 assert_select 'a[href^=?][title=?]',
67 assert_select 'a[href^=?][title=?]',
68 # should be https://mydomain.foo/journals/diff/3?detail_id=4
68 # should be https://mydomain.foo/journals/diff/3?detail_id=4
69 # but the Rails 4.2 DOM assertion doesn't handle the ? in the
69 # but the Rails 4.2 DOM assertion doesn't handle the ? in the
70 # attribute value
70 # attribute value
71 'https://mydomain.foo/journals/diff/3',
71 'https://mydomain.foo/journals/3/diff',
72 'View differences',
72 'View differences',
73 :text => 'diff'
73 :text => 'diff'
74 # link to an attachment
74 # link to an attachment
75 assert_select 'a[href=?]',
75 assert_select 'a[href=?]',
76 'https://mydomain.foo/attachments/download/4/source.rb',
76 'https://mydomain.foo/attachments/download/4/source.rb',
77 :text => 'source.rb'
77 :text => 'source.rb'
78 end
78 end
79 end
79 end
80
80
81 def test_generated_links_with_prefix
81 def test_generated_links_with_prefix
82 relative_url_root = Redmine::Utils.relative_url_root
82 relative_url_root = Redmine::Utils.relative_url_root
83 Setting.host_name = 'mydomain.foo/rdm'
83 Setting.host_name = 'mydomain.foo/rdm'
84 Setting.protocol = 'http'
84 Setting.protocol = 'http'
85
85
86 journal = Journal.find(3)
86 journal = Journal.find(3)
87 assert Mailer.deliver_issue_edit(journal)
87 assert Mailer.deliver_issue_edit(journal)
88
88
89 mail = last_email
89 mail = last_email
90 assert_not_nil mail
90 assert_not_nil mail
91
91
92 assert_select_email do
92 assert_select_email do
93 # link to the main ticket
93 # link to the main ticket
94 assert_select 'a[href=?]',
94 assert_select 'a[href=?]',
95 'http://mydomain.foo/rdm/issues/2#change-3',
95 'http://mydomain.foo/rdm/issues/2#change-3',
96 :text => 'Feature request #2: Add ingredients categories'
96 :text => 'Feature request #2: Add ingredients categories'
97 # link to a referenced ticket
97 # link to a referenced ticket
98 assert_select 'a[href=?][title=?]',
98 assert_select 'a[href=?][title=?]',
99 'http://mydomain.foo/rdm/issues/1',
99 'http://mydomain.foo/rdm/issues/1',
100 "Bug: Cannot print recipes (New)",
100 "Bug: Cannot print recipes (New)",
101 :text => '#1'
101 :text => '#1'
102 # link to a changeset
102 # link to a changeset
103 assert_select 'a[href=?][title=?]',
103 assert_select 'a[href=?][title=?]',
104 'http://mydomain.foo/rdm/projects/ecookbook/repository/revisions/2',
104 'http://mydomain.foo/rdm/projects/ecookbook/repository/revisions/2',
105 'This commit fixes #1, #2 and references #1 & #3',
105 'This commit fixes #1, #2 and references #1 & #3',
106 :text => 'r2'
106 :text => 'r2'
107 # link to a description diff
107 # link to a description diff
108 assert_select 'a[href^=?][title=?]',
108 assert_select 'a[href^=?][title=?]',
109 # should be http://mydomain.foo/rdm/journals/diff/3?detail_id=4
109 # should be http://mydomain.foo/rdm/journals/diff/3?detail_id=4
110 # but the Rails 4.2 DOM assertion doesn't handle the ? in the
110 # but the Rails 4.2 DOM assertion doesn't handle the ? in the
111 # attribute value
111 # attribute value
112 'http://mydomain.foo/rdm/journals/diff/3',
112 'http://mydomain.foo/rdm/journals/3/diff',
113 'View differences',
113 'View differences',
114 :text => 'diff'
114 :text => 'diff'
115 # link to an attachment
115 # link to an attachment
116 assert_select 'a[href=?]',
116 assert_select 'a[href=?]',
117 'http://mydomain.foo/rdm/attachments/download/4/source.rb',
117 'http://mydomain.foo/rdm/attachments/download/4/source.rb',
118 :text => 'source.rb'
118 :text => 'source.rb'
119 end
119 end
120 end
120 end
121
121
122 def test_generated_links_with_port_and_prefix
122 def test_generated_links_with_port_and_prefix
123 with_settings :host_name => '10.0.0.1:81/redmine', :protocol => 'http' do
123 with_settings :host_name => '10.0.0.1:81/redmine', :protocol => 'http' do
124 Mailer.test_email(User.find(1)).deliver
124 Mailer.test_email(User.find(1)).deliver
125 mail = last_email
125 mail = last_email
126 assert_not_nil mail
126 assert_not_nil mail
127 assert_include 'http://10.0.0.1:81/redmine', mail_body(mail)
127 assert_include 'http://10.0.0.1:81/redmine', mail_body(mail)
128 end
128 end
129 end
129 end
130
130
131 def test_generated_links_with_port
131 def test_generated_links_with_port
132 with_settings :host_name => '10.0.0.1:81', :protocol => 'http' do
132 with_settings :host_name => '10.0.0.1:81', :protocol => 'http' do
133 Mailer.test_email(User.find(1)).deliver
133 Mailer.test_email(User.find(1)).deliver
134 mail = last_email
134 mail = last_email
135 assert_not_nil mail
135 assert_not_nil mail
136 assert_include 'http://10.0.0.1:81', mail_body(mail)
136 assert_include 'http://10.0.0.1:81', mail_body(mail)
137 end
137 end
138 end
138 end
139
139
140 def test_issue_edit_should_generate_url_with_hostname_for_relations
140 def test_issue_edit_should_generate_url_with_hostname_for_relations
141 journal = Journal.new(:journalized => Issue.find(1), :user => User.find(1), :created_on => Time.now)
141 journal = Journal.new(:journalized => Issue.find(1), :user => User.find(1), :created_on => Time.now)
142 journal.details << JournalDetail.new(:property => 'relation', :prop_key => 'label_relates_to', :value => 2)
142 journal.details << JournalDetail.new(:property => 'relation', :prop_key => 'label_relates_to', :value => 2)
143 Mailer.deliver_issue_edit(journal)
143 Mailer.deliver_issue_edit(journal)
144 assert_not_nil last_email
144 assert_not_nil last_email
145 assert_select_email do
145 assert_select_email do
146 assert_select 'a[href=?]', 'http://mydomain.foo/issues/2', :text => 'Feature request #2'
146 assert_select 'a[href=?]', 'http://mydomain.foo/issues/2', :text => 'Feature request #2'
147 end
147 end
148 end
148 end
149
149
150 def test_generated_links_with_prefix_and_no_relative_url_root
150 def test_generated_links_with_prefix_and_no_relative_url_root
151 relative_url_root = Redmine::Utils.relative_url_root
151 relative_url_root = Redmine::Utils.relative_url_root
152 Setting.host_name = 'mydomain.foo/rdm'
152 Setting.host_name = 'mydomain.foo/rdm'
153 Setting.protocol = 'http'
153 Setting.protocol = 'http'
154 Redmine::Utils.relative_url_root = nil
154 Redmine::Utils.relative_url_root = nil
155
155
156 journal = Journal.find(3)
156 journal = Journal.find(3)
157 assert Mailer.deliver_issue_edit(journal)
157 assert Mailer.deliver_issue_edit(journal)
158
158
159 mail = last_email
159 mail = last_email
160 assert_not_nil mail
160 assert_not_nil mail
161
161
162 assert_select_email do
162 assert_select_email do
163 # link to the main ticket
163 # link to the main ticket
164 assert_select 'a[href=?]',
164 assert_select 'a[href=?]',
165 'http://mydomain.foo/rdm/issues/2#change-3',
165 'http://mydomain.foo/rdm/issues/2#change-3',
166 :text => 'Feature request #2: Add ingredients categories'
166 :text => 'Feature request #2: Add ingredients categories'
167 # link to a referenced ticket
167 # link to a referenced ticket
168 assert_select 'a[href=?][title=?]',
168 assert_select 'a[href=?][title=?]',
169 'http://mydomain.foo/rdm/issues/1',
169 'http://mydomain.foo/rdm/issues/1',
170 "Bug: Cannot print recipes (New)",
170 "Bug: Cannot print recipes (New)",
171 :text => '#1'
171 :text => '#1'
172 # link to a changeset
172 # link to a changeset
173 assert_select 'a[href=?][title=?]',
173 assert_select 'a[href=?][title=?]',
174 'http://mydomain.foo/rdm/projects/ecookbook/repository/revisions/2',
174 'http://mydomain.foo/rdm/projects/ecookbook/repository/revisions/2',
175 'This commit fixes #1, #2 and references #1 & #3',
175 'This commit fixes #1, #2 and references #1 & #3',
176 :text => 'r2'
176 :text => 'r2'
177 # link to a description diff
177 # link to a description diff
178 assert_select 'a[href^=?][title=?]',
178 assert_select 'a[href^=?][title=?]',
179 # should be http://mydomain.foo/rdm/journals/diff/3?detail_id=4
179 # should be http://mydomain.foo/rdm/journals/diff/3?detail_id=4
180 # but the Rails 4.2 DOM assertion doesn't handle the ? in the
180 # but the Rails 4.2 DOM assertion doesn't handle the ? in the
181 # attribute value
181 # attribute value
182 'http://mydomain.foo/rdm/journals/diff/3',
182 'http://mydomain.foo/rdm/journals/3/diff',
183 'View differences',
183 'View differences',
184 :text => 'diff'
184 :text => 'diff'
185 # link to an attachment
185 # link to an attachment
186 assert_select 'a[href=?]',
186 assert_select 'a[href=?]',
187 'http://mydomain.foo/rdm/attachments/download/4/source.rb',
187 'http://mydomain.foo/rdm/attachments/download/4/source.rb',
188 :text => 'source.rb'
188 :text => 'source.rb'
189 end
189 end
190 ensure
190 ensure
191 # restore it
191 # restore it
192 Redmine::Utils.relative_url_root = relative_url_root
192 Redmine::Utils.relative_url_root = relative_url_root
193 end
193 end
194
194
195 def test_email_headers
195 def test_email_headers
196 issue = Issue.find(1)
196 issue = Issue.find(1)
197 Mailer.deliver_issue_add(issue)
197 Mailer.deliver_issue_add(issue)
198 mail = last_email
198 mail = last_email
199 assert_not_nil mail
199 assert_not_nil mail
200 assert_equal 'All', mail.header['X-Auto-Response-Suppress'].to_s
200 assert_equal 'All', mail.header['X-Auto-Response-Suppress'].to_s
201 assert_equal 'auto-generated', mail.header['Auto-Submitted'].to_s
201 assert_equal 'auto-generated', mail.header['Auto-Submitted'].to_s
202 assert_equal '<redmine.example.net>', mail.header['List-Id'].to_s
202 assert_equal '<redmine.example.net>', mail.header['List-Id'].to_s
203 end
203 end
204
204
205 def test_email_headers_should_include_sender
205 def test_email_headers_should_include_sender
206 issue = Issue.find(1)
206 issue = Issue.find(1)
207 Mailer.deliver_issue_add(issue)
207 Mailer.deliver_issue_add(issue)
208 mail = last_email
208 mail = last_email
209 assert_equal issue.author.login, mail.header['X-Redmine-Sender'].to_s
209 assert_equal issue.author.login, mail.header['X-Redmine-Sender'].to_s
210 end
210 end
211
211
212 def test_plain_text_mail
212 def test_plain_text_mail
213 Setting.plain_text_mail = 1
213 Setting.plain_text_mail = 1
214 journal = Journal.find(2)
214 journal = Journal.find(2)
215 Mailer.deliver_issue_edit(journal)
215 Mailer.deliver_issue_edit(journal)
216 mail = last_email
216 mail = last_email
217 assert_equal "text/plain; charset=UTF-8", mail.content_type
217 assert_equal "text/plain; charset=UTF-8", mail.content_type
218 assert_equal 0, mail.parts.size
218 assert_equal 0, mail.parts.size
219 assert !mail.encoded.include?('href')
219 assert !mail.encoded.include?('href')
220 end
220 end
221
221
222 def test_html_mail
222 def test_html_mail
223 Setting.plain_text_mail = 0
223 Setting.plain_text_mail = 0
224 journal = Journal.find(2)
224 journal = Journal.find(2)
225 Mailer.deliver_issue_edit(journal)
225 Mailer.deliver_issue_edit(journal)
226 mail = last_email
226 mail = last_email
227 assert_equal 2, mail.parts.size
227 assert_equal 2, mail.parts.size
228 assert mail.encoded.include?('href')
228 assert mail.encoded.include?('href')
229 end
229 end
230
230
231 def test_from_header
231 def test_from_header
232 with_settings :mail_from => 'redmine@example.net' do
232 with_settings :mail_from => 'redmine@example.net' do
233 Mailer.test_email(User.find(1)).deliver
233 Mailer.test_email(User.find(1)).deliver
234 end
234 end
235 mail = last_email
235 mail = last_email
236 assert_equal 'redmine@example.net', mail.from_addrs.first
236 assert_equal 'redmine@example.net', mail.from_addrs.first
237 end
237 end
238
238
239 def test_from_header_with_phrase
239 def test_from_header_with_phrase
240 with_settings :mail_from => 'Redmine app <redmine@example.net>' do
240 with_settings :mail_from => 'Redmine app <redmine@example.net>' do
241 Mailer.test_email(User.find(1)).deliver
241 Mailer.test_email(User.find(1)).deliver
242 end
242 end
243 mail = last_email
243 mail = last_email
244 assert_equal 'redmine@example.net', mail.from_addrs.first
244 assert_equal 'redmine@example.net', mail.from_addrs.first
245 assert_equal 'Redmine app <redmine@example.net>', mail.header['From'].to_s
245 assert_equal 'Redmine app <redmine@example.net>', mail.header['From'].to_s
246 end
246 end
247
247
248 def test_should_not_send_email_without_recipient
248 def test_should_not_send_email_without_recipient
249 news = News.first
249 news = News.first
250 user = news.author
250 user = news.author
251 # Remove members except news author
251 # Remove members except news author
252 news.project.memberships.each {|m| m.destroy unless m.user == user}
252 news.project.memberships.each {|m| m.destroy unless m.user == user}
253
253
254 user.pref.no_self_notified = false
254 user.pref.no_self_notified = false
255 user.pref.save
255 user.pref.save
256 User.current = user
256 User.current = user
257 Mailer.news_added(news.reload).deliver
257 Mailer.news_added(news.reload).deliver
258 assert_equal 1, last_email.bcc.size
258 assert_equal 1, last_email.bcc.size
259
259
260 # nobody to notify
260 # nobody to notify
261 user.pref.no_self_notified = true
261 user.pref.no_self_notified = true
262 user.pref.save
262 user.pref.save
263 User.current = user
263 User.current = user
264 ActionMailer::Base.deliveries.clear
264 ActionMailer::Base.deliveries.clear
265 Mailer.news_added(news.reload).deliver
265 Mailer.news_added(news.reload).deliver
266 assert ActionMailer::Base.deliveries.empty?
266 assert ActionMailer::Base.deliveries.empty?
267 end
267 end
268
268
269 def test_issue_add_message_id
269 def test_issue_add_message_id
270 issue = Issue.find(2)
270 issue = Issue.find(2)
271 Mailer.deliver_issue_add(issue)
271 Mailer.deliver_issue_add(issue)
272 mail = last_email
272 mail = last_email
273 assert_match /^redmine\.issue-2\.20060719190421\.[a-f0-9]+@example\.net/, mail.message_id
273 assert_match /^redmine\.issue-2\.20060719190421\.[a-f0-9]+@example\.net/, mail.message_id
274 assert_include "redmine.issue-2.20060719190421@example.net", mail.references
274 assert_include "redmine.issue-2.20060719190421@example.net", mail.references
275 end
275 end
276
276
277 def test_issue_edit_message_id
277 def test_issue_edit_message_id
278 journal = Journal.find(3)
278 journal = Journal.find(3)
279 journal.issue = Issue.find(2)
279 journal.issue = Issue.find(2)
280
280
281 Mailer.deliver_issue_edit(journal)
281 Mailer.deliver_issue_edit(journal)
282 mail = last_email
282 mail = last_email
283 assert_match /^redmine\.journal-3\.\d+\.[a-f0-9]+@example\.net/, mail.message_id
283 assert_match /^redmine\.journal-3\.\d+\.[a-f0-9]+@example\.net/, mail.message_id
284 assert_include "redmine.issue-2.20060719190421@example.net", mail.references
284 assert_include "redmine.issue-2.20060719190421@example.net", mail.references
285 assert_select_email do
285 assert_select_email do
286 # link to the update
286 # link to the update
287 assert_select "a[href=?]",
287 assert_select "a[href=?]",
288 "http://mydomain.foo/issues/#{journal.journalized_id}#change-#{journal.id}"
288 "http://mydomain.foo/issues/#{journal.journalized_id}#change-#{journal.id}"
289 end
289 end
290 end
290 end
291
291
292 def test_message_posted_message_id
292 def test_message_posted_message_id
293 message = Message.find(1)
293 message = Message.find(1)
294 Mailer.message_posted(message).deliver
294 Mailer.message_posted(message).deliver
295 mail = last_email
295 mail = last_email
296 assert_match /^redmine\.message-1\.\d+\.[a-f0-9]+@example\.net/, mail.message_id
296 assert_match /^redmine\.message-1\.\d+\.[a-f0-9]+@example\.net/, mail.message_id
297 assert_include "redmine.message-1.20070512151532@example.net", mail.references
297 assert_include "redmine.message-1.20070512151532@example.net", mail.references
298 assert_select_email do
298 assert_select_email do
299 # link to the message
299 # link to the message
300 assert_select "a[href=?]",
300 assert_select "a[href=?]",
301 "http://mydomain.foo/boards/#{message.board.id}/topics/#{message.id}",
301 "http://mydomain.foo/boards/#{message.board.id}/topics/#{message.id}",
302 :text => message.subject
302 :text => message.subject
303 end
303 end
304 end
304 end
305
305
306 def test_reply_posted_message_id
306 def test_reply_posted_message_id
307 message = Message.find(3)
307 message = Message.find(3)
308 Mailer.message_posted(message).deliver
308 Mailer.message_posted(message).deliver
309 mail = last_email
309 mail = last_email
310 assert_match /^redmine\.message-3\.\d+\.[a-f0-9]+@example\.net/, mail.message_id
310 assert_match /^redmine\.message-3\.\d+\.[a-f0-9]+@example\.net/, mail.message_id
311 assert_include "redmine.message-1.20070512151532@example.net", mail.references
311 assert_include "redmine.message-1.20070512151532@example.net", mail.references
312 assert_select_email do
312 assert_select_email do
313 # link to the reply
313 # link to the reply
314 assert_select "a[href=?]",
314 assert_select "a[href=?]",
315 "http://mydomain.foo/boards/#{message.board.id}/topics/#{message.root.id}?r=#{message.id}#message-#{message.id}",
315 "http://mydomain.foo/boards/#{message.board.id}/topics/#{message.root.id}?r=#{message.id}#message-#{message.id}",
316 :text => message.subject
316 :text => message.subject
317 end
317 end
318 end
318 end
319
319
320 test "#issue_add should notify project members" do
320 test "#issue_add should notify project members" do
321 issue = Issue.find(1)
321 issue = Issue.find(1)
322 assert Mailer.deliver_issue_add(issue)
322 assert Mailer.deliver_issue_add(issue)
323 assert last_email.bcc.include?('dlopper@somenet.foo')
323 assert last_email.bcc.include?('dlopper@somenet.foo')
324 end
324 end
325
325
326 def test_issue_add_should_send_mail_to_all_user_email_address
326 def test_issue_add_should_send_mail_to_all_user_email_address
327 EmailAddress.create!(:user_id => 3, :address => 'otheremail@somenet.foo')
327 EmailAddress.create!(:user_id => 3, :address => 'otheremail@somenet.foo')
328 issue = Issue.find(1)
328 issue = Issue.find(1)
329 assert Mailer.deliver_issue_add(issue)
329 assert Mailer.deliver_issue_add(issue)
330 assert last_email.bcc.include?('dlopper@somenet.foo')
330 assert last_email.bcc.include?('dlopper@somenet.foo')
331 assert last_email.bcc.include?('otheremail@somenet.foo')
331 assert last_email.bcc.include?('otheremail@somenet.foo')
332 end
332 end
333
333
334 test "#issue_add should not notify project members that are not allow to view the issue" do
334 test "#issue_add should not notify project members that are not allow to view the issue" do
335 issue = Issue.find(1)
335 issue = Issue.find(1)
336 Role.find(2).remove_permission!(:view_issues)
336 Role.find(2).remove_permission!(:view_issues)
337 assert Mailer.deliver_issue_add(issue)
337 assert Mailer.deliver_issue_add(issue)
338 assert !last_email.bcc.include?('dlopper@somenet.foo')
338 assert !last_email.bcc.include?('dlopper@somenet.foo')
339 end
339 end
340
340
341 test "#issue_add should notify issue watchers" do
341 test "#issue_add should notify issue watchers" do
342 issue = Issue.find(1)
342 issue = Issue.find(1)
343 user = User.find(9)
343 user = User.find(9)
344 # minimal email notification options
344 # minimal email notification options
345 user.pref.no_self_notified = '1'
345 user.pref.no_self_notified = '1'
346 user.pref.save
346 user.pref.save
347 user.mail_notification = false
347 user.mail_notification = false
348 user.save
348 user.save
349
349
350 Watcher.create!(:watchable => issue, :user => user)
350 Watcher.create!(:watchable => issue, :user => user)
351 assert Mailer.deliver_issue_add(issue)
351 assert Mailer.deliver_issue_add(issue)
352 assert last_email.bcc.include?(user.mail)
352 assert last_email.bcc.include?(user.mail)
353 end
353 end
354
354
355 test "#issue_add should not notify watchers not allowed to view the issue" do
355 test "#issue_add should not notify watchers not allowed to view the issue" do
356 issue = Issue.find(1)
356 issue = Issue.find(1)
357 user = User.find(9)
357 user = User.find(9)
358 Watcher.create!(:watchable => issue, :user => user)
358 Watcher.create!(:watchable => issue, :user => user)
359 Role.non_member.remove_permission!(:view_issues)
359 Role.non_member.remove_permission!(:view_issues)
360 assert Mailer.deliver_issue_add(issue)
360 assert Mailer.deliver_issue_add(issue)
361 assert !last_email.bcc.include?(user.mail)
361 assert !last_email.bcc.include?(user.mail)
362 end
362 end
363
363
364 def test_issue_add_should_include_enabled_fields
364 def test_issue_add_should_include_enabled_fields
365 issue = Issue.find(2)
365 issue = Issue.find(2)
366 assert Mailer.deliver_issue_add(issue)
366 assert Mailer.deliver_issue_add(issue)
367 assert_mail_body_match '* Target version: 1.0', last_email
367 assert_mail_body_match '* Target version: 1.0', last_email
368 assert_select_email do
368 assert_select_email do
369 assert_select 'li', :text => 'Target version: 1.0'
369 assert_select 'li', :text => 'Target version: 1.0'
370 end
370 end
371 end
371 end
372
372
373 def test_issue_add_should_not_include_disabled_fields
373 def test_issue_add_should_not_include_disabled_fields
374 issue = Issue.find(2)
374 issue = Issue.find(2)
375 tracker = issue.tracker
375 tracker = issue.tracker
376 tracker.core_fields -= ['fixed_version_id']
376 tracker.core_fields -= ['fixed_version_id']
377 tracker.save!
377 tracker.save!
378 assert Mailer.deliver_issue_add(issue)
378 assert Mailer.deliver_issue_add(issue)
379 assert_mail_body_no_match 'Target version', last_email
379 assert_mail_body_no_match 'Target version', last_email
380 assert_select_email do
380 assert_select_email do
381 assert_select 'li', :text => /Target version/, :count => 0
381 assert_select 'li', :text => /Target version/, :count => 0
382 end
382 end
383 end
383 end
384
384
385 # test mailer methods for each language
385 # test mailer methods for each language
386 def test_issue_add
386 def test_issue_add
387 issue = Issue.find(1)
387 issue = Issue.find(1)
388 with_each_language_as_default do
388 with_each_language_as_default do
389 assert Mailer.deliver_issue_add(issue)
389 assert Mailer.deliver_issue_add(issue)
390 end
390 end
391 end
391 end
392
392
393 def test_issue_edit
393 def test_issue_edit
394 journal = Journal.find(1)
394 journal = Journal.find(1)
395 with_each_language_as_default do
395 with_each_language_as_default do
396 assert Mailer.deliver_issue_edit(journal)
396 assert Mailer.deliver_issue_edit(journal)
397 end
397 end
398 end
398 end
399
399
400 def test_issue_edit_should_send_private_notes_to_users_with_permission_only
400 def test_issue_edit_should_send_private_notes_to_users_with_permission_only
401 journal = Journal.find(1)
401 journal = Journal.find(1)
402 journal.private_notes = true
402 journal.private_notes = true
403 journal.save!
403 journal.save!
404
404
405 Role.find(2).add_permission! :view_private_notes
405 Role.find(2).add_permission! :view_private_notes
406 Mailer.deliver_issue_edit(journal)
406 Mailer.deliver_issue_edit(journal)
407 assert_equal %w(dlopper@somenet.foo jsmith@somenet.foo), ActionMailer::Base.deliveries.last.bcc.sort
407 assert_equal %w(dlopper@somenet.foo jsmith@somenet.foo), ActionMailer::Base.deliveries.last.bcc.sort
408
408
409 Role.find(2).remove_permission! :view_private_notes
409 Role.find(2).remove_permission! :view_private_notes
410 Mailer.deliver_issue_edit(journal)
410 Mailer.deliver_issue_edit(journal)
411 assert_equal %w(jsmith@somenet.foo), ActionMailer::Base.deliveries.last.bcc.sort
411 assert_equal %w(jsmith@somenet.foo), ActionMailer::Base.deliveries.last.bcc.sort
412 end
412 end
413
413
414 def test_issue_edit_should_send_private_notes_to_watchers_with_permission_only
414 def test_issue_edit_should_send_private_notes_to_watchers_with_permission_only
415 Issue.find(1).set_watcher(User.find_by_login('someone'))
415 Issue.find(1).set_watcher(User.find_by_login('someone'))
416 journal = Journal.find(1)
416 journal = Journal.find(1)
417 journal.private_notes = true
417 journal.private_notes = true
418 journal.save!
418 journal.save!
419
419
420 Role.non_member.add_permission! :view_private_notes
420 Role.non_member.add_permission! :view_private_notes
421 Mailer.deliver_issue_edit(journal)
421 Mailer.deliver_issue_edit(journal)
422 assert_include 'someone@foo.bar', ActionMailer::Base.deliveries.last.bcc.sort
422 assert_include 'someone@foo.bar', ActionMailer::Base.deliveries.last.bcc.sort
423
423
424 Role.non_member.remove_permission! :view_private_notes
424 Role.non_member.remove_permission! :view_private_notes
425 Mailer.deliver_issue_edit(journal)
425 Mailer.deliver_issue_edit(journal)
426 assert_not_include 'someone@foo.bar', ActionMailer::Base.deliveries.last.bcc.sort
426 assert_not_include 'someone@foo.bar', ActionMailer::Base.deliveries.last.bcc.sort
427 end
427 end
428
428
429 def test_issue_edit_should_mark_private_notes
429 def test_issue_edit_should_mark_private_notes
430 journal = Journal.find(2)
430 journal = Journal.find(2)
431 journal.private_notes = true
431 journal.private_notes = true
432 journal.save!
432 journal.save!
433
433
434 with_settings :default_language => 'en' do
434 with_settings :default_language => 'en' do
435 Mailer.deliver_issue_edit(journal)
435 Mailer.deliver_issue_edit(journal)
436 end
436 end
437 assert_mail_body_match '(Private notes)', last_email
437 assert_mail_body_match '(Private notes)', last_email
438 end
438 end
439
439
440 def test_issue_edit_with_relation_should_notify_users_who_can_see_the_related_issue
440 def test_issue_edit_with_relation_should_notify_users_who_can_see_the_related_issue
441 issue = Issue.generate!
441 issue = Issue.generate!
442 issue.init_journal(User.find(1))
442 issue.init_journal(User.find(1))
443 private_issue = Issue.generate!(:is_private => true)
443 private_issue = Issue.generate!(:is_private => true)
444 IssueRelation.create!(:issue_from => issue, :issue_to => private_issue, :relation_type => 'relates')
444 IssueRelation.create!(:issue_from => issue, :issue_to => private_issue, :relation_type => 'relates')
445 issue.reload
445 issue.reload
446 assert_equal 1, issue.journals.size
446 assert_equal 1, issue.journals.size
447 journal = issue.journals.first
447 journal = issue.journals.first
448 ActionMailer::Base.deliveries.clear
448 ActionMailer::Base.deliveries.clear
449
449
450 Mailer.deliver_issue_edit(journal)
450 Mailer.deliver_issue_edit(journal)
451 last_email.bcc.each do |email|
451 last_email.bcc.each do |email|
452 user = User.find_by_mail(email)
452 user = User.find_by_mail(email)
453 assert private_issue.visible?(user), "Issue was not visible to #{user}"
453 assert private_issue.visible?(user), "Issue was not visible to #{user}"
454 end
454 end
455 end
455 end
456
456
457 def test_document_added
457 def test_document_added
458 document = Document.find(1)
458 document = Document.find(1)
459 with_each_language_as_default do
459 with_each_language_as_default do
460 assert Mailer.document_added(document).deliver
460 assert Mailer.document_added(document).deliver
461 end
461 end
462 end
462 end
463
463
464 def test_attachments_added
464 def test_attachments_added
465 attachements = [ Attachment.find_by_container_type('Document') ]
465 attachements = [ Attachment.find_by_container_type('Document') ]
466 with_each_language_as_default do
466 with_each_language_as_default do
467 assert Mailer.attachments_added(attachements).deliver
467 assert Mailer.attachments_added(attachements).deliver
468 end
468 end
469 end
469 end
470
470
471 def test_version_file_added
471 def test_version_file_added
472 attachements = [ Attachment.find_by_container_type('Version') ]
472 attachements = [ Attachment.find_by_container_type('Version') ]
473 assert Mailer.attachments_added(attachements).deliver
473 assert Mailer.attachments_added(attachements).deliver
474 assert_not_nil last_email.bcc
474 assert_not_nil last_email.bcc
475 assert last_email.bcc.any?
475 assert last_email.bcc.any?
476 assert_select_email do
476 assert_select_email do
477 assert_select "a[href=?]", "http://mydomain.foo/projects/ecookbook/files"
477 assert_select "a[href=?]", "http://mydomain.foo/projects/ecookbook/files"
478 end
478 end
479 end
479 end
480
480
481 def test_project_file_added
481 def test_project_file_added
482 attachements = [ Attachment.find_by_container_type('Project') ]
482 attachements = [ Attachment.find_by_container_type('Project') ]
483 assert Mailer.attachments_added(attachements).deliver
483 assert Mailer.attachments_added(attachements).deliver
484 assert_not_nil last_email.bcc
484 assert_not_nil last_email.bcc
485 assert last_email.bcc.any?
485 assert last_email.bcc.any?
486 assert_select_email do
486 assert_select_email do
487 assert_select "a[href=?]", "http://mydomain.foo/projects/ecookbook/files"
487 assert_select "a[href=?]", "http://mydomain.foo/projects/ecookbook/files"
488 end
488 end
489 end
489 end
490
490
491 def test_news_added
491 def test_news_added
492 news = News.first
492 news = News.first
493 with_each_language_as_default do
493 with_each_language_as_default do
494 assert Mailer.news_added(news).deliver
494 assert Mailer.news_added(news).deliver
495 end
495 end
496 end
496 end
497
497
498 def test_news_added_should_notify_project_news_watchers
498 def test_news_added_should_notify_project_news_watchers
499 user1 = User.generate!
499 user1 = User.generate!
500 user2 = User.generate!
500 user2 = User.generate!
501 news = News.find(1)
501 news = News.find(1)
502 news.project.enabled_module('news').add_watcher(user1)
502 news.project.enabled_module('news').add_watcher(user1)
503
503
504 Mailer.news_added(news).deliver
504 Mailer.news_added(news).deliver
505 assert_include user1.mail, last_email.bcc
505 assert_include user1.mail, last_email.bcc
506 assert_not_include user2.mail, last_email.bcc
506 assert_not_include user2.mail, last_email.bcc
507 end
507 end
508
508
509 def test_news_comment_added
509 def test_news_comment_added
510 comment = Comment.find(2)
510 comment = Comment.find(2)
511 with_each_language_as_default do
511 with_each_language_as_default do
512 assert Mailer.news_comment_added(comment).deliver
512 assert Mailer.news_comment_added(comment).deliver
513 end
513 end
514 end
514 end
515
515
516 def test_message_posted
516 def test_message_posted
517 message = Message.first
517 message = Message.first
518 recipients = ([message.root] + message.root.children).collect {|m| m.author.mail if m.author}
518 recipients = ([message.root] + message.root.children).collect {|m| m.author.mail if m.author}
519 recipients = recipients.compact.uniq
519 recipients = recipients.compact.uniq
520 with_each_language_as_default do
520 with_each_language_as_default do
521 assert Mailer.message_posted(message).deliver
521 assert Mailer.message_posted(message).deliver
522 end
522 end
523 end
523 end
524
524
525 def test_wiki_content_added
525 def test_wiki_content_added
526 content = WikiContent.find(1)
526 content = WikiContent.find(1)
527 with_each_language_as_default do
527 with_each_language_as_default do
528 assert_difference 'ActionMailer::Base.deliveries.size' do
528 assert_difference 'ActionMailer::Base.deliveries.size' do
529 assert Mailer.wiki_content_added(content).deliver
529 assert Mailer.wiki_content_added(content).deliver
530 assert_select_email do
530 assert_select_email do
531 assert_select 'a[href=?]',
531 assert_select 'a[href=?]',
532 'http://mydomain.foo/projects/ecookbook/wiki/CookBook_documentation',
532 'http://mydomain.foo/projects/ecookbook/wiki/CookBook_documentation',
533 :text => 'CookBook documentation'
533 :text => 'CookBook documentation'
534 end
534 end
535 end
535 end
536 end
536 end
537 end
537 end
538
538
539 def test_wiki_content_updated
539 def test_wiki_content_updated
540 content = WikiContent.find(1)
540 content = WikiContent.find(1)
541 with_each_language_as_default do
541 with_each_language_as_default do
542 assert_difference 'ActionMailer::Base.deliveries.size' do
542 assert_difference 'ActionMailer::Base.deliveries.size' do
543 assert Mailer.wiki_content_updated(content).deliver
543 assert Mailer.wiki_content_updated(content).deliver
544 assert_select_email do
544 assert_select_email do
545 assert_select 'a[href=?]',
545 assert_select 'a[href=?]',
546 'http://mydomain.foo/projects/ecookbook/wiki/CookBook_documentation',
546 'http://mydomain.foo/projects/ecookbook/wiki/CookBook_documentation',
547 :text => 'CookBook documentation'
547 :text => 'CookBook documentation'
548 end
548 end
549 end
549 end
550 end
550 end
551 end
551 end
552
552
553 def test_account_information
553 def test_account_information
554 user = User.find(2)
554 user = User.find(2)
555 valid_languages.each do |lang|
555 valid_languages.each do |lang|
556 user.update_attribute :language, lang.to_s
556 user.update_attribute :language, lang.to_s
557 user.reload
557 user.reload
558 assert Mailer.account_information(user, 'pAsswORd').deliver
558 assert Mailer.account_information(user, 'pAsswORd').deliver
559 end
559 end
560 end
560 end
561
561
562 def test_lost_password
562 def test_lost_password
563 token = Token.find(2)
563 token = Token.find(2)
564 valid_languages.each do |lang|
564 valid_languages.each do |lang|
565 token.user.update_attribute :language, lang.to_s
565 token.user.update_attribute :language, lang.to_s
566 token.reload
566 token.reload
567 assert Mailer.lost_password(token).deliver
567 assert Mailer.lost_password(token).deliver
568 end
568 end
569 end
569 end
570
570
571 def test_register
571 def test_register
572 token = Token.find(1)
572 token = Token.find(1)
573 Setting.host_name = 'redmine.foo'
573 Setting.host_name = 'redmine.foo'
574 Setting.protocol = 'https'
574 Setting.protocol = 'https'
575
575
576 valid_languages.each do |lang|
576 valid_languages.each do |lang|
577 token.user.update_attribute :language, lang.to_s
577 token.user.update_attribute :language, lang.to_s
578 token.reload
578 token.reload
579 ActionMailer::Base.deliveries.clear
579 ActionMailer::Base.deliveries.clear
580 assert Mailer.register(token).deliver
580 assert Mailer.register(token).deliver
581 mail = last_email
581 mail = last_email
582 assert_select_email do
582 assert_select_email do
583 assert_select "a[href=?]",
583 assert_select "a[href=?]",
584 "https://redmine.foo/account/activate?token=#{token.value}",
584 "https://redmine.foo/account/activate?token=#{token.value}",
585 :text => "https://redmine.foo/account/activate?token=#{token.value}"
585 :text => "https://redmine.foo/account/activate?token=#{token.value}"
586 end
586 end
587 end
587 end
588 end
588 end
589
589
590 def test_test
590 def test_test
591 user = User.find(1)
591 user = User.find(1)
592 valid_languages.each do |lang|
592 valid_languages.each do |lang|
593 user.update_attribute :language, lang.to_s
593 user.update_attribute :language, lang.to_s
594 assert Mailer.test_email(user).deliver
594 assert Mailer.test_email(user).deliver
595 end
595 end
596 end
596 end
597
597
598 def test_reminders
598 def test_reminders
599 Mailer.reminders(:days => 42)
599 Mailer.reminders(:days => 42)
600 assert_equal 1, ActionMailer::Base.deliveries.size
600 assert_equal 1, ActionMailer::Base.deliveries.size
601 mail = last_email
601 mail = last_email
602 assert mail.bcc.include?('dlopper@somenet.foo')
602 assert mail.bcc.include?('dlopper@somenet.foo')
603 assert_mail_body_match 'Bug #3: Error 281 when updating a recipe', mail
603 assert_mail_body_match 'Bug #3: Error 281 when updating a recipe', mail
604 assert_equal '1 issue(s) due in the next 42 days', mail.subject
604 assert_equal '1 issue(s) due in the next 42 days', mail.subject
605 end
605 end
606
606
607 def test_reminders_should_not_include_closed_issues
607 def test_reminders_should_not_include_closed_issues
608 with_settings :default_language => 'en' do
608 with_settings :default_language => 'en' do
609 Issue.create!(:project_id => 1, :tracker_id => 1, :status_id => 5,
609 Issue.create!(:project_id => 1, :tracker_id => 1, :status_id => 5,
610 :subject => 'Closed issue', :assigned_to_id => 3,
610 :subject => 'Closed issue', :assigned_to_id => 3,
611 :due_date => 5.days.from_now,
611 :due_date => 5.days.from_now,
612 :author_id => 2)
612 :author_id => 2)
613 ActionMailer::Base.deliveries.clear
613 ActionMailer::Base.deliveries.clear
614
614
615 Mailer.reminders(:days => 42)
615 Mailer.reminders(:days => 42)
616 assert_equal 1, ActionMailer::Base.deliveries.size
616 assert_equal 1, ActionMailer::Base.deliveries.size
617 mail = last_email
617 mail = last_email
618 assert mail.bcc.include?('dlopper@somenet.foo')
618 assert mail.bcc.include?('dlopper@somenet.foo')
619 assert_mail_body_no_match 'Closed issue', mail
619 assert_mail_body_no_match 'Closed issue', mail
620 end
620 end
621 end
621 end
622
622
623 def test_reminders_for_users
623 def test_reminders_for_users
624 Mailer.reminders(:days => 42, :users => ['5'])
624 Mailer.reminders(:days => 42, :users => ['5'])
625 assert_equal 0, ActionMailer::Base.deliveries.size # No mail for dlopper
625 assert_equal 0, ActionMailer::Base.deliveries.size # No mail for dlopper
626 Mailer.reminders(:days => 42, :users => ['3'])
626 Mailer.reminders(:days => 42, :users => ['3'])
627 assert_equal 1, ActionMailer::Base.deliveries.size # No mail for dlopper
627 assert_equal 1, ActionMailer::Base.deliveries.size # No mail for dlopper
628 mail = last_email
628 mail = last_email
629 assert mail.bcc.include?('dlopper@somenet.foo')
629 assert mail.bcc.include?('dlopper@somenet.foo')
630 assert_mail_body_match 'Bug #3: Error 281 when updating a recipe', mail
630 assert_mail_body_match 'Bug #3: Error 281 when updating a recipe', mail
631 end
631 end
632
632
633 def test_reminder_should_include_issues_assigned_to_groups
633 def test_reminder_should_include_issues_assigned_to_groups
634 with_settings :default_language => 'en' do
634 with_settings :default_language => 'en' do
635 group = Group.generate!
635 group = Group.generate!
636 group.users << User.find(2)
636 group.users << User.find(2)
637 group.users << User.find(3)
637 group.users << User.find(3)
638
638
639 Issue.create!(:project_id => 1, :tracker_id => 1, :status_id => 1,
639 Issue.create!(:project_id => 1, :tracker_id => 1, :status_id => 1,
640 :subject => 'Assigned to group', :assigned_to => group,
640 :subject => 'Assigned to group', :assigned_to => group,
641 :due_date => 5.days.from_now,
641 :due_date => 5.days.from_now,
642 :author_id => 2)
642 :author_id => 2)
643 ActionMailer::Base.deliveries.clear
643 ActionMailer::Base.deliveries.clear
644
644
645 Mailer.reminders(:days => 7)
645 Mailer.reminders(:days => 7)
646 assert_equal 2, ActionMailer::Base.deliveries.size
646 assert_equal 2, ActionMailer::Base.deliveries.size
647 assert_equal %w(dlopper@somenet.foo jsmith@somenet.foo), ActionMailer::Base.deliveries.map(&:bcc).flatten.sort
647 assert_equal %w(dlopper@somenet.foo jsmith@somenet.foo), ActionMailer::Base.deliveries.map(&:bcc).flatten.sort
648 ActionMailer::Base.deliveries.each do |mail|
648 ActionMailer::Base.deliveries.each do |mail|
649 assert_mail_body_match 'Assigned to group', mail
649 assert_mail_body_match 'Assigned to group', mail
650 end
650 end
651 end
651 end
652 end
652 end
653
653
654 def test_reminders_with_version_option
654 def test_reminders_with_version_option
655 with_settings :default_language => 'en' do
655 with_settings :default_language => 'en' do
656 version = Version.generate!(:name => 'Acme', :project_id => 1)
656 version = Version.generate!(:name => 'Acme', :project_id => 1)
657 Issue.generate!(:assigned_to => User.find(2), :due_date => 5.days.from_now)
657 Issue.generate!(:assigned_to => User.find(2), :due_date => 5.days.from_now)
658 Issue.generate!(:assigned_to => User.find(3), :due_date => 5.days.from_now, :fixed_version => version)
658 Issue.generate!(:assigned_to => User.find(3), :due_date => 5.days.from_now, :fixed_version => version)
659 ActionMailer::Base.deliveries.clear
659 ActionMailer::Base.deliveries.clear
660
660
661 Mailer.reminders(:days => 42, :version => 'acme')
661 Mailer.reminders(:days => 42, :version => 'acme')
662 assert_equal 1, ActionMailer::Base.deliveries.size
662 assert_equal 1, ActionMailer::Base.deliveries.size
663
663
664 mail = last_email
664 mail = last_email
665 assert mail.bcc.include?('dlopper@somenet.foo')
665 assert mail.bcc.include?('dlopper@somenet.foo')
666 end
666 end
667 end
667 end
668
668
669 def test_mailer_should_not_change_locale
669 def test_mailer_should_not_change_locale
670 # Set current language to italian
670 # Set current language to italian
671 set_language_if_valid 'it'
671 set_language_if_valid 'it'
672 # Send an email to a french user
672 # Send an email to a french user
673 user = User.find(1)
673 user = User.find(1)
674 user.language = 'fr'
674 user.language = 'fr'
675 Mailer.account_activated(user).deliver
675 Mailer.account_activated(user).deliver
676 mail = last_email
676 mail = last_email
677 assert_mail_body_match 'Votre compte', mail
677 assert_mail_body_match 'Votre compte', mail
678
678
679 assert_equal :it, current_language
679 assert_equal :it, current_language
680 end
680 end
681
681
682 def test_with_deliveries_off
682 def test_with_deliveries_off
683 Mailer.with_deliveries false do
683 Mailer.with_deliveries false do
684 Mailer.test_email(User.find(1)).deliver
684 Mailer.test_email(User.find(1)).deliver
685 end
685 end
686 assert ActionMailer::Base.deliveries.empty?
686 assert ActionMailer::Base.deliveries.empty?
687 # should restore perform_deliveries
687 # should restore perform_deliveries
688 assert ActionMailer::Base.perform_deliveries
688 assert ActionMailer::Base.perform_deliveries
689 end
689 end
690
690
691 def test_token_for_should_strip_trailing_gt_from_address_with_full_name
691 def test_token_for_should_strip_trailing_gt_from_address_with_full_name
692 with_settings :mail_from => "Redmine Mailer<no-reply@redmine.org>" do
692 with_settings :mail_from => "Redmine Mailer<no-reply@redmine.org>" do
693 assert_match /\Aredmine.issue-\d+\.\d+\.[0-9a-f]+@redmine.org\z/, Mailer.token_for(Issue.generate!)
693 assert_match /\Aredmine.issue-\d+\.\d+\.[0-9a-f]+@redmine.org\z/, Mailer.token_for(Issue.generate!)
694 end
694 end
695 end
695 end
696
696
697 def test_layout_should_include_the_emails_header
697 def test_layout_should_include_the_emails_header
698 with_settings :emails_header => "*Header content*" do
698 with_settings :emails_header => "*Header content*" do
699 with_settings :plain_text_mail => 0 do
699 with_settings :plain_text_mail => 0 do
700 assert Mailer.test_email(User.find(1)).deliver
700 assert Mailer.test_email(User.find(1)).deliver
701 assert_select_email do
701 assert_select_email do
702 assert_select ".header" do
702 assert_select ".header" do
703 assert_select "strong", :text => "Header content"
703 assert_select "strong", :text => "Header content"
704 end
704 end
705 end
705 end
706 end
706 end
707 with_settings :plain_text_mail => 1 do
707 with_settings :plain_text_mail => 1 do
708 assert Mailer.test_email(User.find(1)).deliver
708 assert Mailer.test_email(User.find(1)).deliver
709 mail = last_email
709 mail = last_email
710 assert_not_nil mail
710 assert_not_nil mail
711 assert_include "*Header content*", mail.body.decoded
711 assert_include "*Header content*", mail.body.decoded
712 end
712 end
713 end
713 end
714 end
714 end
715
715
716 def test_layout_should_not_include_empty_emails_header
716 def test_layout_should_not_include_empty_emails_header
717 with_settings :emails_header => "", :plain_text_mail => 0 do
717 with_settings :emails_header => "", :plain_text_mail => 0 do
718 assert Mailer.test_email(User.find(1)).deliver
718 assert Mailer.test_email(User.find(1)).deliver
719 assert_select_email do
719 assert_select_email do
720 assert_select ".header", false
720 assert_select ".header", false
721 end
721 end
722 end
722 end
723 end
723 end
724
724
725 def test_layout_should_include_the_emails_footer
725 def test_layout_should_include_the_emails_footer
726 with_settings :emails_footer => "*Footer content*" do
726 with_settings :emails_footer => "*Footer content*" do
727 with_settings :plain_text_mail => 0 do
727 with_settings :plain_text_mail => 0 do
728 assert Mailer.test_email(User.find(1)).deliver
728 assert Mailer.test_email(User.find(1)).deliver
729 assert_select_email do
729 assert_select_email do
730 assert_select ".footer" do
730 assert_select ".footer" do
731 assert_select "strong", :text => "Footer content"
731 assert_select "strong", :text => "Footer content"
732 end
732 end
733 end
733 end
734 end
734 end
735 with_settings :plain_text_mail => 1 do
735 with_settings :plain_text_mail => 1 do
736 assert Mailer.test_email(User.find(1)).deliver
736 assert Mailer.test_email(User.find(1)).deliver
737 mail = last_email
737 mail = last_email
738 assert_not_nil mail
738 assert_not_nil mail
739 assert_include "\n-- \n", mail.body.decoded
739 assert_include "\n-- \n", mail.body.decoded
740 assert_include "*Footer content*", mail.body.decoded
740 assert_include "*Footer content*", mail.body.decoded
741 end
741 end
742 end
742 end
743 end
743 end
744
744
745 def test_layout_should_not_include_empty_emails_footer
745 def test_layout_should_not_include_empty_emails_footer
746 with_settings :emails_footer => "" do
746 with_settings :emails_footer => "" do
747 with_settings :plain_text_mail => 0 do
747 with_settings :plain_text_mail => 0 do
748 assert Mailer.test_email(User.find(1)).deliver
748 assert Mailer.test_email(User.find(1)).deliver
749 assert_select_email do
749 assert_select_email do
750 assert_select ".footer", false
750 assert_select ".footer", false
751 end
751 end
752 end
752 end
753 with_settings :plain_text_mail => 1 do
753 with_settings :plain_text_mail => 1 do
754 assert Mailer.test_email(User.find(1)).deliver
754 assert Mailer.test_email(User.find(1)).deliver
755 mail = last_email
755 mail = last_email
756 assert_not_nil mail
756 assert_not_nil mail
757 assert_not_include "\n-- \n", mail.body.decoded
757 assert_not_include "\n-- \n", mail.body.decoded
758 end
758 end
759 end
759 end
760 end
760 end
761
761
762 def test_should_escape_html_templates_only
762 def test_should_escape_html_templates_only
763 Issue.generate!(:project_id => 1, :tracker_id => 1, :subject => 'Subject with a <tag>')
763 Issue.generate!(:project_id => 1, :tracker_id => 1, :subject => 'Subject with a <tag>')
764 mail = last_email
764 mail = last_email
765 assert_equal 2, mail.parts.size
765 assert_equal 2, mail.parts.size
766 assert_include '<tag>', text_part.body.encoded
766 assert_include '<tag>', text_part.body.encoded
767 assert_include '&lt;tag&gt;', html_part.body.encoded
767 assert_include '&lt;tag&gt;', html_part.body.encoded
768 end
768 end
769
769
770 def test_should_raise_delivery_errors_when_raise_delivery_errors_is_true
770 def test_should_raise_delivery_errors_when_raise_delivery_errors_is_true
771 mail = Mailer.test_email(User.find(1))
771 mail = Mailer.test_email(User.find(1))
772 mail.delivery_method.stubs(:deliver!).raises(Exception.new("delivery error"))
772 mail.delivery_method.stubs(:deliver!).raises(Exception.new("delivery error"))
773
773
774 ActionMailer::Base.raise_delivery_errors = true
774 ActionMailer::Base.raise_delivery_errors = true
775 assert_raise Exception, "delivery error" do
775 assert_raise Exception, "delivery error" do
776 mail.deliver
776 mail.deliver
777 end
777 end
778 ensure
778 ensure
779 ActionMailer::Base.raise_delivery_errors = false
779 ActionMailer::Base.raise_delivery_errors = false
780 end
780 end
781
781
782 def test_should_log_delivery_errors_when_raise_delivery_errors_is_false
782 def test_should_log_delivery_errors_when_raise_delivery_errors_is_false
783 mail = Mailer.test_email(User.find(1))
783 mail = Mailer.test_email(User.find(1))
784 mail.delivery_method.stubs(:deliver!).raises(Exception.new("delivery error"))
784 mail.delivery_method.stubs(:deliver!).raises(Exception.new("delivery error"))
785
785
786 Rails.logger.expects(:error).with("Email delivery error: delivery error")
786 Rails.logger.expects(:error).with("Email delivery error: delivery error")
787 ActionMailer::Base.raise_delivery_errors = false
787 ActionMailer::Base.raise_delivery_errors = false
788 assert_nothing_raised do
788 assert_nothing_raised do
789 mail.deliver
789 mail.deliver
790 end
790 end
791 end
791 end
792
792
793 def test_with_synched_deliveries_should_yield_with_synced_deliveries
793 def test_with_synched_deliveries_should_yield_with_synced_deliveries
794 ActionMailer::Base.delivery_method = :async_smtp
794 ActionMailer::Base.delivery_method = :async_smtp
795 ActionMailer::Base.async_smtp_settings = {:foo => 'bar'}
795 ActionMailer::Base.async_smtp_settings = {:foo => 'bar'}
796
796
797 Mailer.with_synched_deliveries do
797 Mailer.with_synched_deliveries do
798 assert_equal :smtp, ActionMailer::Base.delivery_method
798 assert_equal :smtp, ActionMailer::Base.delivery_method
799 assert_equal({:foo => 'bar'}, ActionMailer::Base.smtp_settings)
799 assert_equal({:foo => 'bar'}, ActionMailer::Base.smtp_settings)
800 end
800 end
801 assert_equal :async_smtp, ActionMailer::Base.delivery_method
801 assert_equal :async_smtp, ActionMailer::Base.delivery_method
802 ensure
802 ensure
803 ActionMailer::Base.delivery_method = :test
803 ActionMailer::Base.delivery_method = :test
804 end
804 end
805
805
806 def test_email_addresses_should_keep_addresses
806 def test_email_addresses_should_keep_addresses
807 assert_equal ["foo@example.net"],
807 assert_equal ["foo@example.net"],
808 Mailer.email_addresses("foo@example.net")
808 Mailer.email_addresses("foo@example.net")
809
809
810 assert_equal ["foo@example.net", "bar@example.net"],
810 assert_equal ["foo@example.net", "bar@example.net"],
811 Mailer.email_addresses(["foo@example.net", "bar@example.net"])
811 Mailer.email_addresses(["foo@example.net", "bar@example.net"])
812 end
812 end
813
813
814 def test_email_addresses_should_replace_users_with_their_email_addresses
814 def test_email_addresses_should_replace_users_with_their_email_addresses
815 assert_equal ["admin@somenet.foo"],
815 assert_equal ["admin@somenet.foo"],
816 Mailer.email_addresses(User.find(1))
816 Mailer.email_addresses(User.find(1))
817
817
818 assert_equal ["admin@somenet.foo", "jsmith@somenet.foo"],
818 assert_equal ["admin@somenet.foo", "jsmith@somenet.foo"],
819 Mailer.email_addresses(User.where(:id => [1,2])).sort
819 Mailer.email_addresses(User.where(:id => [1,2])).sort
820 end
820 end
821
821
822 def test_email_addresses_should_include_notified_emails_addresses_only
822 def test_email_addresses_should_include_notified_emails_addresses_only
823 EmailAddress.create!(:user_id => 2, :address => "another@somenet.foo", :notify => false)
823 EmailAddress.create!(:user_id => 2, :address => "another@somenet.foo", :notify => false)
824 EmailAddress.create!(:user_id => 2, :address => "another2@somenet.foo")
824 EmailAddress.create!(:user_id => 2, :address => "another2@somenet.foo")
825
825
826 assert_equal ["another2@somenet.foo", "jsmith@somenet.foo"],
826 assert_equal ["another2@somenet.foo", "jsmith@somenet.foo"],
827 Mailer.email_addresses(User.find(2)).sort
827 Mailer.email_addresses(User.find(2)).sort
828 end
828 end
829
829
830 private
830 private
831
831
832 def last_email
832 def last_email
833 mail = ActionMailer::Base.deliveries.last
833 mail = ActionMailer::Base.deliveries.last
834 assert_not_nil mail
834 assert_not_nil mail
835 mail
835 mail
836 end
836 end
837
837
838 def text_part
838 def text_part
839 last_email.parts.detect {|part| part.content_type.include?('text/plain')}
839 last_email.parts.detect {|part| part.content_type.include?('text/plain')}
840 end
840 end
841
841
842 def html_part
842 def html_part
843 last_email.parts.detect {|part| part.content_type.include?('text/html')}
843 last_email.parts.detect {|part| part.content_type.include?('text/html')}
844 end
844 end
845
845
846 def with_each_language_as_default(&block)
846 def with_each_language_as_default(&block)
847 valid_languages.each do |lang|
847 valid_languages.each do |lang|
848 with_settings :default_language => lang.to_s do
848 with_settings :default_language => lang.to_s do
849 yield lang
849 yield lang
850 end
850 end
851 end
851 end
852 end
852 end
853 end
853 end
General Comments 0
You need to be logged in to leave comments. Login now