storefront 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Rakefile +76 -0
- data/init.rb +1 -0
- data/lib/storefront.rb +21 -0
- data/lib/storefront/dashboard.rb +184 -0
- data/lib/storefront/form.rb +65 -0
- data/lib/storefront/form/elements.rb +101 -0
- data/lib/storefront/form/errors.rb +57 -0
- data/lib/storefront/form/fields.rb +420 -0
- data/lib/storefront/form/hints.rb +18 -0
- data/lib/storefront/form/inputs.rb +254 -0
- data/lib/storefront/form/labels.rb +81 -0
- data/lib/storefront/form/model.rb +84 -0
- data/lib/storefront/helpers/attribute_helper.rb +60 -0
- data/lib/storefront/helpers/body_helper.rb +18 -0
- data/lib/storefront/helpers/browser_helper.rb +54 -0
- data/lib/storefront/helpers/cache_helper.rb +25 -0
- data/lib/storefront/helpers/collection_helper.rb +14 -0
- data/lib/storefront/helpers/component_helper.rb +35 -0
- data/lib/storefront/helpers/dashboard_helper.rb +37 -0
- data/lib/storefront/helpers/debug_helper.rb +11 -0
- data/lib/storefront/helpers/definition_list_helper.rb +29 -0
- data/lib/storefront/helpers/error_helper.rb +11 -0
- data/lib/storefront/helpers/flash_helper.rb +14 -0
- data/lib/storefront/helpers/form_helper.rb +8 -0
- data/lib/storefront/helpers/format_helper.rb +59 -0
- data/lib/storefront/helpers/head_helper.rb +229 -0
- data/lib/storefront/helpers/image_helper.rb +54 -0
- data/lib/storefront/helpers/list_helper.rb +47 -0
- data/lib/storefront/helpers/locale_helper.rb +175 -0
- data/lib/storefront/helpers/open_graph_helper.rb +5 -0
- data/lib/storefront/helpers/page_helper.rb +73 -0
- data/lib/storefront/helpers/presenter_helper.rb +5 -0
- data/lib/storefront/helpers/request_helper.rb +66 -0
- data/lib/storefront/helpers/semantic/location_helper.rb +18 -0
- data/lib/storefront/helpers/semantic/time_helper.rb +14 -0
- data/lib/storefront/helpers/sidebar_helper.rb +27 -0
- data/lib/storefront/helpers/system_helper.rb +18 -0
- data/lib/storefront/helpers/table_helper.rb +38 -0
- data/lib/storefront/helpers/time_helper.rb +20 -0
- data/lib/storefront/helpers/url_helper.rb +32 -0
- data/lib/storefront/helpers/user_helper.rb +15 -0
- data/lib/storefront/helpers/widget_helper.rb +10 -0
- data/lib/storefront/helpers/workflow_helper.rb +50 -0
- data/lib/storefront/microdata/address.rb +7 -0
- data/lib/storefront/microdata/event.rb +13 -0
- data/lib/storefront/microdata/geo.rb +7 -0
- data/lib/storefront/microdata/organization.rb +7 -0
- data/lib/storefront/microdata/person.rb +7 -0
- data/lib/storefront/microformat/event.rb +7 -0
- data/lib/storefront/microformat/person.rb +7 -0
- data/lib/storefront/railtie.rb +28 -0
- data/lib/storefront/table.rb +191 -0
- data/rails/init.rb +1 -0
- data/test/form_helper_test.rb +5 -0
- data/test/support/mock_controller.rb +15 -0
- data/test/support/mock_response.rb +14 -0
- data/test/support/models.rb +0 -0
- data/test/test_helper.rb +34 -0
- metadata +111 -0
@@ -0,0 +1,18 @@
|
|
1
|
+
module Storefront
|
2
|
+
module BodyHelper
|
3
|
+
def body_id(string)
|
4
|
+
@body_id = string
|
5
|
+
end
|
6
|
+
|
7
|
+
def body_class(string)
|
8
|
+
@body_class = string
|
9
|
+
end
|
10
|
+
|
11
|
+
def body_attributes(options = {})
|
12
|
+
options[:class] = @body_class if @body_class
|
13
|
+
options[:id] = @body_id if @body_id
|
14
|
+
|
15
|
+
options
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
module Storefront
|
2
|
+
module BrowserHelper
|
3
|
+
# check the current browser (via user agent) for the following:
|
4
|
+
# :mozilla / :firefox
|
5
|
+
# :ie6
|
6
|
+
# :ie7
|
7
|
+
# :ie
|
8
|
+
# :iphone
|
9
|
+
# :safari
|
10
|
+
def browser_is? name
|
11
|
+
name = name.to_s.strip
|
12
|
+
|
13
|
+
return true if browser_name == name
|
14
|
+
return true if (name == 'mozilla' or name == "firefox") && browser_name == 'gecko'
|
15
|
+
return true if name == 'ie6' && browser_name.index('ie6')
|
16
|
+
return true if name == 'ie7' && browser_name.index('ie7')
|
17
|
+
return true if name == 'ie' && browser_name.index('ie')
|
18
|
+
return true if name == 'iphone' && browser_name == 'iphone'
|
19
|
+
return true if name == 'webkit' && browser_name == 'safari'
|
20
|
+
end
|
21
|
+
|
22
|
+
# find the current browser name
|
23
|
+
def browser_name
|
24
|
+
@browser_name ||= begin
|
25
|
+
ua = request.user_agent.to_s.downcase
|
26
|
+
if ua.index('msie') && !ua.index('opera') && !ua.index('webtv')
|
27
|
+
'ie'+ua[ua.index('msie')+5].chr
|
28
|
+
elsif ua.index('gecko/')
|
29
|
+
'gecko'
|
30
|
+
elsif ua.index('opera')
|
31
|
+
'opera'
|
32
|
+
elsif ua.index('konqueror')
|
33
|
+
'konqueror'
|
34
|
+
elsif ua.index('iphone')
|
35
|
+
'iphone'
|
36
|
+
elsif ua.index('applewebkit/')
|
37
|
+
'safari'
|
38
|
+
elsif ua.index('mozilla/')
|
39
|
+
'gecko'
|
40
|
+
else
|
41
|
+
""
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def ip_address
|
47
|
+
request.env['REMOTE_ADDR']
|
48
|
+
end
|
49
|
+
|
50
|
+
def user_agent
|
51
|
+
request.user_agent
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Storefront
|
2
|
+
module CacheHelper
|
3
|
+
def optional_cache(key, &block)
|
4
|
+
cache(key, &block) unless session[:disable_caching]
|
5
|
+
end
|
6
|
+
|
7
|
+
def conditional_cache(key, options = {}, &block)
|
8
|
+
condition = true
|
9
|
+
condition = false if options.has_key?(:if) && options[:if] != true
|
10
|
+
if condition
|
11
|
+
cache(key, &block)
|
12
|
+
else
|
13
|
+
haml_concat capture_haml(&block)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def set_cache_buster
|
18
|
+
if request.xhr?
|
19
|
+
response.headers["Cache-Control"] = "no-cache, no-store, max-age=0, must-revalidate"
|
20
|
+
response.headers["Pragma"] = "no-cache"
|
21
|
+
response.headers["Expires"] = "Fri, 01 Jan 1990 00:00:00 GMT"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module Storefront
|
2
|
+
# Encapsulates common browser hacks
|
3
|
+
module ComponentHelper
|
4
|
+
def component_image_tag
|
5
|
+
capture_haml do
|
6
|
+
haml_tag :span
|
7
|
+
haml_concat image_tag()
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
def popup_button(label, path, message, subtitle, submit = nil, kind = :group)
|
12
|
+
link_to "<span>#{label}</span>", path, :class => "delete", "data-message" => message, "data-subtitle" => subtitle, "data-model" => kind.to_s, "data-submit" => submit.to_s
|
13
|
+
end
|
14
|
+
|
15
|
+
def component_button(*args, &block)
|
16
|
+
options = args.extract_options!
|
17
|
+
button_options = options.delete(:button_options) || {}
|
18
|
+
|
19
|
+
if block_given?
|
20
|
+
content = capture(&block)
|
21
|
+
else
|
22
|
+
label = args.shift
|
23
|
+
path = args.shift
|
24
|
+
content = link_to(label, path, button_options)
|
25
|
+
end
|
26
|
+
|
27
|
+
capture_haml do
|
28
|
+
haml_tag :button, options.merge(:class => ["button", options[:class]].compact.uniq.join(" ")) do
|
29
|
+
haml_concat content
|
30
|
+
haml_tag :span
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module Storefront
|
2
|
+
module DashboardHelper
|
3
|
+
def dashboard_for(*args, &block)
|
4
|
+
record = args.shift
|
5
|
+
options = args.extract_options!
|
6
|
+
condition = extract_condition!(options)
|
7
|
+
@dashboard_record = record
|
8
|
+
concat(capture(&block)) if record && condition != false
|
9
|
+
@dashboard_record = nil
|
10
|
+
end
|
11
|
+
|
12
|
+
def dashboard(key, locals = {})
|
13
|
+
render :partial => key, :locals => locals.reverse_merge(:figures => [])
|
14
|
+
end
|
15
|
+
|
16
|
+
def figure(*names, &block)
|
17
|
+
locals = names.extract_options!
|
18
|
+
condition = extract_condition!(locals)
|
19
|
+
|
20
|
+
if condition != false
|
21
|
+
locals.reverse_merge(:locale => nil)
|
22
|
+
locals.merge!(:yielded_content => capture(&block)) if block_given?
|
23
|
+
result = capture_haml do
|
24
|
+
names.each do |name|
|
25
|
+
haml_concat render(:partial => "shared/dashboard/#{name}", :locals => locals)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
result
|
31
|
+
end
|
32
|
+
|
33
|
+
def report_for(record_or_key, hash, options = {}, &block)
|
34
|
+
Semantic::Dashboard.new(self, record_or_key, hash, options, &block).content
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module Storefront
|
2
|
+
module DefinitionListHelper
|
3
|
+
def term(key, value, options = {})
|
4
|
+
return if value.blank?
|
5
|
+
condition = extract_condition!(options)
|
6
|
+
|
7
|
+
if condition != false
|
8
|
+
result = capture_haml do
|
9
|
+
haml_tag :tr, :class => "term" do
|
10
|
+
text = key.is_a?(String) ? key : t("widgets.details.#{key}")
|
11
|
+
haml_tag :td, text.gsub(/:?$/, ":"), :class => "key first"
|
12
|
+
haml_tag :td, value, :class => "value"
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
result
|
18
|
+
end
|
19
|
+
|
20
|
+
def terms(&block)
|
21
|
+
capture_haml do
|
22
|
+
haml_tag :dl do
|
23
|
+
yield
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
alias definition_list terms
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
module Storefront
|
2
|
+
module ErrorHelper
|
3
|
+
def render_not_found(exception)
|
4
|
+
Exceptional::Catcher.handle_with_controller(exception, self, request) if defined?(::Exceptional)
|
5
|
+
logger.error(exception)
|
6
|
+
puts exception.inspect
|
7
|
+
puts exception.backtrace.join("\n")
|
8
|
+
render :template => "/error/404.html", :status => 404, :layout => false
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module Storefront
|
2
|
+
module FlashHelper
|
3
|
+
def render_flash
|
4
|
+
flash_to_render = [:success, :failure, :notice, :error]
|
5
|
+
flash.select { |type, _| flash_to_render.include?(type) }.map do |type, content|
|
6
|
+
content_tag(:output, content, :class => "flash-#{type}")
|
7
|
+
end.join
|
8
|
+
end
|
9
|
+
|
10
|
+
def flash?
|
11
|
+
flash.present?
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
module Storefront
|
2
|
+
module FormatHelper
|
3
|
+
def rendered_text(string)
|
4
|
+
string.to_s.
|
5
|
+
gsub(/<\/?[^>]*>/, '').
|
6
|
+
gsub(/\n/, " ").
|
7
|
+
gsub("\r", " ").
|
8
|
+
squeeze(" ").
|
9
|
+
gsub(/\"/, "″"). # convert quotes to inches
|
10
|
+
gsub(/\'/, "′"). # single quote to feet
|
11
|
+
gsub(/\$0?0*\.(\d+)/, "\\1¢").html_safe
|
12
|
+
end
|
13
|
+
|
14
|
+
def format_money(value)
|
15
|
+
value.present? ? ("$" + number_with_precision(value.to_s.gsub("$", ""), :precision => 2)) : nil
|
16
|
+
end
|
17
|
+
|
18
|
+
def format_percent(value)
|
19
|
+
value.present? ? (number_with_precision(value.to_s.gsub("%", ""), :precision => 0) + "%") : nil
|
20
|
+
end
|
21
|
+
|
22
|
+
def sanitize(text)
|
23
|
+
text.gsub(/<\/?[^>]*>/, '').gsub(/\n/, " ").gsub("\r", " ").squeeze(" ")
|
24
|
+
end
|
25
|
+
|
26
|
+
def n(value)
|
27
|
+
if value.respond_to?(:nan?) && value.nan?
|
28
|
+
0
|
29
|
+
else
|
30
|
+
value
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def s(string, default = "N/A")
|
35
|
+
string.present? ? string : default
|
36
|
+
end
|
37
|
+
|
38
|
+
def num(num, default = "N/A")
|
39
|
+
result = number_with_precision(num, :precision => 1)
|
40
|
+
result.blank? ? default : result
|
41
|
+
end
|
42
|
+
|
43
|
+
def money(value)
|
44
|
+
value ? value.to_s.gsub(/^\$?/, "$") : "$0"
|
45
|
+
end
|
46
|
+
|
47
|
+
def read_binary_file(path)
|
48
|
+
File.open(path, 'rb') {|f| f.read }
|
49
|
+
end
|
50
|
+
|
51
|
+
def encoded_contents(path)
|
52
|
+
Base64.encode64(read_binary_file(path)).gsub(/\n/, '')
|
53
|
+
end
|
54
|
+
|
55
|
+
def font_face_data_uri(path)
|
56
|
+
"url(\"data:font/truetype;base64,#{encoded_contents(path)}\")"
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,229 @@
|
|
1
|
+
module Storefront
|
2
|
+
module HeadHelper
|
3
|
+
def meta_for(post)
|
4
|
+
result = ""
|
5
|
+
unless post.title.blank?
|
6
|
+
result << title(post.title)
|
7
|
+
result << "\n"
|
8
|
+
end
|
9
|
+
unless post.tags.blank?
|
10
|
+
result << keywords(post.tags.join(", "))
|
11
|
+
result << "\n"
|
12
|
+
end
|
13
|
+
unless post.description.blank?
|
14
|
+
result << description(post.description)
|
15
|
+
result << "\n"
|
16
|
+
end
|
17
|
+
result
|
18
|
+
end
|
19
|
+
|
20
|
+
def meta(options = {})
|
21
|
+
title(options[:title]) if options.has_key?(:title)
|
22
|
+
description(options[:description]) if options.has_key?(:description)
|
23
|
+
keywords(options[:keywords]) if options.has_key?(:keywords)
|
24
|
+
copyright(options[:copyright]) if options.has_key?(:copyright)
|
25
|
+
end
|
26
|
+
|
27
|
+
def meta_tags(*args)
|
28
|
+
options = args.extract_options!
|
29
|
+
site = options[:site] || args.first
|
30
|
+
result = title_tag(site, options[:title])
|
31
|
+
result << "\n"
|
32
|
+
result << content_type_tag
|
33
|
+
result << "\n"
|
34
|
+
result << description_tag(options[:description])
|
35
|
+
result << "\n"
|
36
|
+
result << keywords_tag(options[:keywords])
|
37
|
+
result << "\n"
|
38
|
+
result << copyright_tag(options[:copyright])
|
39
|
+
result
|
40
|
+
end
|
41
|
+
|
42
|
+
# country
|
43
|
+
# classification
|
44
|
+
# author
|
45
|
+
# apple-mobile-web-app-capable
|
46
|
+
# apple-touch-fullscreen
|
47
|
+
# google-analytics
|
48
|
+
# Revisit-After
|
49
|
+
# category
|
50
|
+
def meta_tag(name, content = '')
|
51
|
+
tag(:meta, :name => name, :content => content)
|
52
|
+
end
|
53
|
+
|
54
|
+
def title(string)
|
55
|
+
@content_for_title = string
|
56
|
+
end
|
57
|
+
|
58
|
+
def title_tag(*args)
|
59
|
+
options = args.extract_options!
|
60
|
+
|
61
|
+
site = options[:site] || args.first
|
62
|
+
# Prefix (leading space)
|
63
|
+
if options[:prefix]
|
64
|
+
prefix = options[:prefix]
|
65
|
+
elsif options[:prefix] === false
|
66
|
+
prefix = ''
|
67
|
+
else
|
68
|
+
prefix = ' '
|
69
|
+
end
|
70
|
+
|
71
|
+
# Separator
|
72
|
+
unless options[:separator].blank?
|
73
|
+
separator = options[:separator]
|
74
|
+
else
|
75
|
+
separator = '|'
|
76
|
+
end
|
77
|
+
|
78
|
+
# Suffix (trailing space)
|
79
|
+
if options[:suffix]
|
80
|
+
suffix = options[:suffix]
|
81
|
+
elsif options[:suffix] === false
|
82
|
+
suffix = ''
|
83
|
+
else
|
84
|
+
suffix = ' '
|
85
|
+
end
|
86
|
+
|
87
|
+
# Title
|
88
|
+
title = @content_for_title
|
89
|
+
if options[:lowercase] === true
|
90
|
+
title = title.downcase unless title.blank?
|
91
|
+
end
|
92
|
+
|
93
|
+
# title
|
94
|
+
if title.blank?
|
95
|
+
result = content_tag :title, site
|
96
|
+
else
|
97
|
+
title = normalize_title(title)
|
98
|
+
title = [site] + title
|
99
|
+
title.reverse! unless options[:reverse] === false
|
100
|
+
sep = prefix + separator + suffix
|
101
|
+
result = content_tag(:title, title.join(sep))
|
102
|
+
end
|
103
|
+
result
|
104
|
+
end
|
105
|
+
|
106
|
+
def description(string)
|
107
|
+
@content_for_description = string
|
108
|
+
end
|
109
|
+
|
110
|
+
def description_tag(default='')
|
111
|
+
content = normalize_description(@content_for_description || default)
|
112
|
+
tag(:meta, :name => :description, :content => content) unless content.blank?
|
113
|
+
end
|
114
|
+
|
115
|
+
def keywords(string)
|
116
|
+
@content_for_keywords = string
|
117
|
+
end
|
118
|
+
|
119
|
+
def keywords_tag(default = '')
|
120
|
+
content = normalize_keywords(@content_for_keywords || default)
|
121
|
+
tag(:meta, :name => :keywords, :content => content) unless content.blank?
|
122
|
+
end
|
123
|
+
|
124
|
+
def copyright(string)
|
125
|
+
@content_for_copyright = string
|
126
|
+
end
|
127
|
+
|
128
|
+
def copyright_tag(default='')
|
129
|
+
content = @content_for_copyright || default
|
130
|
+
tag(:meta, :name => :copyright, :content => content) unless content.blank?
|
131
|
+
end
|
132
|
+
|
133
|
+
def robots(*args)
|
134
|
+
content_for(:robots, args.join(", "))
|
135
|
+
end
|
136
|
+
|
137
|
+
def robots_tag(*args)
|
138
|
+
options = args.extract_options!
|
139
|
+
|
140
|
+
noindex_name = tags[:noindex].is_a?(String) ? tags[:noindex] : 'robots'
|
141
|
+
nofollow_name = tags[:nofollow].is_a?(String) ? tags[:nofollow] : 'robots'
|
142
|
+
|
143
|
+
if noindex_name == nofollow_name
|
144
|
+
content = [(tags[:noindex] ? 'noindex' : nil), (tags[:nofollow] ? 'nofollow' : nil)].compact.join(', ')
|
145
|
+
unless content.blank?
|
146
|
+
result << "\n"
|
147
|
+
result << tag(:meta, :name => noindex_name, :content => content)
|
148
|
+
end
|
149
|
+
else
|
150
|
+
if tags[:noindex]
|
151
|
+
result << "\n"
|
152
|
+
result << tag(:meta, :name => noindex_name, :content => 'noindex')
|
153
|
+
end
|
154
|
+
if tags[:nofollow]
|
155
|
+
result << "\n"
|
156
|
+
result << tag(:meta, :name => nofollow_name, :content => 'nofollow')
|
157
|
+
end
|
158
|
+
end
|
159
|
+
# canonical
|
160
|
+
unless tags[:canonical].blank?
|
161
|
+
result << "\n"
|
162
|
+
result << tag(:link, :rel => :canonical, :href => tags[:canonical])
|
163
|
+
end
|
164
|
+
|
165
|
+
return result
|
166
|
+
end
|
167
|
+
|
168
|
+
def copyright(from)
|
169
|
+
now = Time.now.year
|
170
|
+
now.eql?(from) ? now : "#{from} - #{now}"
|
171
|
+
end
|
172
|
+
|
173
|
+
# Apple
|
174
|
+
# http://developer.apple.com/safari/library/documentation/AppleApplications/Reference/SafariHTMLRef/Articles/MetaTags.html
|
175
|
+
|
176
|
+
# Facebook (and opengraph)
|
177
|
+
# http://developers.facebook.com/docs/opengraph#types
|
178
|
+
|
179
|
+
# http://wiki.developers.facebook.com/index.php/Facebook_Share/Specifying_Meta_Tags
|
180
|
+
def snapshot_tag(href)
|
181
|
+
tag(:link, :rel => "image_src", :href => href)
|
182
|
+
end
|
183
|
+
|
184
|
+
# http://wiki.developers.facebook.com/index.php/Facebook_Share/Specifying_Meta_Tags
|
185
|
+
def medium(type)
|
186
|
+
tag(:meta, :name => :medium, :content => type)
|
187
|
+
end
|
188
|
+
|
189
|
+
def classification(type)
|
190
|
+
tag(:meta, :name => "og:type", :content => type)
|
191
|
+
end
|
192
|
+
|
193
|
+
def content_type_tag(type = "text/html", charset = "UTF-8")
|
194
|
+
meta_tag("Content-Type", "#{type}; charset=#{charset}")
|
195
|
+
end
|
196
|
+
|
197
|
+
def search_link_tag(href, title)
|
198
|
+
link_tag({:rel => "search", :type => "application/opensearchdescription+xml", :href => href, :title => title})
|
199
|
+
end
|
200
|
+
|
201
|
+
def favicon_link_tag(favicon = "/favicon.ico")
|
202
|
+
link_tag({:rel => "shortcut icon", :href => favicon, :type => "image/x-icon"})
|
203
|
+
end
|
204
|
+
|
205
|
+
def link_tag(options = {})
|
206
|
+
tag(:link, options)
|
207
|
+
end
|
208
|
+
|
209
|
+
private
|
210
|
+
|
211
|
+
def normalize_title(title)
|
212
|
+
if title.is_a? String
|
213
|
+
title = [title]
|
214
|
+
end
|
215
|
+
title.map { |t| h(strip_tags(t)) }
|
216
|
+
end
|
217
|
+
|
218
|
+
def normalize_description(description)
|
219
|
+
return '' unless description
|
220
|
+
truncate(strip_tags(description).gsub(/\s+/, ' '), :length => 200)
|
221
|
+
end
|
222
|
+
|
223
|
+
def normalize_keywords(keywords)
|
224
|
+
return '' unless keywords
|
225
|
+
keywords = keywords.flatten.join(', ') if keywords.is_a?(Array)
|
226
|
+
strip_tags(keywords).mb_chars.downcase
|
227
|
+
end
|
228
|
+
end
|
229
|
+
end
|