forked from refinery/refinerycms
-
Notifications
You must be signed in to change notification settings - Fork 12
/
page.rb
255 lines (212 loc) · 7.83 KB
/
page.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
class Page < ActiveRecord::Base
validates :title, :presence => true
acts_as_nested_set
# Docs for friendly_id http://github.com/norman/friendly_id
has_friendly_id :title, :use_slug => true,
:reserved_words => %w(index new session login logout users refinery admin images wymiframe)
has_many :parts,
:class_name => "PagePart",
:order => "position ASC",
:inverse_of => :page
accepts_nested_attributes_for :parts,
:allow_destroy => true
# Docs for acts_as_indexed http://github.com/dougal/acts_as_indexed
acts_as_indexed :fields => [:title, :meta_keywords, :meta_description,
:custom_title, :browser_title, :all_page_part_content]
before_destroy :deletable?
after_save :reposition_parts!
after_save :invalidate_child_cached_url
# when a dialog pops up to link to a page, how many pages per page should there be
PAGES_PER_DIALOG = 14
# when listing pages out in the admin area, how many pages should show per page
PAGES_PER_ADMIN_INDEX = 20
# when collecting the pages path how is each of the pages seperated?
PATH_SEPARATOR = " - "
# Am I allowed to delete this page?
# If a link_url is set we don't want to break the link so we don't allow them to delete
# If deletable is set to false then we don't allow this page to be deleted. These are often Refinery system pages
def deletable?
self.deletable && self.link_url.blank? and self.menu_match.blank?
end
# Repositions the child page_parts that belong to this page.
# This ensures that they are in the correct 0,1,2,3,4... etc order.
def reposition_parts!
self.parts.each_with_index do |part, index|
part.update_attribute(:position, index)
end
end
# Before destroying a page we check to see if it's a deletable page or not
# Refinery system pages are not deletable.
def destroy
if self.deletable?
super
else
unless Rails.env.test?
# give useful feedback when trying to delete from console
puts "This page is not deletable. Please use .destroy! if you really want it deleted "
puts "unset .link_url," if self.link_url.present?
puts "unset .menu_match," if self.menu_match.present?
puts "set .deletable to true" unless self.deletable
end
return false
end
end
# If you want to destroy a page that is set to be not deletable this is the way to do it.
def destroy!
self.menu_match = nil
self.link_url = nil
self.deletable = true
self.destroy
end
def indented_title
"#{"--" * self.ancestors.size} #{self.title}".chomp
end
# Used for the browser title to get the full path to this page
# It automatically prints out this page title and all of it's parent page titles joined by a PATH_SEPARATOR
def path(reverse = true)
unless self.parent.nil?
parts = [self.title, self.parent.path(reverse)]
parts.reverse! if reverse
parts.join(PATH_SEPARATOR)
else
self.title
end
end
# When this page is rendered in the navigation, where should it link?
# If a custom "link_url" is set, it uses that otherwise it defaults to a normal page URL.
# The "link_url" is often used to link to a plugin rather than a page.
#
# For example if I had a "Contact" page I don't want it to just render a contact us page
# I want it to show the Inquiries form so I can collect inquiries. So I would set the "link_url"
# to "/contact"
def url
if self.link_url.present?
link_url_localised?
elsif use_marketable_urls?
url_marketable
elsif self.to_param.present?
url_normal
end
end
def link_url_localised?
if self.link_url =~ /^\// and defined?(::Refinery::I18n) and ::Refinery::I18n.enabled? and
::I18n.locale != ::Refinery::I18n.default_frontend_locale
"/#{::I18n.locale}#{self.link_url}"
else
self.link_url
end
end
def url_marketable
{:controller => "/pages", :action => "show", :path => self.nested_url}
end
def url_normal
{:controller => "/pages", :action => "show", :id => self.to_param}
end
# Returns an array with all ancestors to_param, allow with its own
# Ex: with an About page and a Mission underneath,
# Page.find('mission').nested_url would return:
#
# ['about', 'mission']
#
def nested_url
Rails.cache.fetch(url_cache_key) { uncached_nested_url }
end
def uncached_nested_url
self.parent ? [parent.nested_url, self.to_param].flatten : [self.to_param]
end
# Returns the string version of nested_url, i.e., the path that should be generated
# by the router
def nested_path
@nested_path ||= "/#{nested_url.join('/')}"
end
def url_cache_key
"#{cache_key}#nested_url"
end
def cache_key
"#{Refinery.base_cache_key}/#{super}"
end
def use_marketable_urls?
RefinerySetting.find_or_set(:use_marketable_urls, true, :scoping => 'pages')
end
# Returns true if this page is "published"
def live?
not self.draft?
end
# Return true if this page can be shown in the navigation.
# If it's a draft or is set to not show in the menu it will return false.
# If any of the page's ancestors aren't to be shown in the menu then this page is not either.
def in_menu?
self.live? && self.show_in_menu? && self.ancestors.all? { |a| a.in_menu? }
end
# Returns true if this page is the home page or links to it.
def home?
self.link_url == "/"
end
# Returns all visible sibling pages that can be rendered for the menu
def shown_siblings
self.siblings.reject { |sibling| not sibling.in_menu? }
end
class << self
# Accessor to find out the default page parts created for each new page
def default_parts
RefinerySetting.find_or_set(:default_page_parts, ["Body", "Side Body"])
end
# Returns how many pages per page should there be when paginating pages
def per_page(dialog = false)
dialog ? PAGES_PER_DIALOG : PAGES_PER_ADMIN_INDEX
end
# Returns all the top level pages, usually to render the top level navigation.
def top_level
self.roots.where(:show_in_menu => true, :draft => false)
end
end
# Accessor method to get a page part from a page.
# Example:
#
# Page.first[:body]
#
# Will return the body page part of the first page.
def [](part_title)
# don't want to override a super method when trying to call a page part.
# the way that we call page parts seems flawed, will probably revert to page.parts[:title] in a future release.
if (super_value = super).blank?
# self.parts is already eager loaded so we can now just grab the first element matching the title we specified.
part = self.parts.detect do |part|
part.title == part_title.to_s or
part.title.downcase.gsub(" ", "_") == part_title.to_s.downcase.gsub(" ", "_")
end
return part.body unless part.nil?
end
super_value
end
# In the admin area we use a slightly different title to inform the which pages are draft or hidden pages
def title_with_meta
title = self.title
title << " <em>(hidden)</em>" unless self.show_in_menu?
title << " <em>(draft)</em>" if self.draft?
title.strip
end
# Used to index all the content on this page so it can be easily searched.
def all_page_part_content
self.parts.collect {|p| p.body}.join(" ")
end
##
# Protects generated slugs from title if they are in the list of reserved words
# This applies mostly to plugin-generated pages.
#
# Returns the sluggified string
def normalize_friendly_id(slug_string)
sluggified = super
if use_marketable_urls? && self.class.friendly_id_config.reserved_words.include?(sluggified)
sluggified += "-page"
end
sluggified
end
private
def invalidate_child_cached_url
return true unless use_marketable_urls?
children.each do |child|
Rails.cache.delete(child.url_cache_key)
end
end
end