]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-3825 favourite measure filters
authorSimon Brandhof <simon.brandhof@gmail.com>
Sun, 25 Nov 2012 18:40:40 +0000 (19:40 +0100)
committerSimon Brandhof <simon.brandhof@gmail.com>
Mon, 26 Nov 2012 12:55:54 +0000 (13:55 +0100)
13 files changed:
sonar-core/src/main/java/org/sonar/core/persistence/DatabaseUtils.java
sonar-core/src/main/java/org/sonar/core/persistence/DatabaseVersion.java
sonar-core/src/main/resources/org/sonar/core/persistence/rows-h2.sql
sonar-core/src/main/resources/org/sonar/core/persistence/schema-h2.ddl
sonar-server/src/main/webapp/WEB-INF/app/controllers/favourites_controller.rb
sonar-server/src/main/webapp/WEB-INF/app/controllers/measures_controller.rb
sonar-server/src/main/webapp/WEB-INF/app/helpers/measures_helper.rb
sonar-server/src/main/webapp/WEB-INF/app/models/measure_filter.rb
sonar-server/src/main/webapp/WEB-INF/app/models/measure_filter_favourite.rb [new file with mode: 0644]
sonar-server/src/main/webapp/WEB-INF/app/models/user.rb
sonar-server/src/main/webapp/WEB-INF/app/views/measures/manage.html.erb
sonar-server/src/main/webapp/WEB-INF/app/views/measures/search.html.erb
sonar-server/src/main/webapp/WEB-INF/db/migrate/358_create_measure_filter_favourites.rb [new file with mode: 0644]

