Class: Alchemy::Page

Inherits:
BaseRecord
  • Object
show all
Includes:
Hints, Logger, PageElements, PageNaming, PageNatures, PageScopes, Taggable
Defined in:
app/models/alchemy/page.rb,
app/models/alchemy/page/url_path.rb

Defined Under Namespace

Modules: PageElements, PageNaming, PageNatures, PageScopes Classes: FixedAttributes, UrlPath

Constant Summary collapse

DEFAULT_ATTRIBUTES_FOR_COPY =
{
  autogenerate_elements: false,
  visible: false,
  public_on: nil,
  public_until: nil,
  locked_at: nil,
  locked_by: nil
}
SKIPPED_ATTRIBUTES_ON_COPY =
%w(
  id
  updated_at
  created_at
  creator_id
  updater_id
  lft
  rgt
  depth
  urlname
  cached_tag_list
)
PERMITTED_ATTRIBUTES =
[
  :meta_description,
  :meta_keywords,
  :name,
  :page_layout,
  :public_on,
  :public_until,
  :restricted,
  :robot_index,
  :robot_follow,
  :sitemap,
  :tag_list,
  :title,
  :urlname,
  :visible,
  :layoutpage,
  :menu_id
]

Constants included from PageNaming

PageNaming::RESERVED_URLNAMES

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from PageElements

#available_element_definitions, #available_element_names, #available_elements_within_current_scope, #descendent_element_definitions, #element_definition_names, #element_definitions, #element_definitions_by_name, #feed_elements, #richtext_contents_ids

Methods included from PageNaming

#external_urlname, #renamed?, #slug, #update_urlname!, #visible_ancestors

Methods included from NameConversions

#convert_to_humanized_name, #convert_to_urlname

Methods included from PageNatures

#cache_key, #cache_page?, #contains_feed?, #controller_and_action, #definition, #editor_roles, #expiration_time, #folded?, #has_controller?, #has_limited_editors?, #layout_display_name, #layout_partial_name, #locked?, #public?, #published_at, #redirects_to_external?, #rootpage?, #status, #status_title, #systempage?, #taggable?

Methods included from Taggable

included, #tag_list=

Methods included from Logger

#log_warning, warn

Methods included from Hints

#has_hint?, #hint

Methods inherited from BaseRecord

#active_record_5_1?

Instance Attribute Details

Returns the value of attribute menu_id.



162
163
164
# File 'app/models/alchemy/page.rb', line 162

def menu_id
  @menu_id
end

Class Method Details

.all_from_clipboard(clipboard) ⇒ Object



255
256
257
258
# File 'app/models/alchemy/page.rb', line 255

def all_from_clipboard(clipboard)
  return [] if clipboard.blank?
  where(id: clipboard.collect { |p| p['id'] })
end

.all_from_clipboard_for_select(clipboard, language_id, layoutpage = false) ⇒ Object



260
261
262
263
264
265
266
# File 'app/models/alchemy/page.rb', line 260

def all_from_clipboard_for_select(clipboard, language_id, layoutpage = false)
  return [] if clipboard.blank?
  clipboard_pages = all_from_clipboard(clipboard)
  allowed_page_layouts = Alchemy::PageLayout.selectable_layouts(language_id, layoutpage)
  allowed_page_layout_names = allowed_page_layouts.collect { |p| p['name'] }
  clipboard_pages.select { |cp| allowed_page_layout_names.include?(cp.page_layout) }
end

.ancestors_for(current) ⇒ Object

Returns an array of all pages in the same branch from current. I.e. used to find the active page in navigation.



280
281
282
283
# File 'app/models/alchemy/page.rb', line 280

def ancestors_for(current)
  return [] if current.nil?
  current.self_and_ancestors.contentpages
end

.copy(source, differences = {}) ⇒ Alchemy::Page

Creates a copy of given source.

Also copies all elements included in source.

Note:

It prevents the element auto generator from running.

Parameters:

  • source (Alchemy::Page)

    The source page the copy is taken from

  • differences (Hash) (defaults to: {})

    A optional hash with attributes that take precedence over the source attributes

