]> source.dussan.org Git - redmine.git/commitdiff
Some fixes for r20321:
authorGo MAEDA <maeda@farend.jp>
Mon, 18 Jan 2021 07:11:47 +0000 (07:11 +0000)
committerGo MAEDA <maeda@farend.jp>
Mon, 18 Jan 2021 07:11:47 +0000 (07:11 +0000)
* Include only visible issues in subtasks stats
* Get subtasks using only one query
* Show all subtasks count as badge
* Add tests

Patch by Marius BALTEANU.

git-svn-id: http://svn.redmine.org/redmine/trunk@20718 e93f8b46-1217-0410-a6f0-8f06a7374b81

app/helpers/issues_helper.rb
app/views/issues/show.html.erb
public/stylesheets/application.css
test/functional/issues_controller_test.rb

index 4a9e7a2aa8f38a0794c6e0f6773b2861d4c7c77a..20a708ca045a8cf307e5c2db1de6497cff710801 100644 (file)
@@ -143,6 +143,52 @@ module IssuesHelper
     s.html_safe
   end
 
+  # Renders descendants stats (total descendants (open - closed)) with query links
+  def render_descendants_stats(issue)
+    # Get issue descendants grouped by status type (open/closed) using a single query
+    subtasks_grouped = issue.descendants.visible.joins(:status).select(:is_closed, :id).group(:is_closed).reorder(:is_closed).count(:id)
+    # Cast keys to boolean in order to have consistent results between database types
+    subtasks_grouped.transform_keys! {|k| ActiveModel::Type::Boolean.new.cast(k)}
+
+    open_subtasks = subtasks_grouped[false].to_i
+    closed_subtasks = subtasks_grouped[true].to_i
+    all_subtasks = open_subtasks + closed_subtasks
+
+    return if all_subtasks == 0
+
+    all_block = content_tag(
+      'span',
+      link_to(all_subtasks, issues_path(parent_id: "~#{issue.id}", set_filter: true, status_id: '*')),
+      class: 'badge badge-issues-count'
+    )
+
+    closed_block = content_tag(
+      'span',
+      link_to_if(
+        closed_subtasks > 0,
+        l(:label_x_closed_issues_abbr, count: closed_subtasks),
+        issues_path(parent_id: "~#{issue.id}", set_filter: true, status_id: 'c')
+      ),
+      class: 'closed'
+    )
+
+    open_block = content_tag(
+      'span',
+      link_to_if(
+        open_subtasks > 0,
+        l(:label_x_open_issues_abbr, :count => open_subtasks),
+        issues_path(:parent_id => "~#{issue.id}", :set_filter => true, :status_id => 'o')
+      ),
+      class: 'open'
+    )
+
+    content_tag(
+      'span',
+      "#{all_block} (#{open_block} &#8212; #{closed_block})".html_safe,
+      :class => 'issues-stat'
+    )
+  end
+
   # Renders the list of related issues on the issue details view
   def render_issue_relations(issue, relations)
     manage_relations = User.current.allowed_to?(:manage_issue_relations, issue.project)
index f40f206d7f9ef7ac1cf07d6313184991ed81a3b0..3a4e1b40f7d669b2566512809db344b6889a2f29 100644 (file)
@@ -109,18 +109,8 @@ end %>
   <%= link_to_new_subtask(@issue) if User.current.allowed_to?(:manage_subtasks, @project) %>
 </div>
 <p>
-<strong><%=l(:label_subtask_plural)%></strong>
-<% if !@issue.leaf? %>
-  (<%= link_to(l(:label_x_issues, :count => @issue.descendants.count),
-                  issues_path(:parent_id => "~#{@issue.id}", :set_filter => true, :status_id => '*')) %>
-  : <%= link_to_if( @issue.descendants.select(&:closed?).count > 0,
-                  l(:label_x_closed_issues_abbr, :count => @issue.descendants.select(&:closed?).count ),
-                  issues_path(:parent_id => "~#{@issue.id}", :set_filter => true, :status_id => 'c')) %>
-  &#8212;
-  <%= link_to_if( @issue.descendants.open.count > 0,
-                 l(:label_x_open_issues_abbr, :count => @issue.descendants.open.count ),
-                 issues_path(:parent_id => "~#{@issue.id}", :set_filter => true, :status_id => 'o')) %>)
-<% end %>
+  <strong><%=l(:label_subtask_plural)%></strong>
+  <%= render_descendants_stats(@issue) unless @issue.leaf? %>
 </p>
 <%= form_tag({}, :data => {:cm_url => issues_context_menu_path}) do %>
 <%= render_descendants_tree(@issue) unless @issue.leaf? %>
