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 |