kuhsaft 1.6.0 → 1.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/README.md CHANGED
@@ -226,6 +226,29 @@ Simply override the default partial for the main navigation in your app with you
226
226
  * Implement the `fulltext` method on your brick, return anything you want to be searchable.
227
227
  * Customize the edit form behaviour of your brick by overriding methods like `to_style_class?`. See the `Brick` and `BrickList` files for more methods.
228
228
 
229
+ ## Integrating search
230
+
231
+ Kuhsaft supports fulltext search when using PostgreSQL with a simple
232
+ LIKE fallback for any other ActiveRecord DB.
233
+
234
+ Add a call to the `search_page_form` helper in your views. This renders
235
+ the default search form. The query will be executed by kuhsaft.
236
+
237
+ # e.g. _footer.html.haml
238
+ = search_page_form
239
+
240
+ To customize the search and result views you can add your own partials
241
+ to your rails app. The following partials are overridable.
242
+
243
+ app/views/kuhsaft/search
244
+ ├── _form.html.haml # Search form
245
+ ├── _results.html.haml # Results list (@pages)
246
+ └── _results_entry.html.haml # Single result entry (@page)
247
+
248
+ When using PostgreSQL, an additional attribute `excerpt` will be
249
+ available on the page model. It includes a highlighted excerpt of the
250
+ matching `fulltext` column.
251
+
229
252
  # LICENSE
230
253
 
231
254
  See the file LICENSE.
@@ -2,6 +2,13 @@ module Kuhsaft
2
2
  class PagesController < ::ApplicationController
3
3
  respond_to :html
4
4
 
5
+ def index
6
+ @search = params[:search]
7
+ if @search.present?
8
+ @pages = Kuhsaft::Page.unscoped.published.content_page.search(@search)
9
+ end
10
+ end
11
+
5
12
  def show
6
13
  url = locale.to_s
7
14
  url += "/#{params[:url]}" if params[:url].present?
@@ -5,6 +5,18 @@ module Kuhsaft
5
5
  def render_language_switch?
6
6
  I18n.available_locales.size > 1
7
7
  end
8
+
9
+ def link_to_content_locale(locale)
10
+ action = params[:action]
11
+ if params[:action] == 'create'
12
+ action = 'new'
13
+ elsif params[:action] == 'update'
14
+ action = 'edit'
15
+ end
16
+
17
+ link_to locale.to_s.upcase, url_for(
18
+ :action => action, :content_locale => locale)
19
+ end
8
20
  end
9
21
  end
10
22
  end
@@ -65,4 +65,14 @@ module PagesHelper
65
65
  @content << content_tag(:p, t('kuhsaft.text_bricks.text_brick.read_less'), :class => 'read-less-text')
66
66
  end
67
67
  end
68
+
69
+ def search_page_form
70
+ form_tag kuhsaft.pages_path, :method => :get, :class => 'form-inline' do
71
+ if block_given?
72
+ yield
73
+ else
74
+ render 'kuhsaft/search/form'
75
+ end
76
+ end
77
+ end
68
78
  end
@@ -29,6 +29,13 @@ module Kuhsaft
29
29
  self.position ||= has_siblings? ? brick_list.bricks.maximum(:position).to_i + 1 : 1
30
30
  end
31
31
 
32
+ after_save do
33
+ # TODO: replace callback with fulltext row on each
34
+ # searchable model
35
+ brick_list.update_fulltext
36
+ brick_list.save!
37
+ end
38
+
32
39
  def to_edit_partial_path
33
40
  path = self.to_partial_path.split '/'
34
41
  path << 'edit'
@@ -1,26 +1,49 @@
1
1
  class Kuhsaft::Page < ActiveRecord::Base
2
+ include Kuhsaft::Engine.routes.url_helpers
2
3
  include Kuhsaft::Orderable
3
4
  include Kuhsaft::Translatable
4
5
  include Kuhsaft::BrickList
6
+ include Kuhsaft::Searchable
5
7
 
6
8
  has_ancestry
7
9
  acts_as_brick_list
8
10
 
9
- translate :title, :slug, :keywords, :description, :body, :redirect_url, :url, :fulltext
10
- attr_accessible :title, :slug, :redirect_url, :url, :page_type, :parent_id, :keywords, :description, :published
11
+ translate :title,
12
+ :slug,
13
+ :keywords,
14
+ :description,
15
+ :body,
16
+ :redirect_url,
17
+ :url
18
+
19
+ attr_accessible :title,
20
+ :slug,
21
+ :redirect_url,
22
+ :url,
23
+ :page_type,
24
+ :parent_id,
25
+ :keywords,
26
+ :description,
27
+ :published
11
28
 
12
29
  default_scope order('position ASC')
13
30
 
14
31
  scope :published, where(:published => Kuhsaft::PublishState::PUBLISHED)
15
- scope :search, lambda{ |term| published.where("`#{locale_attr(:fulltext)}` LIKE ?", "%#{term}%") }
16
- scope :navigation, lambda{ |slug| where(locale_attr(:slug) => slug).where(locale_attr(:page_type) => Kuhsaft::PageType::NAVIGATION) }
17
32
 
