]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-6234 add project issue filter widget
authorStas Vilchik <vilchiks@gmail.com>
Wed, 4 Mar 2015 10:51:29 +0000 (11:51 +0100)
committerStas Vilchik <vilchiks@gmail.com>
Wed, 4 Mar 2015 12:09:22 +0000 (13:09 +0100)
12 files changed:
plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/CorePlugin.java
plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/widgets/issues/ProjectIssueFilterWidget.java [new file with mode: 0644]
plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/widgets/issues/project_issue_filter.html.erb [new file with mode: 0644]
server/sonar-web/src/main/hbs/widgets/_widget-issue-filter-total.hbs
server/sonar-web/src/main/hbs/widgets/widget-issue-filter-action-plans.hbs
server/sonar-web/src/main/hbs/widgets/widget-issue-filter-assignees.hbs
server/sonar-web/src/main/hbs/widgets/widget-issue-filter-resolutions.hbs
server/sonar-web/src/main/hbs/widgets/widget-issue-filter-severities.hbs
server/sonar-web/src/main/hbs/widgets/widget-issue-filter-statuses.hbs
server/sonar-web/src/main/hbs/widgets/widget-issue-filter.hbs
server/sonar-web/src/main/js/widgets/issue-filter.js
sonar-core/src/main/resources/org/sonar/l10n/core.properties