index c8d1d3f41dab366177d36ad4a2880a927aafa969..dff45f0fe94a85d02f09442d4ee196237275ac78 100644 (file)
@@ -67,6 +67,7 @@ public final class DatabaseUtils {
     "manual_measures",
     "measure_data",
     "measure_filters",
+    "measure_filter_favourites",
     "metrics",
     "notifications",
     "projects",
index dbf65739635119258224c10b3c5e623eab976fd7..02c2f84013728c2ecd0dbfe93bc08bdf75b074b2 100644 (file)
@@ -32,7 +32,7 @@ import java.util.List;
  */
 public class DatabaseVersion implements BatchComponent, ServerComponent {
 
-  public static final int LAST_VERSION = 357;
+  public static final int LAST_VERSION = 358;
 
   public static enum Status {
     UP_TO_DATE, REQUIRES_UPGRADE, REQUIRES_DOWNGRADE, FRESH_INSTALL
index a932334fdce31521d1f06d7e91fc6d9de35bfe97..a46a8fdb1b90220e29a7044bcb8b431fdb3520f0 100644 (file)
@@ -185,6 +185,7 @@ INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('354');
 INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('355');
 INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('356');
 INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('357');
+INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('358');
 
 INSERT INTO USERS(ID, LOGIN, NAME, EMAIL, CRYPTED_PASSWORD, SALT, CREATED_AT, UPDATED_AT, REMEMBER_TOKEN, REMEMBER_TOKEN_EXPIRES_AT) VALUES (1, 'admin', 'Administrator', '', 'a373a0e667abb2604c1fd571eb4ad47fe8cc0878', '48bc4b0d93179b5103fd3885ea9119498e9d161b', '2011-09-26 22:27:48.0', '2011-09-26 22:27:48.0', null, null);
 ALTER TABLE USERS ALTER COLUMN ID RESTART WITH 2;
index 98e7e586b04d825deadcb55a6dbb7d69652508e3..576451a88a2539f163466a2506058b190558b244 100644 (file)
@@ -525,6 +525,13 @@ CREATE TABLE "MEASURE_FILTERS" (
   "UPDATED_AT" TIMESTAMP
 );
 
+CREATE TABLE "MEASURE_FILTER_FAVOURITES" (
+  "ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
+  "USER_ID" INTEGER NOT NULL,
+  "MEASURE_FILTER_ID" INTEGER NOT NULL,
+  "CREATED_AT" TIMESTAMP
+);
+
 -- ----------------------------------------------
 -- DDL Statements for indexes
 -- ----------------------------------------------
@@ -631,4 +638,6 @@ CREATE INDEX "SEMAPHORE_NAMES" ON "SEMAPHORES" ("NAME");
 
 CREATE UNIQUE INDEX "UNIQ_AUTHOR_LOGINS" ON "AUTHORS" ("LOGIN");
 
-CREATE INDEX "MEASURE_FILTERS_NAME" ON "MEASURE_FILTERS" ("NAME");
\ No newline at end of file
+CREATE INDEX "MEASURE_FILTERS_NAME" ON "MEASURE_FILTERS" ("NAME");
+
+CREATE INDEX "MEASURE_FILTER_FAVS_USERID" ON "MEASURE_FILTER_FAVOURITES" ("USER_ID");
\ No newline at end of file
index a529ac6d2737b0c04eebd0926bde6923fc1b5d82..1d2283c5e39add3bbc878d8a96ecb1b60bf8a746 100644 (file)
@@ -25,21 +25,21 @@ class FavouritesController < ApplicationController
     favourite_id=params[:id]
     if current_user.favourite?(favourite_id)
       current_user.delete_favourite(favourite_id)
-      @style='notfav'
-      @tooltip='Click to add to favourites'
+      style='notfav'
+      tooltip='Click to add to favourites'
     else
       current_user.add_favourite(favourite_id)
-      @style='fav'
-      @tooltip='Click to remove from favourites'
+      style='fav'
+      tooltip='Click to remove from favourites'
     end
 
     star_id=params[:elt]
     render :update do |page|
       page.element.removeClassName(star_id, 'notfav')
       page.element.removeClassName(star_id, 'fav')
-      page.element.addClassName(star_id, @style)
-      page.element.writeAttribute(star_id, 'alt', @tooltip)
-      page.element.writeAttribute(star_id, 'title', @tooltip)
+      page.element.addClassName(star_id, style)
+      page.element.writeAttribute(star_id, 'alt', tooltip)
+      page.element.writeAttribute(star_id, 'title', tooltip)
     end
   end
 
index ab33a006d21509e7cde6255bfc17413a9685bed5..40d74b6f14e079a038dfa476486de564be1315ad 100644 (file)
@@ -21,6 +21,7 @@ class MeasuresController < ApplicationController
 
   SECTION=Navigation::SECTION_HOME
 
+  # GET /measures/index
   def index
     @filter = MeasureFilter.new
     render :action => 'search'
@@ -37,6 +38,7 @@ class MeasuresController < ApplicationController
   end
 
   # Load existing filter
+  # GET /measures/filter/<filter id>
   def filter
     require_parameters :id
 
@@ -61,17 +63,20 @@ class MeasuresController < ApplicationController
     verify_post_request
     access_denied unless logged_in?
 
+    add_to_favourites=false
     if params[:id].present?
       @filter = find_filter(params[:id])
     else
       @filter = MeasureFilter.new
       @filter.user_id=current_user.id
+      add_to_favourites=true
     end
     @filter.name=params[:name]
     @filter.description=params[:description]
     @filter.shared=(params[:shared]=='true')
     @filter.data=URI.unescape(params[:data])
     if @filter.save
+      current_user.favourited_measure_filters<<@filter
       render :text => @filter.id.to_s, :status => 200
     else
       render :partial => 'measures/save_form', :status => 400
@@ -81,14 +86,21 @@ class MeasuresController < ApplicationController
   # GET /measures/manage
   def manage
     access_denied unless logged_in?
+    @shared_filters = MeasureFilter.find(:all,
+                                         :include => :user,
+                                         :conditions => ['shared=? and user_id<>?', true, current_user.id])
+    @fav_filter_ids = current_user.measure_filter_favourites.map { |fav| fav.measure_filter_id }
+    Api::Utils.insensitive_sort!(@shared_filters) { |elt| elt.name }
   end
 
+  # GET /measures/edit_form/<filter id>
   def edit_form
     require_parameters :id
     @filter = find_filter(params[:id])
     render :partial => 'measures/edit_form'
   end
 
+  # POST /measures/edit/<filter id>?name=<name>&description=<description>&shared=<true|false>
   def edit
     verify_post_request
     access_denied unless logged_in?
@@ -106,6 +118,7 @@ class MeasuresController < ApplicationController
     end
   end
 
+  # POST /measures/delete/<filter id>
   def delete
     verify_post_request
     access_denied unless logged_in?
@@ -116,6 +129,26 @@ class MeasuresController < ApplicationController
     redirect_to :action => 'manage'
   end
 
+  # POST /measures/toggle_fav/<filter id>
+  def toggle_fav
+    access_denied unless logged_in?
+    verify_ajax_request
+    require_parameters :id
+
+    favourites = MeasureFilterFavourite.find(:all,
+                                             :conditions => ['user_id=? and measure_filter_id=?', current_user.id, params[:id]])
+    if favourites.empty?
+      filter = find_filter(params[:id])
+      current_user.favourited_measure_filters<<filter if filter.shared || owner?(filter)
+      is_favourite = true
+    else
+      favourites.each { |fav| fav.delete }
+      is_favourite = false
+    end
+
+    render :text => is_favourite.to_s, :status => 200
+  end
+
   private
   def find_filter(id)
     filter = MeasureFilter.find(id)
@@ -126,4 +159,5 @@ class MeasuresController < ApplicationController
   def owner?(filter)
     current_user && filter.user_id==current_user.id
   end
+
 end
index 77f3593f123fce46e386c6dd4827e4bcefbafa3b..8986cbf5d931ffde53d3becdcef690facd84a674 100644 (file)
@@ -61,4 +61,16 @@ module MeasuresHelper
       html
     end
   end
+
+  def measure_filter_star(filter, is_favourite)
+    if is_favourite
+      style='fav'
+      title=message('click_to_remove_from_favourites')
+    else
+      style='notfav'
+      title=message('click_to_add_to_favourites')
+    end
+
+    "<a href='#' class='measure-filter-star #{style}' filter-id='#{filter.id}' title='#{title}'></a>"
+  end
 end
index e123375ea171777c7d11e8057f099c6cc0217164..087b92b61db0b7413308a620b1c06a5a0421590b 100644 (file)
@@ -137,6 +137,8 @@ class MeasureFilter < ActiveRecord::Base
   attr_reader :base_result, :results
 
   belongs_to :user
+  has_many :measure_filter_favourites, :dependent => :delete_all
+
   validates_presence_of :name, :message => Api::Utils.message('measure_filter.missing_name')
   validates_length_of :name, :maximum => 100, :message => Api::Utils.message('measure_filter.name_too_long')
   validates_length_of :description, :allow_nil => true, :maximum => 4000
diff --git a/sonar-server/src/main/webapp/WEB-INF/app/models/measure_filter_favourite.rb b/sonar-server/src/main/webapp/WEB-INF/app/models/measure_filter_favourite.rb
new file mode 100644 (file)
index 0000000..8bfd80e
--- /dev/null
@@ -0,0 +1,23 @@
+#
+# Sonar, entreprise quality control tool.
+# Copyright (C) 2008-2012 SonarSource
+# mailto:contact AT sonarsource DOT com
+#
+# Sonar 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.
+#
+# Sonar 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 Sonar; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02
+#
+class MeasureFilterFavourite < ActiveRecord::Base
+  belongs_to :user
+  belongs_to :measure_filter
+end
\ No newline at end of file
index ea4fdeb8030d770734dfd6294c72707890eda3fa..5be2a51c417b3950db164d6ec9b24a11b305f923 100644 (file)
@@ -30,7 +30,13 @@ class User < ActiveRecord::Base
   has_many :filters, :dependent => :destroy
   has_many :active_dashboards, :dependent => :destroy, :order => 'order_index'
   has_many :dashboards, :dependent => :destroy
-  has_many :measure_filters, :class_name => 'MeasureFilter', :dependent => :destroy
+  has_many :measure_filters, :class_name => 'MeasureFilter', :dependent => :delete_all
+
+  # measure filters that are marked as favourites
+  has_many :favourited_measure_filters, :class_name => 'MeasureFilter', :through => :measure_filter_favourites, :source => :measure_filter
+
+  # the join table MEASURE_FILTER_FAVOURITES
+  has_many :measure_filter_favourites, :class_name => 'MeasureFilterFavourite', :dependent => :delete_all
 
   include Authentication
   include Authentication::ByPassword
@@ -91,11 +97,13 @@ class User < ActiveRecord::Base
 
     # do not validate user, for example when user created via SSO has no password
     self.save(false)
-    self.user_roles.each { |role| role.delete }
-    self.properties.each { |prop| prop.delete }
-    self.filters.each { |f| f.destroy }
-    self.dashboards.each { |d| d.destroy }
-    self.active_dashboards.each { |ad| ad.destroy }
+    self.user_roles.clear
+    self.properties.clear
+    self.filters.clear
+    self.dashboards.clear
+    self.active_dashboards.clear
+    self.measure_filter_favourites.clear
+    self.measure_filters.clear
   end
 
   # SONAR-3258
index 33f46b05630b35f3a6be24b3df50f87cc01b6a80..695bd565d8b126c0a74428d39f2def138c49dcdc 100644 (file)
-<div class="admin_page">
-  <table class="data" id="actives">
-    <thead>
-    <tr>
-      <th><%= message('name') -%></th>
-      <th><%= message('sharing') -%></th>
-      <th class="right"><%= message('operations') -%></th>
+<% content_for :script do %>
+  <script>
+    $j(document).ready(function () {
+      $j(".measure-filter-star").click(function () {
+        var filterId = $j(this).attr('filter-id');
+        var star = $j(this);
+        $j.ajax({
+          type: 'POST',
+          url:baseUrl + "/measures/toggle_fav",
+          data:{id: filterId},
+          success:function (data) {
+            if (data=='true') {
+              star.removeClass('notfav').addClass('fav');
+              star.attr('title', '<%= escape_javascript message('click_to_remove_from_favourites') -%>');
+            } else {
+              star.removeClass('fav').addClass('notfav');
+              star.attr('title', '<%= escape_javascript message('click_to_add_to_favourites') -%>');
+            }
+          }
+        });
+      });
+    });
+  </script>
+<% end %>
+
+<h2>My Measure Filters</h2>
+<table class="data" id="my-filters">
+  <thead>
+  <tr>
+    <th class="thin"></th>
+    <th><%= message('name') -%></th>
+    <th><%= message('sharing') -%></th>
+    <th class="right"><%= message('operations') -%></th>
+  </tr>
+  </thead>
+  <tbody>
+  <% if current_user.measure_filters.empty? %>
+    <tr class="even">
+      <td colspan="4"><%= message('filters.no_filters') -%></td>
     </tr>
-    </thead>
-    <tbody>
-    <% if current_user.measure_filters.empty? %>
-      <tr class="even">
-        <td colspan="3"><%= message('filters.no_filters') -%></td>
+  <% else %>
+    <% current_user.measure_filters.each do |filter| %>
+      <tr id="my-<%= filter.name.parameterize -%>" class="<%= cycle('even', 'odd', :name => 'my-filters') -%>">
+        <td>
+          <%= measure_filter_star(filter, @fav_filter_ids.include?(filter.id)) -%>
+        </td>
+        <td>
+          <%= link_to h(filter.name), :action => 'filter', :id => filter.id -%>
+          <% if filter.description %>
+            <div><%= h filter.description -%></div>
+          <% end %>
+        </td>
+        <td>
+          <% if filter.shared %>
+            Shared with all users
+          <% else %>
+            Private
+          <% end %>
+        </td>
+        <td class="thin nowrap right">
+          <a id="edit_<%= filter.name.parameterize -%>" href="<%= ApplicationController.root_context -%>/measures/edit_form/<%= filter.id -%>" class="link-action open-modal"><%= message('edit') -%></a>
+          &nbsp;
+          <%= link_to_action message('delete'), "#{ApplicationController.root_context}/measures/delete/#{filter.id}",
+                             :class => 'link-action link-red',
+                             :id => "delete_#{filter.name.parameterize}",
+                             :confirm_button => message('delete'),
+                             :confirm_title => 'measure_filters.delete_confirm_title',
+                             :confirm_msg => 'measure_filters.are_you_sure_want_delete_filter_x',
+                             :confirm_msg_params => [filter.name] -%>
+        </td>
       </tr>
-    <% else %>
-      <% current_user.measure_filters.each do |filter| %>
-        <tr id="active-<%= u filter.name -%>" class="<%= cycle('even', 'odd', :name => 'actives') -%>">
-          <td>
-            <%= link_to h(filter.name), :action => 'filter', :id => filter.id -%>
-            <% if filter.description %>
-              <div><%= h filter.description -%></div>
-            <% end %>
-          </td>
-          <td>
-            <% if filter.shared %>
-              Shared with all users
-            <% else %>
-              Private
-            <% end %>
-          </td>
-          <td class="thin nowrap right">
-            <a id="edit_<%= filter.name.parameterize -%>" href="<%= ApplicationController.root_context -%>/measures/edit_form/<%= filter.id -%>" class="link-action open-modal"><%= message('edit') -%></a>
-            &nbsp;
-            <%= link_to_action message('delete'), "#{ApplicationController.root_context}/measures/delete/#{filter.id}",
-                                             :class => 'link-action link-red',
-                                             :id => "delete_#{filter.name.parameterize}",
-                                             :confirm_button => message('delete'),
-                                             :confirm_title => 'measure_filters.delete_confirm_title',
-                                             :confirm_msg => 'measure_filters.are_you_sure_want_delete_filter_x',
-                                             :confirm_msg_params => [filter.name] -%>
-          </td>
-        </tr>
-      <% end %>
     <% end %>
-    </tbody>
-  </table>
-</div>
+  <% end %>
+  </tbody>
+</table>
+
+<br>
+
+<h2>Shared Measure Filters</h2>
+<table class="data" id="shared-filters">
+  <thead>
+  <tr>
+    <th class="thin"></th>
+    <th><%= message('name') -%></th>
+    <th><%= message('shared_by') -%></th>
+  </tr>
+  </thead>
+  <tbody>
+  <% if @shared_filters.empty? %>
+    <tr class="even">
+      <td colspan="3"><%= message('filters.no_filters') -%></td>
+    </tr>
+  <% else %>
+    <% @shared_filters.each do |filter| %>
+      <tr id="shared-<%= filter.name.parameterize -%>" class="<%= cycle('even', 'odd', :name => 'shared-filters') -%>">
+        <td>
+          <%= measure_filter_star(filter, @fav_filter_ids.include?(filter.id)) -%>
+        </td>
+        <td>
+          <%= link_to h(filter.name), :action => 'filter', :id => filter.id -%>
+          <% if filter.description %>
+            <div><%= h filter.description -%></div>
+          <% end %>
+        </td>
+        <td>
+          <%= h filter.user.name -%>
+        </td>
+      </tr>
+
+    <% end %>
+  <% end %>
+  </tbody>
+</table>
index 90fc6f7d804fdb0adce05421b5e1ac07c2304456..deb3d90ab37f81fe7ec0110d89b13bd30035f29f 100644 (file)
         <tr>
           <td>
             <input type="submit" name="search" value="Search">
-            <a href="<%= ApplicationController.root_context -%>/measures">Reset</a>
+            <a href="<%= ApplicationController.root_context -%>/measures">New search</a>
           </td>
         </tr>
         </tbody>
 
     <% if logged_in? %>
       <ul id="my-filters">
-        <% current_user.measure_filters.each do |my_filter| %>
+        <% current_user.favourited_measure_filters.each do |my_filter| %>
           <li>
             <a href="<%= ApplicationController.root_context -%>/measures/filter/<%= my_filter.id -%>"><%= h my_filter.name -%></a>
           </li>
         <% end %>
       </ul>
+      <a href="<%= ApplicationController.root_context -%>/measures/manage" class="link-action">Manage</a>
     <% end %>
   </div>
 
 
     </div>
   <% end %>
-  </p>
 
 </div>
\ No newline at end of file
diff --git a/sonar-server/src/main/webapp/WEB-INF/db/migrate/358_create_measure_filter_favourites.rb b/sonar-server/src/main/webapp/WEB-INF/db/migrate/358_create_measure_filter_favourites.rb
new file mode 100644 (file)
index 0000000..1d8433c
--- /dev/null
@@ -0,0 +1,34 @@
+#
+# Sonar, entreprise quality control tool.
+# Copyright (C) 2008-2012 SonarSource
+# mailto:contact AT sonarsource DOT com
+#
+# Sonar 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.
+#
+# Sonar 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 Sonar; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02
+#
+
+#
+# Sonar 3.4
+#
+class CreateMeasureFilterFavourites < ActiveRecord::Migration
+  def self.up
+    create_table 'measure_filter_favourites' do |t|
+      t.column 'user_id', :integer, :null => false
+      t.column 'measure_filter_id', :integer, :null => false
+      t.column 'created_at', :datetime
+    end
+    add_index 'measure_filter_favourites', 'user_id', :name => 'measure_filter_favs_userid'
+  end
+end
+