]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-4301 Add severities filter on issues search page
authorJulien Lancelot <julien.lancelot@gmail.com>
Thu, 16 May 2013 13:10:14 +0000 (15:10 +0200)
committerJulien Lancelot <julien.lancelot@gmail.com>
Thu, 16 May 2013 13:10:23 +0000 (15:10 +0200)
plugins/sonar-core-plugin/src/main/resources/org/sonar/l10n/core.properties
sonar-server/src/main/webapp/WEB-INF/app/controllers/issues_controller.rb
sonar-server/src/main/webapp/WEB-INF/app/helpers/application_helper.rb
sonar-server/src/main/webapp/WEB-INF/app/models/issue_filter.rb [new file with mode: 0644]
sonar-server/src/main/webapp/WEB-INF/app/views/issues/_list.html.erb
sonar-server/src/main/webapp/WEB-INF/app/views/issues/_sidebar.html.erb [new file with mode: 0644]
sonar-server/src/main/webapp/WEB-INF/app/views/issues/index.html.erb [deleted file]
sonar-server/src/main/webapp/WEB-INF/app/views/issues/search.html.erb [new file with mode: 0644]

index d5444a8b4f0f306381337b75de26da9b0a08d009..ccf358a071fd1e3e8917952a340299e552f885c5 100644 (file)
@@ -34,6 +34,7 @@ closed=Closed
 code=Code
 color=Color
 compare=Compare
+component=Component
 configurable=Configurable
 configure=Configure
 confirm=Confirm
@@ -554,6 +555,23 @@ issue.resolution.FALSE-POSITIVE=False-positive
 issue.resolution.FIXED=Fixed
 issue.planned_for_x=Planned for {0}
 
+#------------------------------------------------------------------------------
+#
+# ISSUE FILTERS
+#
+#------------------------------------------------------------------------------
+issue_filter.new_search=New search
+issue_filter.header.action_plan=Action plan
+issue_filter.header.assignee=Assignee
+issue_filter.header.creation_date=Creation date
+issue_filter.header.update_date=Update date
+issue_filter.criteria.severities=Severities
+issue_filter.criteria.project=Project
+issue_filter.criteria.status=Status
+issue_filter.criteria.assignee=Assignee
+issue_filter.criteria.reporter=Reporter
+issue_filter.criteria.creation_date=Creation date
+
 
 #------------------------------------------------------------------------------
 #
index 342b68f8da8fd0b3d3b7a265ac0cb4845746473e..0146b498fef5198200f097871a945e2c45b8c2f1 100644 (file)
 class IssuesController < ApplicationController
 
   def index
-    init_params
-    @issue_results = Api.issues.find(params)
-    @paging = @issue_results.paging
-    @issues = @issue_results.issues
+    @filter = IssueFilter.new
+    render :action => 'search'
+  end
+
+  def search
+    @filter = IssueFilter.new
+    @filter.criteria=criteria_params
+    @filter.execute
   end
 
 
   private
 
-  def init_params
+  def criteria_params
     params.merge({:controller => nil, :action => nil, :search => nil, :widget_id => nil, :edit => nil})
-    params['pageSize'] = 25
-    params['pageIndex'] ||= 1
   end
 
 end
\ No newline at end of file
index 391f0830998e3d0a32d4cfec309234ba8f1e1109..1d77704fd0f553ec5f1f6576a46ef2ab40f6a592 100644 (file)
@@ -534,39 +534,6 @@ module ApplicationHelper
     html
   end
 
-  #
-  # Creates a pagination section for the given array (items_array) if its size exceeds the pagination size (default: 20).
-  # Upon completion of this method, the HTML is returned and the given array contains only the selected elements.
-  #
-  # In any case, the HTML that is returned contains the message 'x results', where x is the total number of elements
-  # in the items_array object.
-  #
-  # === Optional parameters
-  # * page_size: the number of elements to display at the same time (= the pagination size)
-  #
-  def paginate_java(pagination)
-    total = pagination.total.to_i
-    page_index = pagination.pageIndex() ? pagination.pageIndex().to_i : 1
-    page_size = pagination.pageSize().to_i || 20
-    pages = pagination.pages().to_i
-
-    html = total.to_s + " " + message('results').downcase
-
-    if total > page_size
-      # render the pagination links
-      html += " | "
-      html += link_to_if page_index>1, message('paging_previous'), {:overwrite_params => {:pageIndex => page_index-1}}
-      html += " "
-      for index in 1..pages
-        html += link_to_unless index==page_index, index.to_s, {:overwrite_params => {:pageIndex => index}}
-        html += " "
-      end
-      html += link_to_if page_index<pages, message('paging_next'), {:overwrite_params => {:pageIndex => 1+page_index}}
-    end
-
-    html
-  end
-
   #
   # Used on the reviews listing page (http://localhost:9000/project_reviews)
   # Prints a label for the given parameter that is used to filter the review list.