index 960ceaefd0d7ed52bd96071a08b3ef04784c6a69..627e0bd58893a25f773ea403545d3a05db2b01a1 100644 (file)
@@ -540,6 +540,8 @@ body.controller-issues h2.inline-flex {padding-right: 0}
 #issue_tree td.checkbox, #relations td.checkbox {display:none;}
 #issue_tree td.subject, #relations td.subject {width: 50%;}
 #issue_tree td.buttons, #relations td.buttons {padding:0;}
+#issue_tree .issues-stat {font-size: 80%}
+#issue_tree .issues-stat .badge {bottom: initial;}
 
 #trackers_description {display:none;}
 #trackers_description dt {font-weight: bold; text-decoration: underline;}
@@ -1470,6 +1472,10 @@ td.gantt_selected_column .gantt_hdr,.gantt_selected_column_container {
   color: #1D781D;
   border: 1px solid #1D781D;
 }
+.badge-issues-count {
+  background: #EEEEEE;
+}
+
 /***** Tooltips *****/
 .ui-tooltip {
   background: #000;
index 83b5a911fe6410ce74a89ada176ed30fe3bd78f7..d62c9f2c1ef3bde79c1e1900ca60f2c1e90b82c0 100644 (file)
@@ -2340,6 +2340,54 @@ class IssuesControllerTest < Redmine::ControllerTest
     end
   end
 
+  def test_show_should_show_subtasks_stats
+    @request.session[:user_id] = 1
+    child1 = Issue.generate!(parent_issue_id: 1, subject: 'Open child issue')
+    Issue.generate!(parent_issue_id: 1, subject: 'Closed child issue', status_id: 5)
+    Issue.generate!(parent_issue_id: child1.id, subject: 'Open child of child')
+    # Issue not visible for anonymous
+    Issue.generate!(parent_issue_id: 1, subject: 'Private child', project_id: 5)
+
+    get(:show, params: {:id => 1})
+    assert_response :success
+
+    assert_select 'div#issue_tree span.issues-stat' do
+      assert_select 'span.badge', text: '4'
+      assert_select 'span.open a', text: '3 open'
+      assert_equal CGI.unescape(css_select('span.open a').first.attr('href')),
+                   "/issues?parent_id=~1&set_filter=true&status_id=o"
+
+      assert_select 'span.closed a', text: '1 closed'
+      assert_equal CGI.unescape(css_select('span.closed a').first.attr('href')),
+                   "/issues?parent_id=~1&set_filter=true&status_id=c"
+    end
+  end
+
+  def test_show_subtasks_stats_should_not_link_if_issue_has_zero_open_or_closed_subtasks
+    child1 = Issue.generate!(parent_issue_id: 1, subject: 'Open child issue')
+
+    get(:show, params: {:id => 1})
+    assert_response :success
+
+    assert_select 'div#issue_tree span.issues-stat' do
+      assert_select 'span.open a', text: '1 open'
+      assert_equal CGI.unescape(css_select('span.open a').first.attr('href')),
+                   "/issues?parent_id=~1&set_filter=true&status_id=o"
+      assert_select 'span.closed', text: '0 closed'
+      assert_select 'span.closed a', 0
+    end
+  end
+
+  def test_show_should_not_show_subtasks_stats_if_subtasks_are_not_visible
+    # Issue not visible for anonymous
+    Issue.generate!(parent_issue_id: 1, subject: 'Private child', project_id: 5)
+
+    get(:show, params: {:id => 1})
+    assert_response :success
+
+    assert_select 'div#issue_tree span.issues-stat', 0
+  end
+
   def test_show_should_list_parents
     issue = Issue.
               create!(