18
- before_validation :create_slug, :create_url, :collect_fulltext
33
+ # TODO: cleanup page_types (content pages => nil or PageType::CONTENT
34
+ scope :content_page, where(
35
+ ["page_type is NULL or page_type = ?",
36
+ Kuhsaft::PageType::CONTENT])
37
+
38
+ scope :navigation, lambda{ |slug|
39
+ where(locale_attr(:slug) => slug).where(
40
+ locale_attr(:page_type) => Kuhsaft::PageType::NAVIGATION) }
41
+
42
+ before_validation :create_slug, :create_url
19
43
 
20
44
  validates :title, :presence => true
21
45
  validates :slug, :presence => true
22
46
  validates :redirect_url, :presence => true, :if => :redirect?
23
- #validates :url, :uniqueness => true, :unless => :navigation?
24
47
 
25
48
  class << self
26
49
  def flat_tree(pages = nil)
@@ -72,19 +95,30 @@ class Kuhsaft::Page < ActiveRecord::Base
72
95
  if bricks.count == 0 && children.count > 0
73
96
  children.first.link
74
97
  else
75
- "/#{url}"
98
+ url_with_locale
76
99
  end
77
100
  end
78
101
 
102
+ # TODO: needs naming and routing refactoring (url/locale/path/slug)
103
+ def path_segments
104
+ paths = parent.present? ? parent.path_segments : []
105
+ paths << slug unless navigation?
106
+ paths
107
+ end
108
+
109
+ def url_without_locale
110
+ path_segments.join('/')
111
+ end
112
+
113
+ def url_with_locale
114
+ opts = { :locale => I18n.locale }
115
+ url = url_without_locale
116
+ opts[:url] = url if url.present?
117
+ page_path(opts)
118
+ end
119
+
79
120
  def create_url
80
- complete_slug = ''
81
- if parent.present?
82
- complete_slug << parent.url.to_s
83
- else
84
- complete_slug = "#{I18n.locale}"
85
- end
86
- complete_slug << "/#{self.slug}" unless navigation?
87
- self.url = complete_slug
121
+ self.url = url_with_locale[1..-1]
88
122
  end
89
123
 
90
124
  def create_slug
@@ -92,10 +126,6 @@ class Kuhsaft::Page < ActiveRecord::Base
92
126
  self.slug = title.downcase.parameterize if has_slug
93
127
  end
94
128
 
95
- def collect_fulltext
96
- self.fulltext =[super, title.to_s, keywords.to_s, description.to_s].join(' ')
97
- end
98
-
99
129
  def nesting_name
100
130
  num_dashes = parent_pages.size
101
131
  num_dashes = 0 if num_dashes < 0
@@ -3,9 +3,9 @@ module Kuhsaft
3
3
  REDIRECT = 'redirect'
4
4
  NAVIGATION = 'navigation'
5
5
  CONTENT = ''
6
-
6
+
7
7
  def self.all
8
8
  [CONTENT, REDIRECT, NAVIGATION]
9
9
  end
10
10
  end
11
- end
11
+ end
@@ -1,5 +1,10 @@
1
+ require 'htmlentities'
2
+
1
3
  module Kuhsaft
2
4
  class TextBrick < Brick
5
+ include ActionView::Helpers::SanitizeHelper
6
+ HTML_ENTITIES = HTMLEntities.new
7
+
3
8
  attr_accessible :text, :read_more_text
4
9
 
5
10
  def user_can_add_childs?
@@ -7,7 +12,12 @@ module Kuhsaft
7
12
  end
8
13
 
9
14
  def collect_fulltext
10
- [super, text, read_more_text].join(' ')
15
+ HTML_ENTITIES.decode(
16
+ strip_tags([
17
+ text,
18
+ read_more_text
19
+ ].compact.join(' ')).squish
20
+ )
11
21
  end
12
22
  end
13
23
  end
@@ -3,5 +3,5 @@
3
3
  %ul.nav.nav-pills
4
4
  - I18n.available_locales.each do |locale|
5
5
  %li{ :class => (:active if I18n.locale.to_s == locale.to_s) }
6
- = link_to locale.to_s.upcase, url_for(:content_locale => locale)
6
+ = link_to_content_locale(locale)
7
7
  .clear
