##// END OF EJS Templates
Removed the "New issue" menu item (#6204)....
Jean-Philippe Lang -
r14962:dcc569fa34f7
parent child
Show More

The requested changes are too big and content was truncated. Show full diff

@@ -1,539 +1,538
1 1 # Redmine - project management software
2 2 # Copyright (C) 2006-2016 Jean-Philippe Lang
3 3 #
4 4 # This program is free software; you can redistribute it and/or
5 5 # modify it under the terms of the GNU General Public License
6 6 # as published by the Free Software Foundation; either version 2
7 7 # of the License, or (at your option) any later version.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU General Public License
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 18 class IssuesController < ApplicationController
19 menu_item :new_issue, :only => [:new, :create]
20 19 default_search_scope :issues
21 20
22 21 before_filter :find_issue, :only => [:show, :edit, :update]
23 22 before_filter :find_issues, :only => [:bulk_edit, :bulk_update, :destroy]
24 23 before_filter :authorize, :except => [:index, :new, :create]
25 24 before_filter :find_optional_project, :only => [:index, :new, :create]
26 25 before_filter :build_new_issue_from_params, :only => [:new, :create]
27 26 accept_rss_auth :index, :show
28 27 accept_api_auth :index, :show, :create, :update, :destroy
29 28
30 29 rescue_from Query::StatementInvalid, :with => :query_statement_invalid
31 30
32 31 helper :journals
33 32 helper :projects
34 33 helper :custom_fields
35 34 helper :issue_relations
36 35 helper :watchers
37 36 helper :attachments
38 37 helper :queries
39 38 include QueriesHelper
40 39 helper :repositories
41 40 helper :sort
42 41 include SortHelper
43 42 helper :timelog
44 43
45 44 def index
46 45 retrieve_query
47 46 sort_init(@query.sort_criteria.empty? ? [['id', 'desc']] : @query.sort_criteria)
48 47 sort_update(@query.sortable_columns)
49 48 @query.sort_criteria = sort_criteria.to_a
50 49
51 50 if @query.valid?
52 51 case params[:format]
53 52 when 'csv', 'pdf'
54 53 @limit = Setting.issues_export_limit.to_i
55 54 if params[:columns] == 'all'
56 55 @query.column_names = @query.available_inline_columns.map(&:name)
57 56 end
58 57 when 'atom'
59 58 @limit = Setting.feeds_limit.to_i
60 59 when 'xml', 'json'
61 60 @offset, @limit = api_offset_and_limit
62 61 @query.column_names = %w(author)
63 62 else
64 63 @limit = per_page_option
65 64 end
66 65
67 66 @issue_count = @query.issue_count
68 67 @issue_pages = Paginator.new @issue_count, @limit, params['page']
69 68 @offset ||= @issue_pages.offset
70 69 @issues = @query.issues(:include => [:assigned_to, :tracker, :priority, :category, :fixed_version],
71 70 :order => sort_clause,
72 71 :offset => @offset,
73 72 :limit => @limit)
74 73 @issue_count_by_group = @query.issue_count_by_group
75 74
76 75 respond_to do |format|
77 76 format.html { render :template => 'issues/index', :layout => !request.xhr? }
78 77 format.api {
79 78 Issue.load_visible_relations(@issues) if include_in_api_response?('relations')
80 79 }
81 80 format.atom { render_feed(@issues, :title => "#{@project || Setting.app_title}: #{l(:label_issue_plural)}") }
82 81 format.csv { send_data(query_to_csv(@issues, @query, params[:csv]), :type => 'text/csv; header=present', :filename => 'issues.csv') }
83 82 format.pdf { send_file_headers! :type => 'application/pdf', :filename => 'issues.pdf' }
84 83 end
85 84 else
86 85 respond_to do |format|
87 86 format.html { render(:template => 'issues/index', :layout => !request.xhr?) }
88 87 format.any(:atom, :csv, :pdf) { render(:nothing => true) }
89 88 format.api { render_validation_errors(@query) }
90 89 end
91 90 end
92 91 rescue ActiveRecord::RecordNotFound
93 92 render_404
94 93 end
95 94
96 95 def show
97 96 @journals = @issue.journals.includes(:user, :details).
98 97 references(:user, :details).
99 98 reorder(:created_on, :id).to_a
100 99 @journals.each_with_index {|j,i| j.indice = i+1}
101 100 @journals.reject!(&:private_notes?) unless User.current.allowed_to?(:view_private_notes, @issue.project)
102 101 Journal.preload_journals_details_custom_fields(@journals)
103 102 @journals.select! {|journal| journal.notes? || journal.visible_details.any?}
104 103 @journals.reverse! if User.current.wants_comments_in_reverse_order?
105 104
106 105 @changesets = @issue.changesets.visible.preload(:repository, :user).to_a
107 106 @changesets.reverse! if User.current.wants_comments_in_reverse_order?
108 107
109 108 @relations = @issue.relations.select {|r| r.other_issue(@issue) && r.other_issue(@issue).visible? }
110 109 @allowed_statuses = @issue.new_statuses_allowed_to(User.current)
111 110 @priorities = IssuePriority.active
112 111 @time_entry = TimeEntry.new(:issue => @issue, :project => @issue.project)
113 112 @relation = IssueRelation.new
114 113
115 114 respond_to do |format|
116 115 format.html {
117 116 retrieve_previous_and_next_issue_ids
118 117 render :template => 'issues/show'
119 118 }
120 119 format.api
121 120 format.atom { render :template => 'journals/index', :layout => false, :content_type => 'application/atom+xml' }
122 121 format.pdf {
123 122 send_file_headers! :type => 'application/pdf', :filename => "#{@project.identifier}-#{@issue.id}.pdf"
124 123 }
125 124 end
126 125 end
127 126
128 127 def new
129 128 respond_to do |format|
130 129 format.html { render :action => 'new', :layout => !request.xhr? }
131 130 format.js
132 131 end
133 132 end
134 133
135 134 def create
136 135 unless User.current.allowed_to?(:add_issues, @issue.project, :global => true)
137 136 raise ::Unauthorized
138 137 end
139 138 call_hook(:controller_issues_new_before_save, { :params => params, :issue => @issue })
140 139 @issue.save_attachments(params[:attachments] || (params[:issue] && params[:issue][:uploads]))
141 140 if @issue.save
142 141 call_hook(:controller_issues_new_after_save, { :params => params, :issue => @issue})
143 142 respond_to do |format|
144 143 format.html {
145 144 render_attachment_warning_if_needed(@issue)
146 145 flash[:notice] = l(:notice_issue_successful_create, :id => view_context.link_to("##{@issue.id}", issue_path(@issue), :title => @issue.subject))
147 146 redirect_after_create
148 147 }
149 148 format.api { render :action => 'show', :status => :created, :location => issue_url(@issue) }
150 149 end
151 150 return
152 151 else
153 152 respond_to do |format|
154 153 format.html {
155 154 if @issue.project.nil?
156 155 render_error :status => 422
157 156 else
158 157 render :action => 'new'
159 158 end
160 159 }
161 160 format.api { render_validation_errors(@issue) }
162 161 end
163 162 end
164 163 end
165 164
166 165 def edit
167 166 return unless update_issue_from_params
168 167
169 168 respond_to do |format|
170 169 format.html { }
171 170 format.js
172 171 end
173 172 end
174 173
175 174 def update
176 175 return unless update_issue_from_params
177 176 @issue.save_attachments(params[:attachments] || (params[:issue] && params[:issue][:uploads]))
178 177 saved = false
179 178 begin
180 179 saved = save_issue_with_child_records
181 180 rescue ActiveRecord::StaleObjectError
182 181 @conflict = true
183 182 if params[:last_journal_id]
184 183 @conflict_journals = @issue.journals_after(params[:last_journal_id]).to_a
185 184 @conflict_journals.reject!(&:private_notes?) unless User.current.allowed_to?(:view_private_notes, @issue.project)
186 185 end
187 186 end
188 187
189 188 if saved
190 189 render_attachment_warning_if_needed(@issue)
191 190 flash[:notice] = l(:notice_successful_update) unless @issue.current_journal.new_record?
192 191
193 192 respond_to do |format|
194 193 format.html { redirect_back_or_default issue_path(@issue, previous_and_next_issue_ids_params) }
195 194 format.api { render_api_ok }
196 195 end
197 196 else
198 197 respond_to do |format|
199 198 format.html { render :action => 'edit' }
200 199 format.api { render_validation_errors(@issue) }
201 200 end
202 201 end
203 202 end
204 203
205 204 # Bulk edit/copy a set of issues
206 205 def bulk_edit
207 206 @issues.sort!
208 207 @copy = params[:copy].present?
209 208 @notes = params[:notes]
210 209
211 210 if @copy
212 211 unless User.current.allowed_to?(:copy_issues, @projects)
213 212 raise ::Unauthorized
214 213 end
215 214 end
216 215
217 216 @allowed_projects = Issue.allowed_target_projects
218 217 if params[:issue]
219 218 @target_project = @allowed_projects.detect {|p| p.id.to_s == params[:issue][:project_id].to_s}
220 219 if @target_project
221 220 target_projects = [@target_project]
222 221 end
223 222 end
224 223 target_projects ||= @projects
225 224
226 225 if @copy
227 226 # Copied issues will get their default statuses
228 227 @available_statuses = []
229 228 else
230 229 @available_statuses = @issues.map(&:new_statuses_allowed_to).reduce(:&)
231 230 end
232 231 @custom_fields = @issues.map{|i|i.editable_custom_fields}.reduce(:&)
233 232 @assignables = target_projects.map(&:assignable_users).reduce(:&)
234 233 @trackers = target_projects.map(&:trackers).reduce(:&)
235 234 @versions = target_projects.map {|p| p.shared_versions.open}.reduce(:&)
236 235 @categories = target_projects.map {|p| p.issue_categories}.reduce(:&)
237 236 if @copy
238 237 @attachments_present = @issues.detect {|i| i.attachments.any?}.present?
239 238 @subtasks_present = @issues.detect {|i| !i.leaf?}.present?
240 239 end
241 240
242 241 @safe_attributes = @issues.map(&:safe_attribute_names).reduce(:&)
243 242
244 243 @issue_params = params[:issue] || {}
245 244 @issue_params[:custom_field_values] ||= {}
246 245 end
247 246
248 247 def bulk_update
249 248 @issues.sort!
250 249 @copy = params[:copy].present?
251 250
252 251 attributes = parse_params_for_bulk_issue_attributes(params)
253 252 copy_subtasks = (params[:copy_subtasks] == '1')
254 253 copy_attachments = (params[:copy_attachments] == '1')
255 254
256 255 if @copy
257 256 unless User.current.allowed_to?(:copy_issues, @projects)
258 257 raise ::Unauthorized
259 258 end
260 259 target_projects = @projects
261 260 if attributes['project_id'].present?
262 261 target_projects = Project.where(:id => attributes['project_id']).to_a
263 262 end
264 263 unless User.current.allowed_to?(:add_issues, target_projects)
265 264 raise ::Unauthorized
266 265 end
267 266 end
268 267
269 268 unsaved_issues = []
270 269 saved_issues = []
271 270
272 271 if @copy && copy_subtasks
273 272 # Descendant issues will be copied with the parent task
274 273 # Don't copy them twice
275 274 @issues.reject! {|issue| @issues.detect {|other| issue.is_descendant_of?(other)}}
276 275 end
277 276
278 277 @issues.each do |orig_issue|
279 278 orig_issue.reload
280 279 if @copy
281 280 issue = orig_issue.copy({},
282 281 :attachments => copy_attachments,
283 282 :subtasks => copy_subtasks,
284 283 :link => link_copy?(params[:link_copy])
285 284 )
286 285 else
287 286 issue = orig_issue
288 287 end
289 288 journal = issue.init_journal(User.current, params[:notes])
290 289 issue.safe_attributes = attributes
291 290 call_hook(:controller_issues_bulk_edit_before_save, { :params => params, :issue => issue })
292 291 if issue.save
293 292 saved_issues << issue
294 293 else
295 294 unsaved_issues << orig_issue
296 295 end
297 296 end
298 297
299 298 if unsaved_issues.empty?
300 299 flash[:notice] = l(:notice_successful_update) unless saved_issues.empty?
301 300 if params[:follow]
302 301 if @issues.size == 1 && saved_issues.size == 1
303 302 redirect_to issue_path(saved_issues.first)
304 303 elsif saved_issues.map(&:project).uniq.size == 1
305 304 redirect_to project_issues_path(saved_issues.map(&:project).first)
306 305 end
307 306 else
308 307 redirect_back_or_default _project_issues_path(@project)
309 308 end
310 309 else
311 310 @saved_issues = @issues
312 311 @unsaved_issues = unsaved_issues
313 312 @issues = Issue.visible.where(:id => @unsaved_issues.map(&:id)).to_a
314 313 bulk_edit
315 314 render :action => 'bulk_edit'
316 315 end
317 316 end
318 317
319 318 def destroy
320 319 @hours = TimeEntry.where(:issue_id => @issues.map(&:id)).sum(:hours).to_f
321 320 if @hours > 0
322 321 case params[:todo]
323 322 when 'destroy'
324 323 # nothing to do
325 324 when 'nullify'
326 325 TimeEntry.where(['issue_id IN (?)', @issues]).update_all('issue_id = NULL')
327 326 when 'reassign'
328 327 reassign_to = @project.issues.find_by_id(params[:reassign_to_id])
329 328 if reassign_to.nil?
330 329 flash.now[:error] = l(:error_issue_not_found_in_project)
331 330 return
332 331 else
333 332 TimeEntry.where(['issue_id IN (?)', @issues]).
334 333 update_all("issue_id = #{reassign_to.id}")
335 334 end
336 335 else
337 336 # display the destroy form if it's a user request
338 337 return unless api_request?
339 338 end
340 339 end
341 340 @issues.each do |issue|
342 341 begin
343 342 issue.reload.destroy
344 343 rescue ::ActiveRecord::RecordNotFound # raised by #reload if issue no longer exists
345 344 # nothing to do, issue was already deleted (eg. by a parent)
346 345 end
347 346 end
348 347 respond_to do |format|
349 348 format.html { redirect_back_or_default _project_issues_path(@project) }
350 349 format.api { render_api_ok }
351 350 end
352 351 end
353 352
354 353 private
355 354
356 355 def retrieve_previous_and_next_issue_ids
357 356 if params[:prev_issue_id].present? || params[:next_issue_id].present?
358 357 @prev_issue_id = params[:prev_issue_id].presence.try(:to_i)
359 358 @next_issue_id = params[:next_issue_id].presence.try(:to_i)
360 359 @issue_position = params[:issue_position].presence.try(:to_i)
361 360 @issue_count = params[:issue_count].presence.try(:to_i)
362 361 else
363 362 retrieve_query_from_session
364 363 if @query
365 364 sort_init(@query.sort_criteria.empty? ? [['id', 'desc']] : @query.sort_criteria)
366 365 sort_update(@query.sortable_columns, 'issues_index_sort')
367 366 limit = 500
368 367 issue_ids = @query.issue_ids(:order => sort_clause, :limit => (limit + 1), :include => [:assigned_to, :tracker, :priority, :category, :fixed_version])
369 368 if (idx = issue_ids.index(@issue.id)) && idx < limit
370 369 if issue_ids.size < 500
371 370 @issue_position = idx + 1
372 371 @issue_count = issue_ids.size
373 372 end
374 373 @prev_issue_id = issue_ids[idx - 1] if idx > 0
375 374 @next_issue_id = issue_ids[idx + 1] if idx < (issue_ids.size - 1)
376 375 end
377 376 end
378 377 end
379 378 end
380 379
381 380 def previous_and_next_issue_ids_params
382 381 {
383 382 :prev_issue_id => params[:prev_issue_id],
384 383 :next_issue_id => params[:next_issue_id],
385 384 :issue_position => params[:issue_position],
386 385 :issue_count => params[:issue_count]
387 386 }.reject {|k,v| k.blank?}
388 387 end
389 388
390 389 # Used by #edit and #update to set some common instance variables
391 390 # from the params
392 391 def update_issue_from_params
393 392 @time_entry = TimeEntry.new(:issue => @issue, :project => @issue.project)
394 393 if params[:time_entry]
395 394 @time_entry.safe_attributes = params[:time_entry]
396 395 end
397 396
398 397 @issue.init_journal(User.current)
399 398
400 399 issue_attributes = params[:issue]
401 400 if issue_attributes && params[:conflict_resolution]
402 401 case params[:conflict_resolution]
403 402 when 'overwrite'
404 403 issue_attributes = issue_attributes.dup
405 404 issue_attributes.delete(:lock_version)
406 405 when 'add_notes'
407 406 issue_attributes = issue_attributes.slice(:notes, :private_notes)
408 407 when 'cancel'
409 408 redirect_to issue_path(@issue)
410 409 return false
411 410 end
412 411 end
413 412 @issue.safe_attributes = issue_attributes
414 413 @priorities = IssuePriority.active
415 414 @allowed_statuses = @issue.new_statuses_allowed_to(User.current)
416 415 true
417 416 end
418 417
419 418 # Used by #new and #create to build a new issue from the params
420 419 # The new issue will be copied from an existing one if copy_from parameter is given
421 420 def build_new_issue_from_params
422 421 @issue = Issue.new
423 422 if params[:copy_from]
424 423 begin
425 424 @issue.init_journal(User.current)
426 425 @copy_from = Issue.visible.find(params[:copy_from])
427 426 unless User.current.allowed_to?(:copy_issues, @copy_from.project)
428 427 raise ::Unauthorized
429 428 end
430 429 @link_copy = link_copy?(params[:link_copy]) || request.get?
431 430 @copy_attachments = params[:copy_attachments].present? || request.get?
432 431 @copy_subtasks = params[:copy_subtasks].present? || request.get?
433 432 @issue.copy_from(@copy_from, :attachments => @copy_attachments, :subtasks => @copy_subtasks, :link => @link_copy)
434 433 @issue.parent_issue_id = @copy_from.parent_id
435 434 rescue ActiveRecord::RecordNotFound
436 435 render_404
437 436 return
438 437 end
439 438 end
440 439 @issue.project = @project
441 440 if request.get?
442 441 @issue.project ||= @issue.allowed_target_projects.first
443 442 end
444 443 @issue.author ||= User.current
445 444 @issue.start_date ||= Date.today if Setting.default_issue_start_date_to_creation_date?
446 445
447 446 attrs = (params[:issue] || {}).deep_dup
448 447 if action_name == 'new' && params[:was_default_status] == attrs[:status_id]
449 448 attrs.delete(:status_id)
450 449 end
451 450 if action_name == 'new' && params[:form_update_triggered_by] == 'issue_project_id'
452 451 # Discard submitted version when changing the project on the issue form
453 452 # so we can use the default version for the new project
454 453 attrs.delete(:fixed_version_id)
455 454 end
456 455 @issue.safe_attributes = attrs
457 456
458 457 if @issue.project
459 458 @issue.tracker ||= @issue.project.trackers.first
460 459 if @issue.tracker.nil?
461 460 render_error l(:error_no_tracker_in_project)
462 461 return false
463 462 end
464 463 if @issue.status.nil?
465 464 render_error l(:error_no_default_issue_status)
466 465 return false
467 466 end
468 467 end
469 468
470 469 @priorities = IssuePriority.active
471 470 @allowed_statuses = @issue.new_statuses_allowed_to(User.current)
472 471 end
473 472
474 473 def parse_params_for_bulk_issue_attributes(params)
475 474 attributes = (params[:issue] || {}).reject {|k,v| v.blank?}
476 475 attributes.keys.each {|k| attributes[k] = '' if attributes[k] == 'none'}
477 476 if custom = attributes[:custom_field_values]
478 477 custom.reject! {|k,v| v.blank?}
479 478 custom.keys.each do |k|
480 479 if custom[k].is_a?(Array)
481 480 custom[k] << '' if custom[k].delete('__none__')
482 481 else
483 482 custom[k] = '' if custom[k] == '__none__'
484 483 end
485 484 end
486 485 end
487 486 attributes
488 487 end
489 488
490 489 # Saves @issue and a time_entry from the parameters
491 490 def save_issue_with_child_records
492 491 Issue.transaction do
493 492 if params[:time_entry] && (params[:time_entry][:hours].present? || params[:time_entry][:comments].present?) && User.current.allowed_to?(:log_time, @issue.project)
494 493 time_entry = @time_entry || TimeEntry.new
495 494 time_entry.project = @issue.project
496 495 time_entry.issue = @issue
497 496 time_entry.user = User.current
498 497 time_entry.spent_on = User.current.today
499 498 time_entry.attributes = params[:time_entry]
500 499 @issue.time_entries << time_entry
501 500 end
502 501
503 502 call_hook(:controller_issues_edit_before_save, { :params => params, :issue => @issue, :time_entry => time_entry, :journal => @issue.current_journal})
504 503 if @issue.save
505 504 call_hook(:controller_issues_edit_after_save, { :params => params, :issue => @issue, :time_entry => time_entry, :journal => @issue.current_journal})
506 505 else
507 506 raise ActiveRecord::Rollback
508 507 end
509 508 end
510 509 end
511 510
512 511 # Returns true if the issue copy should be linked
513 512 # to the original issue
514 513 def link_copy?(param)
515 514 case Setting.link_copied_issue
516 515 when 'yes'
517 516 true
518 517 when 'no'
519 518 false
520 519 when 'ask'
521 520 param == '1'
522 521 end
523 522 end
524 523
525 524 # Redirects user after a successful issue creation
526 525 def redirect_after_create
527 526 if params[:continue]
528 527 attrs = {:tracker_id => @issue.tracker, :parent_issue_id => @issue.parent_issue_id}.reject {|k,v| v.nil?}
529 528 if params[:project_id]
530 529 redirect_to new_project_issue_path(@issue.project, :issue => attrs)
531 530 else
532 531 attrs.merge! :project_id => @issue.project_id
533 532 redirect_to new_issue_path(:issue => attrs)
534 533 end
535 534 else
536 535 redirect_to issue_path(@issue)
537 536 end
538 537 end
539 538 end
@@ -1,121 +1,127
1 1 <div class="contextual">
2 <% if !@query.new_record? && @query.editable_by?(User.current) %>
3 <%= link_to l(:button_edit), edit_query_path(@query), :class => 'icon icon-edit' %>
4 <%= delete_link query_path(@query) %>
5 <% end %>
2 <% if @project && User.current.allowed_to?(:add_issues, @project) && @project.trackers.any? %>
3 <%= link_to l(:label_issue_new), new_project_issue_path(@project), :class => 'icon icon-add new-issue' %>
4 <% end %>
6 5 </div>
7 6
8 7 <h2><%= @query.new_record? ? l(:label_issue_plural) : @query.name %></h2>
9 8 <% html_title(@query.new_record? ? l(:label_issue_plural) : @query.name) %>
10 9
11 10 <%= form_tag({ :controller => 'issues', :action => 'index', :project_id => @project },
12 11 :method => :get, :id => 'query_form') do %>
13 12 <div id="query_form_with_buttons" class="hide-when-print">
14 13 <%= hidden_field_tag 'set_filter', '1' %>
15 14 <div id="query_form_content">
16 15 <fieldset id="filters" class="collapsible <%= @query.new_record? ? "" : "collapsed" %>">
17 16 <legend onclick="toggleFieldset(this);"><%= l(:label_filter_plural) %></legend>
18 17 <div style="<%= @query.new_record? ? "" : "display: none;" %>">
19 18 <%= render :partial => 'queries/filters', :locals => {:query => @query} %>
20 19 </div>
21 20 </fieldset>
22 21 <fieldset id="options" class="collapsible collapsed">
23 22 <legend onclick="toggleFieldset(this);"><%= l(:label_options) %></legend>
24 23 <div style="display: none;">
25 24 <table>
26 25 <tr>
27 26 <td class="field"><%= l(:field_column_names) %></td>
28 27 <td><%= render_query_columns_selection(@query) %></td>
29 28 </tr>
30 29 <tr>
31 30 <td class="field"><label for='group_by'><%= l(:field_group_by) %></label></td>
32 31 <td><%= select_tag('group_by',
33 32 options_for_select(
34 33 [[]] + @query.groupable_columns.collect {|c| [c.caption, c.name.to_s]},
35 34 @query.group_by)
36 35 ) %></td>
37 36 </tr>
38 37 <tr>
39 38 <td class="field"><%= l(:button_show) %></td>
40 39 <td><%= available_block_columns_tags(@query) %></td>
41 40 </tr>
42 41 <tr>
43 42 <td><%= l(:label_total_plural) %></td>
44 43 <td><%= available_totalable_columns_tags(@query) %></td>
45 44 </tr>
46 45 </table>
47 46 </div>
48 47 </fieldset>
49 48 </div>
50 49 <p class="buttons">
51 50 <%= link_to_function l(:button_apply), '$("#query_form").submit()', :class => 'icon icon-checked' %>
52 51 <%= link_to l(:button_clear), { :set_filter => 1, :project_id => @project }, :class => 'icon icon-reload' %>
53 <% if @query.new_record? && User.current.allowed_to?(:save_queries, @project, :global => true) %>
52 <% if @query.new_record? %>
53 <% if User.current.allowed_to?(:save_queries, @project, :global => true) %>
54 54 <%= link_to_function l(:button_save),
55 55 "$('#query_form').attr('action', '#{ @project ? new_project_query_path(@project) : new_query_path }').submit()",
56 56 :class => 'icon icon-save' %>
57 <% end %>
58 <% else %>
59 <% if @query.editable_by?(User.current) %>
60 <%= link_to l(:button_edit), edit_query_path(@query), :class => 'icon icon-edit' %>
61 <%= delete_link query_path(@query) %>
62 <% end %>
57 63 <% end %>
58 64 </p>
59 65 </div>
60 66 <% end %>
61 67
62 68 <%= error_messages_for 'query' %>
63 69 <% if @query.valid? %>
64 70 <% if @issues.empty? %>
65 71 <p class="nodata"><%= l(:label_no_data) %></p>
66 72 <% else %>
67 73 <%= render_query_totals(@query) %>
68 74 <%= render :partial => 'issues/list', :locals => {:issues => @issues, :query => @query} %>
69 75 <span class="pagination"><%= pagination_links_full @issue_pages, @issue_count %></span>
70 76 <% end %>
71 77
72 78 <% other_formats_links do |f| %>
73 79 <%= f.link_to 'Atom', :url => params.merge(:key => User.current.rss_key) %>
74 80 <%= f.link_to 'CSV', :url => params, :onclick => "showModal('csv-export-options', '350px'); return false;" %>
75 81 <%= f.link_to 'PDF', :url => params %>
76 82 <% end %>
77 83
78 84 <div id="csv-export-options" style="display:none;">
79 85 <h3 class="title"><%= l(:label_export_options, :export_format => 'CSV') %></h3>
80 86 <%= form_tag(_project_issues_path(@project, :format => 'csv'), :method => :get, :id => 'csv-export-form') do %>
81 87 <%= query_as_hidden_field_tags(@query) %>
82 88 <%= hidden_field_tag 'sort', @sort_criteria.to_param, :id => nil %>
83 89 <p>
84 90 <label><%= radio_button_tag 'csv[columns]', '', true %> <%= l(:description_selected_columns) %></label><br />
85 91 <label><%= radio_button_tag 'csv[columns]', 'all' %> <%= l(:description_all_columns) %></label>
86 92 </p>
87 93 <p>
88 94 <label><%= check_box_tag 'csv[description]', '1', @query.has_column?(:description) %> <%= l(:field_description) %></label>
89 95 </p>
90 96 <% if @issue_count > Setting.issues_export_limit.to_i %>
91 97 <p class="icon icon-warning">
92 98 <%= l(:setting_issues_export_limit) %>: <%= Setting.issues_export_limit.to_i %>
93 99 </p>
94 100 <% end %>
95 101 <p class="buttons">
96 102 <%= submit_tag l(:button_export), :name => nil, :onclick => "hideModal(this);" %>
97 103 <%= submit_tag l(:button_cancel), :name => nil, :onclick => "hideModal(this);", :type => 'button' %>
98 104 </p>
99 105 <% end %>
100 106 </div>
101 107
102 108 <% end %>
103 109 <%= call_hook(:view_issues_index_bottom, { :issues => @issues, :project => @project, :query => @query }) %>
104 110
105 111 <% content_for :sidebar do %>
106 112 <%= render :partial => 'issues/sidebar' %>
107 113 <% end %>
108 114
109 115 <% content_for :header_tags do %>
110 116 <%= auto_discovery_link_tag(:atom,
111 117 {:query_id => @query, :format => 'atom',
112 118 :page => nil, :key => User.current.rss_key},
113 119 :title => l(:label_issue_plural)) %>
114 120 <%= auto_discovery_link_tag(:atom,
115 121 {:controller => 'journals', :action => 'index',
116 122 :query_id => @query, :format => 'atom',
117 123 :page => nil, :key => User.current.rss_key},
118 124 :title => l(:label_changes_details)) %>
119 125 <% end %>
120 126
121 127 <%= context_menu issues_context_menu_path %>
@@ -1,278 +1,274
1 1 # Redmine - project management software
2 2 # Copyright (C) 2006-2016 Jean-Philippe Lang
3 3 #
4 4 # This program is free software; you can redistribute it and/or
5 5 # modify it under the terms of the GNU General Public License
6 6 # as published by the Free Software Foundation; either version 2
7 7 # of the License, or (at your option) any later version.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU General Public License
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 18 require 'redmine/core_ext'
19 19
20 20 begin
21 21 require 'rmagick' unless Object.const_defined?(:Magick)
22 22 rescue LoadError
23 23 # RMagick is not available
24 24 end
25 25 begin
26 26 require 'redcarpet' unless Object.const_defined?(:Redcarpet)
27 27 rescue LoadError
28 28 # Redcarpet is not available
29 29 end
30 30
31 31 require 'redmine/acts/positioned'
32 32
33 33 require 'redmine/scm/base'
34 34 require 'redmine/access_control'
35 35 require 'redmine/access_keys'
36 36 require 'redmine/activity'
37 37 require 'redmine/activity/fetcher'
38 38 require 'redmine/ciphering'
39 39 require 'redmine/codeset_util'
40 40 require 'redmine/field_format'
41 41 require 'redmine/menu_manager'
42 42 require 'redmine/notifiable'
43 43 require 'redmine/platform'
44 44 require 'redmine/mime_type'
45 45 require 'redmine/notifiable'
46 46 require 'redmine/search'
47 47 require 'redmine/syntax_highlighting'
48 48 require 'redmine/thumbnail'
49 49 require 'redmine/unified_diff'
50 50 require 'redmine/utils'
51 51 require 'redmine/version'
52 52 require 'redmine/wiki_formatting'
53 53
54 54 require 'redmine/default_data/loader'
55 55 require 'redmine/helpers/calendar'
56 56 require 'redmine/helpers/diff'
57 57 require 'redmine/helpers/gantt'
58 58 require 'redmine/helpers/time_report'
59 59 require 'redmine/views/other_formats_builder'
60 60 require 'redmine/views/labelled_form_builder'
61 61 require 'redmine/views/builders'
62 62
63 63 require 'redmine/themes'
64 64 require 'redmine/hook'
65 65 require 'redmine/hook/listener'
66 66 require 'redmine/hook/view_listener'
67 67 require 'redmine/plugin'
68 68
69 69 Redmine::Scm::Base.add "Subversion"
70 70 Redmine::Scm::Base.add "Darcs"
71 71 Redmine::Scm::Base.add "Mercurial"
72 72 Redmine::Scm::Base.add "Cvs"
73 73 Redmine::Scm::Base.add "Bazaar"
74 74 Redmine::Scm::Base.add "Git"
75 75 Redmine::Scm::Base.add "Filesystem"
76 76
77 77 # Permissions
78 78 Redmine::AccessControl.map do |map|
79 79 map.permission :view_project, {:projects => [:show], :activities => [:index]}, :public => true, :read => true
80 80 map.permission :search_project, {:search => :index}, :public => true, :read => true
81 81 map.permission :add_project, {:projects => [:new, :create]}, :require => :loggedin
82 82 map.permission :edit_project, {:projects => [:settings, :edit, :update]}, :require => :member
83 83 map.permission :close_project, {:projects => [:close, :reopen]}, :require => :member, :read => true
84 84 map.permission :select_project_modules, {:projects => :modules}, :require => :member
85 85 map.permission :view_members, {:members => [:index, :show]}, :public => true, :read => true
86 86 map.permission :manage_members, {:projects => :settings, :members => [:index, :show, :new, :create, :update, :destroy, :autocomplete]}, :require => :member
87 87 map.permission :manage_versions, {:projects => :settings, :versions => [:new, :create, :edit, :update, :close_completed, :destroy]}, :require => :member
88 88 map.permission :add_subprojects, {:projects => [:new, :create]}, :require => :member
89 89
90 90 map.project_module :issue_tracking do |map|
91 91 # Issue categories
92 92 map.permission :manage_categories, {:projects => :settings, :issue_categories => [:index, :show, :new, :create, :edit, :update, :destroy]}, :require => :member
93 93 # Issues
94 94 map.permission :view_issues, {:issues => [:index, :show],
95 95 :auto_complete => [:issues],
96 96 :context_menus => [:issues],
97 97 :versions => [:index, :show, :status_by],
98 98 :journals => [:index, :diff],
99 99 :queries => :index,
100 100 :reports => [:issue_report, :issue_report_details]},
101 101 :read => true
102 102 map.permission :add_issues, {:issues => [:new, :create], :attachments => :upload}
103 103 map.permission :edit_issues, {:issues => [:edit, :update, :bulk_edit, :bulk_update], :journals => [:new], :attachments => :upload}
104 104 map.permission :copy_issues, {:issues => [:new, :create, :bulk_edit, :bulk_update], :attachments => :upload}
105 105 map.permission :manage_issue_relations, {:issue_relations => [:index, :show, :create, :destroy]}
106 106 map.permission :manage_subtasks, {}
107 107 map.permission :set_issues_private, {}
108 108 map.permission :set_own_issues_private, {}, :require => :loggedin
109 109 map.permission :add_issue_notes, {:issues => [:edit, :update], :journals => [:new], :attachments => :upload}
110 110 map.permission :edit_issue_notes, {:journals => [:edit, :update]}, :require => :loggedin
111 111 map.permission :edit_own_issue_notes, {:journals => [:edit, :update]}, :require => :loggedin
112 112 map.permission :view_private_notes, {}, :read => true, :require => :member
113 113 map.permission :set_notes_private, {}, :require => :member
114 114 map.permission :delete_issues, {:issues => :destroy}, :require => :member
115 115 # Queries
116 116 map.permission :manage_public_queries, {:queries => [:new, :create, :edit, :update, :destroy]}, :require => :member
117 117 map.permission :save_queries, {:queries => [:new, :create, :edit, :update, :destroy]}, :require => :loggedin
118 118 # Watchers
119 119 map.permission :view_issue_watchers, {}, :read => true
120 120 map.permission :add_issue_watchers, {:watchers => [:new, :create, :append, :autocomplete_for_user]}
121 121 map.permission :delete_issue_watchers, {:watchers => :destroy}
122 122 map.permission :import_issues, {:imports => [:new, :create, :settings, :mapping, :run, :show]}
123 123 end
124 124
125 125 map.project_module :time_tracking do |map|
126 126 map.permission :log_time, {:timelog => [:new, :create]}, :require => :loggedin
127 127 map.permission :view_time_entries, {:timelog => [:index, :report, :show]}, :read => true
128 128 map.permission :edit_time_entries, {:timelog => [:edit, :update, :destroy, :bulk_edit, :bulk_update]}, :require => :member
129 129 map.permission :edit_own_time_entries, {:timelog => [:edit, :update, :destroy,:bulk_edit, :bulk_update]}, :require => :loggedin
130 130 map.permission :manage_project_activities, {:project_enumerations => [:update, :destroy]}, :require => :member
131 131 end
132 132
133 133 map.project_module :news do |map|
134 134 map.permission :manage_news, {:news => [:new, :create, :edit, :update, :destroy], :comments => [:destroy], :attachments => :upload}, :require => :member
135 135 map.permission :view_news, {:news => [:index, :show]}, :public => true, :read => true
136 136 map.permission :comment_news, {:comments => :create}
137 137 end
138 138
139 139 map.project_module :documents do |map|
140 140 map.permission :add_documents, {:documents => [:new, :create, :add_attachment], :attachments => :upload}, :require => :loggedin
141 141 map.permission :edit_documents, {:documents => [:edit, :update, :add_attachment], :attachments => :upload}, :require => :loggedin
142 142 map.permission :delete_documents, {:documents => [:destroy]}, :require => :loggedin
143 143 map.permission :view_documents, {:documents => [:index, :show, :download]}, :read => true
144 144 end
145 145
146 146 map.project_module :files do |map|
147 147 map.permission :manage_files, {:files => [:new, :create], :attachments => :upload}, :require => :loggedin
148 148 map.permission :view_files, {:files => :index, :versions => :download}, :read => true
149 149 end
150 150
151 151 map.project_module :wiki do |map|
152 152 map.permission :manage_wiki, {:wikis => [:edit, :destroy]}, :require => :member
153 153 map.permission :rename_wiki_pages, {:wiki => :rename}, :require => :member
154 154 map.permission :delete_wiki_pages, {:wiki => [:destroy, :destroy_version]}, :require => :member
155 155 map.permission :view_wiki_pages, {:wiki => [:index, :show, :special, :date_index]}, :read => true
156 156 map.permission :export_wiki_pages, {:wiki => [:export]}, :read => true
157 157 map.permission :view_wiki_edits, {:wiki => [:history, :diff, :annotate]}, :read => true
158 158 map.permission :edit_wiki_pages, :wiki => [:edit, :update, :preview, :add_attachment], :attachments => :upload
159 159 map.permission :delete_wiki_pages_attachments, {}
160 160 map.permission :protect_wiki_pages, {:wiki => :protect}, :require => :member
161 161 end
162 162
163 163 map.project_module :repository do |map|
164 164 map.permission :manage_repository, {:repositories => [:new, :create, :edit, :update, :committers, :destroy]}, :require => :member
165 165 map.permission :browse_repository, {:repositories => [:show, :browse, :entry, :raw, :annotate, :changes, :diff, :stats, :graph]}, :read => true
166 166 map.permission :view_changesets, {:repositories => [:show, :revisions, :revision]}, :read => true
167 167 map.permission :commit_access, {}
168 168 map.permission :manage_related_issues, {:repositories => [:add_related_issue, :remove_related_issue]}
169 169 end
170 170
171 171 map.project_module :boards do |map|
172 172 map.permission :manage_boards, {:boards => [:new, :create, :edit, :update, :destroy]}, :require => :member
173 173 map.permission :view_messages, {:boards => [:index, :show], :messages => [:show]}, :public => true, :read => true
174 174 map.permission :add_messages, {:messages => [:new, :reply, :quote], :attachments => :upload}
175 175 map.permission :edit_messages, {:messages => :edit, :attachments => :upload}, :require => :member
176 176 map.permission :edit_own_messages, {:messages => :edit, :attachments => :upload}, :require => :loggedin
177 177 map.permission :delete_messages, {:messages => :destroy}, :require => :member
178 178 map.permission :delete_own_messages, {:messages => :destroy}, :require => :loggedin
179 179 end
180 180
181 181 map.project_module :calendar do |map|
182 182 map.permission :view_calendar, {:calendars => [:show, :update]}, :read => true
183 183 end
184 184
185 185 map.project_module :gantt do |map|
186 186 map.permission :view_gantt, {:gantts => [:show, :update]}, :read => true
187 187 end
188 188 end
189 189
190 190 Redmine::MenuManager.map :top_menu do |menu|
191 191 menu.push :home, :home_path
192 192 menu.push :my_page, { :controller => 'my', :action => 'page' }, :if => Proc.new { User.current.logged? }
193 193 menu.push :projects, { :controller => 'projects', :action => 'index' }, :caption => :label_project_plural
194 194 menu.push :administration, { :controller => 'admin', :action => 'index' }, :if => Proc.new { User.current.admin? }, :last => true
195 195 menu.push :help, Redmine::Info.help_url, :last => true
196 196 end
197 197
198 198 Redmine::MenuManager.map :account_menu do |menu|
199 199 menu.push :login, :signin_path, :if => Proc.new { !User.current.logged? }
200 200 menu.push :register, :register_path, :if => Proc.new { !User.current.logged? && Setting.self_registration? }
201 201 menu.push :my_account, { :controller => 'my', :action => 'account' }, :if => Proc.new { User.current.logged? }
202 202 menu.push :logout, :signout_path, :html => {:method => 'post'}, :if => Proc.new { User.current.logged? }
203 203 end
204 204
205 205 Redmine::MenuManager.map :application_menu do |menu|
206 206 # Empty
207 207 end
208 208
209 209 Redmine::MenuManager.map :admin_menu do |menu|
210 210 menu.push :projects, {:controller => 'admin', :action => 'projects'}, :caption => :label_project_plural
211 211 menu.push :users, {:controller => 'users'}, :caption => :label_user_plural
212 212 menu.push :groups, {:controller => 'groups'}, :caption => :label_group_plural
213 213 menu.push :roles, {:controller => 'roles'}, :caption => :label_role_and_permissions
214 214 menu.push :trackers, {:controller => 'trackers'}, :caption => :label_tracker_plural
215 215 menu.push :issue_statuses, {:controller => 'issue_statuses'}, :caption => :label_issue_status_plural,
216 216 :html => {:class => 'issue_statuses'}
217 217 menu.push :workflows, {:controller => 'workflows', :action => 'edit'}, :caption => :label_workflow
218 218 menu.push :custom_fields, {:controller => 'custom_fields'}, :caption => :label_custom_field_plural,
219 219 :html => {:class => 'custom_fields'}
220 220 menu.push :enumerations, {:controller => 'enumerations'}
221 221 menu.push :settings, {:controller => 'settings'}
222 222 menu.push :ldap_authentication, {:controller => 'auth_sources', :action => 'index'},
223 223 :html => {:class => 'server_authentication'}
224 224 menu.push :plugins, {:controller => 'admin', :action => 'plugins'}, :last => true
225 225 menu.push :info, {:controller => 'admin', :action => 'info'}, :caption => :label_information_plural, :last => true
226 226 end
227 227
228 228 Redmine::MenuManager.map :project_menu do |menu|
229 229 menu.push :overview, { :controller => 'projects', :action => 'show' }
230 230 menu.push :activity, { :controller => 'activities', :action => 'index' }
231 231 menu.push :roadmap, { :controller => 'versions', :action => 'index' }, :param => :project_id,
232 232 :if => Proc.new { |p| p.shared_versions.any? }
233 233 menu.push :issues, { :controller => 'issues', :action => 'index' }, :param => :project_id, :caption => :label_issue_plural
234 menu.push :new_issue, { :controller => 'issues', :action => 'new', :copy_from => nil }, :param => :project_id, :caption => :label_issue_new,
235 :html => { :accesskey => Redmine::AccessKeys.key_for(:new_issue) },
236 :if => Proc.new { |p| p.trackers.any? },
237 :permission => :add_issues
238 234 menu.push :gantt, { :controller => 'gantts', :action => 'show' }, :param => :project_id, :caption => :label_gantt
239 235 menu.push :calendar, { :controller => 'calendars', :action => 'show' }, :param => :project_id, :caption => :label_calendar
240 236 menu.push :news, { :controller => 'news', :action => 'index' }, :param => :project_id, :caption => :label_news_plural
241 237 menu.push :documents, { :controller => 'documents', :action => 'index' }, :param => :project_id, :caption => :label_document_plural
242 238 menu.push :wiki, { :controller => 'wiki', :action => 'show', :id => nil }, :param => :project_id,
243 239 :if => Proc.new { |p| p.wiki && !p.wiki.new_record? }
244 240 menu.push :boards, { :controller => 'boards', :action => 'index', :id => nil }, :param => :project_id,
245 241 :if => Proc.new { |p| p.boards.any? }, :caption => :label_board_plural
246 242 menu.push :files, { :controller => 'files', :action => 'index' }, :caption => :label_file_plural, :param => :project_id
247 243 menu.push :repository, { :controller => 'repositories', :action => 'show', :repository_id => nil, :path => nil, :rev => nil },
248 244 :if => Proc.new { |p| p.repository && !p.repository.new_record? }
249 245 menu.push :settings, { :controller => 'projects', :action => 'settings' }, :last => true
250 246 end
251 247
252 248 Redmine::Activity.map do |activity|
253 249 activity.register :issues, :class_name => %w(Issue Journal)
254 250 activity.register :changesets
255 251 activity.register :news
256 252 activity.register :documents, :class_name => %w(Document Attachment)
257 253 activity.register :files, :class_name => 'Attachment'
258 254 activity.register :wiki_edits, :class_name => 'WikiContent::Version', :default => false
259 255 activity.register :messages, :default => false
260 256 activity.register :time_entries, :default => false
261 257 end
262 258
263 259 Redmine::Search.map do |search|
264 260 search.register :issues
265 261 search.register :news
266 262 search.register :documents
267 263 search.register :changesets
268 264 search.register :wiki_pages
269 265 search.register :messages
270 266 search.register :projects
271 267 end
272 268
273 269 Redmine::WikiFormatting.map do |format|
274 270 format.register :textile
275 271 format.register :markdown if Object.const_defined?(:Redcarpet)
276 272 end
277 273
278 274 ActionView::Template.register_template_handler :rsb, Redmine::Views::ApiTemplateHandler
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
@@ -1,718 +1,694
1 1 # Redmine - project management software
2 2 # Copyright (C) 2006-2016 Jean-Philippe Lang
3 3 #
4 4 # This program is free software; you can redistribute it and/or
5 5 # modify it under the terms of the GNU General Public License
6 6 # as published by the Free Software Foundation; either version 2
7 7 # of the License, or (at your option) any later version.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU General Public License
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 18 require File.expand_path('../../test_helper', __FILE__)
19 19
20 20 class ProjectsControllerTest < ActionController::TestCase
21 21 fixtures :projects, :versions, :users, :email_addresses, :roles, :members,
22 22 :member_roles, :issues, :journals, :journal_details,
23 23 :trackers, :projects_trackers, :issue_statuses,
24 24 :enabled_modules, :enumerations, :boards, :messages,
25 25 :attachments, :custom_fields, :custom_values, :time_entries,
26 26 :wikis, :wiki_pages, :wiki_contents, :wiki_content_versions
27 27
28 28 def setup
29 29 @request.session[:user_id] = nil
30 30 Setting.default_language = 'en'
31 31 end
32 32
33 33 def test_index_by_anonymous_should_not_show_private_projects
34 34 get :index
35 35 assert_response :success
36 36 assert_template 'index'
37 37 projects = assigns(:projects)
38 38 assert_not_nil projects
39 39 assert projects.all?(&:is_public?)
40 40
41 41 assert_select 'ul' do
42 42 assert_select 'li' do
43 43 assert_select 'a', :text => 'eCookbook'
44 44 assert_select 'ul' do
45 45 assert_select 'a', :text => 'Child of private child'
46 46 end
47 47 end
48 48 end
49 49 assert_select 'a', :text => /Private child of eCookbook/, :count => 0
50 50 end
51 51
52 52 def test_index_atom
53 53 get :index, :format => 'atom'
54 54 assert_response :success
55 55 assert_template 'common/feed'
56 56 assert_select 'feed>title', :text => 'Redmine: Latest projects'
57 57 assert_select 'feed>entry', :count => Project.visible(User.current).count
58 58 end
59 59
60 60 test "#index by non-admin user with view_time_entries permission should show overall spent time link" do
61 61 @request.session[:user_id] = 3
62 62 get :index
63 63 assert_template 'index'
64 64 assert_select 'a[href=?]', '/time_entries'
65 65 end
66 66
67 67 test "#index by non-admin user without view_time_entries permission should not show overall spent time link" do
68 68 Role.find(2).remove_permission! :view_time_entries
69 69 Role.non_member.remove_permission! :view_time_entries
70 70 Role.anonymous.remove_permission! :view_time_entries
71 71 @request.session[:user_id] = 3
72 72
73 73 get :index
74 74 assert_template 'index'
75 75 assert_select 'a[href=?]', '/time_entries', 0
76 76 end
77 77
78 78 test "#index by non-admin user with permission should show add project link" do
79 79 Role.find(1).add_permission! :add_project
80 80 @request.session[:user_id] = 2
81 81 get :index
82 82 assert_template 'index'
83 83 assert_select 'a[href=?]', '/projects/new'
84 84 end
85 85
86 86 test "#new by admin user should accept get" do
87 87 @request.session[:user_id] = 1
88 88
89 89 get :new
90 90 assert_response :success
91 91 assert_template 'new'
92 92 end
93 93
94 94 test "#new by non-admin user with add_project permission should accept get" do
95 95 Role.non_member.add_permission! :add_project
96 96 @request.session[:user_id] = 9
97 97
98 98 get :new
99 99 assert_response :success
100 100 assert_template 'new'
101 101 assert_select 'select[name=?]', 'project[parent_id]', 0
102 102 end
103 103
104 104 test "#new by non-admin user with add_subprojects permission should accept get" do
105 105 Role.find(1).remove_permission! :add_project
106 106 Role.find(1).add_permission! :add_subprojects
107 107 @request.session[:user_id] = 2
108 108
109 109 get :new, :parent_id => 'ecookbook'
110 110 assert_response :success
111 111 assert_template 'new'
112 112
113 113 assert_select 'select[name=?]', 'project[parent_id]' do
114 114 # parent project selected
115 115 assert_select 'option[value="1"][selected=selected]'
116 116 # no empty value
117 117 assert_select 'option[value=""]', 0
118 118 end
119 119 end
120 120
121 121 def test_new_should_not_display_invalid_search_link
122 122 @request.session[:user_id] = 1
123 123
124 124 get :new
125 125 assert_response :success
126 126 assert_select '#quick-search form[action=?]', '/search'
127 127 assert_select '#quick-search a[href=?]', '/search'
128 128 end
129 129
130 130 test "#create by admin user should create a new project" do
131 131 @request.session[:user_id] = 1
132 132
133 133 post :create,
134 134 :project => {
135 135 :name => "blog",
136 136 :description => "weblog",
137 137 :homepage => 'http://weblog',
138 138 :identifier => "blog",
139 139 :is_public => 1,
140 140 :custom_field_values => { '3' => 'Beta' },
141 141 :tracker_ids => ['1', '3'],
142 142 # an issue custom field that is not for all project
143 143 :issue_custom_field_ids => ['9'],
144 144 :enabled_module_names => ['issue_tracking', 'news', 'repository']
145 145 }
146 146 assert_redirected_to '/projects/blog/settings'
147 147
148 148 project = Project.find_by_name('blog')
149 149 assert_kind_of Project, project
150 150 assert project.active?
151 151 assert_equal 'weblog', project.description
152 152 assert_equal 'http://weblog', project.homepage
153 153 assert_equal true, project.is_public?
154 154 assert_nil project.parent
155 155 assert_equal 'Beta', project.custom_value_for(3).value
156 156 assert_equal [1, 3], project.trackers.map(&:id).sort
157 157 assert_equal ['issue_tracking', 'news', 'repository'], project.enabled_module_names.sort
158 158 assert project.issue_custom_fields.include?(IssueCustomField.find(9))
159 159 end
160 160
161 161 test "#create by admin user should create a new subproject" do
162 162 @request.session[:user_id] = 1
163 163
164 164 assert_difference 'Project.count' do
165 165 post :create, :project => { :name => "blog",
166 166 :description => "weblog",
167 167 :identifier => "blog",
168 168 :is_public => 1,
169 169 :custom_field_values => { '3' => 'Beta' },
170 170 :parent_id => 1
171 171 }
172 172 assert_redirected_to '/projects/blog/settings'
173 173 end
174 174
175 175 project = Project.find_by_name('blog')
176 176 assert_kind_of Project, project
177 177 assert_equal Project.find(1), project.parent
178 178 end
179 179
180 180 test "#create by admin user should continue" do
181 181 @request.session[:user_id] = 1
182 182
183 183 assert_difference 'Project.count' do
184 184 post :create, :project => {:name => "blog", :identifier => "blog"}, :continue => 'Create and continue'
185 185 end
186 186 assert_redirected_to '/projects/new'
187 187 end
188 188
189 189 test "#create by non-admin user with add_project permission should create a new project" do
190 190 Role.non_member.add_permission! :add_project
191 191 @request.session[:user_id] = 9
192 192
193 193 post :create, :project => { :name => "blog",
194 194 :description => "weblog",
195 195 :identifier => "blog",
196 196 :is_public => 1,
197 197 :custom_field_values => { '3' => 'Beta' },
198 198 :tracker_ids => ['1', '3'],
199 199 :enabled_module_names => ['issue_tracking', 'news', 'repository']
200 200 }
201 201
202 202 assert_redirected_to '/projects/blog/settings'
203 203
204 204 project = Project.find_by_name('blog')
205 205 assert_kind_of Project, project
206 206 assert_equal 'weblog', project.description
207 207 assert_equal true, project.is_public?
208 208 assert_equal [1, 3], project.trackers.map(&:id).sort
209 209 assert_equal ['issue_tracking', 'news', 'repository'], project.enabled_module_names.sort
210 210
211 211 # User should be added as a project member
212 212 assert User.find(9).member_of?(project)
213 213 assert_equal 1, project.members.size
214 214 end
215 215
216 216 test "#create by non-admin user with add_project permission should fail with parent_id" do
217 217 Role.non_member.add_permission! :add_project
218 218 @request.session[:user_id] = 9
219 219
220 220 assert_no_difference 'Project.count' do
221 221 post :create, :project => { :name => "blog",
222 222 :description => "weblog",
223 223 :identifier => "blog",
224 224 :is_public => 1,
225 225 :custom_field_values => { '3' => 'Beta' },
226 226 :parent_id => 1
227 227 }
228 228 end
229 229 assert_response :success
230 230 project = assigns(:project)
231 231 assert_kind_of Project, project
232 232 assert_not_equal [], project.errors[:parent_id]
233 233 end
234 234
235 235 test "#create by non-admin user with add_subprojects permission should create a project with a parent_id" do
236 236 Role.find(1).remove_permission! :add_project
237 237 Role.find(1).add_permission! :add_subprojects
238 238 @request.session[:user_id] = 2
239 239
240 240 post :create, :project => { :name => "blog",
241 241 :description => "weblog",
242 242 :identifier => "blog",
243 243 :is_public => 1,
244 244 :custom_field_values => { '3' => 'Beta' },
245 245 :parent_id => 1
246 246 }
247 247 assert_redirected_to '/projects/blog/settings'
248 248 project = Project.find_by_name('blog')
249 249 end
250 250
251 251 test "#create by non-admin user with add_subprojects permission should fail without parent_id" do
252 252 Role.find(1).remove_permission! :add_project
253 253 Role.find(1).add_permission! :add_subprojects
254 254 @request.session[:user_id] = 2
255 255
256 256 assert_no_difference 'Project.count' do
257 257 post :create, :project => { :name => "blog",
258 258 :description => "weblog",
259 259 :identifier => "blog",
260 260 :is_public => 1,
261 261 :custom_field_values => { '3' => 'Beta' }
262 262 }
263 263 end
264 264 assert_response :success
265 265 project = assigns(:project)
266 266 assert_kind_of Project, project
267 267 assert_not_equal [], project.errors[:parent_id]
268 268 end
269 269
270 270 test "#create by non-admin user with add_subprojects permission should fail with unauthorized parent_id" do
271 271 Role.find(1).remove_permission! :add_project
272 272 Role.find(1).add_permission! :add_subprojects
273 273 @request.session[:user_id] = 2
274 274
275 275 assert !User.find(2).member_of?(Project.find(6))
276 276 assert_no_difference 'Project.count' do
277 277 post :create, :project => { :name => "blog",
278 278 :description => "weblog",
279 279 :identifier => "blog",
280 280 :is_public => 1,
281 281 :custom_field_values => { '3' => 'Beta' },
282 282 :parent_id => 6
283 283 }
284 284 end
285 285 assert_response :success
286 286 project = assigns(:project)
287 287 assert_kind_of Project, project
288 288 assert_not_equal [], project.errors[:parent_id]
289 289 end
290 290
291 291 def test_create_subproject_with_inherit_members_should_inherit_members
292 292 Role.find_by_name('Manager').add_permission! :add_subprojects
293 293 parent = Project.find(1)
294 294 @request.session[:user_id] = 2
295 295
296 296 assert_difference 'Project.count' do
297 297 post :create, :project => {
298 298 :name => 'inherited', :identifier => 'inherited', :parent_id => parent.id, :inherit_members => '1'
299 299 }
300 300 assert_response 302
301 301 end
302 302
303 303 project = Project.order('id desc').first
304 304 assert_equal 'inherited', project.name
305 305 assert_equal parent, project.parent
306 306 assert project.memberships.count > 0
307 307 assert_equal parent.memberships.count, project.memberships.count
308 308 end
309 309
310 310 def test_create_should_preserve_modules_on_validation_failure
311 311 with_settings :default_projects_modules => ['issue_tracking', 'repository'] do
312 312 @request.session[:user_id] = 1
313 313 assert_no_difference 'Project.count' do
314 314 post :create, :project => {
315 315 :name => "blog",
316 316 :identifier => "",
317 317 :enabled_module_names => %w(issue_tracking news)
318 318 }
319 319 end
320 320 assert_response :success
321 321 project = assigns(:project)
322 322 assert_equal %w(issue_tracking news), project.enabled_module_names.sort
323 323 end
324 324 end
325 325
326 326 def test_show_by_id
327 327 get :show, :id => 1
328 328 assert_response :success
329 329 assert_template 'show'
330 330 assert_not_nil assigns(:project)
331 331 end
332 332
333 333 def test_show_by_identifier
334 334 get :show, :id => 'ecookbook'
335 335 assert_response :success
336 336 assert_template 'show'
337 337 assert_not_nil assigns(:project)
338 338 assert_equal Project.find_by_identifier('ecookbook'), assigns(:project)
339 339
340 340 assert_select 'li', :text => /Development status/
341 341 end
342 342
343 343 def test_show_should_not_display_empty_sidebar
344 344 p = Project.find(1)
345 345 p.enabled_module_names = []
346 346 p.save!
347 347
348 348 get :show, :id => 'ecookbook'
349 349 assert_response :success
350 350 assert_select '#main.nosidebar'
351 351 end
352 352
353 353 def test_show_should_not_display_hidden_custom_fields
354 354 ProjectCustomField.find_by_name('Development status').update_attribute :visible, false
355 355 get :show, :id => 'ecookbook'
356 356 assert_response :success
357 357 assert_template 'show'
358 358 assert_not_nil assigns(:project)
359 359
360 360 assert_select 'li', :text => /Development status/, :count => 0
361 361 end
362 362
363 363 def test_show_should_not_display_blank_custom_fields_with_multiple_values
364 364 f1 = ProjectCustomField.generate! :field_format => 'list', :possible_values => %w(Foo Bar), :multiple => true
365 365 f2 = ProjectCustomField.generate! :field_format => 'list', :possible_values => %w(Baz Qux), :multiple => true
366 366 project = Project.generate!(:custom_field_values => {f2.id.to_s => %w(Qux)})
367 367
368 368 get :show, :id => project.id
369 369 assert_response :success
370 370
371 371 assert_select 'li', :text => /#{f1.name}/, :count => 0
372 372 assert_select 'li', :text => /#{f2.name}/
373 373 end
374 374
375 375 def test_show_should_not_display_blank_text_custom_fields
376 376 f1 = ProjectCustomField.generate! :field_format => 'text'
377 377
378 378 get :show, :id => 1
379 379 assert_response :success
380 380
381 381 assert_select 'li', :text => /#{f1.name}/, :count => 0
382 382 end
383 383
384 384 def test_show_should_not_fail_when_custom_values_are_nil
385 385 project = Project.find_by_identifier('ecookbook')
386 386 project.custom_values.first.update_attribute(:value, nil)
387 387 get :show, :id => 'ecookbook'
388 388 assert_response :success
389 389 assert_template 'show'
390 390 assert_not_nil assigns(:project)
391 391 assert_equal Project.find_by_identifier('ecookbook'), assigns(:project)
392 392 end
393 393
394 394 def show_archived_project_should_be_denied
395 395 project = Project.find_by_identifier('ecookbook')
396 396 project.archive!
397 397
398 398 get :show, :id => 'ecookbook'
399 399 assert_response 403
400 400 assert_nil assigns(:project)
401 401 assert_select 'p', :text => /archived/
402 402 end
403 403
404 404 def test_show_should_not_show_private_subprojects_that_are_not_visible
405 405 get :show, :id => 'ecookbook'
406 406 assert_response :success
407 407 assert_template 'show'
408 408 assert_select 'a', :text => /Private child/, :count => 0
409 409 end
410 410
411 411 def test_show_should_show_private_subprojects_that_are_visible
412 412 @request.session[:user_id] = 2 # manager who is a member of the private subproject
413 413 get :show, :id => 'ecookbook'
414 414 assert_response :success
415 415 assert_template 'show'
416 416 assert_select 'a', :text => /Private child/
417 417 end
418 418
419 419 def test_settings
420 420 @request.session[:user_id] = 2 # manager
421 421 get :settings, :id => 1
422 422 assert_response :success
423 423 assert_template 'settings'
424 424 end
425 425
426 426 def test_settings_of_subproject
427 427 @request.session[:user_id] = 2
428 428 get :settings, :id => 'private-child'
429 429 assert_response :success
430 430 assert_template 'settings'
431 431
432 432 assert_select 'input[type=checkbox][name=?]', 'project[inherit_members]'
433 433 end
434 434
435 435 def test_settings_should_be_denied_for_member_on_closed_project
436 436 Project.find(1).close
437 437 @request.session[:user_id] = 2 # manager
438 438
439 439 get :settings, :id => 1
440 440 assert_response 403
441 441 end
442 442
443 443 def test_settings_should_be_denied_for_anonymous_on_closed_project
444 444 Project.find(1).close
445 445
446 446 get :settings, :id => 1
447 447 assert_response 302
448 448 end
449 449
450 450 def test_setting_with_wiki_module_and_no_wiki
451 451 Project.find(1).wiki.destroy
452 452 Role.find(1).add_permission! :manage_wiki
453 453 @request.session[:user_id] = 2
454 454
455 455 get :settings, :id => 1
456 456 assert_response :success
457 457 assert_template 'settings'
458 458
459 459 assert_select 'form[action=?]', '/projects/ecookbook/wiki' do
460 460 assert_select 'input[name=?]', 'wiki[start_page]'
461 461 end
462 462 end
463 463
464 464 def test_update
465 465 @request.session[:user_id] = 2 # manager
466 466 post :update, :id => 1, :project => {:name => 'Test changed name',
467 467 :issue_custom_field_ids => ['']}
468 468 assert_redirected_to '/projects/ecookbook/settings'
469 469 project = Project.find(1)
470 470 assert_equal 'Test changed name', project.name
471 471 end
472 472
473 473 def test_update_with_failure
474 474 @request.session[:user_id] = 2 # manager
475 475 post :update, :id => 1, :project => {:name => ''}
476 476 assert_response :success
477 477 assert_template 'settings'
478 478 assert_select_error /name cannot be blank/i
479 479 end
480 480
481 481 def test_update_should_be_denied_for_member_on_closed_project
482 482 Project.find(1).close
483 483 @request.session[:user_id] = 2 # manager
484 484
485 485 post :update, :id => 1, :project => {:name => 'Closed'}
486 486 assert_response 403
487 487 assert_equal 'eCookbook', Project.find(1).name
488 488 end
489 489
490 490 def test_update_should_be_denied_for_anonymous_on_closed_project
491 491 Project.find(1).close
492 492
493 493 post :update, :id => 1, :project => {:name => 'Closed'}
494 494 assert_response 302
495 495 assert_equal 'eCookbook', Project.find(1).name
496 496 end
497 497
498 498 def test_update_child_project_without_parent_permission_should_not_show_validation_error
499 499 child = Project.generate_with_parent!
500 500 user = User.generate!
501 501 User.add_to_project(user, child, Role.generate!(:permissions => [:edit_project]))
502 502 @request.session[:user_id] = user.id
503 503
504 504 post :update, :id => child.id, :project => {:name => 'Updated'}
505 505 assert_response 302
506 506 assert_match /Successful update/, flash[:notice]
507 507 end
508 508
509 509 def test_modules
510 510 @request.session[:user_id] = 2
511 511 Project.find(1).enabled_module_names = ['issue_tracking', 'news']
512 512
513 513 post :modules, :id => 1, :enabled_module_names => ['issue_tracking', 'repository', 'documents']
514 514 assert_redirected_to '/projects/ecookbook/settings/modules'
515 515 assert_equal ['documents', 'issue_tracking', 'repository'], Project.find(1).enabled_module_names.sort
516 516 end
517 517
518 518 def test_destroy_leaf_project_without_confirmation_should_show_confirmation
519 519 @request.session[:user_id] = 1 # admin
520 520
521 521 assert_no_difference 'Project.count' do
522 522 delete :destroy, :id => 2
523 523 assert_response :success
524 524 assert_template 'destroy'
525 525 end
526 526 end
527 527
528 528 def test_destroy_without_confirmation_should_show_confirmation_with_subprojects
529 529 @request.session[:user_id] = 1 # admin
530 530
531 531 assert_no_difference 'Project.count' do
532 532 delete :destroy, :id => 1
533 533 assert_response :success
534 534 assert_template 'destroy'
535 535 end
536 536 assert_select 'strong',
537 537 :text => ['Private child of eCookbook',
538 538 'Child of private child, eCookbook Subproject 1',
539 539 'eCookbook Subproject 2'].join(', ')
540 540 end
541 541
542 542 def test_destroy_with_confirmation_should_destroy_the_project_and_subprojects
543 543 @request.session[:user_id] = 1 # admin
544 544
545 545 assert_difference 'Project.count', -5 do
546 546 delete :destroy, :id => 1, :confirm => 1
547 547 assert_redirected_to '/admin/projects'
548 548 end
549 549 assert_nil Project.find_by_id(1)
550 550 end
551 551
552 552 def test_archive
553 553 @request.session[:user_id] = 1 # admin
554 554 post :archive, :id => 1
555 555 assert_redirected_to '/admin/projects'
556 556 assert !Project.find(1).active?
557 557 end
558 558
559 559 def test_archive_with_failure
560 560 @request.session[:user_id] = 1
561 561 Project.any_instance.stubs(:archive).returns(false)
562 562 post :archive, :id => 1
563 563 assert_redirected_to '/admin/projects'
564 564 assert_match /project cannot be archived/i, flash[:error]
565 565 end
566 566
567 567 def test_unarchive
568 568 @request.session[:user_id] = 1 # admin
569 569 Project.find(1).archive
570 570 post :unarchive, :id => 1
571 571 assert_redirected_to '/admin/projects'
572 572 assert Project.find(1).active?
573 573 end
574 574
575 575 def test_close
576 576 @request.session[:user_id] = 2
577 577 post :close, :id => 1
578 578 assert_redirected_to '/projects/ecookbook'
579 579 assert_equal Project::STATUS_CLOSED, Project.find(1).status
580 580 end
581 581
582 582 def test_reopen
583 583 Project.find(1).close
584 584 @request.session[:user_id] = 2
585 585 post :reopen, :id => 1
586 586 assert_redirected_to '/projects/ecookbook'
587 587 assert Project.find(1).active?
588 588 end
589 589
590 590 def test_project_breadcrumbs_should_be_limited_to_3_ancestors
591 591 CustomField.delete_all
592 592 parent = nil
593 593 6.times do |i|
594 594 p = Project.generate_with_parent!(parent)
595 595 get :show, :id => p
596 596 assert_select '#header h1' do
597 597 assert_select 'a', :count => [i, 3].min
598 598 end
599 599
600 600 parent = p
601 601 end
602 602 end
603 603
604 604 def test_get_copy
605 605 @request.session[:user_id] = 1 # admin
606 606 get :copy, :id => 1
607 607 assert_response :success
608 608 assert_template 'copy'
609 609 assert assigns(:project)
610 610 assert_equal Project.find(1).description, assigns(:project).description
611 611 assert_nil assigns(:project).id
612 612
613 613 assert_select 'input[name=?][value=?]', 'project[enabled_module_names][]', 'issue_tracking', 1
614 614 end
615 615
616 616 def test_get_copy_with_invalid_source_should_respond_with_404
617 617 @request.session[:user_id] = 1
618 618 get :copy, :id => 99
619 619 assert_response 404
620 620 end
621 621
622 622 def test_get_copy_should_preselect_custom_fields
623 623 field1 = IssueCustomField.generate!(:is_for_all => false)
624 624 field2 = IssueCustomField.generate!(:is_for_all => false)
625 625 source = Project.generate!(:issue_custom_fields => [field1])
626 626 @request.session[:user_id] = 1
627 627
628 628 get :copy, :id => source.id
629 629 assert_response :success
630 630 assert_select 'fieldset#project_issue_custom_fields' do
631 631 assert_select 'input[type=checkbox][value=?][checked=checked]', field1.id.to_s
632 632 assert_select 'input[type=checkbox][value=?]:not([checked])', field2.id.to_s
633 633 end
634 634 end
635 635
636 636 def test_post_copy_should_copy_requested_items
637 637 @request.session[:user_id] = 1 # admin
638 638 CustomField.delete_all
639 639
640 640 assert_difference 'Project.count' do
641 641 post :copy, :id => 1,
642 642 :project => {
643 643 :name => 'Copy',
644 644 :identifier => 'unique-copy',
645 645 :tracker_ids => ['1', '2', '3', ''],
646 646 :enabled_module_names => %w(issue_tracking time_tracking)
647 647 },
648 648 :only => %w(issues versions)
649 649 end
650 650 project = Project.find('unique-copy')
651 651 source = Project.find(1)
652 652 assert_equal %w(issue_tracking time_tracking), project.enabled_module_names.sort
653 653
654 654 assert_equal source.versions.count, project.versions.count, "All versions were not copied"
655 655 assert_equal source.issues.count, project.issues.count, "All issues were not copied"
656 656 assert_equal 0, project.members.count
657 657 end
658 658
659 659 def test_post_copy_should_redirect_to_settings_when_successful
660 660 @request.session[:user_id] = 1 # admin
661 661 post :copy, :id => 1, :project => {:name => 'Copy', :identifier => 'unique-copy'}
662 662 assert_response :redirect
663 663 assert_redirected_to :controller => 'projects', :action => 'settings', :id => 'unique-copy'
664 664 end
665 665
666 666 def test_post_copy_with_failure
667 667 @request.session[:user_id] = 1
668 668 post :copy, :id => 1, :project => {:name => 'Copy', :identifier => ''}
669 669 assert_response :success
670 670 assert_template 'copy'
671 671 end
672 672
673 673 def test_jump_should_redirect_to_active_tab
674 674 get :show, :id => 1, :jump => 'issues'
675 675 assert_redirected_to '/projects/ecookbook/issues'
676 676 end
677 677
678 678 def test_jump_should_not_redirect_to_inactive_tab
679 679 get :show, :id => 3, :jump => 'documents'
680 680 assert_response :success
681 681 assert_template 'show'
682 682 end
683 683
684 684 def test_jump_should_not_redirect_to_unknown_tab
685 685 get :show, :id => 3, :jump => 'foobar'
686 686 assert_response :success
687 687 assert_template 'show'
688 688 end
689 689
690 690 def test_body_should_have_project_css_class
691 691 get :show, :id => 1
692 692 assert_select 'body.project-ecookbook'
693 693 end
694
695 def test_project_menu_should_include_new_issue_link
696 @request.session[:user_id] = 2
697 get :show, :id => 1
698 assert_select '#main-menu a.new-issue[href="/projects/ecookbook/issues/new"]', :text => 'New issue'
699 end
700
701 def test_project_menu_should_not_include_new_issue_link_for_project_without_trackers
702 Project.find(1).trackers.clear
703
704 @request.session[:user_id] = 2
705 get :show, :id => 1
706 assert_select '#main-menu a.new-issue', 0
707 end
708
709 def test_project_menu_should_not_include_new_issue_link_for_users_with_copy_issues_permission_only
710 role = Role.find(1)
711 role.remove_permission! :add_issues
712 role.add_permission! :copy_issues
713
714 @request.session[:user_id] = 2
715 get :show, :id => 1
716 assert_select '#main-menu a.new-issue', 0
717 end
718 694 end
@@ -1,86 +1,80
1 1 # Redmine - project management software
2 2 # Copyright (C) 2006-2016 Jean-Philippe Lang
3 3 #
4 4 # This program is free software; you can redistribute it and/or
5 5 # modify it under the terms of the GNU General Public License
6 6 # as published by the Free Software Foundation; either version 2
7 7 # of the License, or (at your option) any later version.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU General Public License
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 18 require File.expand_path('../../../test_helper', __FILE__)
19 19
20 20 module RedmineMenuTestHelper
21 21 # Assertions
22 22 def assert_number_of_items_in_menu(menu_name, count)
23 23 assert Redmine::MenuManager.items(menu_name).size >= count, "Menu has less than #{count} items"
24 24 end
25 25
26 26 def assert_menu_contains_item_named(menu_name, item_name)
27 27 assert Redmine::MenuManager.items(menu_name).collect(&:name).include?(item_name.to_sym), "Menu did not have an item named #{item_name}"
28 28 end
29 29
30 30 # Helpers
31 31 def get_menu_item(menu_name, item_name)
32 32 Redmine::MenuManager.items(menu_name).find {|item| item.name == item_name.to_sym}
33 33 end
34 34 end
35 35
36 36 class RedmineTest < ActiveSupport::TestCase
37 37 include RedmineMenuTestHelper
38 38
39 39 def test_top_menu
40 40 assert_number_of_items_in_menu :top_menu, 5
41 41 assert_menu_contains_item_named :top_menu, :home
42 42 assert_menu_contains_item_named :top_menu, :my_page
43 43 assert_menu_contains_item_named :top_menu, :projects
44 44 assert_menu_contains_item_named :top_menu, :administration
45 45 assert_menu_contains_item_named :top_menu, :help
46 46 end
47 47
48 48 def test_account_menu
49 49 assert_number_of_items_in_menu :account_menu, 4
50 50 assert_menu_contains_item_named :account_menu, :login
51 51 assert_menu_contains_item_named :account_menu, :register
52 52 assert_menu_contains_item_named :account_menu, :my_account
53 53 assert_menu_contains_item_named :account_menu, :logout
54 54 end
55 55
56 56 def test_application_menu
57 57 assert_number_of_items_in_menu :application_menu, 0
58 58 end
59 59
60 60 def test_admin_menu
61 61 assert_number_of_items_in_menu :admin_menu, 0
62 62 end
63 63
64 64 def test_project_menu
65 assert_number_of_items_in_menu :project_menu, 14
65 assert_number_of_items_in_menu :project_menu, 13
66 66 assert_menu_contains_item_named :project_menu, :overview
67 67 assert_menu_contains_item_named :project_menu, :activity
68 68 assert_menu_contains_item_named :project_menu, :roadmap
69 69 assert_menu_contains_item_named :project_menu, :issues
70 assert_menu_contains_item_named :project_menu, :new_issue
71 70 assert_menu_contains_item_named :project_menu, :calendar
72 71 assert_menu_contains_item_named :project_menu, :gantt
73 72 assert_menu_contains_item_named :project_menu, :news
74 73 assert_menu_contains_item_named :project_menu, :documents
75 74 assert_menu_contains_item_named :project_menu, :wiki
76 75 assert_menu_contains_item_named :project_menu, :boards
77 76 assert_menu_contains_item_named :project_menu, :files
78 77 assert_menu_contains_item_named :project_menu, :repository
79 78 assert_menu_contains_item_named :project_menu, :settings
80 79 end
81
82 def test_new_issue_should_have_root_as_a_parent
83 new_issue = get_menu_item(:project_menu, :new_issue)
84 assert_equal :root, new_issue.parent.name
85 end
86 80 end
General Comments 0
You need to be logged in to leave comments. Login now