@@ -871,4 +838,70 @@ module ApplicationHelper
     html
   end
 
+
+  # Add a <tfoot> section to a table with pagination details and links.
+  # Options :
+  # :id HTML id of the <tfoot> node
+  # :colspan number of columns in the table
+  # :include_loading_icon add a hidden loading icon, only if value is true and if the option :id is set as well. The HTML id of the generated icon
+  #    is '<id>_loading'
+  def paginate_java(pagination, options={}, &block)
+    total = pagination.total.to_i
+    page_index = pagination.pageIndex() ? pagination.pageIndex().to_i : 1
+    pages = pagination.pages().to_i
+
+    html = '<tfoot'
+    html += " id='#{options[:id]}'" if options[:id]
+    html += "><tr><td"
+    html += " colspan='#{options[:colspan]}'" if options[:colspan]
+    html += '>'
+    if options[:include_loading_icon] && options[:id]
+      html += "<img src='#{ApplicationController.root_context}/images/loading-small.gif' style='display: none' id='#{options[:id]}_loading'>"
+    end
+    html += '<div'
+    html += " id='#{options[:id]}_pages'" if options[:id]
+    html += '>'
+    html += message('x_results', :params => [total]) if total>0
+
+    if pages > 1
+      max_pages = pages
+      current_page = page_index
+      start_page = 1
+      end_page = max_pages
+      if max_pages > 20
+        if current_page < 12
+          start_page = 1
+          end_page = 20
+        elsif current_page > max_pages-10
+          start_page = max_pages-20
+          end_page = max_pages
+        else
+          start_page = current_page-10
+          end_page = current_page+9
+        end
+      end
+
+      html += ' | '
+      if max_pages > 20 && start_page > 1
+        html += (current_page!=1 ? yield(message('paging_first'), 1) : message('paging_first'))
+        html += ' '
+      end
+      html += (page_index>1 ? yield(message('paging_previous'), current_page-1) : message('paging_previous'))
+      html += ' '
+      for index in start_page..end_page
+        html += (index != current_page ? yield(index.to_s, index) : index.to_s)
+        html += ' '
+      end
+      html += (page_index<pages ? yield(message('paging_next'), current_page+1) : message('paging_next'))
+      html += ' '
+      if max_pages > 20 && end_page < max_pages
+        html += (current_page != max_pages ? yield(message('paging_last'), max_pages) : message('paging_last'))
+        html += ' '
+      end
+    end
+    html += '</div></td></tr></tfoot>'
+    html
+  end
+
+
 end