@@ -0,0 +1,7 @@
1
+ .search.has-section-space-bottom
2
+ .row-fluid
3
+ %h1=t 'kuhsaft.search.results.title'
4
+ .row-fluid
5
+ = render 'kuhsaft/search/results'
6
+ .row-fluid
7
+ = search_page_form
@@ -0,0 +1,2 @@
1
+ = text_field_tag 'search', params[:search], :placeholder => t('.placeholder')
2
+ = submit_tag t('.search')
@@ -0,0 +1,8 @@
1
+ - if @pages.present?
2
+ %h2.success
3
+ = t('.number-of-results', :count => @pages.count)
4
+ %ul.search-results.success
5
+ - @pages.each do |page|
6
+ %li= render 'kuhsaft/search/results_entry', :page => page
7
+ - else
8
+ %h2.no-results= t('.no-results')
@@ -0,0 +1,13 @@
1
+ .link
2
+ = link_to page.link do
3
+ %h4= page.title
4
+ %h5= kuhsaft.page_url(:url => page.url_without_locale)
5
+ .summary
6
+ -if page.excerpt.present?
7
+ .excerpt!= page.excerpt
8
+ -elsif page.fulltext.present?
9
+ .fulltext!= excerpt(highlight(page.fulltext, @search), @search)
10
+
11
+ -if page.keywords.present?
12
+ .keywords
13
+ = page.keywords
@@ -0,0 +1,12 @@
1
+ de:
2
+ kuhsaft:
3
+ search:
4
+ results:
5
+ title: 'Suchresultate'
6
+ number-of-results:
7
+ one: '1 Resultat'
8
+ other: '%{count} Resultate'
9
+ no-results: 'Keine Resultate'
10
+ form:
11
+ placeholder: 'Ihre Suche'
12
+ search: 'Suchen'
@@ -0,0 +1,12 @@
1
+ en:
2
+ kuhsaft:
3
+ search:
4
+ results:
5
+ title: Search results
6
+ number-of-results:
7
+ one: '1 result'
8
+ other: '%{count} results'
9
+ no-results: 'No results'
10
+ form:
11
+ placeholder: 'Search'
12
+ search: 'Search'
@@ -10,7 +10,9 @@ Kuhsaft::Engine.routes.draw do
10
10
  end
11
11
 
12
12
  scope ":locale", :locale => /#{I18n.available_locales.join('|')}/ do
13
- resources :pages, :only => [:index]
14
- match '*url' => 'pages#show'
13
+ resources :pages,
14
+ :only => [:index],
15
+ :defaults => { :locale => I18n.locale }
16
+ match '(*url)' => 'pages#show', :as => :page
15
17
  end
16
18
  end
@@ -0,0 +1,12 @@
1
+ class RegenerateFulltext < ActiveRecord::Migration
2
+ def up
3
+ Kuhsaft::Page.all.each do |p|
4
+ I18n.available_locales.each do |locale|
5
+ I18n.with_locale do
6
+ puts "Save #{p.locale_attr(:fulltext)} #{p.link}"
7
+ p.save!
8
+ end
9
+ end
10
+ end
11
+ end
12
+ end
@@ -2,6 +2,7 @@ module Kuhsaft
2
2
  require 'kuhsaft/engine'
3
3
  require 'kuhsaft/orderable'
4
4
  require 'kuhsaft/translatable'
5
+ require 'kuhsaft/searchable'
5
6
  require 'kuhsaft/brick_list'
6
7
  require 'kuhsaft/partial_extractor'
7
8
  require 'simple_form'
@@ -3,7 +3,10 @@ module Kuhsaft
3
3
 
4
4
  def self.included(base)
5
5
  def base.acts_as_brick_list
6
- self.has_many :bricks, :class_name => 'Kuhsaft::Brick', :dependent => :destroy, :as => :brick_list
6
+ self.has_many :bricks,
7
+ :class_name => 'Kuhsaft::Brick',
8
+ :dependent => :destroy,
9
+ :as => :brick_list
7
10
  end
8
11
  end
9
12
 
@@ -0,0 +1,72 @@
1
+ require 'active_support/concern'
2
+ require 'pg_search'
3
+
4
+ module Kuhsaft
5
+ module Searchable
6
+ extend ActiveSupport::Concern
7
+
8
+ DICTIONARIES = {
9
+ :en => 'english',
10
+ :de => 'german',
11
+ }
12
+
13
+ def update_fulltext
14
+ self.fulltext = collect_fulltext
15
+ end
16
+
17
+ included do
18
+ unless included_modules.include?(BrickList)
19
+ raise 'Kuhsaft::Searchable needs Kuhsaft::BrickList to be included'
20
+ end
21
+
22
+ if included_modules.include?(Translatable)
23
+ translate :fulltext
24
+ else
25
+ attr_accessible :fulltext
26
+ end
27
+
28
+ before_validation :update_fulltext
29
+
30
+ if ActiveRecord::Base.connection.instance_values['config'][:adapter] == 'postgresql'
31
+ include ::PgSearch
32
+ cb = lambda do |query|
33
+ {
34
+ :against => {
35
+ locale_attr(:title) => 'A',
36
+ locale_attr(:keywords) => 'B',
37
+ locale_attr(:description) => 'C',
38
+ locale_attr(:fulltext) => 'C',
39
+ },
40
+ :query => query,
41
+ :using => { :tsearch => { :dictionary => DICTIONARIES[I18n.locale] || 'simple' }}
42
+ }
43
+ end
44
+ pg_search_scope :search_without_excerpt, cb
45
+ scope :search, lambda { |query|
46
+ ts_headline = sanitize_sql_array([
47
+ "ts_headline(%s, plainto_tsquery('%s')) AS excerpt",
48
+ locale_attr(:fulltext),
49
+ query
50
+ ])
51
+ search_without_excerpt(query).select(ts_headline)
52
+ }
53
+ else
54
+ # TODO: Tests run in this branch because dummy app uses mysql. Change it!
55
+ # define empty fallback excerpt attribute
56
+ attr_reader :excerpt
57
+ scope :search, lambda { |query|
58
+ if query.is_a? Hash
59
+ where("#{query.first[0]} LIKE ?", "%#{query.first[1]}%")
60
+ else
61
+ stmt = ""
62
+ stmt += "#{locale_attr(:keywords)} LIKE ? OR "
63
+ stmt += "#{locale_attr(:title)} LIKE ? OR "
64
+ stmt += "#{locale_attr(:description)} LIKE ? OR "
65
+ stmt += "#{locale_attr(:fulltext)} LIKE ?"
66
+ where(stmt, *(["%#{query}%"] * 4))
67
+ end
68
+ }
69
+ end
70
+ end
71
+ end
72
+ end
@@ -1,3 +1,3 @@
1
1
  module Kuhsaft
