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 |