Returns:



216
217
218
219
220
221
222
223
# File 'app/models/alchemy/page.rb', line 216

def copy(source, differences = {})
  page = Alchemy::Page.new(attributes_from_source_for_copy(source, differences))
  page.tag_list = source.tag_list
  if page.save!
    copy_elements(source, page)
    page
  end
end

.copy_and_paste(source, new_parent, new_name) ⇒ Object



242
243
244
245
246
247
248
249
250
251
252
253
# File 'app/models/alchemy/page.rb', line 242

def copy_and_paste(source, new_parent, new_name)
  page = copy(source, {
    parent_id: new_parent.id,
    language: new_parent.language,
    name: new_name,
    title: new_name
  })
  if source.children.any?
    source.copy_children_to(page)
  end
  page
end

.current_previewObject

Returns the current page previewed in the edit page template.



190
191
192
# File 'app/models/alchemy/page.rb', line 190

def current_preview
  RequestStore.store[:alchemy_current_preview]
end

.current_preview=(page) ⇒ Object

Used to store the current page previewed in the edit page template.



184
185
186
# File 'app/models/alchemy/page.rb', line 184

def current_preview=(page)
  RequestStore.store[:alchemy_current_preview] = page
end

.find_or_create_layout_root_for(language_id) ⇒ Object



229
230
231
232
233
234
235
236
237
238
239
240
# File 'app/models/alchemy/page.rb', line 229

def find_or_create_layout_root_for(language_id)
  layoutroot = layout_root_for(language_id)
  return layoutroot if layoutroot
  language = Language.find(language_id)
  Page.create!(
    name: "Layoutroot for #{language.name}",
    layoutpage: true,
    language: language,
    autogenerate_elements: false,
    parent_id: Page.root.id
  )
end

.language_root_for(language_id) ⇒ Object

Returns the language root page for given language id.

Parameters:

  • language_id (Fixnum)

Returns:

  • the language root page for given language id.



197
198
199
# File 'app/models/alchemy/page.rb', line 197

def language_root_for(language_id)
  language_roots.find_by_language_id(language_id)
end

.layout_root_for(language_id) ⇒ Object



225
226
227
# File 'app/models/alchemy/page.rb', line 225

def layout_root_for(language_id)
  where({parent_id: Page.root.id, layoutpage: true, language_id: language_id}).limit(1).first
end


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

def link_target_options
  options = [[Alchemy.t(:default, scope: 'link_target_options'), '']]
  link_target_options = Config.get(:link_target_options)
  link_target_options.each do |option|
    options << [Alchemy.t(option, scope: 'link_target_options',
                          default: option.to_s.humanize), option]
  end
  options
end

.rootObject Also known as: rootpage

The root page of the page tree

Internal use only. You wouldn’t use this page ever.

Automatically created when accessed the first time.



177
178
179
# File 'app/models/alchemy/page.rb', line 177

def root
  super || create!(name: 'Root')
end

Instance Method Details

#attribute_fixed?(name) ⇒ Boolean

True if given attribute name is defined as fixed

Returns:

  • (Boolean)


498
499
500
# File 'app/models/alchemy/page.rb', line 498

def attribute_fixed?(name)
  fixed_attributes.fixed?(name)
end

#copy_children_to(new_parent) ⇒ Object



445
446
447
448
449
450
451
452
453
454
455
# File 'app/models/alchemy/page.rb', line 445

def copy_children_to(new_parent)
  children.each do |child|
    next if child == new_parent
    new_child = Page.copy(child, {
      language_id: new_parent.language_id,
      language_code: new_parent.language_code
    })
    new_child.move_to_child_of(new_parent)
    child.copy_children_to(new_child) unless child.children.blank?
  end
end

#creator_nameObject

Returns the name of the creator of this page.

If no creator could be found or associated user model does not respond to #name it returns ‘unknown’



533
534
535
# File 'app/models/alchemy/page.rb', line 533