2
- VERSION = "1.6.0"
2
+ VERSION = "1.7.0"
3
3
  end
@@ -3,6 +3,24 @@ require 'spec_helper'
3
3
  describe Kuhsaft::PagesController do
4
4
  subject { described_class }
5
5
 
6
+ describe '#index' do
7
+ before do
8
+ @pages = [
9
+ create(:page, :page_type => Kuhsaft::PageType::CONTENT, :published => true, :fulltext_de => 'foobar'),
10
+ create(:page, :page_type => Kuhsaft::PageType::CONTENT, :published => true, :fulltext_de => 'barfoo')
11
+ ]
12
+ end
13
+
14
+ context 'with search parameter' do
15
+ it 'assigns the search results' do
16
+ I18n.with_locale :de do
17
+ get(:index, { :use_route => :kuhsaft, :search => 'foobar' })
18
+ end
19
+ assigns(:pages).should eq([@pages.first])
20
+ end
21
+ end
22
+ end
23
+
6
24
  describe '#show' do
7
25
  describe 'redirect' do
8
26
  around(:each) do |example|
@@ -14,7 +32,7 @@ describe Kuhsaft::PagesController do
14
32
  context 'when page is not a redirect page' do
15
33
  it 'responds with page' do
16
34
  page = FactoryGirl.create(:page, :slug => 'dumdidum', :url => 'de/dumdidum')
17
- get :show, { :url => page.slug, :use_route => :kuhsaft, :locale => :de }
35
+ get :show, { :url => page.slug, :use_route => :kuhsaft }
18
36
  assigns(:page).should eq(page)
19
37
  end
20
38
  end
@@ -22,7 +40,7 @@ describe Kuhsaft::PagesController do
22
40
  context 'when page is a redirect page' do
23
41
  it 'redirects to the redirected url' do
24
42
  page = FactoryGirl.create(:page, :page_type => 'redirect', :slug => 'dumdidum', :url => 'de/dumdidum', :redirect_url => 'de/redirect_page')
25
- get :show, { :url => page.slug, :use_route => :kuhsaft, :locale => :de }
43
+ get :show, { :url => page.slug, :use_route => :kuhsaft }
26
44
  expect(response).to redirect_to("/de/redirect_page")
27
45
  end
28
46
  end
@@ -52,5 +70,29 @@ describe Kuhsaft::PagesController do
52
70
  end
53
71
  end
54
72
  end
73
+
74
+ describe 'page type' do
75
+ around(:each) do |example|
76
+ I18n.with_locale :de do
77
+ example.run
78
+ end
79
+ end
80
+
81
+ context 'when page is not a redirect page' do
82
+ it 'responds with page' do
83
+ page = FactoryGirl.create(:page, :slug => 'dumdidum', :url => 'de/dumdidum')
84
+ get :show, { :url => page.slug, :use_route => :kuhsaft }
85
+ assigns(:page).should eq(page)
86
+ end
87
+ end
88
+
89
+ context 'when page is a redirect page' do
90
+ it 'redirects to the redirected url' do
91
+ page = FactoryGirl.create(:page, :page_type => 'redirect', :slug => 'dumdidum', :url => 'de/dumdidum', :redirect_url => 'de/redirect_page')
92
+ get :show, { :url => page.slug, :use_route => :kuhsaft }
93
+ expect(response).to redirect_to("/de/redirect_page")
94
+ end
95
+ end
96
+ end
55
97
  end
56
98
  end
@@ -1,3 +1,4 @@
1
1
  class ApplicationController < ActionController::Base
2
2
  protect_from_forgery
3
+ helper Kuhsaft::Engine.helpers
3
4
  end
@@ -13,12 +13,27 @@ describe 'Cms/Pages' do
13
13
  end
14
14
 
15
15
  describe '#create' do
