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 |