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 |