16
- it 'creates a new page' do
17
- expect { click_on 'Create Seite' }.to change(Kuhsaft::Page, :count).by(1)
16
+ context 'when page is valid' do
17
+ it 'creates a new page' do
18
+ expect { click_on 'Create Seite' }.to change(Kuhsaft::Page, :count).by(1)
19
+ end
20
+
21
+ it 'is not possible to change the value in url' do
22
+ page.find('#page_url')['disabled'].should be_true
23
+ end
18
24
  end
19
25
 
20
- it 'is not possible to change the value in url' do
21
- page.find('#page_url')['disabled'].should be_true
26
+ context 'when page is invalid' do
27
+ it 'does not create a routing error by switching the locale' do
28
+ @page = FactoryGirl.create(:page, :title => 'DummyPage', :title_en => 'DummyEN', :slug => 'dummy_page')
29
+ visit kuhsaft.edit_cms_page_path(@page)
30
+ fill_in 'page_title', :with => ''
31
+ click_on 'Update Seite'
32
+ within '.nav-pills' do
33
+ click_on 'EN'
34
+ end
35
+ page.should have_content(@page.title_en)
36
+ end
22
37
  end
23
38
  end
24
39
 
@@ -0,0 +1,80 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'pages#index' do
4
+ context 'with search parameter' do
5
+ let! :page1 do
6
+ p = create :page,
7
+ :page_type => Kuhsaft::PageType::CONTENT,
8
+ :published => true,
9
+ :title => 'Chromodorididae Ardeadoris'
10
+ p.bricks << Kuhsaft::TextBrick.new(:locale => I18n.locale, :text => "#{'foo bar' * 300} Chromodorididae #{'foo bar' * 300}")
11
+ p.save!
12
+ p
13
+ end
14
+
15
+ let! :page2 do
16
+ create :page,
17
+ :page_type => Kuhsaft::PageType::CONTENT,
18
+ :published => true,
19
+ :title => 'Chromodorididae Berlanguella'
20
+ end
21
+
22
+ let! :page3 do
23
+ create :page,
24
+ :page_type => Kuhsaft::PageType::CONTENT,
25
+ :published => true,
26
+ :title => 'Gastropoda'
27
+ end
28
+
29
+ context 'with fulltext' do
30
+ before do
31
+ visit kuhsaft.pages_path(:locale => :en, :search => 'Chromodorididae')
32
+ end
33
+
34
+ it 'highlights search term in preview' do
35
+ within("ul.search-results strong.highlight") do
36
+ page.should have_content('Chromodorididae')
37
+ end
38
+ end
39
+
40
+ it 'truncates the text' do
41
+ find('.summary .fulltext').text.length.should < 200
42
+ end
43
+ end
44
+
45
+ context 'with multiple matches' do
46
+ before do
47
+ visit kuhsaft.pages_path(:locale => :en, :search => 'Chromodorididae')
48
+ end
49
+
50
+ it 'renders match count' do
51
+ page.should have_content("2 results")
52
+ end
53
+
54
+ it 'renders the search results list' do
55
+ within("ul.search-results.success") do
56
+ page.should have_content('Chromodorididae Ardeadoris')
57
+ page.should have_content('Chromodorididae Berlanguella')
58
+ page.should_not have_content('Gastropoda')
59
+ end
60
+ end
61
+
62
+ it 'renders links to the pages' do
63
+ within("ul.search-results.success") do
64
+ page.should have_link('Chromodorididae Ardeadoris', href: page1.link)
65
+ page.should have_link('Chromodorididae Berlanguella', href: page2.link)
66
+ end
67
+ end
68
+ end
69
+
70
+ context 'without matches' do
71
+ before do
72
+ visit kuhsaft.pages_path(:locale => :en, :search => 'foobar')
73
+ end
74
+
75
+ it 'renders match count' do
76
+ page.should have_content("No results")
77
+ end
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,21 @@
1
+ require 'spec_helper'
2
+
3
+ describe PagesHelper do
4
+ describe '#search_page_form' do
5
+
6
+ context 'without block' do
7
+ it 'renders the default search form' do
8
+ form = search_page_form
9
+ form.should have_css('form.form-inline')
10
+ form.should have_css('input[type=text]')
11
+ form.should have_css('input[type=submit]')
12
+ end
13
+ end
14
+
15
+ context 'with block' do
16
+ it 'calls the given block' do
17
+ expect { |b| search_page_form(&b) }.to yield_with_no_args
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,30 @@
1
+ require 'spec_helper'
2
+
3
+ describe Kuhsaft::Searchable do
4
+
5
+ context 'with missing includes' do
6
+ it 'raises exteption when class does not include Kuhsaft::Bricklist' do
7
+ expect {
8
+ class Foo
9
+ include Kuhsaft::Searchable
10
+ end
11
+ }.to raise_error(/needs Kuhsaft::BrickList to be included/)
12
+ end
13
+ end
14
+
15
+ context 'with Bricklist included' do
16
+ class SearchableDemo < ActiveRecord::Base
17
+ include Kuhsaft::BrickList
18
+ end
19
+
20
+ context 'without postgresql' do
21
+ it 'initializes scope' do
22
+ ActiveRecord::Base.connection.instance_values.should_not == 'postgresql'
23
+ SearchableDemo.should_receive :scope
24
+ SearchableDemo.class_eval do
25
+ include Kuhsaft::Searchable
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -107,4 +107,28 @@ describe Kuhsaft::Brick do
107
107
  brick.uploader?.should be_false
