Class: Refinery::Page

Inherits:
Core::BaseModel
  • Object
show all
Extended by:
FriendlyId
Defined in:
app/models/refinery/page.rb

Defined Under Namespace

Classes: Translation

Constant Summary collapse

PATH_SEPARATOR =

when collecting the pages path how is each of the pages seperated?

" - "

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Instance Attribute Details

#localeObject

to hold temporarily



31
32
33
# File 'app/models/refinery/page.rb', line 31

def locale
  @locale
end

to hold temporarily



31
32
33
# File 'app/models/refinery/page.rb', line 31

def page_menu_title
  @page_menu_title
end

#page_titleObject

to hold temporarily



31
32
33
# File 'app/models/refinery/page.rb', line 31

def page_title
  @page_title
end

Class Method Details

.by_slug(slug) ⇒ Object

Finds a page using its slug. See by_title



106
107
108
109
110
111
112
# File 'app/models/refinery/page.rb', line 106

def by_slug(slug)
  if defined?(::Refinery::I18n)
    with_globalize(:locale => Refinery::I18n.frontend_locales, :slug => slug)
  else
    with_globalize(:locale => ::I18n.locale, :slug => slug)
  end
end

.by_title(title) ⇒ Object

Finds a page using its title. This method is necessary because pages are translated which means the title attribute does not exist on the pages table thus requiring us to find the attribute on the translations table and then join to the pages table again to return the associated record.



101
102
103
# File 'app/models/refinery/page.rb', line 101

def by_title(title)
  with_globalize(:title => title)
end

.different_frontend_locale?Boolean

Wraps up all the checks that we need to do to figure out whether the current frontend locale is different to the current one set by ::I18n.locale. This terminates in a false if i18n extension is not defined or enabled.

Returns:

  • (Boolean)


167
168
169
# File 'app/models/refinery/page.rb', line 167

def different_frontend_locale?
  ::Refinery.i18n_enabled? && ::Refinery::I18n.current_frontend_locale != ::I18n.locale
end

.expire_page_cachingObject



182
183
184
185
186
187
188
189
190
191
# File 'app/models/refinery/page.rb', line 182

def expire_page_caching
  begin
    Rails.cache.delete_matched(/.*pages.*/)
  rescue NotImplementedError
    Rails.cache.clear
    warn "**** [REFINERY] The cache store you are using is not compatible with Rails.cache#delete_matched - clearing entire cache instead ***"
  ensure
    return true # so that other callbacks process.
  end
end

.fast_menu(columns = []) ⇒ Object

Because pages are translated this can have a negative performance impact on your website and can introduce scaling issues. What fast_menu does is finds all of the columns necessary to render a Refinery::Menu structure using only one SQL query. This has limitations, including not being able to access any other attributes of the pages but you can specify more columns by passing in an array e.g. fast_menu([:column1, :column2])



128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
# File 'app/models/refinery/page.rb', line 128

def fast_menu(columns = [])
  # First, apply a filter to determine which pages to show.
  pages = live.in_menu.order('lft ASC').includes(:translations)

  # Now we only want to select particular columns to avoid any further queries.
  # Title and menu_title are retrieved in the next block below so they are not here.
  (menu_columns | columns).each do |column|
    pages = pages.select(arel_table[column.to_sym])
  end

  # We have to get title and menu_title from the translations table.
  # To avoid calling globalize3 an extra time, we get title as page_title
  # and we get menu_title as page_menu_title.
  # These is used in 'to_refinery_menu_item' in the Page model.
  %w(title menu_title).each do |column|
    pages = pages.joins(:translations).select(
      "#{translation_class.table_name}.#{column} as page_#{column}"
    )
  end

  pages
end

.find_by_path(path) ⇒ Object

With slugs scoped to the parent page we need to find a page by its full path. For example with about/example we would need to find ‘about’ and then its child called ‘example’ otherwise it may clash with another page called /example.



74
75
76
77
78
79
80
# File 'app/models/refinery/page.rb', line 74

