##// END OF EJS Templates
ProjectsController#list_issues, #export_issues_csv and #export_issues_pdf merged into IssuesController#index...
Jean-Philippe Lang -
r874:8509cf80f009
parent child
Show More
@@ -0,0 +1,30
1 xml.instruct!
2 xml.feed "xmlns" => "http://www.w3.org/2005/Atom" do
3 xml.title @title
4 xml.link "rel" => "self", "href" => url_for(:controller => 'feeds', :action => 'history', :format => 'atom', :only_path => false)
5 xml.link "rel" => "alternate", "href" => url_for(:controller => 'welcome', :only_path => false)
6 xml.id url_for(:controller => 'welcome', :only_path => false)
7 xml.updated((@changes.first ? @changes.first.event_datetime : Time.now).xmlschema)
8 xml.author { xml.name "#{Setting.app_title}" }
9 @changes.each do |change|
10 issue = change.issue
11 xml.entry do
12 xml.title "#{issue.project.name} - #{issue.tracker.name} ##{issue.id}: #{issue.subject}"
13 xml.link "rel" => "alternate", "href" => url_for(:controller => 'issues' , :action => 'show', :id => issue, :only_path => false)
14 xml.id url_for(:controller => 'issues' , :action => 'show', :id => issue, :journal_id => change, :only_path => false)
15 xml.updated change.created_on.xmlschema
16 xml.author do
17 xml.name change.user.name
18 xml.email(change.user.mail)
19 end
20 xml.content "type" => "html" do
21 xml.text! '<ul>'
22 change.details.each do |detail|
23 xml.text! '<li>' + show_detail(detail, false) + '</li>'
24 end
25 xml.text! '</ul>'
26 xml.text! textilizable(change.notes) unless change.notes.blank?
27 end
28 end
29 end
30 end No newline at end of file
@@ -0,0 +1,86
1 # redMine - project management software
2 # Copyright (C) 2006-2007 Jean-Philippe Lang
3 #
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
8 #
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
13 #
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
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
18 require File.dirname(__FILE__) + '/../test_helper'
19 require 'issues_controller'
20
21 # Re-raise errors caught by the controller.
22 class IssuesController; def rescue_action(e) raise e end; end
23
24 class IssuesControllerTest < Test::Unit::TestCase
25 fixtures :projects, :users, :roles, :members, :issues, :enabled_modules, :enumerations
26
27 def setup
28 @controller = IssuesController.new
29 @request = ActionController::TestRequest.new
30 @response = ActionController::TestResponse.new
31 User.current = nil
32 end
33
34 def test_index
35 get :index
36 assert_response :success
37 assert_template 'index.rhtml'
38 assert_not_nil assigns(:issues)
39 assert_nil assigns(:project)
40 end
41
42 def test_index_with_project
43 get :index, :project_id => 1
44 assert_response :success
45 assert_template 'index.rhtml'
46 assert_not_nil assigns(:issues)
47 end
48
49 def test_index_with_project_and_filter
50 get :index, :project_id => 1, :set_filter => 1
51 assert_response :success
52 assert_template 'index.rhtml'
53 assert_not_nil assigns(:issues)
54 end
55
56 def test_index_csv_with_project
57 get :index, :format => 'csv'
58 assert_response :success
59 assert_not_nil assigns(:issues)
60 assert_equal 'text/csv', @response.content_type
61
62 get :index, :project_id => 1, :format => 'csv'
63 assert_response :success
64 assert_not_nil assigns(:issues)
65 assert_equal 'text/csv', @response.content_type
66 end
67
68 def test_index_pdf
69 get :index, :format => 'pdf'
70 assert_response :success
71 assert_not_nil assigns(:issues)
72 assert_equal 'application/pdf', @response.content_type
73
74 get :index, :project_id => 1, :format => 'pdf'
75 assert_response :success
76 assert_not_nil assigns(:issues)
77 assert_equal 'application/pdf', @response.content_type
78 end
79
80 def test_changes
81 get :changes, :project_id => 1
82 assert_response :success
83 assert_not_nil assigns(:changes)
84 assert_equal 'application/atom+xml', @response.content_type
85 end
86 end
@@ -16,9 +16,10
16 16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 17
18 18 class IssuesController < ApplicationController
19 layout 'base', :except => :export_pdf
20 before_filter :find_project, :authorize, :except => [:index, :preview]
21 accept_key_auth :index
19 layout 'base'
20 before_filter :find_project, :authorize, :except => [:index, :changes, :preview]
21 before_filter :find_optional_project, :only => [:index, :changes]
22 accept_key_auth :index, :changes
22 23
23 24 cache_sweeper :issue_sweeper, :only => [ :edit, :change_status, :destroy ]
24 25
@@ -37,37 +38,54 class IssuesController < ApplicationController
37 38 helper :queries
38 39 helper :sort
39 40 include SortHelper
41 include IssuesHelper
40 42
41 43 def index
42 44 sort_init "#{Issue.table_name}.id", "desc"
43 45 sort_update
44 46 retrieve_query
45 47 if @query.valid?
46 @issue_count = Issue.count(:include => [:status, :project], :conditions => @query.statement)
47 @issue_pages = Paginator.new self, @issue_count, 25, params['page']
48 limit = %w(pdf csv).include?(params[:format]) ? Setting.issues_export_limit.to_i : 25
49 @issue_count = Issue.count(:include => [:status, :project], :conditions => @query.statement)
50 @issue_pages = Paginator.new self, @issue_count, limit, params['page']
48 51 @issues = Issue.find :all, :order => sort_clause,
49 52 :include => [ :assigned_to, :status, :tracker, :project, :priority, :category ],
50 53 :conditions => @query.statement,
51 :limit => @issue_pages.items_per_page,
52 :offset => @issue_pages.current.offset
54 :limit => limit,
55 :offset => @issue_pages.current.offset
56 respond_to do |format|
57 format.html { render :template => 'issues/index.rhtml', :layout => !request.xhr? }
58 format.atom { render_feed(@issues, :title => l(:label_issue_plural)) }
59 format.csv { send_data(issues_to_csv(@issues, @project).read, :type => 'text/csv; header=present', :filename => 'export.csv') }
60 format.pdf { send_data(render(:template => 'issues/index.rfpdf', :layout => false), :type => 'application/pdf', :filename => 'export.pdf') }
61 end
62 else
63 # Send html if the query is not valid
64 render(:template => 'issues/index.rhtml', :layout => !request.xhr?)
53 65 end
54 respond_to do |format|
55 format.html { render :layout => false if request.xhr? }
56 format.atom { render_feed(@issues, :title => l(:label_issue_plural)) }
66 end
67
68 def changes
69 sort_init "#{Issue.table_name}.id", "desc"
70 sort_update
71 retrieve_query
72 if @query.valid?
73 @changes = Journal.find :all, :include => [ :details, :user, {:issue => [:project, :author, :tracker, :status]} ],
74 :conditions => @query.statement,
75 :limit => 25,
76 :order => "#{Journal.table_name}.created_on DESC"
57 77 end
78 @title = (@project ? @project.name : Setting.app_title) + ": " + (@query.new_record? ? l(:label_changes_details) : @query.name)
79 render :layout => false, :content_type => 'application/atom+xml'
58 80 end
59 81
60 82 def show
61 83 @custom_values = @issue.custom_values.find(:all, :include => :custom_field)
62 84 @journals = @issue.journals.find(:all, :include => [:user, :details], :order => "#{Journal.table_name}.created_on ASC")
63
64 if params[:format]=='pdf'
65 @options_for_rfpdf ||= {}
66 @options_for_rfpdf[:file_name] = "#{@project.identifier}-#{@issue.id}.pdf"
67 render :template => 'issues/show.rfpdf', :layout => false
68 else
69 @status_options = @issue.status.find_new_statuses_allowed_to(logged_in_user.role_for_project(@project), @issue.tracker) if logged_in_user
70 render :template => 'issues/show.rhtml'
85 @status_options = @issue.status.find_new_statuses_allowed_to(logged_in_user.role_for_project(@project), @issue.tracker) if logged_in_user
86 respond_to do |format|
87 format.html { render :template => 'issues/show.rhtml' }
88 format.pdf { send_data(render(:template => 'issues/show.rfpdf', :layout => false), :type => 'application/pdf', :filename => "#{@project.identifier}-#{@issue.id}.pdf") }
71 89 end
72 90 end
73 91
@@ -152,7 +170,7 class IssuesController < ApplicationController
152 170
153 171 def destroy
154 172 @issue.destroy
155 redirect_to :controller => 'projects', :action => 'list_issues', :id => @project
173 redirect_to :action => 'index', :project_id => @project
156 174 end
157 175
158 176 def destroy_attachment
@@ -195,23 +213,38 private
195 213 render_404
196 214 end
197 215
216 def find_optional_project
217 return true unless params[:project_id]
218 @project = Project.find(params[:project_id])
219 authorize
220 rescue ActiveRecord::RecordNotFound
221 render_404
222 end
223
198 224 # Retrieve query from session or build a new query
199 225 def retrieve_query
200 if params[:set_filter] or !session[:query] or session[:query].project_id
201 # Give it a name, required to be valid
202 @query = Query.new(:name => "_", :executed_by => logged_in_user)
203 if params[:fields] and params[:fields].is_a? Array
204 params[:fields].each do |field|
205 @query.add_filter(field, params[:operators][field], params[:values][field])
226 if params[:query_id]
227 @query = Query.find(params[:query_id], :conditions => {:project_id => (@project ? @project.id : nil)})
228 @query.executed_by = logged_in_user
229 session[:query] = @query
230 else
231 if params[:set_filter] or !session[:query] or session[:query].project != @project
232 # Give it a name, required to be valid
233 @query = Query.new(:name => "_", :executed_by => logged_in_user)
234 @query.project = @project
235 if params[:fields] and params[:fields].is_a? Array
236 params[:fields].each do |field|
237 @query.add_filter(field, params[:operators][field], params[:values][field])
238 end
239 else
240 @query.available_filters.keys.each do |field|
241 @query.add_short_filter(field, params[field]) if params[field]
242 end
206 243 end
244 session[:query] = @query
207 245 else
208 @query.available_filters.keys.each do |field|
209 @query.add_short_filter(field, params[field]) if params[field]
210 end
246 @query = session[:query]
211 247 end
212 session[:query] = @query
213 else
214 @query = session[:query]
215 248 end
216 249 end
217 250 end
@@ -15,8 +15,6
15 15 # along with this program; if not, write to the Free Software
16 16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 17
18 require 'csv'
19
20 18 class ProjectsController < ApplicationController
21 19 layout 'base'
22 20 before_filter :find_project, :except => [ :index, :list, :add ]
@@ -238,118 +236,13 class ProjectsController < ApplicationController
238 236 end
239 237 flash[:notice] = l(:notice_successful_create)
240 238 Mailer.deliver_issue_add(@issue) if Setting.notified_events.include?('issue_added')
241 redirect_to :action => 'list_issues', :id => @project
239 redirect_to :controller => 'issues', :action => 'index', :project_id => @project
242 240 return
243 241 end
244 242 end
245 243 @priorities = Enumeration::get_values('IPRI')
246 244 end
247 245
248 # Show filtered/sorted issues list of @project
249 def list_issues
250 sort_init "#{Issue.table_name}.id", "desc"
251 sort_update
252
253 retrieve_query
254
255 @results_per_page_options = [ 15, 25, 50, 100 ]
256 if params[:per_page] and @results_per_page_options.include? params[:per_page].to_i
257 @results_per_page = params[:per_page].to_i
258 session[:results_per_page] = @results_per_page
259 else
260 @results_per_page = session[:results_per_page] || 25
261 end
262
263 if @query.valid?
264 @issue_count = Issue.count(:include => [:status, :project], :conditions => @query.statement)
265 @issue_pages = Paginator.new self, @issue_count, @results_per_page, params['page']
266 @issues = Issue.find :all, :order => sort_clause,
267 :include => [ :assigned_to, :status, :tracker, :project, :priority, :category ],
268 :conditions => @query.statement,
269 :limit => @issue_pages.items_per_page,
270 :offset => @issue_pages.current.offset
271 end
272
273 render :layout => false if request.xhr?
274 end
275
276 # Export filtered/sorted issues list to CSV
277 def export_issues_csv
278 sort_init "#{Issue.table_name}.id", "desc"
279 sort_update
280
281 retrieve_query
282 render :action => 'list_issues' and return unless @query.valid?
283
284 @issues = Issue.find :all, :order => sort_clause,
285 :include => [ :assigned_to, :author, :status, :tracker, :priority, :project, {:custom_values => :custom_field} ],
286 :conditions => @query.statement,
287 :limit => Setting.issues_export_limit.to_i
288
289 ic = Iconv.new(l(:general_csv_encoding), 'UTF-8')
290 export = StringIO.new
291 CSV::Writer.generate(export, l(:general_csv_separator)) do |csv|
292 # csv header fields
293 headers = [ "#", l(:field_status),
294 l(:field_project),
295 l(:field_tracker),
296 l(:field_priority),
297 l(:field_subject),
298 l(:field_assigned_to),
299 l(:field_author),
300 l(:field_start_date),
301 l(:field_due_date),
302 l(:field_done_ratio),
303 l(:field_created_on),
304 l(:field_updated_on)
305 ]
306 for custom_field in @project.all_custom_fields
307 headers << custom_field.name
308 end
309 csv << headers.collect {|c| begin; ic.iconv(c.to_s); rescue; c.to_s; end }
310 # csv lines
311 @issues.each do |issue|
312 fields = [issue.id, issue.status.name,
313 issue.project.name,
314 issue.tracker.name,
315 issue.priority.name,
316 issue.subject,
317 (issue.assigned_to ? issue.assigned_to.name : ""),
318 issue.author.name,
319 issue.start_date ? l_date(issue.start_date) : nil,
320 issue.due_date ? l_date(issue.due_date) : nil,
321 issue.done_ratio,
322 l_datetime(issue.created_on),
323 l_datetime(issue.updated_on)
324 ]
325 for custom_field in @project.all_custom_fields
326 fields << (show_value issue.custom_value_for(custom_field))
327 end
328 csv << fields.collect {|c| begin; ic.iconv(c.to_s); rescue; c.to_s; end }
329 end
330 end
331 export.rewind
332 send_data(export.read, :type => 'text/csv; header=present', :filename => 'export.csv')
333 end
334
335 # Export filtered/sorted issues to PDF
336 def export_issues_pdf
337 sort_init "#{Issue.table_name}.id", "desc"
338 sort_update
339
340 retrieve_query
341 render :action => 'list_issues' and return unless @query.valid?
342
343 @issues = Issue.find :all, :order => sort_clause,
344 :include => [ :author, :status, :tracker, :priority, :project ],
345 :conditions => @query.statement,
346 :limit => Setting.issues_export_limit.to_i
347
348 @options_for_rfpdf ||= {}
349 @options_for_rfpdf[:file_name] = "export.pdf"
350 render :layout => false
351 end
352
353 246 # Bulk edit issues
354 247 def bulk_edit_issues
355 248 if request.post?
@@ -383,7 +276,7 class ProjectsController < ApplicationController
383 276 else
384 277 flash[:error] = l(:notice_failed_to_save_issues, unsaved_issue_ids.size, issues.size, '#' + unsaved_issue_ids.join(', #'))
385 278 end
386 redirect_to :action => 'list_issues', :id => @project
279 redirect_to :controller => 'issues', :action => 'index', :project_id => @project
387 280 return
388 281 end
389 282 if current_role && User.current.allowed_to?(:change_issue_status, @project)
@@ -399,7 +292,7 class ProjectsController < ApplicationController
399 292
400 293 def move_issues
401 294 @issues = @project.issues.find(params[:issue_ids]) if params[:issue_ids]
402 redirect_to :action => 'list_issues', :id => @project and return unless @issues
295 redirect_to :controller => 'issues', :action => 'index', :project_id => @project and return unless @issues
403 296 @projects = []
404 297 # find projects to which the user is allowed to move the issue
405 298 User.current.memberships.each {|m| @projects << m.project if m.role.allowed_to?(:controller => 'projects', :action => 'move_issues')}
@@ -424,7 +317,7 class ProjectsController < ApplicationController
424 317 i.save
425 318 end
426 319 flash[:notice] = l(:notice_successful_update)
427 redirect_to :action => 'list_issues', :id => @project
320 redirect_to :controller => 'issues', :action => 'index', :project_id => @project
428 321 end
429 322 end
430 323
@@ -659,31 +552,4 private
659 552 @selected_tracker_ids = selectable_trackers.collect {|t| t.id.to_s }
660 553 end
661 554 end
662
663 # Retrieve query from session or build a new query
664 def retrieve_query
665 if params[:query_id]
666 @query = @project.queries.find(params[:query_id])
667 @query.executed_by = logged_in_user
668 session[:query] = @query
669 else
670 if params[:set_filter] or !session[:query] or session[:query].project_id != @project.id
671 # Give it a name, required to be valid
672 @query = Query.new(:name => "_", :executed_by => logged_in_user)
673 @query.project = @project
674 if params[:fields] and params[:fields].is_a? Array
675 params[:fields].each do |field|
676 @query.add_filter(field, params[:operators][field], params[:values][field])
677 end
678 else
679 @query.available_filters.keys.each do |field|
680 @query.add_short_filter(field, params[field]) if params[field]
681 end
682 end
683 session[:query] = @query
684 else
685 @query = session[:query]
686 end
687 end
688 end
689 555 end
@@ -39,7 +39,7 class QueriesController < ApplicationController
39 39
40 40 if request.post? && params[:confirm] && @query.save
41 41 flash[:notice] = l(:notice_successful_create)
42 redirect_to :controller => 'projects', :action => 'list_issues', :id => @project, :query_id => @query
42 redirect_to :controller => 'issues', :action => 'index', :project_id => @project, :query_id => @query
43 43 return
44 44 end
45 45 render :layout => false if request.xhr?
@@ -57,7 +57,7 class QueriesController < ApplicationController
57 57
58 58 if @query.save
59 59 flash[:notice] = l(:notice_successful_update)
60 redirect_to :controller => 'projects', :action => 'list_issues', :id => @project, :query_id => @query
60 redirect_to :controller => 'issues', :action => 'index', :project_id => @project, :query_id => @query
61 61 end
62 62 end
63 63 end
@@ -15,6 +15,8
15 15 # along with this program; if not, write to the Free Software
16 16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 17
18 require 'csv'
19
18 20 module IssuesHelper
19 21
20 22 def render_issue_tooltip(issue)
@@ -101,4 +103,54 module IssuesHelper
101 103 end
102 104 end
103 105 end
106
107 def issues_to_csv(issues, project = nil)
108 ic = Iconv.new(l(:general_csv_encoding), 'UTF-8')
109 export = StringIO.new
110 CSV::Writer.generate(export, l(:general_csv_separator)) do |csv|
111 # csv header fields
112 headers = [ "#",
113 l(:field_status),
114 l(:field_project),
115 l(:field_tracker),
116 l(:field_priority),
117 l(:field_subject),
118 l(:field_assigned_to),
119 l(:field_author),
120 l(:field_start_date),
121 l(:field_due_date),
122 l(:field_done_ratio),
123 l(:field_created_on),
124 l(:field_updated_on)
125 ]
126 # only export custom fields if project is given
127 for custom_field in project.all_custom_fields
128 headers << custom_field.name
129 end if project
130 csv << headers.collect {|c| begin; ic.iconv(c.to_s); rescue; c.to_s; end }
131 # csv lines
132 issues.each do |issue|
133 fields = [issue.id,
134 issue.status.name,
135 issue.project.name,
136 issue.tracker.name,
137 issue.priority.name,
138 issue.subject,
139 (issue.assigned_to ? issue.assigned_to.name : ""),
140 issue.author.name,
141 issue.start_date ? l_date(issue.start_date) : nil,
142 issue.due_date ? l_date(issue.due_date) : nil,
143 issue.done_ratio,
144 l_datetime(issue.created_on),
145 l_datetime(issue.updated_on)
146 ]
147 for custom_field in project.all_custom_fields
148 fields << (show_value issue.custom_value_for(custom_field))
149 end if project
150 csv << fields.collect {|c| begin; ic.iconv(c.to_s); rescue; c.to_s; end }
151 end
152 end
153 export.rewind
154 export
155 end
104 156 end
@@ -29,6 +29,10 class Journal < ActiveRecord::Base
29 29 :project_key => "#{Issue.table_name}.project_id",
30 30 :date_column => "#{Issue.table_name}.created_on"
31 31
32 acts_as_event :title => Proc.new {|o| "#{o.issue.tracker.name} ##{o.issue.id}: #{o.issue.subject}"},
33 :description => :notes,
34 :url => Proc.new {|o| {:controller => 'issues', :action => 'show', :id => o.issue.id}}
35
32 36 def save
33 37 # Do not save an empty journal
34 38 (details.empty? && notes.blank?) ? false : super
@@ -4,7 +4,7
4 4 <% end %>
5 5
6 6 <h3><%= l(:label_issue_plural) %></h3>
7 <%= link_to l(:label_issue_view_all), :controller => 'projects', :action => 'list_issues', :id => @project, :set_filter => 1 %><br />
7 <%= link_to l(:label_issue_view_all), { :set_filter => 1 } %><br />
8 8 <%= link_to l(:field_summary), :controller => 'reports', :action => 'issue_report', :id => @project %><br />
9 9 <%= link_to l(:label_change_log), :controller => 'projects', :action => 'changelog', :id => @project %>
10 10
@@ -14,5 +14,5
14 14 :order => "name ASC",
15 15 :conditions => ["is_public = ? or user_id = ?", true, (User.current.logged? ? User.current.id : 0)])
16 16 queries.each do |query| %>
17 <%= link_to query.name, :controller => 'projects', :action => 'list_issues', :id => @project, :query_id => query %><br />
17 <%= link_to query.name, :controller => 'issues', :action => 'index', :project_id => @project, :query_id => query %><br />
18 18 <% end %>
@@ -1,4 +1,4
1 <% back_to = url_for(:controller => 'projects', :action => 'list_issues', :id => @project) %>
1 <% back_to = url_for(:controller => 'issues', :action => 'index', :project_id => @project) %>
2 2 <ul>
3 3 <li><%= context_menu_link l(:button_edit), {:controller => 'issues', :action => 'edit', :id => @issue},
4 4 :class => 'icon-edit', :disabled => !@can[:edit] %></li>
@@ -1,49 +1,50
1 <% pdf=IfpdfHelper::IFPDF.new(current_language)
2 pdf.SetTitle("#{@project.name} - #{l(:label_issue_plural)}")
3 pdf.AliasNbPages
4 pdf.footer_date = format_date(Date.today)
5 pdf.AddPage("L")
6 row_height = 7
7
8 #
9 # title
10 #
11 pdf.SetFontStyle('B',11)
12 pdf.Cell(190,10, "#{@project.name} - #{l(:label_issue_plural)}")
13 pdf.Ln
14
15 #
16 # headers
17 #
18 pdf.SetFontStyle('B',10)
19 pdf.SetFillColor(230, 230, 230)
20 pdf.Cell(15, row_height, "#", 0, 0, 'L', 1)
21 pdf.Cell(30, row_height, l(:field_tracker), 0, 0, 'L', 1)
22 pdf.Cell(30, row_height, l(:field_status), 0, 0, 'L', 1)
23 pdf.Cell(30, row_height, l(:field_priority), 0, 0, 'L', 1)
24 pdf.Cell(40, row_height, l(:field_author), 0, 0, 'L', 1)
25 pdf.Cell(25, row_height, l(:field_updated_on), 0, 0, 'L', 1)
26 pdf.Cell(0, row_height, l(:field_subject), 0, 0, 'L', 1)
27 pdf.Line(10, pdf.GetY, 287, pdf.GetY)
28 pdf.Ln
29 pdf.Line(10, pdf.GetY, 287, pdf.GetY)
30 pdf.SetY(pdf.GetY() + 1)
31
32 #
33 # rows
34 #
35 pdf.SetFontStyle('',9)
36 pdf.SetFillColor(255, 255, 255)
37 @issues.each do |issue|
38 pdf.Cell(15, row_height, issue.id.to_s, 0, 0, 'L', 1)
39 pdf.Cell(30, row_height, issue.tracker.name, 0, 0, 'L', 1)
40 pdf.Cell(30, row_height, issue.status.name, 0, 0, 'L', 1)
41 pdf.Cell(30, row_height, issue.priority.name, 0, 0, 'L', 1)
42 pdf.Cell(40, row_height, issue.author.name, 0, 0, 'L', 1)
43 pdf.Cell(25, row_height, format_date(issue.updated_on), 0, 0, 'L', 1)
44 pdf.MultiCell(0, row_height, (@project == issue.project ? issue.subject : "#{issue.project.name} - #{issue.subject}"))
45 pdf.Line(10, pdf.GetY, 287, pdf.GetY)
46 pdf.SetY(pdf.GetY() + 1)
47 end
48 %>
1 <% pdf=IfpdfHelper::IFPDF.new(current_language)
2 title = @project ? "#{@project.name} - #{l(:label_issue_plural)}" : "#{l(:label_issue_plural)}"
3 pdf.SetTitle(title)
4 pdf.AliasNbPages
5 pdf.footer_date = format_date(Date.today)
6 pdf.AddPage("L")
7 row_height = 7
8
9 #
10 # title
11 #
12 pdf.SetFontStyle('B',11)
13 pdf.Cell(190,10, title)
14 pdf.Ln
15
16 #
17 # headers
18 #
19 pdf.SetFontStyle('B',10)
20 pdf.SetFillColor(230, 230, 230)
21 pdf.Cell(15, row_height, "#", 0, 0, 'L', 1)
22 pdf.Cell(30, row_height, l(:field_tracker), 0, 0, 'L', 1)
23 pdf.Cell(30, row_height, l(:field_status), 0, 0, 'L', 1)
24 pdf.Cell(30, row_height, l(:field_priority), 0, 0, 'L', 1)
25 pdf.Cell(40, row_height, l(:field_assigned_to), 0, 0, 'L', 1)
26 pdf.Cell(25, row_height, l(:field_updated_on), 0, 0, 'L', 1)
27 pdf.Cell(0, row_height, l(:field_subject), 0, 0, 'L', 1)
28 pdf.Line(10, pdf.GetY, 287, pdf.GetY)
29 pdf.Ln
30 pdf.Line(10, pdf.GetY, 287, pdf.GetY)
31 pdf.SetY(pdf.GetY() + 1)
32
33 #
34 # rows
35 #
36 pdf.SetFontStyle('',9)
37 pdf.SetFillColor(255, 255, 255)
38 @issues.each do |issue|
39 pdf.Cell(15, row_height, issue.id.to_s, 0, 0, 'L', 1)
40 pdf.Cell(30, row_height, issue.tracker.name, 0, 0, 'L', 1)
41 pdf.Cell(30, row_height, issue.status.name, 0, 0, 'L', 1)
42 pdf.Cell(30, row_height, issue.priority.name, 0, 0, 'L', 1)
43 pdf.Cell(40, row_height, issue.assigned_to ? issue.assigned_to.name : '', 0, 0, 'L', 1)
44 pdf.Cell(25, row_height, format_date(issue.updated_on), 0, 0, 'L', 1)
45 pdf.MultiCell(0, row_height, (@project == issue.project ? issue.subject : "#{issue.project.name} - #{issue.subject}"))
46 pdf.Line(10, pdf.GetY, 287, pdf.GetY)
47 pdf.SetY(pdf.GetY() + 1)
48 end
49 %>
49 50 <%= pdf.Output %> No newline at end of file
@@ -1,35 +1,72
1 <h2><%=l(:label_issue_plural)%></h2>
1 <% if @query.new_record? %>
2 <h2><%=l(:label_issue_plural)%></h2>
3 <% set_html_title l(:label_issue_plural) %>
2 4
3 <% form_tag({}, :id => 'query_form') do %>
4 <%= render :partial => 'queries/filters', :locals => {:query => @query} %>
5 <% form_tag({ :controller => 'queries', :action => 'new', :project_id => @project }, :id => 'query_form') do %>
6 <%= render :partial => 'queries/filters', :locals => {:query => @query} %>
7 <div class="contextual">
8 <%= link_to_remote l(:button_apply),
9 { :url => { :set_filter => 1 },
10 :update => "content",
11 :with => "Form.serialize('query_form')"
12 }, :class => 'icon icon-edit' %>
13
14 <%= link_to_remote l(:button_clear),
15 { :url => { :set_filter => 1 },
16 :update => "content",
17 }, :class => 'icon icon-reload' %>
18
19 <% if current_role && current_role.allowed_to?(:save_queries) %>
20 <%= link_to l(:button_save), {}, :onclick => "$('query_form').submit(); return false;", :class => 'icon icon-save' %>
21 <% end %>
22 </div>
23 <br />
24 &nbsp;
25 <% end %>
26 <% else %>
27 <div class="contextual">
28 <% if @query.editable_by?(User.current) %>
29 <%= link_to l(:button_edit), {:controller => 'queries', :action => 'edit', :id => @query}, :class => 'icon icon-edit' %>
30 <%= link_to l(:button_delete), {:controller => 'queries', :action => 'destroy', :id => @query}, :confirm => l(:text_are_you_sure), :method => :post, :class => 'icon icon-del' %>
31 <% end %>
32 </div>
33
34 <h2><%= @query.name %></h2>
35 <div id="query_form"></div>
36 <% set_html_title @query.name %>
5 37 <% end %>
6 <div class="contextual">
7 <%= link_to_remote l(:button_apply),
8 { :url => { :set_filter => 1 },
9 :update => "content",
10 :with => "Form.serialize('query_form')"
11 }, :class => 'icon icon-edit' %>
12
13 <%= link_to_remote l(:button_clear),
14 { :url => { :set_filter => 1 },
15 :update => "content",
16 }, :class => 'icon icon-reload' %>
17 </div>
18 <br />&nbsp;
19
20 38 <%= error_messages_for 'query' %>
21 39 <% if @query.valid? %>
22 40 <% if @issues.empty? %>
23 <p><i><%= l(:label_no_data) %></i></p>
41 <p class="nodata"><%= l(:label_no_data) %></p>
24 42 <% else %>
25 &nbsp;
43 <% form_tag({:controller => 'projects', :action => 'bulk_edit_issues', :id => @project}, :id => 'issues_form', :onsubmit => "if (!checkBulkEdit(this)) {alert('#{l(:notice_no_issue_selected)}'); return false;}" ) do %>
26 44 <%= render :partial => 'issues/list', :locals => {:issues => @issues, :query => @query} %>
27
45 <div class="contextual">
46 <%= l(:label_export_to) %>
47 <%= link_to 'CSV', {:format => 'csv'}, :class => 'icon icon-csv' %>,
48 <%= link_to 'PDF', {:format => 'pdf'}, :class => 'icon icon-pdf' %>
49 </div>
28 50 <p><%= pagination_links_full @issue_pages %>
29 51 [ <%= @issue_pages.current.first_item %> - <%= @issue_pages.current.last_item %> / <%= @issue_count %> ]</p>
30 52 <% end %>
31 53 <% end %>
54 <% end %>
55
56 <% content_for :sidebar do %>
57 <%= render :partial => 'issues/sidebar' %>
58 <% end if @project%>
32 59
33 60 <% content_for :header_tags do %>
34 <%= auto_discovery_link_tag(:atom, params.merge({:format => 'atom', :page => nil, :key => User.current.rss_key})) %>
61 <%= auto_discovery_link_tag(:atom, {:query_id => @query, :format => 'atom', :page => nil, :key => User.current.rss_key}, :title => l(:label_issue_plural)) %>
62 <%= auto_discovery_link_tag(:atom, {:action => 'changes', :query_id => @query, :format => 'atom', :page => nil, :key => User.current.rss_key}, :title => l(:label_changes_details)) %>
63 <%= javascript_include_tag 'calendar/calendar' %>
64 <%= javascript_include_tag "calendar/lang/calendar-#{current_language}.js" %>
65 <%= javascript_include_tag 'calendar/calendar-setup' %>
66 <%= stylesheet_link_tag 'calendar' %>
67 <%= javascript_include_tag 'context_menu' %>
68 <%= stylesheet_link_tag 'context_menu' %>
35 69 <% end %>
70
71 <div id="context-menu" style="display: none;"></div>
72 <%= javascript_tag 'new ContextMenu({})' %>
@@ -34,8 +34,8
34 34 <% end %>
35 35 </tr>
36 36 </table>
37 <em><%= link_to(complete, :controller => 'projects', :action => 'list_issues', :id => @project, :status_id => 'c', :fixed_version_id => version, :set_filter => 1) %> <%= lwr(:label_closed_issues, complete) %> (<%= percentComplete %>%) &#160;
38 <%= link_to((total - complete), :controller => 'projects', :action => 'list_issues', :id => @project, :status_id => 'o', :fixed_version_id => version, :set_filter => 1) %> <%= lwr(:label_open_issues, total - complete)%> (<%= percentIncomplete %>%)</em>
37 <em><%= link_to(complete, :controller => 'issues', :action => 'index', :project_id => @project, :status_id => 'c', :fixed_version_id => version, :set_filter => 1) %> <%= lwr(:label_closed_issues, complete) %> (<%= percentComplete %>%) &#160;
38 <%= link_to((total - complete), :controller => 'issues', :action => 'index', :project_id => @project, :status_id => 'o', :fixed_version_id => version, :set_filter => 1) %> <%= lwr(:label_open_issues, total - complete)%> (<%= percentIncomplete %>%)</em>
39 39 <br />
40 40 <br />
41 41 <%= render(:partial => "wiki/content", :locals => {:content => version.wiki_page.content}) if version.wiki_page %>
@@ -22,14 +22,14
22 22 <h3 class="icon22 icon22-tracker"><%=l(:label_issue_tracking)%></h3>
23 23 <ul>
24 24 <% for tracker in @trackers %>
25 <li><%= link_to tracker.name, :controller => 'projects', :action => 'list_issues', :id => @project,
25 <li><%= link_to tracker.name, :controller => 'issues', :action => 'index', :project_id => @project,
26 26 :set_filter => 1,
27 27 "tracker_id" => tracker.id %>:
28 28 <%= @open_issues_by_tracker[tracker] || 0 %> <%= lwr(:label_open_issues, @open_issues_by_tracker[tracker] || 0) %>
29 29 <%= l(:label_on) %> <%= @total_issues_by_tracker[tracker] || 0 %></li>
30 30 <% end %>
31 31 </ul>
32 <p><%= link_to l(:label_issue_view_all), :controller => 'projects', :action => 'list_issues', :id => @project, :set_filter => 1 %></p>
32 <p><%= link_to l(:label_issue_view_all), :controller => 'issues', :action => 'index', :project_id => @project, :set_filter => 1 %></p>
33 33 </div>
34 34 <% end %>
35 35 </div>
@@ -11,7 +11,7
11 11 <% @queries.each do |query| %>
12 12 <tr class="<%= cycle('odd', 'even') %>">
13 13 <td>
14 <%= link_to query.name, :controller => 'projects', :action => 'list_issues', :id => @project, :query_id => query %>
14 <%= link_to query.name, :controller => 'issues', :action => 'index', :project_id => @project, :query_id => query %>
15 15 </td>
16 16 <td align="right">
17 17 <small>
@@ -15,28 +15,28
15 15 <tbody>
16 16 <% for row in rows %>
17 17 <tr class="<%= cycle("odd", "even") %>">
18 <td><%= link_to row.name, :controller => 'projects', :action => 'list_issues', :id => ((row.is_a?(Project) ? row : @project)),
18 <td><%= link_to row.name, :controller => 'issues', :action => 'index', :project_id => ((row.is_a?(Project) ? row : @project)),
19 19 :set_filter => 1,
20 20 "#{field_name}" => row.id %></td>
21 21 <% for status in @statuses %>
22 22 <td align="center"><%= aggregate_link data, { field_name => row.id, "status_id" => status.id },
23 :controller => 'projects', :action => 'list_issues', :id => ((row.is_a?(Project) ? row : @project)),
23 :controller => 'issues', :action => 'index', :project_id => ((row.is_a?(Project) ? row : @project)),
24 24 :set_filter => 1,
25 25 "status_id" => status.id,
26 26 "#{field_name}" => row.id %></td>
27 27 <% end %>
28 28 <td align="center"><%= aggregate_link data, { field_name => row.id, "closed" => 0 },
29 :controller => 'projects', :action => 'list_issues', :id => ((row.is_a?(Project) ? row : @project)),
29 :controller => 'issues', :action => 'index', :project_id => ((row.is_a?(Project) ? row : @project)),
30 30 :set_filter => 1,
31 31 "#{field_name}" => row.id,
32 32 "status_id" => "o" %></td>
33 33 <td align="center"><%= aggregate_link data, { field_name => row.id, "closed" => 1 },
34 :controller => 'projects', :action => 'list_issues', :id => ((row.is_a?(Project) ? row : @project)),
34 :controller => 'issues', :action => 'index', :project_id => ((row.is_a?(Project) ? row : @project)),
35 35 :set_filter => 1,
36 36 "#{field_name}" => row.id,
37 37 "status_id" => "c" %></td>
38 38 <td align="center"><%= aggregate_link data, { field_name => row.id },
39 :controller => 'projects', :action => 'list_issues', :id => ((row.is_a?(Project) ? row : @project)),
39 :controller => 'issues', :action => 'index', :project_id => ((row.is_a?(Project) ? row : @project)),
40 40 :set_filter => 1,
41 41 "#{field_name}" => row.id,
42 42 "status_id" => "*" %></td>
@@ -11,21 +11,21
11 11 <tbody>
12 12 <% for row in rows %>
13 13 <tr class="<%= cycle("odd", "even") %>">
14 <td><%= link_to row.name, :controller => 'projects', :action => 'list_issues', :id => ((row.is_a?(Project) ? row : @project)),
14 <td><%= link_to row.name, :controller => 'issues', :action => 'index', :project_id => ((row.is_a?(Project) ? row : @project)),
15 15 :set_filter => 1,
16 16 "#{field_name}" => row.id %></td>
17 17 <td align="center"><%= aggregate_link data, { field_name => row.id, "closed" => 0 },
18 :controller => 'projects', :action => 'list_issues', :id => ((row.is_a?(Project) ? row : @project)),
18 :controller => 'issues', :action => 'index', :project_id => ((row.is_a?(Project) ? row : @project)),
19 19 :set_filter => 1,
20 20 "#{field_name}" => row.id,
21 21 "status_id" => "o" %></td>
22 22 <td align="center"><%= aggregate_link data, { field_name => row.id, "closed" => 1 },
23 :controller => 'projects', :action => 'list_issues', :id => ((row.is_a?(Project) ? row : @project)),
23 :controller => 'issues', :action => 'index', :project_id => ((row.is_a?(Project) ? row : @project)),
24 24 :set_filter => 1,
25 25 "#{field_name}" => row.id,
26 26 "status_id" => "c" %></td>
27 27 <td align="center"><%= aggregate_link data, { field_name => row.id },
28 :controller => 'projects', :action => 'list_issues', :id => ((row.is_a?(Project) ? row : @project)),
28 :controller => 'issues', :action => 'index', :project_id => ((row.is_a?(Project) ? row : @project)),
29 29 :set_filter => 1,
30 30 "#{field_name}" => row.id,
31 31 "status_id" => "*" %></td>
@@ -77,7 +77,10 ActiveRecord::Errors.default_error_messages = {
77 77 }
78 78
79 79 ActionView::Base.field_error_proc = Proc.new{ |html_tag, instance| "#{html_tag}" }
80
80
81 Mime::Type.register 'text/csv', :csv
82 Mime::Type.register 'application/pdf', :pdf
83
81 84 GLoc.set_config :default_language => :en
82 85 GLoc.clear_strings
83 86 GLoc.set_kcode
@@ -14,6 +14,7 ActionController::Routing::Routes.draw do |map|
14 14 #map.connect ':controller/:action/:id/:sort_key/:sort_order'
15 15
16 16 map.connect 'issues/:issue_id/relations/:action/:id', :controller => 'issue_relations'
17 map.connect 'projects/:project_id/issues/:action', :controller => 'issues'
17 18 map.connect 'projects/:project_id/boards/:action/:id', :controller => 'boards'
18 19 map.connect 'boards/:board_id/topics/:action/:id', :controller => 'messages'
19 20
@@ -25,8 +25,8 Redmine::AccessControl.map do |map|
25 25 # Issue categories
26 26 map.permission :manage_categories, {:projects => [:settings, :add_issue_category], :issue_categories => [:edit, :destroy]}, :require => :member
27 27 # Issues
28 map.permission :view_issues, {:projects => [:list_issues, :export_issues_csv, :export_issues_pdf, :changelog, :roadmap],
29 :issues => [:show, :context_menu],
28 map.permission :view_issues, {:projects => [:changelog, :roadmap],
29 :issues => [:index, :changes, :show, :context_menu],
30 30 :queries => :index,
31 31 :reports => :issue_report}, :public => true
32 32 map.permission :add_issues, {:projects => :add_issue}, :require => :loggedin
@@ -92,7 +92,7 Redmine::MenuManager.map :project_menu do |menu|
92 92 menu.push :label_overview, :controller => 'projects', :action => 'show'
93 93 menu.push :label_activity, :controller => 'projects', :action => 'activity'
94 94 menu.push :label_roadmap, :controller => 'projects', :action => 'roadmap'
95 menu.push :label_issue_plural, :controller => 'projects', :action => 'list_issues'
95 menu.push :label_issue_plural, { :controller => 'issues', :action => 'index' }, :param => :project_id
96 96 menu.push :label_news_plural, :controller => 'projects', :action => 'list_news'
97 97 menu.push :label_document_plural, :controller => 'projects', :action => 'list_documents'
98 98 menu.push :label_wiki, { :controller => 'wiki', :action => 'index', :page => nil }, :if => Proc.new { |p| p.wiki && !p.wiki.new_record? }
@@ -55,33 +55,6 class ProjectsControllerTest < Test::Unit::TestCase
55 55 assert_response :success
56 56 assert_template 'list_documents'
57 57 assert_not_nil assigns(:grouped)
58 end
59
60 def test_list_issues
61 get :list_issues, :id => 1
62 assert_response :success
63 assert_template 'list_issues'
64 assert_not_nil assigns(:issues)
65 end
66
67 def test_list_issues_with_filter
68 get :list_issues, :id => 1, :set_filter => 1
69 assert_response :success
70 assert_template 'list_issues'
71 assert_not_nil assigns(:issues)
72 end
73
74 def test_list_issues_reset_filter
75 post :list_issues, :id => 1
76 assert_response :success
77 assert_template 'list_issues'
78 assert_not_nil assigns(:issues)
79 end
80
81 def test_export_issues_csv
82 get :export_issues_csv, :id => 1
83 assert_response :success
84 assert_not_nil assigns(:issues)
85 58 end
86 59
87 60 def test_bulk_edit_issues
@@ -150,7 +123,7 class ProjectsControllerTest < Test::Unit::TestCase
150 123 assert_response :success
151 124 assert_template 'add_issue'
152 125 post :add_issue, :id => 1, :issue => {:tracker_id => 1, :subject => 'This is the test_add_issue issue', :description => 'This is the description', :priority_id => 5}
153 assert_redirected_to 'projects/list_issues'
126 assert_redirected_to 'projects/1/issues'
154 127 assert Issue.find_by_subject('This is the test_add_issue issue')
155 128 end
156 129
@@ -24,7 +24,7 class IssuesTest < ActionController::IntegrationTest
24 24 assert_kind_of Issue, issue
25 25
26 26 # check redirection
27 assert_redirected_to "projects/list_issues/1"
27 assert_redirected_to "projects/1/issues"
28 28 follow_redirect!
29 29 assert assigns(:issues).include?(issue)
30 30
1 NO CONTENT: file was removed
General Comments 0
You need to be logged in to leave comments. Login now