108
108
  end
109
109
  end
110
+
111
+ describe '#after_save' do
112
+ describe 'update_fulltext' do
113
+ let! :brick do
114
+ Kuhsaft::Brick.new.tap do |b|
115
+ b.type = Kuhsaft::BrickType.new
116
+ end
117
+ end
118
+
119
+ let! :brick_list do
120
+ p = create(:page)
121
+ p.bricks << brick
122
+ p.save
123
+ p
124
+ end
125
+
126
+ it 'updates fulltext on bricklist after saving a single brick' do
127
+ brick.brick_list.should_receive(:update_fulltext)
128
+ brick.brick_list.should_receive(:save!)
129
+ brick.text = 'foobar'
130
+ brick.save
131
+ end
132
+ end
133
+ end
110
134
  end
@@ -21,6 +21,10 @@ describe Kuhsaft::Page do
21
21
  it 'should only find published results' do
22
22
  Kuhsaft::Page.search('English Title').should be_all { |p| p.published? == true }
23
23
  end
24
+
25
+ it 'should find by using the old api' do
26
+ Kuhsaft::Page.search('English').should == Kuhsaft::Page.search('English')
27
+ end
24
28
  end
25
29
 
26
30
  describe '.position_of' do
@@ -72,6 +76,15 @@ describe Kuhsaft::Page do
72
76
  end
73
77
  end
74
78
 
79
+ describe '#content_page' do
80
+ it 'returns only content pages ("" or nil)' do
81
+ p1, p2, p3 = 3.times.map { create(:page) }
82
+ p2.update_attribute :page_type, Kuhsaft::PageType::REDIRECT
83
+ p3.update_attribute :page_type, nil
84
+ Kuhsaft::Page.content_page.should == [p1, p3]
85
+ end
86
+ end
87
+
75
88
  describe "#state_class" do
76
89
 
77
90
  let(:page) { Kuhsaft::Page.new }
@@ -259,6 +272,13 @@ describe Kuhsaft::Page do
259
272
  page.link.should eq('/en/news')
260
273
  end
261
274
  end
275
+
276
+ context 'when url part is empty' do
277
+ it 'strips the trailing slash' do
278
+ page = create(:page, :page_type => Kuhsaft::PageType::NAVIGATION)
279
+ page.link.should eq('/en')
280
+ end
281
+ end
262
282
  end
263
283
 
264
284
  describe '#navigation?' do
@@ -303,18 +323,6 @@ describe Kuhsaft::Page do
303
323
  page.save
304
324
  end
305
325
 
306
- it 'contains the title' do
307
- page.fulltext.should include('my title')
308
- end
309
-
310
- it 'contains the keywords' do
311
- page.fulltext.should include('key words')
312
- end
313
-
314
- it 'contains the description' do
315
- page.fulltext.should include('descrip tion')
316
- end
317
-
318
326
  it 'contains the page part content' do
319
327
  page.fulltext.should include('oh la la')
320
328
  end
@@ -333,4 +341,56 @@ describe Kuhsaft::Page do
333
341
  page.url.should be_present
334
342
  end
335
343
  end
344
+
345
+ describe '#url_without_locale' do
346
+ let :page do
347
+ create(:page, :slug => 'page')
348
+ end
349
+
350
+ context 'without parent' do
351
+ it 'returns url without leading /' do
352
+ page.url_without_locale.should_not start_with '/'
353
+ end
354
+
355
+ it 'returns a single slug' do
356
+ page.url_without_locale.should == 'page'
357
+ end
358
+ end
359
+
360
+ context 'when parent is navigation' do
361
+ let :parent do
362
+ create(:page, :page_type => Kuhsaft::PageType::NAVIGATION)
363
+ end
364
+
365
+ let :child do
366
+ create(:page, :slug => 'child', :parent => parent)
367
+ end
368
+
369
+ it 'returns url without leading /' do
370
+ child.url_without_locale.should_not start_with '/'
371
+ end
372
+
373
+ it 'does not concatenate the parent slug' do
374
+ child.url_without_locale.should == 'child'
375
+ end
376
+ end
377
+
378
+ context 'when parent is normal page' do
379
+ let :parent do
380
+ create(:page, :slug => 'parent')
381
+ end
382
+
383
+ let :child do
384
+ create(:page, :slug => 'child', :parent => parent)
385
+ end
386
+
387
+ it 'returns url without leading /' do
388
+ child.url_without_locale.should_not start_with '/'
389
+ end
390
+
391
+ it 'does not concatenate the parent slug' do
392
+ child.url_without_locale.should == 'parent/child'
393
+ end
394
+ end
395
+ end
336
396
  end