diff --git a/sonar-server/src/main/webapp/WEB-INF/app/models/issue_filter.rb b/sonar-server/src/main/webapp/WEB-INF/app/models/issue_filter.rb
new file mode 100644 (file)
index 0000000..32d30ee
--- /dev/null
@@ -0,0 +1,82 @@
+#
+# Sonar, entreprise quality control tool.
+# Copyright (C) 2008-2013 SonarSource
+# mailto:contact AT sonarsource DOT com
+#
+# SonarQube is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 3 of the License, or (at your option) any later version.
+#
+# SonarQube is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+#
+require 'set'
+class IssueFilter
+
+  CRITERIA_SEPARATOR = '|'
+  CRITERIA_KEY_VALUE_SEPARATOR = ','
+
+  attr_reader :paging, :issues, :issues_result
+
+  def criteria(key=nil)
+    @criteria ||= HashWithIndifferentAccess.new
+    if key
+      @criteria[key]
+    else
+      @criteria
+    end
+  end
+
+  def criteria=(hash)
+    @criteria = HashWithIndifferentAccess.new
+    hash.each_pair do |k, v|
+      set_criteria_value(k, v)
+    end
+  end
+
+  def override_criteria(hash)
+    @criteria ||= HashWithIndifferentAccess.new
+    hash.each_pair do |k, v|
+      set_criteria_value(k, v)
+    end
+  end
+
+  # API used by Displays
+  def set_criteria_value(key, value)
+    @criteria ||= HashWithIndifferentAccess.new
+    if key
+      if value!=nil && value!='' && value!=['']
+        value = (value.kind_of?(Array) ? value : value.to_s)
+        @criteria[key]=value
+      else
+        @criteria.delete(key)
+      end
+    end
+  end
+
+  def execute
+    init_results
+    @issues_result = Api.issues.find(criteria)
+    @paging = @issues_result.paging
+    @issues = @issues_result.issues
+    self
+  end
+
+  private
+
+  def init_results
+    @issues_result = nil
+    @paging = nil
+    @issues = nil
+    criteria['pageSize'] = 25
+    self
+  end
+
+end
\ No newline at end of file
index 46b5c75d37c8610baac6d325353819c46d277911..3bd595a6eacaef837d3d087fd8c4a62aaf3f3e2c 100644 (file)
@@ -1,69 +1,85 @@
 <%
-   if @issues && !@issues.empty?
+   if @filter.issues && !@filter.issues.empty?
+    colspan = 8
 %>
-  <table id="issues-list" class="data width100">
-    <thead>
-    <tr>
-      <th width="1%" nowrap>
-        <%= message('status_abbreviated') -%>
-      </th>
-      <th width="1%" nowrap>
-        <%= message('severity_abbreviated') -%>
-      </th>
-      <th>
-        Message
-      </th>
-      <th width="1%" nowrap>
-        Component name
-      </th>
-      <th>
-        <%= message('assignee') -%>
-      </th>
-      <th>
-        Creation date
-      </th>
-    </tr>
-    </thead>
-    <tfoot>
-    <tr>
-      <td colspan="6">
-        <%= paginate_java(@paging) -%>
-      </td>
-    </tr>
-    </tfoot>
-    <tbody>
-    <%
-       @issues.each do |issue|
-    %>
-      <tr class="<%= cycle('even', 'odd') -%>">
-        <td>
-          <img src="<%= ApplicationController.root_context -%>/images/status/<%= issue.status -%>.png" title="<%= message(issue.status.downcase).capitalize -%>"/>
-        </td>
-        <td>
-          <% if issue.severity %>
+  <div>
+    <table class="data width100">
+      <thead>
+        <tr>
+          <th width="1%" nowrap>
+            <%= message('severity_abbreviated') -%>
+          </th>
+          <th width="1%" nowrap>
+            <%= message('status_abbreviated') -%>
+          </th>
+          <th>
+            <%= message('description') -%>
+          </th>
+          <th width="1%" nowrap>
+            <%= message('component') -%>
+          </th>
+          <th>
+            <%= message('issue_filter.header.assignee') -%>
+          </th>
+          <th>
+            <%= message('issue_filter.header.action_plan') -%>
+          </th>
+          <th>
+            <%= message('issue_filter.header.creation_date') -%>
+          </th>
+          <th>
+            <%= message('issue_filter.header.update_date') -%>
+          </th>
+        </tr>
+      </thead>
+      <tbody>
+      <%
+         @filter.issues.each do |issue|
+      %>
+        <tr class="<%= cycle('even', 'odd') -%>">
+          <td>
             <img src="<%= ApplicationController.root_context -%>/images/priority/<%= issue.severity -%>.png" title="<%= message(issue.severity.downcase).capitalize -%>"/>