def find_by_path(path)
  split_path = path.to_s.split('/')
  page = ::Refinery::Page.by_slug(split_path.shift).first
  page = page.children.by_slug(split_path.shift).first until page.nil? || split_path.empty?

  page
end

.find_by_path_or_id(path, id) ⇒ Object

Helps to resolve the situation where you have a path and an id and if the path is unfriendly then a different finder method is required than find_by_path.



85
86
87
88
89
90
91
92
93
94
95
# File 'app/models/refinery/page.rb', line 85

def find_by_path_or_id(path, id)
  if Refinery::Pages.marketable_urls && path.present?
    if path.friendly_id?
      find_by_path(path)
    else
      find(path)
    end
  elsif id.present?
    find(id)
  end
end

.in_menuObject

Shows all pages with :show_in_menu set to true, but it also rejects any page that has not been translated to the current locale. This works using a query against the translated content first and then using all of the page_ids we further filter against this model’s table.



118
119
120
# File 'app/models/refinery/page.rb', line 118

def in_menu
  where(:show_in_menu => true).with_globalize
end

.liveObject

Live pages are ‘allowed’ to be shown in the frontend of your website. By default, this is all pages that are not set as ‘draft’.



67
68
69
# File 'app/models/refinery/page.rb', line 67

def live
  where(:draft => false)
end

Override this method to change which columns you want to select to render your menu. title and menu_title are always retrieved so omit these.



173
174
175
# File 'app/models/refinery/page.rb', line 173

def menu_columns
  %w(id depth parent_id lft rgt link_url menu_match slug)
end

.per_page(dialog = false) ⇒ Object

Returns how many pages per page should there be when paginating pages



178
179
180
# File 'app/models/refinery/page.rb', line 178

def per_page(dialog = false)
  dialog ? Pages.pages_per_dialog : Pages.config.pages_per_admin_index
end

.with_globalize(conditions = {}) ⇒ Object

Wrap up the logic of finding the pages based on the translations table.



152
153
154
155
156
157
158
159
160
161
162
# File 'app/models/refinery/page.rb', line 152

def with_globalize(conditions = {})
  conditions = {:locale => ::Globalize.locale}.merge(conditions)
  globalized_conditions = {}
  conditions.keys.each do |key|
    if (translated_attribute_names.map(&:to_s) | %w(locale)).include?(key.to_s)
      globalized_conditions["#{self.translation_class.table_name}.#{key}"] = conditions.delete(key)
    end
  end
  # A join implies readonly which we don't really want.
  joins(:translations).where(globalized_conditions).where(conditions).readonly(false)
end

Instance Method Details

#all_page_part_contentObject

Used to index all the content on this page so it can be easily searched.



431
432
433
# File 'app/models/refinery/page.rb', line 431

def all_page_part_content
  parts.map(&:body).join(" ")
end

#cache_keyObject



343
344
345
# File 'app/models/refinery/page.rb', line 343

def cache_key
  [Refinery::Core.base_cache_key, 'page', ::I18n.locale, id].compact.join('/')
end

#content_for(part_title) ⇒ Object

Accessor method to get a page part from a page. Example:

::Refinery::Page.first.content_for(:body)

Will return the body page part of the first page.



395
396
397
# File 'app/models/refinery/page.rb', line 395

def content_for(part_title)
  part_with_title(part_title).try(:body)
end

#custom_slug_or_titleObject

Returns in cascading order: custom_slug or menu_title or title depending on which attribute is first found to be present for this page.



196
197
198
199
200
201
202
203
204
# File 'app/models/refinery/page.rb', line 196

def custom_slug_or_title
  if custom_slug.present?
    custom_slug
  elsif menu_title.present?
    menu_title
  else
    title
  end
end

#deletable?Boolean

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

Returns:

  • (Boolean)


209
210
211
# File 'app/models/refinery/page.rb', line 209

def deletable?
  deletable && link_url.blank? and menu_match.blank?
end

#destroyObject

Before destroying a page we check to see if it’s a deletable page or not Refinery system pages are not deletable.



223
224
225
226
227
228
229
230
231
232
233
234
235
# File 'app/models/refinery/page.rb', line 223

