radiant-sitemap_search-extension 1.0.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/HELP.textile +11 -0
- data/README.textile +51 -0
- data/Rakefile +135 -0
- data/VERSION +1 -0
- data/app/controllers/admin/search_controller.rb +13 -0
- data/app/helpers/admin/search_helper.rb +31 -0
- data/app/views/admin/pages/index.html.haml +28 -0
- data/app/views/admin/pages/page_list_view.html.haml +27 -0
- data/app/views/admin/search/_banner.html.haml +11 -0
- data/app/views/admin/search/_banner_index_bottom.html.haml +5 -0
- data/app/views/admin/search/_layout.html.haml +8 -0
- data/app/views/admin/search/_layout_index_bottom.html.haml +5 -0
- data/app/views/admin/search/_results_nav.html.haml +15 -0
- data/app/views/admin/search/_search_action.html.haml +7 -0
- data/app/views/admin/search/_snippet.html.haml +8 -0
- data/app/views/admin/search/_snippet_index_bottom.html.haml +5 -0
- data/app/views/admin/search/_template.html.haml +8 -0
- data/app/views/admin/search/_template_index_bottom.html.haml +4 -0
- data/app/views/admin/search/index.html.haml +128 -0
- data/config/locales/en.yml +7 -0
- data/config/routes.rb +3 -0
- data/lib/sitemap_search/model.rb +41 -0
- data/lib/tasks/sitemap_search_extension_tasks.rake +28 -0
- data/sitemap_search_extension.rb +35 -0
- data/spec/controllers/search_controller_spec.rb +111 -0
- data/spec/models/page_extensions_spec.rb +36 -0
- data/spec/models/snippet_extensions_spec.rb +24 -0
- data/spec/spec.opts +6 -0
- data/spec/spec_helper.rb +37 -0
- metadata +114 -0
data/HELP.textile
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
The *Sitemap Search* extension, as its name implies, enables you to search your sitemap; pages, snippets and layouts
|
2
|
+
|
3
|
+
h2. Usage
|
4
|
+
|
5
|
+
The Search tab is located under 'Content' to the right of 'Pages'
|
6
|
+
|
7
|
+
The Search box is located in the lower bar of the index page for Pages, Snippets, Layouts.
|
8
|
+
If you have the the BannerRotator extension and/or the Templates extension installed, it will be on those index pages also.
|
9
|
+
And of course it is also on the Search Results page.
|
10
|
+
|
11
|
+
Enter your search string, and click the button. Results are sortable by attribute.
|
data/README.textile
ADDED
@@ -0,0 +1,51 @@
|
|
1
|
+
h2. Sitemap Search
|
2
|
+
|
3
|
+
Created by Sean Cribbs in Fall 2007.
|
4
|
+
Revised and generalized by Sean Cribbs and Andrew vonderLuft in August 2008.
|
5
|
+
Extended and refactored by Andrew vonderLuft since then.
|
6
|
+
|
7
|
+
Provides a simple content search of pages, snippets, and layouts.
|
8
|
+
Contains rudimentary support for scoping searches within a given site when using the multi_site extension.
|
9
|
+
|
10
|
+
h2. Revision History
|
11
|
+
|
12
|
+
* 4/2011 - UI upgrade for Radiant >= 1.0.0; added Layouts; added templates to search from "Templates extension":https://github.com/avonderluft/radiant-templates-extension
|
13
|
+
* 2/2010 - add banners to search, from "BannerRotator extension":https://github.com/avonderluft/radiant-banner_rotator-extension
|
14
|
+
* 2/2009 - upgraded for compatibility with Radiant 0.7
|
15
|
+
* 12/2008 - initial commit
|
16
|
+
|
17
|
+
h2. Requirements
|
18
|
+
|
19
|
+
* current version requires Radiant >= 1.0.0
|
20
|
+
* tag 0.7.2 available for earlier versions
|
21
|
+
|
22
|
+
page_list_view extension must be installed
|
23
|
+
|
24
|
+
From your RADIANT_ROOT:
|
25
|
+
|
26
|
+
<pre>
|
27
|
+
$ script/extension install page_list_view
|
28
|
+
</pre>
|
29
|
+
|
30
|
+
h2. Installation
|
31
|
+
|
32
|
+
From your RADIANT_ROOT:
|
33
|
+
|
34
|
+
<pre>
|
35
|
+
$ script/extension install sitemap_search
|
36
|
+
</pre>
|
37
|
+
|
38
|
+
Restart your radiant app, and you should be good to go.
|
39
|
+
|
40
|
+
h2. Usage
|
41
|
+
|
42
|
+
See the Help doc.
|
43
|
+
|
44
|
+
h2. To do
|
45
|
+
|
46
|
+
* Include page fields, e.g. 'keywords' and 'description'
|
47
|
+
|
48
|
+
h2. Acknowledgments
|
49
|
+
|
50
|
+
Thanks to Digital Pulp for funding the initial development of this extension as part of the Redken.com project.
|
51
|
+
Further support for generalization and release was provided by Con-way.
|
data/Rakefile
ADDED
@@ -0,0 +1,135 @@
|
|
1
|
+
begin
|
2
|
+
require 'jeweler'
|
3
|
+
Jeweler::Tasks.new do |gem|
|
4
|
+
gem.name = "radiant-sitemap_search-extension"
|
5
|
+
gem.summary = %Q{Sitemap Search Extension for Radiant CMS}
|
6
|
+
gem.description = %Q{Adds search feature for pages, snippets, layouts, et al.}
|
7
|
+
gem.email = "avonderluft@avlux.net"
|
8
|
+
gem.homepage = "https://github.com/avonderluft/radiant-sitemap_search-extension"
|
9
|
+
gem.authors = ['Andrew vonderLuft','Sean Cribbs']
|
10
|
+
gem.add_dependency 'radiant', ">=1.0.0"
|
11
|
+
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
12
|
+
end
|
13
|
+
rescue LoadError
|
14
|
+
puts "Jeweler (or a dependency) not available. This is only required if you plan to package banner_rotator as a gem."
|
15
|
+
end
|
16
|
+
# I think this is the one that should be moved to the extension Rakefile template
|
17
|
+
|
18
|
+
# In rails 1.2, plugins aren't available in the path until they're loaded.
|
19
|
+
# Check to see if the rspec plugin is installed first and require
|
20
|
+
# it if it is. If not, use the gem version.
|
21
|
+
|
22
|
+
# Determine where the RSpec plugin is by loading the boot
|
23
|
+
unless defined? RADIANT_ROOT
|
24
|
+
ENV["RAILS_ENV"] = "test"
|
25
|
+
case
|
26
|
+
when ENV["RADIANT_ENV_FILE"]
|
27
|
+
require File.dirname(ENV["RADIANT_ENV_FILE"]) + "/boot"
|
28
|
+
when File.dirname(__FILE__) =~ %r{vendor/radiant/vendor/extensions}
|
29
|
+
require "#{File.expand_path(File.dirname(__FILE__) + "/../../../../../")}/config/boot"
|
30
|
+
else
|
31
|
+
require "#{File.expand_path(File.dirname(__FILE__) + "/../../../")}/config/boot"
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
require 'rake'
|
36
|
+
require 'rake/rdoctask'
|
37
|
+
require 'rake/testtask'
|
38
|
+
|
39
|
+
rspec_base = File.expand_path(RADIANT_ROOT + '/vendor/plugins/rspec/lib')
|
40
|
+
$LOAD_PATH.unshift(rspec_base) if File.exist?(rspec_base)
|
41
|
+
require 'spec/rake/spectask'
|
42
|
+
# require 'spec/translator'
|
43
|
+
|
44
|
+
# Cleanup the RADIANT_ROOT constant so specs will load the environment
|
45
|
+
Object.send(:remove_const, :RADIANT_ROOT)
|
46
|
+
|
47
|
+
extension_root = File.expand_path(File.dirname(__FILE__))
|
48
|
+
|
49
|
+
task :default => :spec
|
50
|
+
task :stats => "spec:statsetup"
|
51
|
+
|
52
|
+
desc "Run all specs in spec directory"
|
53
|
+
Spec::Rake::SpecTask.new(:spec) do |t|
|
54
|
+
t.spec_opts = ['--options', "\"#{extension_root}/spec/spec.opts\""]
|
55
|
+
t.spec_files = FileList['spec/**/*_spec.rb']
|
56
|
+
end
|
57
|
+
|
58
|
+
namespace :spec do
|
59
|
+
desc "Run all specs in spec directory with RCov"
|
60
|
+
Spec::Rake::SpecTask.new(:rcov) do |t|
|
61
|
+
t.spec_opts = ['--options', "\"#{extension_root}/spec/spec.opts\""]
|
62
|
+
t.spec_files = FileList['spec/**/*_spec.rb']
|
63
|
+
t.rcov = true
|
64
|
+
t.rcov_opts = ['--exclude', 'spec', '--rails']
|
65
|
+
end
|
66
|
+
|
67
|
+
desc "Print Specdoc for all specs"
|
68
|
+
Spec::Rake::SpecTask.new(:doc) do |t|
|
69
|
+
t.spec_opts = ["--format", "specdoc", "--dry-run"]
|
70
|
+
t.spec_files = FileList['spec/**/*_spec.rb']
|
71
|
+
end
|
72
|
+
|
73
|
+
[:models, :controllers, :views, :helpers].each do |sub|
|
74
|
+
desc "Run the specs under spec/#{sub}"
|
75
|
+
Spec::Rake::SpecTask.new(sub) do |t|
|
76
|
+
t.spec_opts = ['--options', "\"#{extension_root}/spec/spec.opts\""]
|
77
|
+
t.spec_files = FileList["spec/#{sub}/**/*_spec.rb"]
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
# Hopefully no one has written their extensions in pre-0.9 style
|
82
|
+
# desc "Translate specs from pre-0.9 to 0.9 style"
|
83
|
+
# task :translate do
|
84
|
+
# translator = ::Spec::Translator.new
|
85
|
+
# dir = RAILS_ROOT + '/spec'
|
86
|
+
# translator.translate(dir, dir)
|
87
|
+
# end
|
88
|
+
|
89
|
+
# Setup specs for stats
|
90
|
+
task :statsetup do
|
91
|
+
require 'code_statistics'
|
92
|
+
::STATS_DIRECTORIES << %w(Model\ specs spec/models)
|
93
|
+
::STATS_DIRECTORIES << %w(View\ specs spec/views)
|
94
|
+
::STATS_DIRECTORIES << %w(Controller\ specs spec/controllers)
|
95
|
+
::STATS_DIRECTORIES << %w(Helper\ specs spec/views)
|
96
|
+
::CodeStatistics::TEST_TYPES << "Model specs"
|
97
|
+
::CodeStatistics::TEST_TYPES << "View specs"
|
98
|
+
::CodeStatistics::TEST_TYPES << "Controller specs"
|
99
|
+
::CodeStatistics::TEST_TYPES << "Helper specs"
|
100
|
+
::STATS_DIRECTORIES.delete_if {|a| a[0] =~ /test/}
|
101
|
+
end
|
102
|
+
|
103
|
+
namespace :db do
|
104
|
+
namespace :fixtures do
|
105
|
+
desc "Load fixtures (from spec/fixtures) into the current environment's database. Load specific fixtures using FIXTURES=x,y"
|
106
|
+
task :load => :environment do
|
107
|
+
require 'active_record/fixtures'
|
108
|
+
ActiveRecord::Base.establish_connection(RAILS_ENV.to_sym)
|
109
|
+
(ENV['FIXTURES'] ? ENV['FIXTURES'].split(/,/) : Dir.glob(File.join(RAILS_ROOT, 'spec', 'fixtures', '*.{yml,csv}'))).each do |fixture_file|
|
110
|
+
Fixtures.create_fixtures('spec/fixtures', File.basename(fixture_file, '.*'))
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
desc 'Generate documentation for the sitemap_search extension.'
|
118
|
+
Rake::RDocTask.new(:rdoc) do |rdoc|
|
119
|
+
rdoc.rdoc_dir = 'rdoc'
|
120
|
+
rdoc.title = 'SitemapSearchExtension'
|
121
|
+
rdoc.options << '--line-numbers' << '--inline-source'
|
122
|
+
rdoc.rdoc_files.include('README')
|
123
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
124
|
+
end
|
125
|
+
|
126
|
+
# For extensions that are in transition
|
127
|
+
desc 'Test the sitemap_search extension.'
|
128
|
+
Rake::TestTask.new(:test) do |t|
|
129
|
+
t.libs << 'lib'
|
130
|
+
t.pattern = 'test/**/*_test.rb'
|
131
|
+
t.verbose = true
|
132
|
+
end
|
133
|
+
|
134
|
+
# Load any custom rakefiles for extension
|
135
|
+
Dir[File.dirname(__FILE__) + '/tasks/*.rake'].sort.each { |f| require f }
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
1.0.0
|
@@ -0,0 +1,13 @@
|
|
1
|
+
class Admin::SearchController < ApplicationController
|
2
|
+
def index
|
3
|
+
if params[:q]
|
4
|
+
@pages = Page.search(params[:q]).uniq
|
5
|
+
if current_user.designer? || current_user.admin?
|
6
|
+
@snippets = Snippet.search(params[:q]).uniq
|
7
|
+
@layouts = Layout.search(params[:q]).uniq
|
8
|
+
@templates = Template.search(params[:q]).uniq if defined?(TemplatesExtension)
|
9
|
+
@banners = Banner.search(params[:q]).uniq if defined?(BannerRotatorExtension)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module Admin::SearchHelper
|
2
|
+
include Admin::ListViewHelper
|
3
|
+
|
4
|
+
def link_text(object)
|
5
|
+
case object
|
6
|
+
when Page
|
7
|
+
icon = object.virtual? ? 'virtual-page' : 'page'
|
8
|
+
%Q{#{image(icon, :height => 18)} <span class="title">#{object.title}</span>}
|
9
|
+
when Snippet
|
10
|
+
%Q{#{image('snippet', :height => 18)} <span class="title">#{object.name}</span>}
|
11
|
+
when Banner
|
12
|
+
%Q{#{image('pictures', :height => 18)} <span class="title">#{object.name}</span>}
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def link_url(object)
|
17
|
+
case object
|
18
|
+
when Page; edit_admin_page_path(:id => object)
|
19
|
+
when Snippet; edit_admin_snippet_path(:id => object)
|
20
|
+
when Banner; edit_admin_banner_path(:id => object)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def link_attrs(object)
|
25
|
+
{:title => object.url } if Page === object
|
26
|
+
end
|
27
|
+
|
28
|
+
def cell_class(object)
|
29
|
+
'page'
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
- @page_title = t('pages') + ' - ' + default_page_title
|
2
|
+
|
3
|
+
.outset
|
4
|
+
= render_region :top
|
5
|
+
%table.index#pages{:summary=>t('page_hierarchy')}
|
6
|
+
%thead
|
7
|
+
%tr
|
8
|
+
- render_region :sitemap_head do |sitemap_head|
|
9
|
+
- sitemap_head.title_column_header do
|
10
|
+
%th.name= t('page')
|
11
|
+
- sitemap_head.status_column_header do
|
12
|
+
%th.status= t('status')
|
13
|
+
- sitemap_head.actions_column_header do
|
14
|
+
%th.actions= t('modify')
|
15
|
+
%tbody
|
16
|
+
- if @homepage
|
17
|
+
= render_node @homepage
|
18
|
+
- else
|
19
|
+
%tr
|
20
|
+
%td.empty{:colspan => admin.page.index.node.length}= t('no_pages')
|
21
|
+
= render_region :bottom
|
22
|
+
|
23
|
+
#actions
|
24
|
+
%ul
|
25
|
+
- unless @homepage
|
26
|
+
%li= link_to image('plus') + " " + t("new_homepage"), new_admin_page_path, :class => 'action_button'
|
27
|
+
- else
|
28
|
+
= render :partial => 'admin/search/search_action'
|
@@ -0,0 +1,27 @@
|
|
1
|
+
- include_javascript 'admin/tablekit'
|
2
|
+
- include_javascript 'admin/ruledtable'
|
3
|
+
- @page_title = t('pages') + ' - ' + default_page_title
|
4
|
+
|
5
|
+
.outset
|
6
|
+
= render_region :top
|
7
|
+
%table.index.sortable{:summary=>"Sortable list of pages"}
|
8
|
+
%thead
|
9
|
+
%tr
|
10
|
+
- list_display_attributes.each do |att|
|
11
|
+
%th{:class => attribute_header_class(att), :id => attribute_header_id(att) }
|
12
|
+
%a
|
13
|
+
%span.w1= att.humanize
|
14
|
+
%tbody
|
15
|
+
- if @pages.any?
|
16
|
+
= render :partial => 'page', :collection => @pages
|
17
|
+
- else
|
18
|
+
%tr
|
19
|
+
%td.empty{:colspan => list_display_attributes.length}= t('no_pages')
|
20
|
+
#actions
|
21
|
+
= pagination_for(@pages)
|
22
|
+
%ul
|
23
|
+
- unless @pages.any?
|
24
|
+
%li= link_to image('plus') + " " + t("new_homepage"), new_admin_page_path, :class => 'action_button'
|
25
|
+
= render :partial => 'admin/search/search_action'
|
26
|
+
|
27
|
+
= render :partial => 'page_list_view_styles'
|
@@ -0,0 +1,11 @@
|
|
1
|
+
%tr[banner]
|
2
|
+
%td.preview
|
3
|
+
%a{:href => edit_admin_banner_url(banner)}
|
4
|
+
%img.snapshot{:src => banner.background_image, :alt => "#{banner.name} #{t('banner_rotator.banner')}", :title => "#{t('banner_rotator.edit')} #{banner.name} #{t('banner_rotator.banner')}"}
|
5
|
+
%td.name= link_to banner.name, edit_admin_banner_url(banner)
|
6
|
+
%td.status
|
7
|
+
= banner.description if banner.description
|
8
|
+
%td.status{:style=>"font-size:75%"}
|
9
|
+
= banner.link_url if banner.link_url
|
10
|
+
%td.status
|
11
|
+
= banner.updated_at.strftime("%m/%d/%Y at %I:%M %p") if banner.updated_at
|
@@ -0,0 +1,15 @@
|
|
1
|
+
.nav
|
2
|
+
= link_to t('pages') + " (#{@pages.size})", '#pages'
|
3
|
+
- if designer?
|
4
|
+
•
|
5
|
+
= link_to t('snippets') + " (#{@snippets.size})", '#snippets'
|
6
|
+
•
|
7
|
+
= link_to t('layouts') + " (#{@layouts.size})", '#layouts'
|
8
|
+
- if defined?(TemplatesExtension)
|
9
|
+
•
|
10
|
+
= link_to t('templates') + " (#{@templates.size})", '#templates'
|
11
|
+
- if defined?(BannerRotatorExtension)
|
12
|
+
•
|
13
|
+
= link_to t('banner_rotator.banners') + " (#{@banners.size})", '#banners'
|
14
|
+
•
|
15
|
+
= link_to 'Top', '#top'
|
@@ -0,0 +1,7 @@
|
|
1
|
+
%li
|
2
|
+
#site_search.action_button
|
3
|
+
- form_for :site_search, :url => search_path, :html => { :class => 'button_form', :multipart => true } do |f|
|
4
|
+
%span
|
5
|
+
= f.label :site_search, image('search')
|
6
|
+
= text_field_tag :q, params[:q], :title => t('sitemap_search.search_content'), :cols => "20"
|
7
|
+
= f.submit t('sitemap_search.search_content')
|
@@ -0,0 +1,8 @@
|
|
1
|
+
%tr[snippet]
|
2
|
+
%td.name
|
3
|
+
= image('snippet', :alt => '')
|
4
|
+
= link_to snippet.name, edit_admin_snippet_url(snippet)
|
5
|
+
%td.status
|
6
|
+
= snippet.updated_by.name if snippet.updated_by
|
7
|
+
%td.status
|
8
|
+
= snippet.updated_at.strftime("%m/%d/%Y at %I:%M %p") if snippet.updated_at
|
@@ -0,0 +1,8 @@
|
|
1
|
+
%tr[template]
|
2
|
+
%td.name
|
3
|
+
= image('template', :alt => '')
|
4
|
+
= link_to template.name, edit_admin_template_url(template)
|
5
|
+
%td.status
|
6
|
+
= template.page_class_name if template.page_class_name
|
7
|
+
%td.status
|
8
|
+
= template.updated_at.strftime("%m/%d/%Y at %I:%M %p") if template.updated_at
|
@@ -0,0 +1,128 @@
|
|
1
|
+
- include_javascript 'admin/ruledtable'
|
2
|
+
- include_javascript 'admin/tablekit'
|
3
|
+
- @page_title = t('sitemap_search.search_content') + ' - ' + default_page_title
|
4
|
+
|
5
|
+
- if params[:q]
|
6
|
+
- query = ': ' + t('sitemap_search.search_results_for') + " '#{params[:q]}'"
|
7
|
+
- results_label = t('pages') + query
|
8
|
+
%h3#pages{:style=>"margin-top:-10px"}
|
9
|
+
= results_label
|
10
|
+
= render :partial => 'results_nav'
|
11
|
+
%table#page_list.index.sortable{:summary=>results_label}
|
12
|
+
%thead
|
13
|
+
%tr
|
14
|
+
- list_display_attributes.each do |att|
|
15
|
+
%th{:class => attribute_header_class(att), :id => attribute_header_id(att) }
|
16
|
+
%a
|
17
|
+
%span.w1= att.humanize
|
18
|
+
%tbody
|
19
|
+
- unless @pages.blank?
|
20
|
+
= render :partial => "admin/pages/page", :collection => @pages
|
21
|
+
- else
|
22
|
+
%tr
|
23
|
+
%td.note{:colspan => list_display_attributes.size }
|
24
|
+
= params[:q].blank? ? "Empty search." : "No results from query."
|
25
|
+
|
26
|
+
- if designer?
|
27
|
+
- results_label = t('snippets') + query
|
28
|
+
%h3#snippets
|
29
|
+
= results_label
|
30
|
+
= render :partial => 'results_nav'
|
31
|
+
%table#snippet_list.index.sortable{:summary=>results_label}
|
32
|
+
%thead
|
33
|
+
%tr
|
34
|
+
- atts = %w{name updated_by updated_at}
|
35
|
+
- atts.each do |att|
|
36
|
+
%th{:class => attribute_header_class(att), :id => attribute_header_id(att) }
|
37
|
+
%a
|
38
|
+
%span.w1= att.humanize
|
39
|
+
%tbody
|
40
|
+
- unless @snippets.blank?
|
41
|
+
= render :partial => "snippet", :collection => @snippets
|
42
|
+
- else
|
43
|
+
%tr
|
44
|
+
%td.note{:colspan => atts.size }
|
45
|
+
= params[:q].blank? ? "Empty search." : "No results from query."
|
46
|
+
|
47
|
+
- results_label = t('layouts') + query
|
48
|
+
%h3#layouts
|
49
|
+
= results_label
|
50
|
+
= render :partial => 'results_nav'
|
51
|
+
%table#layout_list.index.sortable{:summary=>results_label}
|
52
|
+
%thead
|
53
|
+
%tr
|
54
|
+
- atts = %w{name updated_by updated_at}
|
55
|
+
- atts.each do |att|
|
56
|
+
%th{:class => attribute_header_class(att), :id => attribute_header_id(att) }
|
57
|
+
%a
|
58
|
+
%span.w1= att.humanize
|
59
|
+
%tbody
|
60
|
+
- unless @layouts.blank?
|
61
|
+
= render :partial => "layout", :collection => @layouts
|
62
|
+
- else
|
63
|
+
%tr
|
64
|
+
%td.note{:colspan => atts.size }
|
65
|
+
= params[:q].blank? ? "Empty search." : "No results from query."
|
66
|
+
|
67
|
+
- results_label = t('templates') + query
|
68
|
+
%h3#templates
|
69
|
+
= results_label
|
70
|
+
= render :partial => 'results_nav'
|
71
|
+
%table#template_list.index.sortable{:summary=>results_label}
|
72
|
+
%thead
|
73
|
+
%tr
|
74
|
+
- atts = %w{name page_class_name updated_at}
|
75
|
+
- atts.each do |att|
|
76
|
+
%th{:class => attribute_header_class(att), :id => attribute_header_id(att) }
|
77
|
+
%a
|
78
|
+
%span.w1= att.humanize
|
79
|
+
%tbody
|
80
|
+
- unless @templates.blank?
|
81
|
+
= render :partial => "template", :collection => @templates
|
82
|
+
- else
|
83
|
+
%tr
|
84
|
+
%td.note{:colspan => atts.size }
|
85
|
+
= params[:q].blank? ? "Empty search." : "No results from query."
|
86
|
+
|
87
|
+
- if defined?(BannerRotatorExtension)
|
88
|
+
- results_label = t('banner_rotator.banners') + query
|
89
|
+
%h3#banners
|
90
|
+
= results_label
|
91
|
+
= render :partial => 'results_nav'
|
92
|
+
%table#banner_list.index.sortable{:summary=>results_label}
|
93
|
+
%thead
|
94
|
+
%tr
|
95
|
+
%th.image
|
96
|
+
- atts = %w{name description link_url updated_at}
|
97
|
+
- atts.each do |att|
|
98
|
+
%th{:class => attribute_header_class(att), :id => attribute_header_id(att) }
|
99
|
+
%a
|
100
|
+
%span.w1= att.humanize
|
101
|
+
%tbody
|
102
|
+
- unless @banners.blank?
|
103
|
+
= render :partial => "banner", :collection => @banners
|
104
|
+
- else
|
105
|
+
%tr
|
106
|
+
%td.note{:colspan => atts.size + 1 }
|
107
|
+
= params[:q].blank? ? "Empty search." : "No results from query."
|
108
|
+
- else
|
109
|
+
%h3= t('sitemap_search.search_content')
|
110
|
+
= t('sitemap_search.instructions')
|
111
|
+
|
112
|
+
#actions
|
113
|
+
%ul
|
114
|
+
= render :partial => 'search_action'
|
115
|
+
|
116
|
+
= render :partial => 'admin/pages/page_list_view_styles'
|
117
|
+
- content_for :page_css do
|
118
|
+
:sass
|
119
|
+
.nav
|
120
|
+
float: right
|
121
|
+
font-size: 80%
|
122
|
+
h3
|
123
|
+
padding: 5px
|
124
|
+
td
|
125
|
+
.snapshot
|
126
|
+
height: 40px
|
127
|
+
max-width: 320px
|
128
|
+
border: 1px solid d5f0ff
|
data/config/routes.rb
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
module SitemapSearch::Model
|
2
|
+
def self.included(base)
|
3
|
+
base.extend ClassMethods
|
4
|
+
base.send :cattr_accessor, :search_fields
|
5
|
+
base.send :cattr_accessor, :includes
|
6
|
+
if base == Page
|
7
|
+
base.search_fields = [:title, :slug, :breadcrumb, :content]
|
8
|
+
base.search_fields << :draft_content if defined?(ConcurrentDraftExtension)
|
9
|
+
base.includes = [:parts]
|
10
|
+
elsif base == Snippet
|
11
|
+
base.search_fields = [:name, :content]
|
12
|
+
base.search_fields << :draft_content if defined?(ConcurrentDraftExtension)
|
13
|
+
elsif base == Layout
|
14
|
+
base.search_fields = [:name, :content]
|
15
|
+
base.search_fields << :draft_content if defined?(ConcurrentDraftExtension)
|
16
|
+
elsif defined?(TemplatesExtension) && base == Template
|
17
|
+
base.search_fields = [:name, :content]
|
18
|
+
elsif defined?(BannerRotatorExtension) && base == Banner
|
19
|
+
base.search_fields = [:name, :description, :background_image, :foreground_image, :link_url]
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
module ClassMethods
|
24
|
+
def search(query)
|
25
|
+
# split up the words in the query, clear out blank ones
|
26
|
+
words = query.strip.downcase.split(" ").reject(&:blank?)
|
27
|
+
# return no results if query was blank
|
28
|
+
return [] if words.empty?
|
29
|
+
# wrap each one in the matchers and replicate by the number of fields
|
30
|
+
wrapped_words = words.map {|word| ["%#{word}%"] * self.search_fields.size }
|
31
|
+
# Construct conditions for each field
|
32
|
+
field_conditions = self.search_fields.map {|field| "LOWER(#{field}) LIKE ?"}
|
33
|
+
# join them with with an OR
|
34
|
+
fields_per_word = field_conditions.join(" OR ")
|
35
|
+
# replicate the condition for each word, join them all with an AND
|
36
|
+
all_conditions = ([fields_per_word] * words.size).map {|stmt| "(#{stmt})"}.join(" AND ")
|
37
|
+
query_conditions = [all_conditions, wrapped_words].flatten
|
38
|
+
find(:all, :conditions => query_conditions, :joins => includes)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
namespace :radiant do
|
2
|
+
namespace :extensions do
|
3
|
+
namespace :sitemap_search do
|
4
|
+
|
5
|
+
desc "Runs the migration of the Sitemap Search extension"
|
6
|
+
task :migrate => :environment do
|
7
|
+
require 'radiant/extension_migrator'
|
8
|
+
if ENV["VERSION"]
|
9
|
+
SitemapSearchExtension.migrator.migrate(ENV["VERSION"].to_i)
|
10
|
+
else
|
11
|
+
SitemapSearchExtension.migrator.migrate
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
desc "Copies public assets of the Sitemap Search to the instance public/ directory."
|
16
|
+
task :update => :environment do
|
17
|
+
is_svn_or_dir = proc {|path| path =~ /\.svn/ || File.directory?(path) }
|
18
|
+
Dir[SitemapSearchExtension.root + "/public/**/*"].reject(&is_svn_or_dir).each do |file|
|
19
|
+
path = file.sub(SitemapSearchExtension.root, '')
|
20
|
+
directory = File.dirname(path)
|
21
|
+
puts "Copying #{path}..."
|
22
|
+
mkdir_p RAILS_ROOT + directory
|
23
|
+
cp file, RAILS_ROOT + path
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# Uncomment this if you reference any of your controllers in activate
|
2
|
+
# require_dependency 'application'
|
3
|
+
|
4
|
+
class SitemapSearchExtension < Radiant::Extension
|
5
|
+
version "#{File.read(File.expand_path(File.dirname(__FILE__)) + '/VERSION')}"
|
6
|
+
description "Adds simple search feature for pages, snippets, layouts, et al."
|
7
|
+
url "https://github.com/avonderluft/radiant-sitemap_search-extension"
|
8
|
+
|
9
|
+
def activate
|
10
|
+
unless defined?(PageListView)
|
11
|
+
raise "The Page List View extension is required - install from RAILS_ROOT with 'script/extension install page_list_view'"
|
12
|
+
end
|
13
|
+
tab "Content" do
|
14
|
+
add_item "Search", '/admin/search'
|
15
|
+
end
|
16
|
+
[Page, Snippet, Layout].each do |klass|
|
17
|
+
klass.send :include, SitemapSearch::Model
|
18
|
+
end
|
19
|
+
admin.snippet.index.bottom.delete 'new_button'
|
20
|
+
admin.snippet.index.add :bottom, 'admin/search/snippet_index_bottom'
|
21
|
+
admin.layout.index.bottom.delete 'new_button'
|
22
|
+
admin.layout.index.add :bottom, 'admin/search/layout_index_bottom'
|
23
|
+
if defined?(TemplatesExtension)
|
24
|
+
Template.send :include, SitemapSearch::Model
|
25
|
+
admin.template.index.bottom.delete 'new_button'
|
26
|
+
admin.template.index.add :bottom, 'admin/search/banner_index_bottom'
|
27
|
+
end
|
28
|
+
if defined?(BannerRotatorExtension)
|
29
|
+
Banner.send :include, SitemapSearch::Model
|
30
|
+
admin.banner.index.bottom.delete 'new_button'
|
31
|
+
admin.banner.index.add :bottom, 'admin/search/template_index_bottom'
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
@@ -0,0 +1,111 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../spec_helper'
|
2
|
+
|
3
|
+
describe Admin::SearchController do
|
4
|
+
dataset :users
|
5
|
+
|
6
|
+
[:admin, :designer].each do |user|
|
7
|
+
describe "search as user with #{user} privileges" do
|
8
|
+
before :each do
|
9
|
+
@page, @snippet, @layout = mock_model(Page), mock_model(Snippet), mock_model(Layout)
|
10
|
+
Page.stub!(:search).and_return([@page])
|
11
|
+
Snippet.stub!(:search).and_return([@snippet])
|
12
|
+
Layout.stub!(:search).and_return([@layout])
|
13
|
+
login_as user
|
14
|
+
end
|
15
|
+
|
16
|
+
def do_get
|
17
|
+
get :index, :q => 'radiant'
|
18
|
+
end
|
19
|
+
it "should search pages" do
|
20
|
+
Page.should_receive(:search).with('radiant').and_return([@page])
|
21
|
+
do_get
|
22
|
+
end
|
23
|
+
it "should search snippets" do
|
24
|
+
Snippet.should_receive(:search).with('radiant').and_return([@snippet])
|
25
|
+
do_get
|
26
|
+
end
|
27
|
+
it "should search layouts" do
|
28
|
+
Layout.should_receive(:search).with('radiant').and_return([@layout])
|
29
|
+
do_get
|
30
|
+
end
|
31
|
+
if defined?(TemplatesExtension)
|
32
|
+
it "should search templates" do
|
33
|
+
@banner = mock_model(Template)
|
34
|
+
Template.stub!(:search).and_return([@template])
|
35
|
+
Template.should_receive(:search).with('radiant').and_return([@template])
|
36
|
+
do_get
|
37
|
+
end
|
38
|
+
end
|
39
|
+
if defined?(BannerRotatorExtension)
|
40
|
+
it "should search banners" do
|
41
|
+
@banner = mock_model(Banner)
|
42
|
+
Banner.stub!(:search).and_return([@banner])
|
43
|
+
Banner.should_receive(:search).with('radiant').and_return([@banner])
|
44
|
+
do_get
|
45
|
+
end
|
46
|
+
end
|
47
|
+
it "should render the index template" do
|
48
|
+
do_get
|
49
|
+
response.should render_template('index')
|
50
|
+
end
|
51
|
+
it "should be successful" do
|
52
|
+
do_get
|
53
|
+
response.should be_success
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
[:existing, :non_admin].each do |user|
|
59
|
+
describe "search as user with #{user} privileges" do
|
60
|
+
before :each do
|
61
|
+
@page, @snippet, @layout = mock_model(Page), mock_model(Snippet), mock_model(Layout)
|
62
|
+
Page.stub!(:search).and_return([@page])
|
63
|
+
Snippet.stub!(:search).and_return([@snippet])
|
64
|
+
Layout.stub!(:search).and_return([@layout])
|
65
|
+
login_as user
|
66
|
+
end
|
67
|
+
|
68
|
+
def do_get
|
69
|
+
get :index, :q => 'radiant'
|
70
|
+
end
|
71
|
+
|
72
|
+
it "should search pages" do
|
73
|
+
Page.should_receive(:search).with('radiant').and_return([@page])
|
74
|
+
do_get
|
75
|
+
end
|
76
|
+
it "should not search snippets" do
|
77
|
+
Snippet.should_not_receive(:search).with('radiant')
|
78
|
+
do_get
|
79
|
+
end
|
80
|
+
it "should not search layouts" do
|
81
|
+
Layout.should_not_receive(:search).with('radiant')
|
82
|
+
do_get
|
83
|
+
end
|
84
|
+
if defined?(TemplatesExtension)
|
85
|
+
it "should not search templates" do
|
86
|
+
@banner = mock_model(Template)
|
87
|
+
Template.stub!(:search).and_return([@template])
|
88
|
+
Template.should_not_receive(:search).with('radiant')
|
89
|
+
do_get
|
90
|
+
end
|
91
|
+
end
|
92
|
+
if defined?(BannerRotatorExtension)
|
93
|
+
it "should not search banners" do
|
94
|
+
@banner = mock_model(Banner)
|
95
|
+
Banner.stub!(:search).and_return([@banner])
|
96
|
+
Banner.should_not_receive(:search).with('radiant')
|
97
|
+
do_get
|
98
|
+
end
|
99
|
+
end
|
100
|
+
it "should render the index template" do
|
101
|
+
do_get
|
102
|
+
response.should render_template('index')
|
103
|
+
end
|
104
|
+
it "should be successful" do
|
105
|
+
do_get
|
106
|
+
response.should be_success
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../spec_helper'
|
2
|
+
|
3
|
+
describe Page, "search" do
|
4
|
+
dataset :pages
|
5
|
+
|
6
|
+
before :each do
|
7
|
+
pages(:radius).update_attributes(:breadcrumb => "Wheat and Rye")
|
8
|
+
if defined?(ConcurrentDraft)
|
9
|
+
pages(:first).parts.first.update_attributes(:draft_content => "Special draft content")
|
10
|
+
else
|
11
|
+
pages(:first).parts.first.update_attributes(:content => "Special content")
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
it "should find pages from words in the title" do
|
16
|
+
Page.search('wheat').should include(pages(:radius))
|
17
|
+
end
|
18
|
+
it "should find pages from words in the breadcrumb" do
|
19
|
+
Page.search('f').should include(pages(:f))
|
20
|
+
end
|
21
|
+
# it "should find pages from words in the keywords" do
|
22
|
+
# Page.search('harmonious').should include(pages(:assorted))
|
23
|
+
# end
|
24
|
+
# it "should find pages from words in the description" do
|
25
|
+
# Page.search('sweet').should include(pages(:assorted))
|
26
|
+
# end
|
27
|
+
it "should find pages from words in the slug" do
|
28
|
+
Page.search('article-4').should include(pages(:article_4))
|
29
|
+
end
|
30
|
+
it "should find pages from words in the parts content" do
|
31
|
+
Page.search('world!').should include(pages(:home))
|
32
|
+
end
|
33
|
+
it "should find pages from words in the content" do
|
34
|
+
Page.search('special').should include(pages(:first))
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../spec_helper'
|
2
|
+
|
3
|
+
describe Snippet, "search" do
|
4
|
+
dataset :snippets
|
5
|
+
before :each do
|
6
|
+
if defined?(ConcurrentDraft)
|
7
|
+
snippets(:first).update_attributes(:draft_content => "Special draft content")
|
8
|
+
else
|
9
|
+
snippets(:first).update_attributes(:content => "Special content")
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
it "should find snippets from words in the name" do
|
14
|
+
Snippet.search('first').should include(snippets(:first))
|
15
|
+
end
|
16
|
+
|
17
|
+
it "should find snippets from words in the content" do
|
18
|
+
Snippet.search('Before').should include(snippets(:yielding))
|
19
|
+
end
|
20
|
+
|
21
|
+
it "should find snippets from words in the content" do
|
22
|
+
Snippet.search('special').should include(snippets(:first))
|
23
|
+
end
|
24
|
+
end
|
data/spec/spec.opts
ADDED
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
unless defined? RADIANT_ROOT
|
2
|
+
ENV["RAILS_ENV"] = "test"
|
3
|
+
case
|
4
|
+
when ENV["RADIANT_ENV_FILE"]
|
5
|
+
require ENV["RADIANT_ENV_FILE"]
|
6
|
+
when File.dirname(__FILE__) =~ %r{vendor/radiant/vendor/extensions}
|
7
|
+
require "#{File.expand_path(File.dirname(__FILE__) + "/../../../../../../")}/config/environment"
|
8
|
+
else
|
9
|
+
require "#{File.expand_path(File.dirname(__FILE__) + "/../../../../")}/config/environment"
|
10
|
+
end
|
11
|
+
end
|
12
|
+
require "#{RADIANT_ROOT}/spec/spec_helper"
|
13
|
+
|
14
|
+
if File.directory?(File.dirname(__FILE__) + "/scenarios")
|
15
|
+
Scenario.load_paths.unshift File.dirname(__FILE__) + "/scenarios"
|
16
|
+
end
|
17
|
+
if File.directory?(File.dirname(__FILE__) + "/matchers")
|
18
|
+
Dir[File.dirname(__FILE__) + "/matchers/*.rb"].each {|file| require file }
|
19
|
+
end
|
20
|
+
|
21
|
+
Spec::Runner.configure do |config|
|
22
|
+
# config.use_transactional_fixtures = true
|
23
|
+
# config.use_instantiated_fixtures = false
|
24
|
+
# config.fixture_path = RAILS_ROOT + '/spec/fixtures'
|
25
|
+
|
26
|
+
# You can declare fixtures for each behaviour like this:
|
27
|
+
# describe "...." do
|
28
|
+
# fixtures :table_a, :table_b
|
29
|
+
#
|
30
|
+
# Alternatively, if you prefer to declare them only once, you can
|
31
|
+
# do so here, like so ...
|
32
|
+
#
|
33
|
+
# config.global_fixtures = :table_a, :table_b
|
34
|
+
#
|
35
|
+
# If you declare global fixtures, be aware that they will be declared
|
36
|
+
# for all of your examples, even those that don't use them.
|
37
|
+
end
|
metadata
ADDED
@@ -0,0 +1,114 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: radiant-sitemap_search-extension
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 23
|
5
|
+
prerelease:
|
6
|
+
segments:
|
7
|
+
- 1
|
8
|
+
- 0
|
9
|
+
- 0
|
10
|
+
version: 1.0.0
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Andrew vonderLuft
|
14
|
+
- Sean Cribbs
|
15
|
+
autorequire:
|
16
|
+
bindir: bin
|
17
|
+
cert_chain: []
|
18
|
+
|
19
|
+
date: 2011-04-22 00:00:00 -07:00
|
20
|
+
default_executable:
|
21
|
+
dependencies:
|
22
|
+
- !ruby/object:Gem::Dependency
|
23
|
+
name: radiant
|
24
|
+
prerelease: false
|
25
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
26
|
+
none: false
|
27
|
+
requirements:
|
28
|
+
- - ">="
|
29
|
+
- !ruby/object:Gem::Version
|
30
|
+
hash: 23
|
31
|
+
segments:
|
32
|
+
- 1
|
33
|
+
- 0
|
34
|
+
- 0
|
35
|
+
version: 1.0.0
|
36
|
+
type: :runtime
|
37
|
+
version_requirements: *id001
|
38
|
+
description: Adds search feature for pages, snippets, layouts, et al.
|
39
|
+
email: avonderluft@avlux.net
|
40
|
+
executables: []
|
41
|
+
|
42
|
+
extensions: []
|
43
|
+
|
44
|
+
extra_rdoc_files:
|
45
|
+
- README.textile
|
46
|
+
files:
|
47
|
+
- HELP.textile
|
48
|
+
- README.textile
|
49
|
+
- Rakefile
|
50
|
+
- VERSION
|
51
|
+
- app/controllers/admin/search_controller.rb
|
52
|
+
- app/helpers/admin/search_helper.rb
|
53
|
+
- app/views/admin/pages/index.html.haml
|
54
|
+
- app/views/admin/pages/page_list_view.html.haml
|
55
|
+
- app/views/admin/search/_banner.html.haml
|
56
|
+
- app/views/admin/search/_banner_index_bottom.html.haml
|
57
|
+
- app/views/admin/search/_layout.html.haml
|
58
|
+
- app/views/admin/search/_layout_index_bottom.html.haml
|
59
|
+
- app/views/admin/search/_results_nav.html.haml
|
60
|
+
- app/views/admin/search/_search_action.html.haml
|
61
|
+
- app/views/admin/search/_snippet.html.haml
|
62
|
+
- app/views/admin/search/_snippet_index_bottom.html.haml
|
63
|
+
- app/views/admin/search/_template.html.haml
|
64
|
+
- app/views/admin/search/_template_index_bottom.html.haml
|
65
|
+
- app/views/admin/search/index.html.haml
|
66
|
+
- config/locales/en.yml
|
67
|
+
- config/routes.rb
|
68
|
+
- lib/sitemap_search/model.rb
|
69
|
+
- lib/tasks/sitemap_search_extension_tasks.rake
|
70
|
+
- sitemap_search_extension.rb
|
71
|
+
- spec/controllers/search_controller_spec.rb
|
72
|
+
- spec/models/page_extensions_spec.rb
|
73
|
+
- spec/models/snippet_extensions_spec.rb
|
74
|
+
- spec/spec.opts
|
75
|
+
- spec/spec_helper.rb
|
76
|
+
has_rdoc: true
|
77
|
+
homepage: https://github.com/avonderluft/radiant-sitemap_search-extension
|
78
|
+
licenses: []
|
79
|
+
|
80
|
+
post_install_message:
|
81
|
+
rdoc_options: []
|
82
|
+
|
83
|
+
require_paths:
|
84
|
+
- lib
|
85
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
86
|
+
none: false
|
87
|
+
requirements:
|
88
|
+
- - ">="
|
89
|
+
- !ruby/object:Gem::Version
|
90
|
+
hash: 3
|
91
|
+
segments:
|
92
|
+
- 0
|
93
|
+
version: "0"
|
94
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
95
|
+
none: false
|
96
|
+
requirements:
|
97
|
+
- - ">="
|
98
|
+
- !ruby/object:Gem::Version
|
99
|
+
hash: 3
|
100
|
+
segments:
|
101
|
+
- 0
|
102
|
+
version: "0"
|
103
|
+
requirements: []
|
104
|
+
|
105
|
+
rubyforge_project:
|
106
|
+
rubygems_version: 1.6.2
|
107
|
+
signing_key:
|
108
|
+
specification_version: 3
|
109
|
+
summary: Sitemap Search Extension for Radiant CMS
|
110
|
+
test_files:
|
111
|
+
- spec/controllers/search_controller_spec.rb
|
112
|
+
- spec/models/page_extensions_spec.rb
|
113
|
+
- spec/models/snippet_extensions_spec.rb
|
114
|
+
- spec/spec_helper.rb
|