Feature #3058 » 0005-load-changesets-and-time-entries-tabs-async.patch
| app/controllers/issues_controller.rb | ||
|---|---|---|
| 20 | 20 | 
    class IssuesController < ApplicationController  | 
| 21 | 21 | 
    default_search_scope :issues  | 
| 22 | 22 | |
| 23 | 
    before_action :find_issue, :only => [:show, :edit, :update]  | 
|
| 23 | 
      before_action :find_issue, :only => [:show, :edit, :update, :issue_tab]
   | 
|
| 24 | 24 | 
    before_action :find_issues, :only => [:bulk_edit, :bulk_update, :destroy]  | 
| 25 | 25 | 
    before_action :authorize, :except => [:index, :new, :create]  | 
| 26 | 26 | 
    before_action :find_optional_project, :only => [:index, :new, :create]  | 
| ... | ... | |
| 86 | 86 | |
| 87 | 87 | 
    def show  | 
| 88 | 88 | 
    @journals = @issue.visible_journals_with_index  | 
| 89 | 
        @changesets = @issue.changesets.visible.preload(:repository, :user).to_a
   | 
|
| 89 | 
        @has_changesets = @issue.changesets.visible.preload(:repository, :user).exists?
   | 
|
| 90 | 90 | 
        @relations = @issue.relations.select {|r| r.other_issue(@issue) && r.other_issue(@issue).visible? }
   | 
| 91 | 91 | |
| 92 | 
    if User.current.wants_comments_in_reverse_order?  | 
|
| 93 | 
    @journals.reverse!  | 
|
| 94 | 
    @changesets.reverse!  | 
|
| 95 | 
    end  | 
|
| 92 | 
    @journals.reverse! if User.current.wants_comments_in_reverse_order?  | 
|
| 96 | 93 | |
| 97 | 94 | 
    if User.current.allowed_to?(:view_time_entries, @project)  | 
| 98 | 95 | 
    Issue.load_visible_spent_hours([@issue])  | 
| ... | ... | |
| 109 | 106 | 
    retrieve_previous_and_next_issue_ids  | 
| 110 | 107 | 
    render :template => 'issues/show'  | 
| 111 | 108 | 
    }  | 
| 112 | 
    format.api  | 
|
| 109 | 
          format.api {
   | 
|
| 110 | 
    @changesets = @issue.changesets.visible.preload(:repository, :user).to_a  | 
|
| 111 | 
    @changesets.reverse! if User.current.wants_comments_in_reverse_order?  | 
|
| 112 | 
    }  | 
|
| 113 | 113 | 
          format.atom { render :template => 'journals/index', :layout => false, :content_type => 'application/atom+xml' }
   | 
