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



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

def locale
  @locale
end

to hold temporarily



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

def page_menu_title
  @page_menu_title
end

#page_titleObject

to hold temporarily



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

def page_title
  @page_title
end

Class Method Details

.by_slug(slug, conditions = {}) ⇒ Object

Finds pages by their slug. See by_title



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

def by_slug(slug, conditions={})
  locales = Refinery.i18n_enabled? ? Refinery::I18n.frontend_locales.map(&:to_s) : ::I18n.locale.to_s
  with_globalize({ :locale => locales, :slug => slug }.merge(conditions))
end

.by_title(title) ⇒ Object

Finds pages by their 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.



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

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)


165
166
167
# File 'app/models/refinery/page.rb', line 165

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

.expire_page_cachingObject



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

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])



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

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.



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

def find_by_path(path)
  split_path = path.to_s.split('/').reject(&:blank?)
  page = ::Refinery::Page.by_slug(split_path.shift, :parent_id => nil).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.



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

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.



116
117
118
# File 'app/models/refinery/page.rb', line 116

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’.



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

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.



171
172
173
# File 'app/models/refinery/page.rb', line 171

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



176
177
178
# File 'app/models/refinery/page.rb', line 176

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

.rebuild_with_invalidate_cached_urls!Object



191
192
193
194
# File 'app/models/refinery/page.rb', line 191

def rebuild_with_invalidate_cached_urls!
  rebuild_without_invalidate_cached_urls!
  find_each { |page| page.send :invalidate_cached_urls }
end

.with_globalize(conditions = {}) ⇒ Object

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



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

def with_globalize(conditions = {})
  conditions = {:locale => ::Globalize.locale.to_s}.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.



424
425
426
# File 'app/models/refinery/page.rb', line 424

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

#cache_key(locale) ⇒ Object



351
352
353
# File 'app/models/refinery/page.rb', line 351

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

#canonicalObject

The canonical page for this particular page. Consists of:

* The default locale's translated slug


201
202
203
# File 'app/models/refinery/page.rb', line 201

def canonical
  Globalize.with_locale(Refinery.i18n_enabled? && Refinery::I18n.default_frontend_locale || ::I18n.locale) { url }
end

#canonical_slugObject

The canonical slug for this particular page. This is the slug for the default frontend locale.



207
208
209
# File 'app/models/refinery/page.rb', line 207

def canonical_slug
  Globalize.with_locale(Refinery.i18n_enabled? && Refinery::I18n.default_frontend_locale || ::I18n.locale) { slug }
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.



403
404
405
# File 'app/models/refinery/page.rb', line 403

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.



213
214
215
# File 'app/models/refinery/page.rb', line 213

def custom_slug_or_title
  custom_slug.presence || menu_title.presence || title
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)


220
221
222
# File 'app/models/refinery/page.rb', line 220

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.



234
235
236
237
238
239
240
241
242
243
244
245
246
# File 'app/models/refinery/page.rb', line 234

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.



249
250
251
252
253
254
255
# File 'app/models/refinery/page.rb', line 249

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)


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

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)


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

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)


291
292
293
294
295
296
297
298
299
300
301
# File 'app/models/refinery/page.rb', line 291

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)


356
357
358
# File 'app/models/refinery/page.rb', line 356

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



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

def nested_path
  Rails.cache.fetch(path_cache_key) { ['', nested_url].join('/') }
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



434
435
436
437
438
439
440
# File 'app/models/refinery/page.rb', line 434

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)


366
367
368
# File 'app/models/refinery/page.rb', line 366

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.



413
414
415
416
417
418
419
420
421
# File 'app/models/refinery/page.rb', line 413

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



259
260
261
262
263
264
265
266
267
268
269
270
# File 'app/models/refinery/page.rb', line 259

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_key(locale = Globalize.locale) ⇒ Object



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

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

#refinery_menu_titleObject



380
381
382
# File 'app/models/refinery/page.rb', line 380

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.



226
227
228
229
230
# File 'app/models/refinery/page.rb', line 226

def reposition_parts!
  reload.parts.each_with_index do |part, index|
    part.update_attributes :position => index
  end
end

#shown_siblingsObject

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



376
377
378
# File 'app/models/refinery/page.rb', line 376

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

#to_refinery_menu_itemObject



384
385
386
387
388
389
390
391
392
393
394
395
# File 'app/models/refinery/page.rb', line 384

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 Also known as: nested_url



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

def uncached_nested_url
  [parent.try(:uncached_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”



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

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_key(locale = Globalize.locale) ⇒ Object



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

def url_cache_key(locale = Globalize.locale)
  [cache_key(locale), '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.



306
307
308
309
# File 'app/models/refinery/page.rb', line 306

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.



313
314
315
# File 'app/models/refinery/page.rb', line 313

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

#with_locale_param(url_hash, locale = nil) ⇒ 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.



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

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