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 +23 -0
- data/app/controllers/kuhsaft/pages_controller.rb +7 -0
- data/app/helpers/kuhsaft/cms/admin_helper.rb +12 -0
- data/app/helpers/pages_helper.rb +10 -0
- data/app/models/kuhsaft/brick.rb +7 -0
- data/app/models/kuhsaft/page.rb +49 -19
- data/app/models/kuhsaft/page_type.rb +2 -2
- data/app/models/kuhsaft/text_brick.rb +11 -1
- data/app/views/kuhsaft/cms/admin/_content_language_switch.html.haml +1 -1
- data/app/views/kuhsaft/pages/index.html.haml +7 -0
- data/app/views/kuhsaft/search/_form.html.haml +2 -0
- data/app/views/kuhsaft/search/_results.html.haml +8 -0
- data/app/views/kuhsaft/search/_results_entry.html.haml +13 -0
- data/config/locales/views/kuhsaft/search/de.yml +12 -0
- data/config/locales/views/kuhsaft/search/en.yml +12 -0
- data/config/routes.rb +4 -2
- data/db/migrate/12_regenerate_fulltext.rb +12 -0
- data/lib/kuhsaft.rb +1 -0
- data/lib/kuhsaft/brick_list.rb +4 -1
- data/lib/kuhsaft/searchable.rb +72 -0
- data/lib/kuhsaft/version.rb +1 -1
- data/spec/controllers/kuhsaft/pages_controller_spec.rb +44 -2
- data/spec/dummy/app/controllers/application_controller.rb +1 -0
- data/spec/features/cms_pages_spec.rb +19 -4
- data/spec/features/search_spec.rb +80 -0
- data/spec/helpers/kuhsaft/pages_helper_spec.rb +21 -0
- data/spec/lib/searchable_spec.rb +30 -0
- data/spec/models/brick_spec.rb +24 -0
- data/spec/models/page_spec.rb +72 -12
- data/spec/models/text_brick_spec.rb +11 -1
- data/spec/support/default_url_options.rb +36 -0
- metadata +52 -4
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
|
data/app/helpers/pages_helper.rb
CHANGED
@@ -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
|
data/app/models/kuhsaft/brick.rb
CHANGED
@@ -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'
|
data/app/models/kuhsaft/page.rb
CHANGED
@@ -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,
|
10
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
@@ -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
|
-
|
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
|
@@ -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
|
data/config/routes.rb
CHANGED
@@ -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,
|
14
|
-
|
13
|
+
resources :pages,
|
14
|
+
:only => [:index],
|
15
|
+
:defaults => { :locale => I18n.locale }
|
16
|
+
match '(*url)' => 'pages#show', :as => :page
|
15
17
|
end
|
16
18
|
end
|
data/lib/kuhsaft.rb
CHANGED
data/lib/kuhsaft/brick_list.rb
CHANGED
@@ -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,
|
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
|
data/lib/kuhsaft/version.rb
CHANGED
@@ -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
|
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
|
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
|
@@ -13,12 +13,27 @@ describe 'Cms/Pages' do
|
|
13
13
|
end
|
14
14
|
|
15
15
|
describe '#create' do
|
16
|
-
|
17
|
-
|
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
|
-
|
21
|
-
|
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
|
data/spec/models/brick_spec.rb
CHANGED
@@ -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
|
data/spec/models/page_spec.rb
CHANGED
@@ -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.
|
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-
|
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:
|
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:
|
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
|