| 114 | 114 | 
          format.pdf  {
   | 
| 115 | 115 | 
            send_file_headers! :type => 'application/pdf', :filename => "#{@project.identifier}-#{@issue.id}.pdf"
   | 
| ... | ... | |
| 194 | 194 | 
    end  | 
| 195 | 195 | 
    end  | 
| 196 | 196 | |
| 197 | 
    def issue_tab  | 
|
| 198 | 
    return render_error :status => 422 unless request.xhr?  | 
|
| 199 | 
    tab = params[:name]  | 
|
| 200 | ||
| 201 | 
    case tab  | 
|
| 202 | 
    when 'time_entries'  | 
|
| 203 | 
    @time_entries = @issue.time_entries.visible.preload(:activity, :user).to_a  | 
|
| 204 | 
          render :partial => 'issues/tabs/time_entries', :locals => {:time_entries => @time_entries}
   | 
|
| 205 | 
    when 'changesets'  | 
|
| 206 | 
    @changesets = @issue.changesets.visible.preload(:repository, :user).to_a  | 
|
| 207 | 
    changesets.reverse! if User.current.wants_comments_in_reverse_order?  | 
|
| 208 | 
          render :partial => 'issues/tabs/changesets', :locals => {:changesets => @changesets}
   | 
|
| 209 | 
    end  | 
|
| 210 | 
    end  | 
|
| 211 | ||
| 197 | 212 | 
    # Bulk edit/copy a set of issues  | 
| 198 | 213 | 
    def bulk_edit  | 
| 199 | 214 | 
    @issues.sort!  | 
| app/helpers/issues_helper.rb | ||
|---|---|---|
| 557 | 557 | 
          tabs << {:name => 'notes', :label => :label_issue_history_notes, :onclick => 'showIssueHistory("notes", this.href)'} if journals_with_notes.any?
   | 
| 558 | 558 | 
          tabs << {:name => 'properties', :label => :label_issue_history_properties, :onclick => 'showIssueHistory("properties", this.href)'} if journals_without_notes.any?
   | 
| 559 | 559 | 
    end  | 
| 560 | 
        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
   | 
|
| 561 | 
        tabs << {:name => 'changesets', :label => :label_associated_revisions, :partial => 'issues/tabs/changesets', :locals => {:changesets => @changesets}} if @changesets.present?
   | 
|
| 560 | 
        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
   | 
|
| 561 | 
        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
   | 
|
| 562 | 562 | 
    tabs  | 
| 563 | 563 | 
    end  | 
| 564 | ||
| 564 | 565 | 
    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 | ||
|---|---|---|
| 188 | 188 | 
    member do  | 
| 189 | 189 | 
    # Used when updating the form of an existing issue  | 
| 190 | 190 | 
    patch 'edit', :to => 'issues#edit'  | 
| 191 | 
    get 'tab/:name', :action => 'issue_tab', :as => 'tab'  | 
|
| 191 | 192 | 
    end  | 
| 192 | 193 | 
    collection do  | 
| 193 | 194 | 
    match 'bulk_edit', :via => [:get, :post]  | 
| lib/redmine.rb | ||
|---|---|---|
| 94 | 94 | |
| 95 | 95 | 
    map.project_module :issue_tracking do |map|  | 
| 96 | 96 | 
    # Issues  | 
| 97 | 
        map.permission :view_issues, {:issues => [:index, :show],
   | 
|
| 97 | 
        map.permission :view_issues, {:issues => [:index, :show, :issue_tab],
   | 
|
| 98 | 98 | 
    :auto_complete => [:issues],  | 
| 99 | 99 | 
    :context_menus => [:issues],  | 
| 100 | 100 | 
    :versions => [:index, :show, :status_by],  | 
| public/javascripts/application.js | ||
|---|---|---|
| 385 | 385 | 
    return false;  | 
| 386 | 386 | 
    }  | 
| 387 | 387 | |
| 388 | 
    function getRemoteTab(name, remote_url, url) {
   | 
|
| 389 | 
      $('#tab-content-' + name).parent().find('.tab-content').hide();
   | 
|
| 390 | 
      $('#tab-content-' + name).parent().find('div.tabs a').removeClass('selected');
   | 
|
| 391 | 
      $('#tab-' + name).addClass('selected')
   | 
|
| 392 | ||
| 393 | 
    replaceInHistory(url)  | 
|
| 394 | ||
| 395 | 
      $.ajax({
   | 
|
| 396 | 
    url: remote_url,  | 
|
| 397 | 
    type: 'get',  | 
|
| 398 | 
        success: function(data){
   | 
|
| 399 | 
          $('#tab-content-' + name).html(data).show();
   | 
|
| 400 | 
    }  | 
|
| 401 | 
    });  | 
|
| 402 | ||
| 403 | 
    return false;  | 
|
| 404 | 
    }  | 
|
| 405 | ||
| 388 | 406 | 
    //replaces current URL with the "href" attribute of the current link  | 
| 389 | 407 | 
    //(only triggered if supported by browser)  | 
| 390 | 408 | 
    function replaceInHistory(url) {
   | 
| test/functional/issues_controller_test.rb | ||
|---|---|---|
| 2146 | 2146 | 
    project.disable_module! :repository  | 
| 2147 | 2147 | |
| 2148 | 2148 | 
    @request.session[:user_id] = 2  | 
| 2149 | 
        get :show, :params => {
   | 
|
| 2150 | 
    :id => issue.id  | 
|
| 2151 | 
    }  | 
|
| 2149 | 
        get :issue_tab, :params => {
   | 
|
| 2150 | 
    :id => issue.id,  | 
|
| 2151 | 
    :name => 'changesets'  | 
|
| 2152 | 
    },  | 
|
| 2153 | 
    :xhr => true  | 
|
| 2152 | 2154 | |
| 2153 | 2155 | 
    assert_select 'a[href=?]', '/projects/ecookbook/repository/10/revisions/3'  | 
| 2154 | 2156 | 
    end  | 
| ... | ... | |
| 2538 | 2540 | 
    assert_select 'div.tabs a[id=?]', 'tab-time_entries', :text => 'Spent time'  | 
| 2539 | 2541 | 
    end  | 
| 2540 | 2542 | |
| 2543 | 
        get :issue_tab, :params => {
   | 
|
| 2544 | 
    :id => 3,  | 
|
| 2545 | 
    :name => 'time_entries'  | 
|
| 2546 | 
    },  | 
|
| 2547 | 
    :xhr => true  | 
|
| 2548 | 
    assert_response :success  | 
|
| 2549 | ||
| 2541 | 2550 | 
    assert_select 'div[id=?]', 'time-entry-3' do  | 
| 2542 | 2551 | 
    assert_select 'a[title=?][href=?]', 'Edit', '/time_entries/3/edit'  | 
| 2543 | 2552 | 
    assert_select 'a[title=?][href=?]', 'Delete', '/time_entries/3'  | 
| test/integration/routing/issues_test.rb | ||
|---|---|---|
| 35 | 35 | 
    should_route 'GET /issues/64/edit' => 'issues#edit', :id => '64'  | 
| 36 | 36 | 
    should_route 'PUT /issues/64' => 'issues#update', :id => '64'  | 
| 37 | 37 | 
    should_route 'DELETE /issues/64' => 'issues#destroy', :id => '64'  | 
| 38 | ||
| 39 | 
    should_route "GET /issues/3/tab/time_entries" => 'issues#issue_tab', :id => '3', :name => 'time_entries'  | 
|
| 38 | 40 | 
    end  | 
| 39 | 41 | |
| 40 | 42 | 
    def test_issues_bulk_edit  |