Feature #28471 » 0001-Include-only-visible-issues-in-subtasks-stats.patch
| app/helpers/issues_helper.rb | ||
|---|---|---|
| 143 | 143 |
s.html_safe |
| 144 | 144 |
end |
| 145 | 145 | |
| 146 |
# Renders descendants stats (total descendants (open - closed)) with query links |
|
| 147 |
def render_descendants_stats(issue) |
|
| 148 |
# Get issue descendants grouped by status type (open/closed) using a single query |
|
| 149 |
subtasks_grouped = issue.descendants.visible.joins(:status).select(:is_closed, :id).group(:is_closed).reorder(:is_closed).count(:id) |
|
| 150 |
# Cast keys to boolean in order to have consistent results between database types |
|
| 151 |
subtasks_grouped.transform_keys! {|k| ActiveModel::Type::Boolean.new.cast(k)}
|
|
| 152 | ||
| 153 |
open_subtasks = subtasks_grouped[false].to_i |
|
| 154 |
closed_subtasks = subtasks_grouped[true].to_i |
|
| 155 |
all_subtasks = open_subtasks + closed_subtasks |
|
| 156 | ||
| 157 |
return if all_subtasks == 0 |
|
| 158 | ||
| 159 |
all_block = content_tag( |
|
| 160 |
'span', |
|
| 161 |
link_to(all_subtasks, issues_path(parent_id: "~#{issue.id}", set_filter: true, status_id: '*')),
|
|
| 162 |
class: 'badge badge-issues-count' |
|
| 163 |
) |
|
| 164 | ||
| 165 |
closed_block = content_tag( |
|
| 166 |
'span', |
|
| 167 |
link_to_if( |
|
| 168 |
closed_subtasks > 0, |
|
| 169 |
l(:label_x_closed_issues_abbr, count: closed_subtasks), |
|
| 170 |
issues_path(parent_id: "~#{issue.id}", set_filter: true, status_id: 'c')
|
|
| 171 |
), |
|
| 172 |
class: 'closed' |
|
| 173 |
) |
|
| 174 | ||
| 175 |
open_block = content_tag( |
|
| 176 |
'span', |
|
| 177 |
link_to_if( |
|
| 178 |
open_subtasks > 0, |
|
| 179 |
l(:label_x_open_issues_abbr, :count => open_subtasks), |
|
| 180 |
issues_path(:parent_id => "~#{issue.id}", :set_filter => true, :status_id => 'o')
|
|
| 181 |
), |
|
| 182 |
class: 'open' |
|
| 183 |
) |
|
| 184 | ||
| 185 |
content_tag( |
|
| 186 |
"span", |
|
| 187 |
"#{all_block} (#{open_block} — #{closed_block})".html_safe,
|
|
| 188 |
:class => 'issues-stat' |
|
| 189 |
) |
|
| 190 |
end |
|
| 191 | ||
| 146 | 192 |
# Renders the list of related issues on the issue details view |
| 147 | 193 |
def render_issue_relations(issue, relations) |
| 148 | 194 |
manage_relations = User.current.allowed_to?(:manage_issue_relations, issue.project) |
| app/views/issues/show.html.erb | ||
|---|---|---|
| 109 | 109 |
<%= link_to_new_subtask(@issue) if User.current.allowed_to?(:manage_subtasks, @project) %> |
| 110 | 110 |
</div> |
| 111 | 111 |
<p> |
| 112 |
<strong><%=l(:label_subtask_plural)%></strong> |
|
| 113 |
<% if !@issue.leaf? %> |
|
| 114 |
(<%= link_to(l(:label_x_issues, :count => @issue.descendants.count), |
|
| 115 |
issues_path(:parent_id => "~#{@issue.id}", :set_filter => true, :status_id => '*')) %>
|
|
| 116 |
: <%= link_to_if( @issue.descendants.select(&:closed?).count > 0, |
|
| 117 |
l(:label_x_closed_issues_abbr, :count => @issue.descendants.select(&:closed?).count ), |
|
| 118 |
issues_path(:parent_id => "~#{@issue.id}", :set_filter => true, :status_id => 'c')) %>
|
|
| 119 |
— |
|
| 120 |
<%= link_to_if( @issue.descendants.open.count > 0, |
|
| 121 |
l(:label_x_open_issues_abbr, :count => @issue.descendants.open.count ), |
|
| 122 |
issues_path(:parent_id => "~#{@issue.id}", :set_filter => true, :status_id => 'o')) %>)
|
|
| 123 |
<% end %> |
|
| 112 |
<strong><%=l(:label_subtask_plural)%></strong> |
|
| 113 |
<%= render_descendants_stats(@issue) unless @issue.leaf? %> |
|
| 124 | 114 |
</p> |
| 125 | 115 |
<%= form_tag({}, :data => {:cm_url => issues_context_menu_path}) do %>
|
| 126 | 116 |
<%= render_descendants_tree(@issue) unless @issue.leaf? %> |
| public/stylesheets/application.css | ||
|---|---|---|
| 540 | 540 |
#issue_tree td.checkbox, #relations td.checkbox {display:none;}
|
| 541 | 541 |
#issue_tree td.subject, #relations td.subject {width: 50%;}
|
| 542 | 542 |
#issue_tree td.buttons, #relations td.buttons {padding:0;}
|
| 543 |
#issue_tree .issues-stat {font-size: 80%}
|
|
| 544 |
#issue_tree .issues-stat .badge {bottom: initial;}
|
|
| 543 | 545 | |
| 544 | 546 |
#trackers_description {display:none;}
|
| 545 | 547 |
#trackers_description dt {font-weight: bold; text-decoration: underline;}
|
| ... | ... | |
| 1469 | 1471 |
color: #1D781D; |
| 1470 | 1472 |
border: 1px solid #1D781D; |
| 1471 | 1473 |
} |
| 1474 |
.badge-issues-count {
|
|
| 1475 |
background: #EEEEEE; |
|
| 1476 |
} |
|
| 1477 | ||
| 1472 | 1478 |
/***** Tooltips *****/ |
| 1473 | 1479 |
.ui-tooltip {
|
| 1474 | 1480 |
background: #000; |
| test/functional/issues_controller_test.rb | ||
|---|---|---|
| 2299 | 2299 |
end |
| 2300 | 2300 |
end |
| 2301 | 2301 | |
| 2302 |
def test_show_should_show_subtasks_stats |
|
| 2303 |
@request.session[:user_id] = 1 |
|
| 2304 |
child1 = Issue.generate!(parent_issue_id: 1, subject: 'Open child issue') |
|
| 2305 |
Issue.generate!(parent_issue_id: 1, subject: 'Closed child issue', status_id: 5) |
|
| 2306 |
Issue.generate!(parent_issue_id: child1.id, subject: 'Open child of child') |
|
| 2307 |
# Issue not visible for anonymous |
|
| 2308 |
Issue.generate!(parent_issue_id: 1, subject: 'Private child', project_id: 5) |
|
| 2309 | ||
| 2310 |
get(:show, params: {:id => 1})
|
|
| 2311 |
assert_response :success |
|
| 2312 | ||
| 2313 |
assert_select 'div#issue_tree span.issues-stat' do |
|
| 2314 |
assert_select 'span.badge', text: '4' |
|
| 2315 |
assert_select 'span.open a[href=?]', "/issues?parent_id=~1&set_filter=true&status_id=o", text: '3 open' |
|
| 2316 |
assert_select 'span.closed a[href=?]', "/issues?parent_id=~1&set_filter=true&status_id=c", text: '1 closed' |
|
| 2317 |
end |
|
| 2318 |
end |
|
| 2319 | ||
| 2320 |
def test_show_subtasks_stats_should_not_link_if_issue_has_zero_open_or_closed_subtasks |
|
| 2321 |
child1 = Issue.generate!(parent_issue_id: 1, subject: 'Open child issue') |
|
| 2322 | ||
| 2323 |
get(:show, params: {:id => 1})
|
|
| 2324 |
assert_response :success |
|
| 2325 | ||
| 2326 |
assert_select 'div#issue_tree span.issues-stat' do |
|
| 2327 |
assert_select 'span.open a[href=?]', "/issues?parent_id=~1&set_filter=true&status_id=o", text: '1 open' |
|
| 2328 |
assert_select 'span.closed', text: '0 closed' |
|
| 2329 |
assert_select 'span.closed a', 0 |
|
| 2330 |
end |
|
| 2331 |
end |
|
| 2332 | ||
| 2333 |
def test_show_should_not_show_subtasks_stats_if_subtasks_are_not_visible |
|
| 2334 |
# Issue not visible for anonymous |
|
| 2335 |
Issue.generate!(parent_issue_id: 1, subject: 'Private child', project_id: 5) |
|
| 2336 | ||
| 2337 |
get(:show, params: {:id => 1})
|
|
| 2338 |
assert_response :success |
|
| 2339 | ||
| 2340 |
assert_select 'div#issue_tree span.issues-stat', 0 |
|
| 2341 |
end |
|
| 2342 | ||
| 2302 | 2343 |
def test_show_should_list_parents |
| 2303 | 2344 |
issue = Issue. |
| 2304 | 2345 |
create!( |