index 5a4962b256f838c28ef43b0a724d466a0b3580bf..e3f95633802bc58fcdce1be523188a105ea466f7 100644 (file)
@@ -263,6 +263,7 @@ public final class CorePlugin extends SonarPlugin {
       UnresolvedIssuesPerAssigneeWidget.class,
       UnresolvedIssuesStatusesWidget.class,
       IssueFilterWidget.class,
+      ProjectIssueFilterWidget.class,
       IssueTagCloudWidget.class,
 
       // batch
diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/widgets/issues/ProjectIssueFilterWidget.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/widgets/issues/ProjectIssueFilterWidget.java
new file mode 100644 (file)
index 0000000..3bfdc9b
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 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.
+ */
+package org.sonar.plugins.core.widgets.issues;
+
+import org.sonar.api.web.*;
+import org.sonar.plugins.core.widgets.CoreWidget;
+
+import static org.sonar.api.web.WidgetScope.PROJECT;
+
+@WidgetCategory({"Filters", "Issues"})
+@WidgetScope(PROJECT)
+@WidgetProperties({
+  @WidgetProperty(key = ProjectIssueFilterWidget.FILTER_PROPERTY, type = WidgetPropertyType.ISSUE_FILTER, optional = false),
+  @WidgetProperty(key = ProjectIssueFilterWidget.DISTRIBUTION_AXIS_PROPERTY, type = WidgetPropertyType.SINGLE_SELECT_LIST, defaultValue = "severities",
+          options = {"severities", "resolutions", "statuses", "rules", "tags", "assignees", "reporters", "authors",
+                  "languages", "actionPlans", "createdAt"}),
+  @WidgetProperty(key = ProjectIssueFilterWidget.DISPLAY_FILTER_DESCRIPTION, type = WidgetPropertyType.BOOLEAN, defaultValue = "false")
+})
+public class ProjectIssueFilterWidget extends CoreWidget {
+
+  public static final String FILTER_PROPERTY = "filter";
+  public static final String DISTRIBUTION_AXIS_PROPERTY = "distributionAxis";
+  public static final String DISPLAY_FILTER_DESCRIPTION = "displayFilterDescription";
+  public static final String ID = "project_issue_filter";
+
+  public ProjectIssueFilterWidget() {
+    super(ID, "Issue Filter", "/org/sonar/plugins/core/widgets/issues/project_issue_filter.html.erb");
+  }
+}
diff --git a/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/widgets/issues/project_issue_filter.html.erb b/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/widgets/issues/project_issue_filter.html.erb
new file mode 100644 (file)
index 0000000..c68f075
--- /dev/null
@@ -0,0 +1,49 @@
+<%
+   container_id = 'widget-issue-filter-' + widget.id.to_s
+   filter_id = widget_properties['filter']
+   filter = Internal.issues.findIssueFilterById(filter_id.to_i)
+   distribution_axis = widget_properties['distributionAxis']
+
+   if @dashboard_configuration.selected_period?
+     period_date = @snapshot.period_datetime(@dashboard_configuration.period_index).strftime('%FT%T%z')
+   else
+     period_date = nil
+   end
+%>
+
+<% if filter %>
+  <% if Internal.issues.isUserAuthorized(filter) %>
+
+    <% @widget_title = "<a href=\"#{url_for({:controller => 'component_issues', :action => 'index'})}?id=#{u(@project.key)}##{filter.data}\">#{h(filter.name)}</a>" %>
+    <% if widget_properties['displayFilterDescription'] && !filter.description.blank? %>
+      <p class="note spacer-bottom"><%= h filter.description -%></p>
+    <% end %>
+
+    <div id="<%= container_id -%>"></div>
+    <script>
+      require(['widgets/issue-filter'], function (IssueFilter) {
+        window.requestMessages().done(function () {
+          new IssueFilter({
+            el: '#<%= container_id -%>',
+            query: '<%= filter.data -%>',
+            distributionAxis: '<%= distribution_axis -%>',
+            <% if period_date %>
+            periodDate: '<%= period_date -%>',
+            <% end %>
+            componentUuid: '<%= @project.uuid -%>',
+            componentKey: '<%= @project.key -%>'
+          });
+        });
+      });
+    </script>
+
+  <% else %>
+
+    <p class="message-alert"><%= message 'widget.issue_filter.insufficient_privileges_warning' -%></p>
+
+  <% end %>
+<% else %>
+
+  <p class="message-alert"><%= message 'widget.issue_filter.unknown_filter_warning' -%></p>
+
+<% end %>
index 407209bb5ab670fa1cb29d8cba3b190d33dfaceb..1619ed5ab8c617fadb7a05ec98254ae2099053d3 100644 (file)
@@ -1,8 +1,8 @@
 <tr>
   <td>
-    <a href="{{link '/issues/search#' query}}"><strong>{{t 'total'}}</strong></a>
+    <a href="{{issueFilterTotalLink parsedQuery}}"><strong>{{t 'total'}}</strong></a>
   </td>
-  <td class="text-right"><strong>{{total}}</strong></td>
+  <td class="text-right"><strong>{{#notNull periodDate}}+{{/notNull}}{{total}}</strong></td>
   <td class="barchart">
     <div class="barchart" style="width: 100%;">
       <div style="width: 100%;"></div>
index 748c57df613a82a667e186a0880d28b9c880345b..cec1a9a6748bc29292046874a11c03d9f3d64b07 100644 (file)
@@ -10,7 +10,7 @@
         {{/eq}}
       </td>
       <td class="text-right nowrap">
-        {{numberShort count}}
+        {{#notNull ../periodDate}}+{{/notNull}}{{numberShort count}}
       </td>
       <td class="barchart">
         <div class="barchart" style="width: 100%;">
index 030e94370c173653fc8d6ffdbedda668c28ec60e..3e1add4abd2206825e2a3ac6f46fe8863cd87a2d 100644 (file)
@@ -10,7 +10,7 @@
         {{/eq}}
       </td>
       <td class="text-right nowrap">
-        {{numberShort count}}
+        {{#notNull ../periodDate}}+{{/notNull}}{{numberShort count}}
       </td>
       <td class="barchart">
         <div class="barchart" style="width: 100%;">
index 2d83a97ba4afed624f7adbca8d0c45f720c313e7..8c03461b61998475c132c7b1371e479e07e49cb6 100644 (file)
@@ -10,7 +10,7 @@
         {{/eq}}
       </td>
       <td class="text-right nowrap">
-        {{numberShort count}}
+        {{#notNull ../periodDate}}+{{/notNull}}{{numberShort count}}
       </td>
       <td class="barchart">
         <div class="barchart" style="width: 100%;">
index 64cb0477d9f65b9199941485aa49f0f93546843b..c18b9ecbe82b2e59709718128e9e7463950e7f83 100644 (file)
@@ -12,7 +12,7 @@
         <a href="{{issueFilterItemLink ../parsedQuery ../property val}}">{{t 'severity' val}}</a>
       </td>
       <td class="text-right nowrap">
-        {{numberShort count}}
+        {{#notNull ../periodDate}}+{{/notNull}}{{numberShort count}}
       </td>
     </tr>
   {{/each}}
index 97dc5ec5e707840944aefc484bc3453febcd50a1..b27fc32964fdf1a5a0fdc0cd7c71d5636d0bd684 100644 (file)
@@ -1,13 +1,5 @@
 <table class="data zebra">
-  <tr>
-    <td><strong>{{t 'total'}}</strong></td>
-    <td class="text-right"><strong>{{total}}</strong></td>
-    <td class="barchart">
-      <div class="barchart" style="width: 100%;">
-        <div style="width: 100%;"></div>
-      </div>
-    </td>
-  </tr>
+  {{> '_widget-issue-filter-total'}}
   {{#each items}}
     <tr>
       <td>
@@ -15,7 +7,7 @@
         <a href="{{issueFilterItemLink ../parsedQuery ../property val}}">{{t 'issue.status' val}}</a>
       </td>
       <td class="text-right nowrap">
-        {{numberShort count}}
+        {{#notNull ../periodDate}}+{{/notNull}}{{numberShort count}}
       </td>
       <td class="barchart">
         <div class="barchart" style="width: 100%;">
index e635a2fdcde4e4b78573235751cd92e11f481d94..ffb3e072557a58ecf2c8a30ec5180ec77c4aef61 100644 (file)
@@ -6,7 +6,7 @@
         <a href="{{searchLink}}">{{default label val}}</a>
       </td>
       <td class="text-right nowrap">
-        {{numberShort count}}
+        {{#notNull ../periodDate}}+{{/notNull}}{{numberShort count}}
       </td>
       <td class="barchart">
         <div class="barchart" style="width: 100%;">
index de810bf8e66018c29bd815e775fa59c113ca3ede..2e40e16578c64c283db68e8f35264623a1e1ccb6 100644 (file)
@@ -16,7 +16,12 @@ define(['templates/widgets'], function () {
         var criterion = {};
         criterion[property] = item.val;
         var r = _.extend({}, query, criterion);
-        return baseUrl + '/issues/search#' + getQuery(r);
+        if (r.componentKey != null) {
+          return baseUrl + '/component_issues/index?id=' + encodeURIComponent(r.componentKey) +
+              '#' + getQuery(_.omit(r, 'componentKey'));
+        } else {
+          return baseUrl + '/issues/search#' + getQuery(r);
+        }
       },
       byDistributionConf = {
         'severities': {
@@ -38,6 +43,13 @@ define(['templates/widgets'], function () {
           comparator: function (item) {
             var order = ['', 'FALSE-POSITIVE', 'WONTFIX', 'FIXED', 'REMOVED'];
             return order.indexOf(item.val);
+          },
+          filter: function (item) {
+            if ('' + this.query.resolved === 'false') {
+              return item.val === '';
+            } else {
+              return defaultFilter.call(this, item);
+            }
           }
         },
         'rules': {
@@ -115,7 +127,12 @@ define(['templates/widgets'], function () {
               createdAfter: createdAfter.format('YYYY-MM-DD'),
               createdBefore: createdBefore.format('YYYY-MM-DD')
             });
-            return baseUrl + '/issues/search#' + getQuery(r);
+            if (r.componentKey != null) {
+              return baseUrl + '/component_issues/index?id=' + encodeURIComponent(r.componentKey) +
+                  '#' + getQuery(_.omit(r, 'componentKey'));
+            } else {
+              return baseUrl + '/issues/search#' + getQuery(r);
+            }
           }
         }
       };
@@ -133,7 +150,22 @@ define(['templates/widgets'], function () {
     var criterion = {};
     criterion[property] = value;
     var r = _.extend({}, query, criterion);
-    return baseUrl + '/issues/search#' + getQuery(r);
+    if (r.componentKey != null) {
+      return baseUrl + '/component_issues/index?id=' + encodeURIComponent(r.componentKey) +
+          '#' + getQuery(_.omit(r, 'componentKey'));
+    } else {
+      return baseUrl + '/issues/search#' + getQuery(r);
+    }
+  });
+
+  Handlebars.registerHelper('issueFilterTotalLink', function (query) {
+    var r = _.extend({}, query);
+    if (r.componentKey != null) {
+      return baseUrl + '/component_issues/index?id=' + encodeURIComponent(r.componentKey) +
+          '#' + getQuery(_.omit(r, 'componentKey'));
+    } else {
+      return baseUrl + '/issues/search#' + getQuery(r);
+    }
   });
 
   return Marionette.ItemView.extend({
@@ -147,7 +179,8 @@ define(['templates/widgets'], function () {
       this.model = new Backbone.Model({
         query: this.options.query,
         parsedQuery: this.getParsedQuery(),
-        property: this.options.distributionAxis
+        property: this.options.distributionAxis,
+        periodDate: this.options.periodDate
       });
       this.listenTo(this.model, 'change', this.render);
       this.conf = byDistributionConf[this.options.distributionAxis];
@@ -164,6 +197,12 @@ define(['templates/widgets'], function () {
           query[criterion[0]] = criterion[1];
         }
       });
+      if (this.options.componentKey != null) {
+        _.extend(query, { componentKey: this.options.componentKey });
+      }
+      if (this.options.periodDate != null) {
+        _.extend(query, { createdAfter: this.options.periodDate });
+      }
       return query;
     },
 
@@ -202,6 +241,12 @@ define(['templates/widgets'], function () {
             ps: 1,
             facets: this.options.distributionAxis
           });
+      if (this.options.componentUuid != null) {
+        _.extend(options, { componentUuids: this.options.componentUuid });
+      }
+      if (this.options.periodDate != null) {
+        _.extend(options, { createdAfter: this.options.periodDate });
+      }
       return $.get(url, options).done(function (r) {
         if (_.isArray(r.facets) && r.facets.length === 1) {
           // save response object, but do not trigger repaint
index 72e60258277123a57411ab0f7f32db8b6fa7faba..751e4344d18b497648a2d9579480b3b4fde1caa6 100644 (file)
@@ -1376,6 +1376,25 @@ widget.issue_filter.property.distributionAxis.option.languages.name=By Language
 widget.issue_filter.property.distributionAxis.option.reporters.name=By Reporter
 widget.issue_filter.property.distributionAxis.option.authors.name=By Author
 
+widget.project_issue_filter.name=Project Issue Filter
+widget.project_issue_filter.description=Displays the result of a pre-configured issue filter applied to the project.
+widget.project_issue_filter.property.filter.name=Filter
+widget.project_issue_filter.property.distributionAxis.name=Distribution Axis
+widget.project_issue_filter.property.displayFilterDescription.name=Display Filter Description
+widget.project_issue_filter.unknown_filter_warning=This widget is configured to display an issue filter that doesn't exist anymore.
+widget.project_issue_filter.insufficient_privileges_warning=Widget cannot be displayed: insufficient privileges.
+widget.project_issue_filter.property.distributionAxis.option.severities.name=By Severity
+widget.project_issue_filter.property.distributionAxis.option.statuses.name=By Status
+widget.project_issue_filter.property.distributionAxis.option.createdAt.name=New Issues
+widget.project_issue_filter.property.distributionAxis.option.actionPlans.name=By Action Plan
+widget.project_issue_filter.property.distributionAxis.option.assignees.name=By Assignee
+widget.project_issue_filter.property.distributionAxis.option.tags.name=By Tag
+widget.project_issue_filter.property.distributionAxis.option.rules.name=By Rule
+widget.project_issue_filter.property.distributionAxis.option.resolutions.name=By Resolution
+widget.project_issue_filter.property.distributionAxis.option.languages.name=By Language
+widget.project_issue_filter.property.distributionAxis.option.reporters.name=By Reporter
+widget.project_issue_filter.property.distributionAxis.option.authors.name=By Author
+
 widget.issue_tag_cloud.name=Project Issue Tag Cloud
 widget.issue_tag_cloud.title=Issue Tag Cloud
 widget.issue_tag_cloud.description=Displays the cloud of tags associated to unresolved issues.