@@ -1,7 +1,6 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe Kuhsaft::TextBrick do
4
-
5
4
  let :text_brick do
6
5
  Kuhsaft::TextBrick.new
7
6
  end
@@ -17,4 +16,15 @@ describe Kuhsaft::TextBrick do
17
16
  text_brick.user_can_add_childs?.should be_false
18
17
  end
19
18
  end
19
+
20
+ describe '#collect_fulltext' do
21
+ before do
22
+ text_brick.text = '<div><b>foo</b> <b>bar</b></div>'
23
+ text_brick.read_more_text = '<div><span>foo</span><span>bar</span></div>'
24
+ end
25
+
26
+ it 'sanitizes text and read_more_text' do
27
+ text_brick.collect_fulltext.should == 'foo bar foobar'
28
+ end
29
+ end
20
30
  end
@@ -0,0 +1,36 @@
1
+ class ActionView::TestCase::TestController
2
+ def default_url_options(options={})
3
+ { :locale => I18n.locale }.merge options
4
+ end
5
+ end
6
+
7
+ # The following snippet breaks url_for
8
+ # as described by franca
9
+ # https://github.com/screenconcept/shoestrap/issues/23
10
+ # <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
11
+ #class ActionDispatch::Routing::RouteSet
12
+ #def default_url_options(options={})
13
+ #{ :locale => I18n.locale }
14
+ #end
15
+ #end
16
+ # >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
17
+
18
+ # Fixes the missing default locale problem in controller specs
19
+ # See http://www.ruby-forum.com/topic/3448797#1041659
20
+ class ActionController::TestCase
21
+ module Behavior
22
+ def process_with_default_locale(action, parameters = nil, session = nil, flash = nil, http_method = 'GET')
23
+ parameters = { :locale => I18n.locale }.merge( parameters || {} )
24
+ process_without_default_locale(action, parameters, session, flash, http_method)
25
+ end
26
+ alias_method_chain :process, :default_locale
27
+ end
28
+ end
29
+
30
+ module ActionDispatch::Assertions::RoutingAssertions
31
+ def assert_recognizes_with_default_locale(expected_options, path, extras = {}, message=nil)
32
+ expected_options = { :locale => I18n.locale.to_s }.merge(expected_options || {} )
33
+ assert_recognizes_without_default_locale(expected_options, path, extras, message)
34
+ end
35
+ alias_method_chain :assert_recognizes, :default_locale
36
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: kuhsaft
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.6.0
4
+ version: 1.7.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -13,7 +13,7 @@ authors:
13
13
  autorequire:
14
14
  bindir: bin
15
15
  cert_chain: []
16
- date: 2013-05-27 00:00:00.000000000 Z
16
+ date: 2013-05-31 00:00:00.000000000 Z
17
17
  dependencies:
18
18
  - !ruby/object:Gem::Dependency
19
19
  name: rspec
@@ -319,6 +319,38 @@ dependencies:
319
319
  - - '='
320
320
  - !ruby/object:Gem::Version
321
321
  version: 4.0.2
322
+ - !ruby/object:Gem::Dependency
323
+ name: pg_search
324
+ requirement: !ruby/object:Gem::Requirement
325
+ none: false
326
+ requirements:
327
+ - - ! '>='
328
+ - !ruby/object:Gem::Version
329
+ version: '0'
330
+ type: :runtime
331
+ prerelease: false
332
+ version_requirements: !ruby/object:Gem::Requirement
333
+ none: false
334
+ requirements:
335
+ - - ! '>='
336
+ - !ruby/object:Gem::Version
337
+ version: '0'
338
+ - !ruby/object:Gem::Dependency
339
+ name: htmlentities
340
+ requirement: !ruby/object:Gem::Requirement
341
+ none: false
342
+ requirements:
343
+ - - ! '>='
344
+ - !ruby/object:Gem::Version
345
+ version: '0'
346
+ type: :runtime
347
+ prerelease: false
348
+ version_requirements: !ruby/object:Gem::Requirement
349
+ none: false
350
+ requirements:
351
+ - - ! '>='
352
+ - !ruby/object:Gem::Version
353
+ version: '0'
322
354
  description: Kuhsaft is a Rails engine that offers a simple CMS.
323
355
  email: developers@screenconcept.ch
324
356
  executables: []
@@ -402,9 +434,13 @@ files:
402
434
  - app/views/kuhsaft/image_bricks/image_brick/_edit.html.haml
403
435
  - app/views/kuhsaft/link_bricks/_link_brick.html.haml
404
436
  - app/views/kuhsaft/link_bricks/link_brick/_edit.html.haml
437
+ - app/views/kuhsaft/pages/index.html.haml
405
438
  - app/views/kuhsaft/pages/show.html.haml
406
439
  - app/views/kuhsaft/placeholder_bricks/_placeholder_brick.html.haml
407
440
  - app/views/kuhsaft/placeholder_bricks/placeholder_brick/_edit.html.haml