def destroy
  return super if deletable?

  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 link_url.present?
    puts "unset .menu_match," if menu_match.present?
    puts "set .deletable to true" unless deletable
  end

  false
end

#destroy!Object

If you want to destroy a page that is set to be not deletable this is the way to do it.



238
239
240
241
242
243
244
# File 'app/models/refinery/page.rb', line 238

def destroy!
  self.menu_match = nil
  self.link_url = nil
  self.deletable = true

  destroy
end

#home?Boolean

Returns true if this page is the home page or links to it.

Returns:

  • (Boolean)


363
364
365
# File 'app/models/refinery/page.rb', line 363

def home?
  link_url == '/'
end

#in_menu?Boolean

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.

Returns:

  • (Boolean)


354
355
356
# File 'app/models/refinery/page.rb', line 354

def in_menu?
  live? && show_in_menu?
end

Adds the locale key into the URI for this page’s link_url attribute, unless the current locale is set as the default locale.

Returns:

  • (Boolean)


280
281
282
283
284
285
286
287
288
289
290
# File 'app/models/refinery/page.rb', line 280

def link_url_localised?
  return link_url unless ::Refinery.i18n_enabled?

  current_url = link_url

  if current_url =~ %r{^/} && ::Refinery::I18n.current_frontend_locale != ::Refinery::I18n.default_frontend_locale
    current_url = "/#{::Refinery::I18n.current_frontend_locale}#{current_url}"
  end

  current_url
end

#live?Boolean

Returns true if this page is “published”

Returns:

  • (Boolean)


348
349
350
# File 'app/models/refinery/page.rb', line 348

def live?
  not draft?
end

#nested_pathObject

Returns the string version of nested_url, i.e., the path that should be generated by the router



331
332
333
# File 'app/models/refinery/page.rb', line 331

def nested_path
  Rails.cache.fetch(path_cache_key) { ['', nested_url].join('/') }
end

#nested_urlObject

Returns an array with all ancestors to_param, allow with its own Ex: with an About page and a Mission underneath, ::Refinery::Page.find(‘mission’).nested_url would return:

['about', 'mission']


321
322
323
# File 'app/models/refinery/page.rb', line 321

def nested_url
  Rails.cache.fetch(url_cache_key) { uncached_nested_url }
end

#normalize_friendly_id_with_marketable_urls(slug_string) ⇒ Object

Protects generated slugs from title if they are in the list of reserved words This applies mostly to plugin-generated pages. This only kicks in when Refinery::Pages.marketable_urls is enabled.

Returns the sluggified string



441
442
443
444
445
446
447
# File 'app/models/refinery/page.rb', line 441

def normalize_friendly_id_with_marketable_urls(slug_string)
  sluggified = slug_string.to_slug.normalize!
  if Refinery::Pages.marketable_urls && self.class.friendly_id_config.reserved_words.include?(sluggified)
    sluggified << "-page"
  end
  sluggified
end

#not_in_menu?Boolean

Returns:

  • (Boolean)


358
359
360
# File 'app/models/refinery/page.rb', line 358

def not_in_menu?
  not in_menu?
end

#part_with_title(part_title) ⇒ Object

Accessor method to get a page part object from a page. Example:

::Refinery::Page.first.part_with_title(:body)

Will return the Refinery::PagePart object with that title using the first page.



405
406
407
408
409
410
411
412
413
# File 'app/models/refinery/page.rb', line 405

def part_with_title(part_title)
  # self.parts is usually already eager loaded so we can now just grab
  # the first element matching the title we specified.
  self.parts.detect do |part|
    part.title.present? and # protecting against the problem that occurs when have nil title
    part.title == part_title.to_s or
    part.title.downcase.gsub(" ", "_") == part_title.to_s.downcase.gsub(" ", "_")
  end
end

#path(options = {}) ⇒ Object

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



248
249
250
251
252
253
254
255
256
257
258
259
# File 'app/models/refinery/page.rb', line 248