-          <% end %>
-        </td>
-        <td>
-          <%= link_to h(issue.message), :controller => "issue", :action => "view", :id => issue.key -%>
-        </td>
-        <td>
-          <%= h(@issue_results.component(issue).name) -%>
-        </td>
-        <td>
-          <%= @issue_results.user(issue.assignee).name if issue.assignee -%>
-        </td>
-        <td>
-          <%= human_short_date(Api::Utils.java_to_ruby_datetime(issue.creationDate())) -%>
-        </td>
-      </tr>
-    <%
-       end
-    %>
-    </tbody>
-  </table>
+          </td>
+          <td>
+            <img src="<%= ApplicationController.root_context -%>/images/status/<%= issue.status -%>.png" title="<%= message(issue.status.downcase).capitalize -%>"/>
+          </td>
+          <td>
+            <%= link_to h(issue.message), :controller => 'issue', :action => 'view', :id => issue.key -%>
+          </td>
+          <td>
+            <%= h @filter.issues_result.component(issue).name -%>
+          </td>
+          <td>
+            <%= h @filter.issues_result.user(issue.assignee).name if issue.assignee -%>
+          </td>
+          <td>
+            <%= h @filter.issues_result.actionPlan(issue).name if issue.actionPlanKey() -%>
+          </td>
+          <td>
+            <%= human_short_date(Api::Utils.java_to_ruby_datetime(issue.creationDate())) -%>
+          </td>
+          <td>
+            <%= human_short_date(Api::Utils.java_to_ruby_datetime(issue.updateDate())) -%>
+          </td>
+        </tr>
+      <%
+         end
+      %>
+      <% if @filter.issues.empty? %>
+        <tr class="even">
+          <td colspan="<%= colspan -%>"><%= message 'no_data' -%></td>
+        </tr>
+      <% end %>
+      </tbody>
+
+      <%= paginate_java(@filter.paging, :colspan => colspan, :include_loading_icon => true) { |label, page_id|
+        link_to(label, @filter.criteria.merge({:pageIndex => page_id}))
+      } -%>
+
+    </table>
+  </div>
 <%
-   elsif @issues
+   elsif @filter.issues
 %>
   <p><%= message('no_results') -%></p>
 <%
diff --git a/sonar-server/src/main/webapp/WEB-INF/app/views/issues/_sidebar.html.erb b/sonar-server/src/main/webapp/WEB-INF/app/views/issues/_sidebar.html.erb
new file mode 100644 (file)
index 0000000..984fa2c
--- /dev/null
@@ -0,0 +1,17 @@
+<ul class="sidebar gray-sidebar">
+  <form method="GET" action="<%= ApplicationController.root_context -%>/issues/search" >
+    <li class="sidebar-title">
+      <%= message('issue_filter.new_search') -%>
+    </li>
+    <li id="criteria-severity" class="marginbottom5">
+      <%= message 'issue_filter.criteria.severities' -%>:
+      <%= dropdown_tag 'severities[]', options_for_select(RulesConfigurationController::RULE_PRIORITIES, @filter.criteria('severities')),
+                       {:width => '100%', :placeholder => message('issue_filter.criteria.severities')},
+                       {:id => 'select-severities', :multiple => true}-%>
+    </li>
+    <li>
+      <input type="submit" name="search" value="<%= message('search_verb') -%>" id="search-button" />
+      <a href="<%= ApplicationController.root_context -%>/issues" class="link-action"><%= message 'issue_filter.new_search' -%></a>
+    </li>
+  </form>
+</ul>
\ No newline at end of file
diff --git a/sonar-server/src/main/webapp/WEB-INF/app/views/issues/index.html.erb b/sonar-server/src/main/webapp/WEB-INF/app/views/issues/index.html.erb
deleted file mode 100644 (file)
index 4b0572b..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-<div id="reviews-search">
-  <div id="content">
-    <%= render :partial => "list" -%>
-  </div>
-</div>
\ No newline at end of file
diff --git a/sonar-server/src/main/webapp/WEB-INF/app/views/issues/search.html.erb b/sonar-server/src/main/webapp/WEB-INF/app/views/issues/search.html.erb
new file mode 100644 (file)
index 0000000..1234b29
--- /dev/null
@@ -0,0 +1,13 @@
+<div xmlns="http://www.w3.org/1999/html">
+  <div class="page-split-left">
+    <%= render :partial => 'issues/sidebar' -%>
+  </div>
+
+  <div class="page-split-right">
+    <div id="content">
+      <div class="marginbottom10">
+        <%= render :partial => 'list' -%>
+      </div>
+    </div>
+  </div>
+</div>
\ No newline at end of file