441
+ - app/views/kuhsaft/search/_form.html.haml
442
+ - app/views/kuhsaft/search/_results.html.haml
443
+ - app/views/kuhsaft/search/_results_entry.html.haml
408
444
  - app/views/kuhsaft/slider_bricks/_slider_brick.html.haml
409
445
  - app/views/kuhsaft/slider_bricks/slider_brick/_edit.html.haml
410
446
  - app/views/kuhsaft/text_bricks/_text_brick.html.haml
@@ -438,6 +474,8 @@ files:
438
474
  - config/locales/views/kuhsaft/cms/bricks/de.yml
439
475
  - config/locales/views/kuhsaft/cms/pages/de.yml
440
476
  - config/locales/views/kuhsaft/cms/video_bricks/de.yml
477
+ - config/locales/views/kuhsaft/search/de.yml
478
+ - config/locales/views/kuhsaft/search/en.yml
441
479
  - config/locales/views/kuhsaft/text_brick/de.yml
442
480
  - config/locales/views/layouts/de.yml
443
481
  - config/routes.rb
@@ -452,6 +490,7 @@ files:
452
490
  - db/migrate/09_add_additional_fields_to_kuhsaft_bricks.rb
453
491
  - db/migrate/10_add_redirect_url_to_kuhsaft_pages.rb
454
492
  - db/migrate/11_update_url_and_redirect_url_value.rb
493
+ - db/migrate/12_regenerate_fulltext.rb
455
494
  - db/seeds.rb
456
495
  - lib/generators/kuhsaft/assets/install_generator.rb
457
496
  - lib/generators/kuhsaft/translations/add_generator.rb
@@ -459,6 +498,7 @@ files:
459
498
  - lib/kuhsaft/engine.rb
460
499
  - lib/kuhsaft/orderable.rb
461
500
  - lib/kuhsaft/partial_extractor.rb
501
+ - lib/kuhsaft/searchable.rb
462
502
  - lib/kuhsaft/translatable.rb
463
503
  - lib/kuhsaft/version.rb
464
504
  - lib/kuhsaft.rb
@@ -505,10 +545,13 @@ files:
505
545
  - spec/dummy/script/rails
506
546
  - spec/factories.rb
507
547
  - spec/features/cms_pages_spec.rb
548
+ - spec/features/search_spec.rb
508
549
  - spec/helpers/kuhsaft/cms/admin_helper_spec.rb
550
+ - spec/helpers/kuhsaft/pages_helper_spec.rb
509
551
  - spec/kuhsaft_spec.rb
510
552
  - spec/lib/brick_list_spec.rb
511
553
  - spec/lib/engine_spec.rb
554
+ - spec/lib/searchable_spec.rb
512
555
  - spec/lib/translatable_spec.rb
513
556
  - spec/models/accordion_brick_spec.rb
514
557
  - spec/models/accordion_item_brick_spec.rb
@@ -529,6 +572,7 @@ files:
529
572
  - spec/models/two_column_brick_spec.rb
530
573
  - spec/models/video_brick_spec.rb
531
574
  - spec/spec_helper.rb
575
+ - spec/support/default_url_options.rb
532
576
  - spec/support/kuhsaft_spec_helper.rb
533
577
  homepage: http://github.com/screenconcept/kuhsaft
534
578
  licenses: []
@@ -544,7 +588,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
544
588
  version: '0'
545
589
  segments:
546
590
  - 0
547
- hash: 1502563843709159469
591
+ hash: 2803199904668343026
548
592
  required_rubygems_version: !ruby/object:Gem::Requirement
549
593
  none: false
550
594
  requirements:
@@ -553,7 +597,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
553
597
  version: '0'
554
598
  segments:
555
599
  - 0
556
- hash: 1502563843709159469
600
+ hash: 2803199904668343026
557
601
  requirements: []
558
602
  rubyforge_project: kuhsaft
559
603
  rubygems_version: 1.8.24
@@ -597,10 +641,13 @@ test_files:
597
641
  - spec/dummy/script/rails
598
642
  - spec/factories.rb
599
643
  - spec/features/cms_pages_spec.rb
644
+ - spec/features/search_spec.rb
600
645
  - spec/helpers/kuhsaft/cms/admin_helper_spec.rb
646
+ - spec/helpers/kuhsaft/pages_helper_spec.rb
601
647
  - spec/kuhsaft_spec.rb
602
648
  - spec/lib/brick_list_spec.rb
603
649
  - spec/lib/engine_spec.rb
650
+ - spec/lib/searchable_spec.rb
604
651
  - spec/lib/translatable_spec.rb
605
652
  - spec/models/accordion_brick_spec.rb
606
653
  - spec/models/accordion_item_brick_spec.rb
@@ -621,4 +668,5 @@ test_files:
621
668
  - spec/models/two_column_brick_spec.rb
622
669
  - spec/models/video_brick_spec.rb
623
670
  - spec/spec_helper.rb
671
+ - spec/support/default_url_options.rb
624
672
  - spec/support/kuhsaft_spec_helper.rb