aboutsummaryrefslogtreecommitdiffstats
path: root/sonar-server
diff options
context:
space:
mode:
authorSimon Brandhof <simon.brandhof@gmail.com>2011-12-19 15:00:29 +0100
committerSimon Brandhof <simon.brandhof@gmail.com>2011-12-19 15:18:09 +0100
commitc5edb54175be630d973a3a994e6cb44c46d3edfb (patch)
treede9aee036f00cfb2ce10a7d0d02ffa5035564cd3 /sonar-server
parent610521d0c0b66af51935816efdcaa63b2da709ad (diff)
downloadsonarqube-c5edb54175be630d973a3a994e6cb44c46d3edfb.tar.gz
sonarqube-c5edb54175be630d973a3a994e6cb44c46d3edfb.zip
SONAR-983 first implementation of the search engine of projects, directories and files
* The POST request to the URL /search/reset starts the indexation of resources * The search engine is available at /search
Diffstat (limited to 'sonar-server')
-rw-r--r--sonar-server/src/main/java/org/sonar/server/ui/JRubyFacade.java12
-rw-r--r--sonar-server/src/main/webapp/WEB-INF/app/controllers/search_controller.rb63
-rw-r--r--sonar-server/src/main/webapp/WEB-INF/app/helpers/application_helper.rb52
-rw-r--r--sonar-server/src/main/webapp/WEB-INF/app/models/resource_index.rb30
-rw-r--r--sonar-server/src/main/webapp/WEB-INF/app/views/search/index.html.erb46
-rw-r--r--sonar-server/src/main/webapp/WEB-INF/db/migrate/237_create_table_resource_index.rb2
-rw-r--r--sonar-server/src/main/webapp/stylesheets/style.css9
7 files changed, 193 insertions, 21 deletions
diff --git a/sonar-server/src/main/java/org/sonar/server/ui/JRubyFacade.java b/sonar-server/src/main/java/org/sonar/server/ui/JRubyFacade.java
index 0514b55e8b7..424dbef4868 100644
--- a/sonar-server/src/main/java/org/sonar/server/ui/JRubyFacade.java
+++ b/sonar-server/src/main/java/org/sonar/server/ui/JRubyFacade.java
@@ -37,6 +37,8 @@ import org.sonar.core.i18n.RuleI18nManager;
import org.sonar.markdown.Markdown;
import org.sonar.persistence.Database;
import org.sonar.persistence.DatabaseMigrator;
+import org.sonar.persistence.resource.ResourceIndexDao;
+import org.sonar.persistence.resource.ResourceIndexerFilter;
import org.sonar.server.configuration.Backup;
import org.sonar.server.configuration.ProfilesManager;
import org.sonar.server.filters.Filter;
@@ -378,4 +380,14 @@ public final class JRubyFacade {
public ComponentContainer getContainer() {
return Platform.getInstance().getContainer();
}
+
+
+ // RESOURCE SEARCH ENGINE
+ public void indexResources() {
+ getContainer().getComponentByType(ResourceIndexDao.class).index(new ResourceIndexerFilter());
+ }
+
+ public boolean isValidResourceSearchInput(String input) {
+ return ResourceIndexDao.isValidInput(input);
+ }
}
diff --git a/sonar-server/src/main/webapp/WEB-INF/app/controllers/search_controller.rb b/sonar-server/src/main/webapp/WEB-INF/app/controllers/search_controller.rb
new file mode 100644
index 00000000000..d68792f8143
--- /dev/null
+++ b/sonar-server/src/main/webapp/WEB-INF/app/controllers/search_controller.rb
@@ -0,0 +1,63 @@
+#
+# Sonar, entreprise quality control tool.
+# Copyright (C) 2008-2011 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 SearchController < ApplicationController
+
+ SECTION=Navigation::SECTION_HOME
+
+ verify :method => :post, :only => [:reset]
+ before_filter :admin_required, :except => ['index']
+
+ # Do not exceed 1000 because of the Oracle limition on IN statements
+ MAX_RESULTS = 50
+
+ def index
+ @start_time = Time.now
+ @search = params[:s]
+ if @search
+ if java_facade.isValidResourceSearchInput(@search.to_s)
+ normalized_search = @search.downcase
+ @results = ResourceIndex.find(:all,
+ :conditions => ["resource_index.kee like ?", normalized_search + '%'],
+ :order => 'name_size, position')
+
+ @results = select_authorized(:user, @results)
+ @total = @results.size
+ @results = @results[0...MAX_RESULTS]
+
+ @resources_by_id = {}
+ unless @results.empty?
+ Project.find(:all, :conditions => ['id in (?)', @results.map { |resource_index| resource_index.resource_id }]).each do |resource|
+ @resources_by_id[resource.id]=resource
+ end
+ end
+ else
+ flash[:warning]='Please refine your search'
+ end
+ end
+ end
+
+ # Start indexing resources
+ #
+ # curl -v -u admin:admin -X POST http://localhost:9000/search/reset
+ def reset
+ java_facade.indexResources()
+ render :text => 'indexing'
+ end
+end
diff --git a/sonar-server/src/main/webapp/WEB-INF/app/helpers/application_helper.rb b/sonar-server/src/main/webapp/WEB-INF/app/helpers/application_helper.rb
index 2321c8a6b00..62f4d8b44f2 100644
--- a/sonar-server/src/main/webapp/WEB-INF/app/helpers/application_helper.rb
+++ b/sonar-server/src/main/webapp/WEB-INF/app/helpers/application_helper.rb
@@ -93,7 +93,7 @@ module ApplicationHelper
if date
label = message('since_version_detailed', :params => [mode_param.to_s, date.strftime("%Y %b %d").to_s])
else
- label = message('since_version', :params => mode_param.to_s)
+ label = message('since_version', :params => mode_param.to_s)
end
elsif mode=='previous_analysis'
if !date.nil?
@@ -232,7 +232,7 @@ module ApplicationHelper
end
url_params={:controller => 'drilldown', :action => 'measures', :metric => metric_key, :id => options[:resource]||@project.id}
-
+
url_for(options.merge(url_params))
end
@@ -341,6 +341,20 @@ module ApplicationHelper
end
+ def link_to_resource_home(resource, options={})
+ period_index=options[:period]
+ period_index=nil if period_index && period_index<=0
+ if resource.display_dashboard?
+ link_to(options[:name] || resource.name, {:controller => 'dashboard', :action => 'index', :id => (resource.copy_resource_id||resource.id), :period => period_index, :tab => options[:tab], :rule => options[:rule]}, :title => options[:title])
+ else
+ if options[:line]
+ anchor= 'L' + options[:line].to_s
+ end
+ link_to(options[:name] || resource.name, {:controller => 'resource', :action => 'index', :anchor => anchor, :id => resource.id, :period => period_index, :tab => options[:tab], :rule => options[:rule], :metric => options[:metric]}, :popup => ['resource', 'height=800,width=900,scrollbars=1,resizable=1'], :title => options[:title])
+ end
+ end
+
+
#
#
# JFree Eastwood is a partial implementation of Google Chart Api
@@ -423,8 +437,8 @@ module ApplicationHelper
initial_tooltip=message('click_to_remove_from_favourites')
end
- link_to_remote('', :url => { :controller => 'favourites', :action => 'toggle', :id => resource_id, :elt => html_id},
- :method => :post, :html => {:class => initial_class, :id => html_id, :alt => initial_tooltip, :title => initial_tooltip})
+ link_to_remote('', :url => {:controller => 'favourites', :action => 'toggle', :id => resource_id, :elt => html_id},
+ :method => :post, :html => {:class => initial_class, :id => html_id, :alt => initial_tooltip, :title => initial_tooltip})
end
#
@@ -453,12 +467,12 @@ module ApplicationHelper
filename = m.tendency.to_s
case m.tendency_qualitative
- when 0
- filename+= '-black'
- when -1
- filename+= '-red'
- when 1
- filename+= '-green'
+ when 0
+ filename+= '-black'
+ when -1
+ filename+= '-red'
+ when 1
+ filename+= '-green'
end
image_tag("tendency/#{filename}-small.png")
end
@@ -550,7 +564,7 @@ module ApplicationHelper
html = options[:default].to_s if html.nil? && options[:default]
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.
@@ -563,8 +577,8 @@ module ApplicationHelper
#
def paginate(items_array, options={})
html = items_array.size.to_s + " " + message('results').downcase
-
- page_size = options[:page_size] || 20
+
+ page_size = options[:page_size] || 20
if items_array.size > page_size
# computes the pagination elements
page_id = (params[:page_id] ? params[:page_id].to_i : 1)
@@ -573,7 +587,7 @@ module ApplicationHelper
from = (page_id-1) * page_size
to = (page_id*page_size)-1
to = items_array.size-1 if to >= items_array.size
-
+
# render the pagination links
html += " | "
html += link_to_if page_id>1, message('paging_previous'), {:overwrite_params => {:page_id => page_id-1}}
@@ -583,14 +597,14 @@ module ApplicationHelper
html += " "
end
html += link_to_if page_id<page_count, message('paging_next'), {:overwrite_params => {:page_id => 1+page_id}}
-
+
# and adapt the items_array object according to the pagination
items_to_keep = items_array[from..to]
items_array.clear
- items_to_keep.each {|i| items_array << i}
+ items_to_keep.each { |i| items_array << i }
end
-
- html
+
+ html
end
-
+
end
diff --git a/sonar-server/src/main/webapp/WEB-INF/app/models/resource_index.rb b/sonar-server/src/main/webapp/WEB-INF/app/models/resource_index.rb
new file mode 100644
index 00000000000..1db1a1cd361
--- /dev/null
+++ b/sonar-server/src/main/webapp/WEB-INF/app/models/resource_index.rb
@@ -0,0 +1,30 @@
+#
+# Sonar, entreprise quality control tool.
+# Copyright (C) 2008-2011 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 ResourceIndex < ActiveRecord::Base
+
+ set_table_name 'resource_index'
+
+ belongs_to :resource, :class_name => 'Project', :foreign_key => 'resource_id'
+ belongs_to :project, :class_name => 'Project', :foreign_key => 'project_id'
+
+ def resource_id_for_authorization
+ project_id
+ end
+end \ No newline at end of file
diff --git a/sonar-server/src/main/webapp/WEB-INF/app/views/search/index.html.erb b/sonar-server/src/main/webapp/WEB-INF/app/views/search/index.html.erb
new file mode 100644
index 00000000000..d9b0de36c0d
--- /dev/null
+++ b/sonar-server/src/main/webapp/WEB-INF/app/views/search/index.html.erb
@@ -0,0 +1,46 @@
+<form method="GET" action="index">
+ <input type="text" name="s" id="searchText" value="<%= @search -%>">
+ <input type="submit" value="Search" id="submitSearch">
+</form>
+
+<% if @results %>
+ <table class="data width100" id="searchResults">
+ <thead>
+ <tr>
+ <th colspan="3"></th>
+ </tr>
+ </thead>
+ <tbody>
+ <% @results.each do |resource_index|
+ resource=@resources_by_id[resource_index.resource_id]
+ %>
+ <tr class="<%= cycle('even', 'odd') -%>">
+ <td class="thin nowrap">
+ <% if resource.display_dashboard? %>
+ <a href="<%= ApplicationController.root_context -%>/components/index/<%= resource.id -%>"><img src="<%= ApplicationController.root_context -%>/images/zoom.png"></a>
+ <% end %>
+ </td>
+ <td class="thin nowrap">
+ <%= qualifier_icon resource -%>
+ </td>
+ <td>
+ <%= link_to_resource_home resource, :name => highlight(resource.name(true), @search) -%>
+ </td>
+ </tr>
+ <% end %>
+ </tbody>
+ <tfoot>
+ <tr>
+ <td colspan="3">
+ <% if @total>@results.size %>
+ <%= @results.size -%> among
+ <% end %>
+ <%= @total -%> results (<%= Time.now-@start_time -%> seconds)
+ </td>
+ </tr>
+ </tfoot>
+ </table>
+<% end %>
+<script type="text/javascript">
+ $('searchText').focus();
+</script> \ No newline at end of file
diff --git a/sonar-server/src/main/webapp/WEB-INF/db/migrate/237_create_table_resource_index.rb b/sonar-server/src/main/webapp/WEB-INF/db/migrate/237_create_table_resource_index.rb
index 8b651d244f1..c6190f0cfe6 100644
--- a/sonar-server/src/main/webapp/WEB-INF/db/migrate/237_create_table_resource_index.rb
+++ b/sonar-server/src/main/webapp/WEB-INF/db/migrate/237_create_table_resource_index.rb
@@ -27,10 +27,12 @@ class CreateTableResourceIndex < ActiveRecord::Migration
create_table 'resource_index', :id => false do |t|
t.column 'kee', :string, :null => false, :limit => 100
t.column 'position', :integer, :null => false
+ t.column 'name_size', :integer, :null => false
t.column 'resource_id', :integer, :null => false
t.column 'project_id', :integer, :null => false
end
add_index 'resource_index', 'kee', :name => 'resource_index_key'
+ add_index 'resource_index', 'resource_id', :name => 'resource_index_rid'
end
end
diff --git a/sonar-server/src/main/webapp/stylesheets/style.css b/sonar-server/src/main/webapp/stylesheets/style.css
index 9fb6b224d75..c45ce56b77a 100644
--- a/sonar-server/src/main/webapp/stylesheets/style.css
+++ b/sonar-server/src/main/webapp/stylesheets/style.css
@@ -491,6 +491,10 @@ h4, .h4 {
color: #777;
}
+.highlight {
+ font-weight: bold;
+}
+
.subtitle {
color: #777;
font-size: 85%;
@@ -1239,7 +1243,9 @@ div.progress td {
}
div.progress td a {
- display: block; width: 100%; height: 100%;
+ display: block;
+ width: 100%;
+ height: 100%;
}
div.progress td.resolved {
@@ -1257,7 +1263,6 @@ div.progress div.note {
white-space: nowrap;
}
-
/* AUTOCOMPLETE FIELDS */
div.autocomplete {
position: absolute;