def path(options = {})
  # Override default options with any supplied.
  options = {:reversed => true}.merge(options)

  unless parent_id.nil?
    parts = [title, parent.path(options)]
    parts.reverse! if options[:reversed]
    parts.join(PATH_SEPARATOR)
  else
    title
  end
end

#path_cache_keyObject



335
336
337
# File 'app/models/refinery/page.rb', line 335

def path_cache_key
  [cache_key, 'nested_path'].join('#')
end

#refinery_menu_titleObject



372
373
374
# File 'app/models/refinery/page.rb', line 372

def refinery_menu_title
  [page_menu_title, page_title, menu_title, title].detect(&:present?)
end

#reposition_parts!Object

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.



215
216
217
218
219
# File 'app/models/refinery/page.rb', line 215

def reposition_parts!
  reload.parts.each_with_index do |part, index|
    part.update_attribute(:position, index)
  end
end

#shown_siblingsObject

Returns all visible sibling pages that can be rendered for the menu



368
369
370
# File 'app/models/refinery/page.rb', line 368

def shown_siblings
  siblings.reject(&:not_in_menu?)
end

#title_with_metaObject

In the admin area we use a slightly different title to inform the which pages are draft or hidden pages We show the title from the next available locale if there is no title for the current locale



417
418
419
420
421
422
423
424
425
426
427
428
# File 'app/models/refinery/page.rb', line 417

def title_with_meta
  if self.title.present?
    title = [self.title]
  else
    title = [self.translations.detect {|t| t.title.present?}.title]
  end

  title << "<em>(#{::I18n.t('hidden', :scope => 'refinery.admin.pages.page')})</em>" unless show_in_menu?
  title << "<em>(#{::I18n.t('draft', :scope => 'refinery.admin.pages.page')})</em>" if draft?

  title.join(' ')
end

#to_refinery_menu_itemObject



376
377
378
379
380
381
382
383
384
385
386
387
# File 'app/models/refinery/page.rb', line 376

def to_refinery_menu_item
  {
    :id => id,
    :lft => lft,
    :menu_match => menu_match,
    :parent_id => parent_id,
    :rgt => rgt,
    :title => refinery_menu_title,
    :type => self.class.name,
    :url => url
  }
end

#uncached_nested_urlObject



325
326
327
# File 'app/models/refinery/page.rb', line 325

def uncached_nested_url
  [parent.try(:nested_url), to_param.to_s].compact.flatten
end

#urlObject

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”



268
269
270
271
272
273
274
275
276
# File 'app/models/refinery/page.rb', line 268

def url
  if link_url.present?
    link_url_localised?
  elsif Refinery::Pages.marketable_urls
    with_locale_param url_marketable
  elsif to_param.present?
    with_locale_param url_normal
  end
end

#url_cache_keyObject



339
340
341
# File 'app/models/refinery/page.rb', line 339

def url_cache_key
  [cache_key, 'nested_url'].join('#')
end

#url_marketableObject

Add ‘marketable url’ attributes into this page’s url. This sets ‘path’ as the nested_url value and sets ‘id’ to nil. For example, this might evaluate to /about for the “About” page.



295
296
297
298
# File 'app/models/refinery/page.rb', line 295

def url_marketable
  # :id => nil is important to prevent any other params[:id] from interfering with this route.
  url_normal.merge(:path => nested_url, :id => nil)
end

#url_normalObject

Returns a url suitable to be used in url_for in Rails (such as link_to). For example, this might evaluate to /pages/about for the “About” page.



302
303
304
# File 'app/models/refinery/page.rb', line 302

def url_normal
  {:controller => '/refinery/pages', :action => 'show', :path => nil, :id => to_param, :only_path => true}
end

#with_locale_param(url_hash) ⇒ Object

If the current locale is set to something other than the default locale then the :locale attribute will be set on the url hash, otherwise it won’t be.



308
309
310
311
312
313
# File 'app/models/refinery/page.rb', line 308

def with_locale_param(url_hash)
  if self.class.different_frontend_locale?
    url_hash.update(:locale => ::Refinery::I18n.current_frontend_locale)
  end
  url_hash
end