def creator_name
  creator.try(:name) || Alchemy.t('unknown')
end

#editable_by?(user) ⇒ Boolean

Checks the current page’s list of editors, if defined.

This allows us to pass in a user and see if any of their roles are enable them to make edits

Returns:

  • (Boolean)


507
508
509
510
# File 'app/models/alchemy/page.rb', line 507

def editable_by?(user)
  return true unless has_limited_editors?
  (editor_roles & user.alchemy_roles).any?
end

#find_elements(options = {}, show_non_public = false) ⇒ ActiveRecord::Relation

Returns elements from page.

Parameters:

  • options (Hash) (defaults to: {})

    a customizable set of options

Options Hash (options):

  • :only (Array<String>|String)

    Returns only elements with given names

  • :except (Array<String>|String)

    Returns all elements except the ones with given names

  • :count (Integer)

    Limit the count of returned elements

  • :offset (Integer)

    Starts with an offset while returning elements

  • :include_hidden (Boolean) — default: false

    Return hidden elements as well

  • :random (Boolean) — default: false

    Return elements randomly shuffled

  • :reverse (Boolean) — default: false

    Reverse the load order

  • :finder (Class) — default: Alchemy::ElementsFinder

    A class that will return elements from page. Use this for your custom element loading logic.

Returns:

  • (ActiveRecord::Relation)


343
344
345
346
347
348
349
350
351
352
# File 'app/models/alchemy/page.rb', line 343

def find_elements(options = {}, show_non_public = false)
  if show_non_public
    Alchemy::Deprecation.warn "Passing true as second argument to page#find_elements to include" \
      " invisible elements has been removed. Please implement your own ElementsFinder" \
      " and pass it with options[:finder]."
  end

  finder = options[:finder] || Alchemy::ElementsFinder.new(options)
  finder.elements(page: self)
end

#first_public_childObject

Returns the first published child



436
437
438
# File 'app/models/alchemy/page.rb', line 436

def first_public_child
  children.published.first
end

#fixed_attributesObject

Holds an instance of FixedAttributes



493
494
495
# File 'app/models/alchemy/page.rb', line 493

def fixed_attributes
  @_fixed_attributes ||= Alchemy::Page::FixedAttributes.new(self)
end

#fold!(user_id, status) ⇒ Object



419
420
421
422
423
# File 'app/models/alchemy/page.rb', line 419

def fold!(user_id, status)
  folded_page = folded_pages.find_or_create_by(user_id: user_id)
  folded_page.folded = status
  folded_page.save!
end

#get_language_rootObject

Gets the language_root page for page



441
442
443
# File 'app/models/alchemy/page.rb', line 441

def get_language_root
  self_and_ancestors.find_by(language_root: true)
end

#inherit_restricted_statusObject



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

def inherit_restricted_status
  self.restricted = parent.restricted?
end

#lock_to!(user) ⇒ Object

Locks the page to given user



407
408
409
# File 'app/models/alchemy/page.rb', line 407

def lock_to!(user)
  update_columns(locked_at: Time.current, locked_by: user.id)
end

#locker_nameObject

Returns the name of the user currently editing this page.

If no locker could be found or associated user model does not respond to #name it returns ‘unknown’



551
552
553
# File 'app/models/alchemy/page.rb', line 551

def locker_name
  locker.try(:name) || Alchemy.t('unknown')
end

Menus (aka. root nodes) this page is attached to



557
558
559
# File 'app/models/alchemy/page.rb', line 557

def menus
  @_menus ||= nodes.map(&:root)
end

#next(options = {}) ⇒ Object Also known as: next_page

Returns the next page on the same level or nil.

Parameters:

  • options (Hash) (defaults to: {})

    a customizable set of options

Options Hash (options):

  • :restricted (Boolean) — default: false

    only restricted pages (true), skip restricted pages (false)

  • :public (Boolean) — default: true

    only public pages (true), skip public pages (false)



399
400
401
402
# File 'app/models/alchemy/page.rb', line 399

