diff options
author | Simon Brandhof <simon.brandhof@gmail.com> | 2011-12-19 15:00:29 +0100 |
---|---|---|
committer | Simon Brandhof <simon.brandhof@gmail.com> | 2011-12-19 15:18:09 +0100 |
commit | c5edb54175be630d973a3a994e6cb44c46d3edfb (patch) | |
tree | de9aee036f00cfb2ce10a7d0d02ffa5035564cd3 /sonar-server | |
parent | 610521d0c0b66af51935816efdcaa63b2da709ad (diff) | |
download | sonarqube-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')
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; |