Feature #3058 » 0005-load-changesets-and-time-entries-tabs-async.patch
| app/controllers/issues_controller.rb | ||
|---|---|---|
| 18 | 18 | class IssuesController < ApplicationController | 
| 19 | 19 | default_search_scope :issues | 
| 20 | 20 | |
| 21 | before_action :find_issue, :only => [:show, :edit, :update] | |
| 21 |   before_action :find_issue, :only => [:show, :edit, :update, :issue_tab] | |
| 22 | 22 | before_action :find_issues, :only => [:bulk_edit, :bulk_update, :destroy] | 
| 23 | 23 | before_action :authorize, :except => [:index, :new, :create] | 
| 24 | 24 | before_action :find_optional_project, :only => [:index, :new, :create] | 
| ... | ... | |
| 84 | 84 | |
| 85 | 85 | def show | 
| 86 | 86 | @journals = @issue.visible_journals_with_index | 
| 87 |     @changesets = @issue.changesets.visible.preload(:repository, :user).to_a | |
| 87 |     @has_changesets = @issue.changesets.visible.preload(:repository, :user).exists? | |
| 88 | 88 |     @relations = @issue.relations.select {|r| r.other_issue(@issue) && r.other_issue(@issue).visible? } | 
| 89 | 89 | |
| 90 | if User.current.wants_comments_in_reverse_order? | |
| 91 | @journals.reverse! | |
| 92 | @changesets.reverse! | |
| 93 | end | |
| 90 | @journals.reverse! if User.current.wants_comments_in_reverse_order? | |
| 94 | 91 | |
| 95 | 92 | if User.current.allowed_to?(:view_time_entries, @project) | 
| 96 | 93 | Issue.load_visible_spent_hours([@issue]) | 
| ... | ... | |
| 107 | 104 | retrieve_previous_and_next_issue_ids | 
| 108 | 105 | render :template => 'issues/show' | 
| 109 | 106 | } | 
| 110 | format.api | |
| 107 |       format.api { | |
| 108 | @changesets = @issue.changesets.visible.preload(:repository, :user).to_a | |
| 109 | @changesets.reverse! if User.current.wants_comments_in_reverse_order? | |
| 110 | } | |
| 111 | 111 |       format.atom { render :template => 'journals/index', :layout => false, :content_type => 'application/atom+xml' } | 
| 112 | 112 |       format.pdf  { | 
| 113 | 113 |         send_file_headers! :type => 'application/pdf', :filename => "#{@project.identifier}-#{@issue.id}.pdf" | 
| ... | ... | |
| 192 | 192 | end | 
| 193 | 193 | end | 
| 194 | 194 | |
| 195 | def issue_tab | |
| 196 | return render_error :status => 422 unless request.xhr? | |
| 197 | tab = params[:name] | |
| 198 | ||
| 199 | case tab | |
| 200 | when 'time_entries' | |
| 201 | @time_entries = @issue.time_entries.visible.preload(:activity, :user).to_a | |
| 202 |       render :partial => 'issues/tabs/time_entries', :locals => {:time_entries => @time_entries} | |
| 203 | when 'changesets' | |
| 204 | @changesets = @issue.changesets.visible.preload(:repository, :user).to_a | |
| 205 | changesets.reverse! if User.current.wants_comments_in_reverse_order? | |
| 206 |       render :partial => 'issues/tabs/changesets', :locals => {:changesets => @changesets} | |
| 207 | end | |
| 208 | end | |
| 209 | ||
| 195 | 210 | # Bulk edit/copy a set of issues | 
| 196 | 211 | def bulk_edit | 
| 197 | 212 | @issues.sort! | 
| app/helpers/issues_helper.rb | ||
|---|---|---|
| 554 | 554 |       tabs << {:name => 'notes', :label => :label_issue_history_notes, :onclick => 'showIssueHistory("notes", this.href)'} if journals_with_notes.any? | 
| 555 | 555 |       tabs << {:name => 'properties', :label => :label_issue_history_properties, :onclick => 'showIssueHistory("properties", this.href)'} if journals_without_notes.any? | 
| 556 | 556 | end | 
| 557 |     tabs << {:name => 'time_entries', :label => :label_time_entry_plural, :partial => 'issues/tabs/time_entries', :locals => {:time_entries => @time_entries}} if User.current.allowed_to?(:view_time_entries, @project) && @issue.spent_hours > 0 | |
| 558 |     tabs << {:name => 'changesets', :label => :label_associated_revisions, :partial => 'issues/tabs/changesets', :locals => {:changesets => @changesets}} if @changesets.present? | |
| 557 |     tabs << {:name => 'time_entries', :label => :label_time_entry_plural, :remote => true, :onclick => "getRemoteTab('time_entries', '#{tab_issue_path(@issue, :name => 'time_entries')}', '#{issue_path(@issue, :tab => 'time_entries')}')"} if User.current.allowed_to?(:view_time_entries, @project) && @issue.spent_hours > 0 | |
| 558 |     tabs << {:name => 'changesets', :label => :label_associated_revisions, :remote => true, :onclick => "getRemoteTab('changesets', '#{tab_issue_path(@issue, :name => 'changesets')}', '#{issue_path(@issue, :tab => 'changesets')}')"} if @has_changesets | |
| 559 | 559 | tabs | 
| 560 | 560 | end | 
| 561 | ||
| 561 | 562 | end | 
| app/views/common/_tabs.html.erb | ||
|---|---|---|
| 18 | 18 | </div> | 
| 19 | 19 | |
| 20 | 20 | <% tabs.each do |tab| -%> | 
| 21 |   <%= content_tag('div', render(:partial => tab[:partial], :locals => {:tab => tab} ), | |
| 21 |   <%= content_tag('div', (render(:partial => tab[:partial], :locals => {:tab => tab}) if tab[:partial]) , | |
| 22 | 22 |                        :id => "tab-content-#{tab[:name]}", | 
| 23 | 23 | :style => (tab[:name] != selected_tab ? 'display:none' : nil), | 
| 24 | :class => 'tab-content') if tab[:partial] %> | |
| 24 |                        :class => 'tab-content') if tab[:partial] || tab[:remote] %> | |
| 25 | 25 | <% end -%> | 
| 26 | 26 | |
| 27 | 27 | <%= javascript_tag default_action if default_action %> | 
| app/views/issues/_changesets.html.erb | ||
|---|---|---|
| 1 | <% changesets.each do |changeset| %> | |
| 2 | <div class="changeset"> | |
| 3 | <p><%= link_to_revision(changeset, changeset.repository, | |
| 4 |                             :text => "#{l(:label_revision)} #{changeset.format_identifier}") %> | |
| 5 | <% if changeset.filechanges.any? && User.current.allowed_to?(:browse_repository, changeset.project) %> | |
| 6 | (<%= link_to(l(:label_diff), | |
| 7 | :controller => 'repositories', | |
| 8 | :action => 'diff', | |
| 9 | :id => changeset.project, | |
| 10 | :repository_id => changeset.repository.identifier_param, | |
| 11 | :path => "", | |
| 12 | :rev => changeset.identifier) %>) | |
| 13 | <% end %> | |
| 14 | <br /> | |
| 15 | <span class="author"><%= authoring(changeset.committed_on, changeset.author) %></span></p> | |
| 16 | <div class="wiki changeset-comments"> | |
| 17 | <%= format_changeset_comments changeset %> | |
| 18 | </div> | |
| 19 | </div> | |
| 20 | <% end %> | |
| app/views/issues/tabs/_changesets.html.erb | ||
|---|---|---|
| 1 | <% tab[:locals][:changesets].each do |changeset| %> | |
| 1 | <% @changesets.each do |changeset| %> | |
| 2 | 2 | <div id="changeset-<%= changeset.id %>" class="changeset journal"> | 
| 3 | 3 | <h4> | 
| 4 | 4 | <%= avatar(changeset.user, :size => "24") %> | 
| app/views/issues/tabs/_time_entries.html.erb | ||
|---|---|---|
| 1 | <% for time_entry in tab[:locals][:time_entries] %> | |
| 1 | <% for time_entry in time_entries%> | |
| 2 | 2 | <div id="time-entry-<%= time_entry.id %>" class="time_entry journal"> | 
| 3 | 3 | <% if time_entry.editable_by?(User.current) -%> | 
| 4 | 4 | <div class="contextual"> | 
| config/routes.rb | ||
|---|---|---|
| 184 | 184 | member do | 
| 185 | 185 | # Used when updating the form of an existing issue | 
| 186 | 186 | patch 'edit', :to => 'issues#edit' | 
| 187 | get 'tab/:name', :action => 'issue_tab', :as => 'tab' | |
| 187 | 188 | end | 
| 188 | 189 | collection do | 
| 189 | 190 | match 'bulk_edit', :via => [:get, :post] | 
| lib/redmine.rb | ||
|---|---|---|
| 90 | 90 | |
| 91 | 91 | map.project_module :issue_tracking do |map| | 
| 92 | 92 | # Issues | 
| 93 |     map.permission :view_issues, {:issues => [:index, :show], | |
| 93 |     map.permission :view_issues, {:issues => [:index, :show, :issue_tab], | |
| 94 | 94 | :auto_complete => [:issues], | 
| 95 | 95 | :context_menus => [:issues], | 
| 96 | 96 | :versions => [:index, :show, :status_by], | 
| public/javascripts/application.js | ||
|---|---|---|
| 377 | 377 | return false; | 
| 378 | 378 | } | 
| 379 | 379 | |
| 380 | function getRemoteTab(name, remote_url, url) { | |
| 381 |   $('#tab-content-' + name).parent().find('.tab-content').hide(); | |
| 382 |   $('#tab-content-' + name).parent().find('div.tabs a').removeClass('selected'); | |
| 383 |   $('#tab-' + name).addClass('selected') | |
| 384 | ||
| 385 | replaceInHistory(url) | |
| 386 | ||
| 387 |   $.ajax({ | |
| 388 | url: remote_url, | |
| 389 | type: 'get', | |
| 390 |     success: function(data){ | |
| 391 |       $('#tab-content-' + name).html(data).show(); | |
| 392 | } | |
| 393 | }); | |
| 394 | ||
| 395 | return false; | |
| 396 | } | |
| 397 | ||
| 380 | 398 | //replaces current URL with the "href" attribute of the current link | 
| 381 | 399 | //(only triggered if supported by browser) | 
| 382 | 400 | function replaceInHistory(url) { | 
| test/functional/issues_controller_test.rb | ||
|---|---|---|
| 2108 | 2108 | project.disable_module! :repository | 
| 2109 | 2109 | |
| 2110 | 2110 | @request.session[:user_id] = 2 | 
| 2111 |     get :show, :params => { | |
| 2112 | :id => issue.id | |
| 2113 | } | |
| 2111 |     get :issue_tab, :params => { | |
| 2112 | :id => issue.id, | |
| 2113 | :name => 'changesets' | |
| 2114 | }, | |
| 2115 | :xhr => true | |
| 2114 | 2116 | |
| 2115 | 2117 | assert_select 'a[href=?]', '/projects/ecookbook/repository/10/revisions/3' | 
| 2116 | 2118 | end | 
| ... | ... | |
| 2503 | 2505 | assert_select 'div.tabs a[id=?]', 'tab-time_entries', :text => 'Spent time' | 
| 2504 | 2506 | end | 
| 2505 | 2507 | |
| 2508 |     get :issue_tab, :params => { | |
| 2509 | :id => 3, | |
| 2510 | :name => 'time_entries' | |
| 2511 | }, | |
| 2512 | :xhr => true | |
| 2513 | assert_response :success | |
| 2514 | ||
| 2506 | 2515 | assert_select 'div[id=?]', 'time-entry-3' do | 
| 2507 | 2516 | assert_select 'a[title=?][href=?]', 'Edit', '/time_entries/3/edit' | 
| 2508 | 2517 | assert_select 'a[title=?][href=?]', 'Delete', '/time_entries/3' | 
| test/integration/routing/issues_test.rb | ||
|---|---|---|
| 33 | 33 | should_route 'GET /issues/64/edit' => 'issues#edit', :id => '64' | 
| 34 | 34 | should_route 'PUT /issues/64' => 'issues#update', :id => '64' | 
| 35 | 35 | should_route 'DELETE /issues/64' => 'issues#destroy', :id => '64' | 
| 36 | ||
| 37 | should_route "GET /issues/3/tab/time_entries" => 'issues#issue_tab', :id => '3', :name => 'time_entries' | |
| 36 | 38 | end | 
| 37 | 39 | |
| 38 | 40 | def test_issues_bulk_edit |