def next(options = {})
  pages = self_and_siblings.where('lft > ?', lft)
  select_page(pages, options.merge(order: :asc))
end

#previous(options = {}) ⇒ Object Also known as: previous_page

Returns the previous page on the same level or nil.

Parameters:

  • options (Hash) (defaults to: {})

    a customizable set of options

Options Hash (options):

  • :restricted (Boolean) — default: false

    only restricted pages (true), skip restricted pages (false)

  • :public (Boolean) — default: true

    only public pages (true), skip public pages (false)



386
387
388
389
# File 'app/models/alchemy/page.rb', line 386

def previous(options = {})
  pages = self_and_siblings.where('lft < ?', lft)
  select_page(pages, options.merge(order: :desc))
end

#public_onObject

Returns the value of public_on attribute

If it’s a fixed attribute then the fixed value is returned instead



516
517
518
# File 'app/models/alchemy/page.rb', line 516

def public_on
  attribute_fixed?(:public_on) ? fixed_attributes[:public_on] : self[:public_on]
end

#public_untilObject

Returns the value of public_until attribute

If it’s a fixed attribute then the fixed value is returned instead



524
525
526
# File 'app/models/alchemy/page.rb', line 524

def public_until
  attribute_fixed?(:public_until) ? fixed_attributes[:public_until] : self[:public_until]
end

#publish!Object

Publishes the page.

Sets public_on and the published_at value to current time and resets public_until to nil

The published_at attribute is used as cache_key.



464
465
466
467
468
469
470
471
# File 'app/models/alchemy/page.rb', line 464

def publish!
  current_time = Time.current
  update_columns(
    published_at: current_time,
    public_on: already_public_for?(current_time) ? public_on : current_time,
    public_until: still_public_for?(current_time) ? public_until : nil
  )
end

#set_restrictions_to_child_pagesObject



425
426
427
428
429
# File 'app/models/alchemy/page.rb', line 425

def set_restrictions_to_child_pages
  descendants.each do |child|
    child.update(restricted: restricted?)
  end
end

#to_partial_pathObject

The page’s view partial is dependent from its page layout

Define page layouts

Page layouts are defined in the config/alchemy/page_layouts.yml file

- name: contact
  elements: [contactform]
  ...

Override the view

Page layout partials live in app/views/alchemy/page_layouts



375
376
377
# File 'app/models/alchemy/page.rb', line 375

def to_partial_path
  "alchemy/page_layouts/#{layout_partial_name}"
end

#unlock!Object

Unlocks the page without updating the timestamps



413
414
415
416
417
# File 'app/models/alchemy/page.rb', line 413

def unlock!
  if update_columns(locked_at: nil, locked_by: nil)
    Page.current_preview = nil
  end
end

#update_node!(node) ⇒ Object

Updates an Alchemy::Page based on a new ordering to be applied to it

Note: Page’s urls should not be updated (and a legacy URL created) if nesting is OFF or if a page is external or if the URL is the same

Parameters:

  • A (TreeNode)

    tree node with new lft, rgt, depth, url, parent_id and restricted indexes to be updated



481
482
483
484
485
486
487
488
489
490
# File 'app/models/alchemy/page.rb', line 481

def update_node!(node)
  hash = {lft: node.left, rgt: node.right, parent_id: node.parent, depth: node.depth, restricted: node.restricted}

  if Config.get(:url_nesting) && !definition['redirects_to_external'] && urlname != node.url
    LegacyPageUrl.create(page_id: id, urlname: urlname)
    hash[:urlname] = node.url
  end

  update_columns(hash)
end

#updater_nameObject

Returns the name of the last updater of this page.

If no updater could be found or associated user model does not respond to #name it returns ‘unknown’



542
543
544
# File 'app/models/alchemy/page.rb', line 542

def updater_name
  updater.try(:name) || Alchemy.t('unknown')
end

#url_pathObject

The url_path for this page



357
358
359
# File 'app/models/alchemy/page.rb', line 357

def url_path
  Alchemy::Page::UrlPath.new(self).call
end