Ticket #523: search_extension.diff

File search_extension.diff, 15.0 kB (added by vitali, 11 months ago)
  • vendor/extensions/search/test/unit/search_page_test.rb

    old new  
     1require File.dirname(__FILE__) + '/../test_helper' 
     2 
     3class SearchPageTest < Test::Unit::TestCase 
     4 
     5  test_helper :render 
     6  fixtures :pages, :page_parts 
     7   
     8  def setup 
     9    @page = pages(:search) 
     10     
     11    @request = ActionController::TestRequest.new(:url => '/search/') 
     12    @response = ActionController::TestResponse.new 
     13  end 
     14   
     15  def test_search_simple_found 
     16    expected_result = [pages(:homepage)] 
     17    ['home', 'HOME', 'homE', 'home page'].each do |query| 
     18      @request.parameters[:q] = query 
     19      @page.process(@request, @response) 
     20      assert_response(:success) 
     21      assert(@page.query_result.eql?(expected_result)) 
     22      assert_equal(query, @page.query) 
     23    end 
     24  end 
     25   
     26  def test_search_in_title 
     27    query = @request.parameters[:q] = 'services title' 
     28    @page.process(@request, @response) 
     29    assert_response(:success) 
     30    assert(@page.query_result.eql?([pages(:services)])) 
     31    assert_equal(query, @page.query) 
     32  end 
     33   
     34  def test_search_blank 
     35    @page.process(@request, @response) 
     36    assert_response(:success) 
     37    assert(@page.query_result.empty?) 
     38    assert_equal("", @page.query) 
     39  end 
     40   
     41  def test_search_not_found 
     42    @request.parameters[:q] = 'not_existent_word' 
     43    @page.process(@request, @response) 
     44    assert_response(:success) 
     45    assert(@page.query_result.empty?) 
     46  end 
     47   
     48  def test_search_with_exclusion 
     49    search_page = pages(:search) 
     50    home_page = pages(:homepage) 
     51    # full result contains :homepage and :search 
     52    query = @request.parameters[:q] = 'page' 
     53    @page.process(@request, @response) 
     54    assert_response(:success) 
     55    assert(@page.query_result.eql?([home_page, search_page])) 
     56    # exclude :search 
     57    @request.parameters[:exclude_pages] = search_page.url 
     58    @page.process(@request, @response) 
     59    assert_response(:success) 
     60    assert(@page.query_result.eql?([home_page])) 
     61    # exclude multiple pages 
     62    @request.parameters[:exclude_pages] = search_page.url + "," + home_page.url 
     63    @page.process(@request, @response) 
     64    assert_response(:success) 
     65    assert(@page.query_result.eql?([])) 
     66  end 
     67   
     68  def test_ajax_search_renders_ajax_body 
     69    @request.parameters[:q] = 'home' 
     70    @request.env['HTTP_X_REQUESTED_WITH'] = 'XMLHttpRequest' 
     71    #assert(@request.xhr?) 
     72    @page.process(@request, @response) 
     73    assert_response(:success) 
     74    assert_equal(@page.render_part(:ajax_body), @response.body) 
     75    @page.part('ajax_body').destroy 
     76    @page.process(@request, @response) 
     77    assert_response(:success) 
     78    assert_equal(@page.render_part(:body), @response.body) 
     79  end 
     80end 
  • vendor/extensions/search/test/unit/search_tags_test.rb

    old new  
     1require File.dirname(__FILE__) + '/../test_helper' 
     2 
     3class SearchTagsTest < Test::Unit::TestCase 
     4 
     5  test_helper :render 
     6  fixtures :pages 
     7   
     8  def setup 
     9    @page = pages(:homepage) 
     10  end 
     11   
     12  def test_global_tags 
     13    assert_tag_defined @page, "search:form" 
     14    assert_tag_defined @page, "search:form:input" 
     15  end 
     16   
     17  def test_tag_search_form_minimal 
     18    assert_render_error "`search:form' tag must contain `action' attribute", "<r:search:form />" 
     19    assert_render_match %r{<form [^>]*action="/search/".*>}, "<r:search:form action='/search/'></r:search:form>" 
     20    assert_render_match %r{<form [^>]*action="/another_search/".*>}, "<r:search:form action='/another_search/'></r:search:form>" 
     21  end 
     22   
     23  def test_tag_search_form_with_custom_attributes 
     24    add_atts = {:action => "/search/", :id => "search_form",  
     25      :class => "default", :method => "get"} 
     26       
     27    add_atts.each do |att, value| 
     28      assert_render_match( 
     29        %r{<form [^>]*#{att}="#{value}".*>}, 
     30        "<r:search:form " +  
     31          add_atts.collect {|key, value| %{#{key}="#{value}"}}.join(" ") +  
     32          "/>" 
     33      ) 
     34    end 
     35  end 
     36   
     37  def test_tag_search_form_input 
     38    assert_render_error "`search:form' tag must contain `action' attribute",  
     39      "<r:search:form:input />" 
     40    assert_render_match %r{<input[\s]+type="text"[\s]+name="q"[\s]+id="q"[\s]+/>},  
     41      %{<r:search:form action="/search/"><r:input /></r:search:form>} 
     42  end 
     43   
     44  def test_tag_search_form_input_with_custom_attributes 
     45    add_atts = {:id => "q", :size => 10, 
     46      :class => "query_input", :maxlength => 15} 
     47       
     48    add_atts.each do |att, value| 
     49      assert_render_match( 
     50        %r{<input[\s]+[^>]*#{att}="#{value}".*>}, 
     51        "<r:search:form action='/search/'><r:input " +  
     52          add_atts.collect {|key, value| %{#{key}="#{value}"}}.join(" ") +  
     53          "/></r:search:form>" 
     54      ) 
     55    end 
     56  end 
     57   
     58  def test_tag_search_form_input_with_restricted_attributes 
     59    add_atts = {:name => "blah", :type => "checkbox"} 
     60     
     61    add_atts.each do |att, value| 
     62      assert_render_error "`search:form:input' tag can't contain `#{att}' attribute",  
     63        %{<r:search:form action='/search/'><r:input #{att}="#{value}" /> 
     64          </r:search:form>} 
     65    end 
     66  end 
     67   
     68  def test_tag_search_form_input_with_custom_id 
     69    ids = ['my_id1', 'my_id2'] 
     70    ids.each do |id| 
     71      assert_render_match %r{<input[\s]+[^/>]*id="#{id}"[\s]+[^\>]*/>}, 
     72        %{<r:search:form action="/search/"><r:input id="#{id}" /> 
     73          </r:search:form>} 
     74    end 
     75  end 
     76 
     77end 
  • vendor/extensions/search/test/functional/search_extension_test.rb

    old new  
    1010  end   
    1111   
    1212  def test_initialization 
    13     assert_equal RADIANT_ROOT + '/vendor/extensions/search', SearchExtension.root 
     13    assert_equal File.join(File.expand_path(RAILS_ROOT), 'vendor', 'extensions', 'search'), SearchExtension.root 
    1414    assert_equal 'Search', SearchExtension.extension_name 
    1515  end 
    1616   
  • vendor/extensions/search/test/fixtures/pages.yml

    old new  
     1homepage: 
     2  id: 1 
     3  title: Ruby Home Page 
     4  breadcrumb: Home 
     5  slug: / 
     6  status_id: 100 
     7  parent_id: 
     8  published_at: 2006-01-30 08:41:07  
     9search: 
     10  id: 2 
     11  title: Search 
     12  slug: search 
     13  status_id: 100 
     14  parent_id: 1 
     15  published_at: 2006-02-05 08:44:07 
     16  class_name: SearchPage 
     17services: 
     18  id: 3 
     19  title: Services Title 
     20  slug: services 
     21  status_id: 100 
     22  parent_id: 1 
     23  published_at: 2006-02-05 08:44:07 
  • vendor/extensions/search/test/fixtures/page_parts.yml

    old new  
     1# Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html 
     2homepage_body: 
     3  id: 1 
     4  name: body 
     5  content: This is the body portion of the Ruby home page. 
     6  page_id: 1 
     7homepage_extended: 
     8  id: 2 
     9  name: extended 
     10  content: This is an extended portion of the Ruby home page. 
     11  page_id: 1 
     12search_body: 
     13  id: 3 
     14  name: body 
     15  content: This is the body of the Search page. 
     16  page_id: 2 
     17services_body: 
     18  id: 4 
     19  name: body 
     20  content: We provide great services. 
     21  page_id: 3 
     22search_ajax_body: 
     23  id: 5 
     24  name: ajax_body 
     25  content: AJAX body of the Search page. 
     26  page_id: 2 
  • vendor/extensions/search/app/models/search_page.rb

    old new  
    77    tag.expand 
    88  end 
    99 
    10   desc %{    <r:search:form [label="Search:"] /> 
    11     Renders a search form, with the optional label.} 
    12   tag 'search:form' do |tag| 
    13     label = tag.attr['label'].nil? ? "Search:" : tag.attr['label'] 
    14     content = %{<form action="#{self.url.chop}" method="get" id="search_form"><p><label for="q">#{label}</label> <input type="text" id="q" name="q" value="" size="15" /></p></form>} 
    15     content << "\n" 
    16   end 
     10#  desc %{    <r:search:form [label="Search:"] /> 
     11#    Renders a search form, with the optional label.} 
     12#  tag 'search:form' do |tag| 
     13#    label = tag.attr['label'].nil? ? "Search:" : tag.attr['label'] 
     14#    content = %{<form action="#{self.url.chop}" method="get" id="search_form"><p><label for="q">#{label}</label> <input type="text" id="q" name="q" value="" size="15" /></p></form>} 
     15#    content << "\n" 
     16#  end 
    1717    
    1818  desc %{    Renders the passed query.} 
    1919  tag 'search:query' do |tag| 
     
    6464    @query_result = [] 
    6565    @query = "" 
    6666    q = @request.parameters[:q] 
     67    exclude_pages = (@request.parameters[:exclude_pages] || '').split(",") 
    6768    unless (@query = q.to_s.strip).blank? 
    6869      tokens = query.split.collect { |c| "%#{c.downcase}%"} 
    6970      pages = Page.find(:all, :include => [ :parts ], 
    7071          :conditions => [(["((LOWER(content) LIKE ?) OR (LOWER(title) LIKE ?))"] * tokens.size).join(" AND "),  
    7172                         *tokens.collect { |token| [token] * 2 }.flatten]) 
    72       @query_result = pages.delete_if { |p| !p.published? } 
     73      @query_result = pages.delete_if { |p| !p.published? ||  
     74        exclude_pages.include?(p.url) } 
    7375    end 
    74     lazy_initialize_parser_and_context 
    75     if layout 
    76       parse_object(layout) 
     76     
     77    if @request.xhr? 
     78      if part :ajax_body 
     79        render_part :ajax_body   
     80      else 
     81        render_part :body 
     82      end 
    7783    else 
    78       render_page_part(:body) 
     84      super 
    7985    end 
    8086  end 
    8187   
  • vendor/extensions/search/app/models/search_tags.rb

    old new  
     1module SearchTags 
     2  include Radiant::Taggable 
     3 
     4  desc %{    The namespace for all search tags.} 
     5  tag 'search' do |tag| 
     6    tag.expand 
     7  end 
     8   
     9  desc %{    Outputs <form> element of the search form. 
     10    <r:search:form action="controller_url"[ ajax_results="element_id"[ ajax_delay="500"]][exclude_pages="/url/1/,/url/2/"]> 
     11     
     12    action        - url to the page of type Search that will handle search request 
     13    ajax_results  - if you want your form to be AJAX enabled, specify ID of element that will contain search results 
     14    ajax_delay    - time of inactivity before search request submitted to the server 
     15    exclude_pages - comma separated list of page URLs to exclude from results. Don't forget trailing slashes 
     16     
     17    Any additional parameters will be added to the tag as attributes. 
     18    If you use AJAX enabled form you can have separate page part for displaying AJAX results. It should be called `ajax_body'.  
     19     
     20    *Usage:* 
     21<pre><code><r:search:form action="controller_url"> 
     22  <r:search:form:input /> 
     23  <input type="submit" /> 
     24  ... 
     25</r:search:form></code></pre> 
     26  } 
     27  tag 'search:form' do |tag| 
     28    unless action = tag.attr['action'] 
     29      raise StandardTags::TagError.new("`search:form' tag must contain `action' attribute") 
     30    end 
     31    result_id = tag.attr['ajax_results'] || nil 
     32    ajax_delay = tag.attr['ajax_delay'] || 500 
     33    exclude_pages = tag.attr['exclude_pages'] || '' 
     34    int_attrs = ['ajax_results', 'ajax_delay', 'exclude_pages'] 
     35    output = %{<form method="get"} + 
     36      tag.attr.delete_if {|key, value| int_attrs.include?(key) 
     37        }.collect {|key, value| %{#{key}="#{value}"}}.join(" ") + 
     38      %{> 
     39        <input type="hidden" name="exclude_pages" value="#{exclude_pages}" /> 
     40        #{tag.expand} 
     41      </form>} 
     42    # if result_id not given, skip AJAX form init 
     43    return output if result_id.nil? 
     44    # otherwise init AJAX 
     45    output << %{      <script type="text/javascript"> 
     46      var search_previous_value = ''; 
     47      var search_handler; 
     48      Event.observe("q", "keyup", function(ev){ 
     49        clearTimeout(search_handler); 
     50        search_form = Event.findElement(ev, 'form'); 
     51        search_handler = setTimeout(function(ev){ 
     52          if (this.getValue() == search_previous_value) { return; } 
     53          search_previous_value = this.getValue(); 
     54          new Ajax.Updater("#{result_id}", search_form.action, { 
     55            parameters: $(search_form).serialize(true), 
     56            evalScripts: true, 
     57            onLoading: function(){ 
     58              search_form.addClassName('loading'); 
     59            }, 
     60            onComplete: function(){ 
     61              search_form.removeClassName('loading'); 
     62              $("#{result_id}").show(); 
     63            } 
     64          }); 
     65        }.bind(this, ev), #{ajax_delay}); 
     66      }.bindAsEventListener($('q'))); 
     67    </script> 
     68    } 
     69  end 
     70   
     71  desc %{    Outputs input element for search form. All additional parameters will 
     72    be added as tag attributes. However don't use type and name. ID attribute 
     73    can be overwritten} 
     74  tag 'search:form:input' do |tag| 
     75    ["type", "name"].each do |attr| 
     76      if tag.attr[attr] 
     77        raise StandardTags::TagError.new("`search:form:input' tag can't contain `#{attr}' attribute") 
     78      end 
     79    end 
     80     
     81    tag_id = tag.attr['id'] || "q" 
     82     
     83    output = %{<input type="text" name="q" id="#{tag_id}" } +  
     84      tag.attr.delete_if {|attr, value| attr == 'id' 
     85      }.collect {|key, value| %{#{key}="#{value}"}}.join(" ") + 
     86      "/>" 
     87  end 
     88   
     89end 
  • vendor/extensions/search/search_extension.rb

    old new  
    99   
    1010  def activate 
    1111    # admin.tabs.add "Search", "/admin/search", :after => "Layouts", :visibility => [:all] 
     12    Page.send :include, SearchTags 
    1213    SearchPage 
    1314  end 
    1415