refinerycms-pages 2.1.5 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (105) hide show
  1. checksums.yaml +4 -4
  2. data/app/controllers/refinery/admin/page_parts_controller.rb +6 -1
  3. data/app/controllers/refinery/admin/pages_controller.rb +53 -45
  4. data/app/controllers/refinery/pages/admin/preview_controller.rb +12 -3
  5. data/app/controllers/refinery/pages_controller.rb +12 -12
  6. data/app/helpers/refinery/admin/pages_helper.rb +5 -4
  7. data/app/helpers/refinery/pages/content_pages_helper.rb +6 -1
  8. data/app/models/refinery/page.rb +115 -141
  9. data/app/models/refinery/page_part.rb +32 -7
  10. data/app/presenters/refinery/pages/content_presenter.rb +7 -5
  11. data/app/presenters/refinery/pages/menu_presenter.rb +41 -15
  12. data/app/presenters/refinery/pages/page_part_section_presenter.rb +1 -7
  13. data/app/presenters/refinery/pages/section_presenter.rb +2 -6
  14. data/app/views/refinery/admin/pages/_actions.html.erb +4 -13
  15. data/app/views/refinery/admin/pages/_form.html.erb +7 -10
  16. data/app/views/refinery/admin/pages/_form_advanced_options.html.erb +5 -3
  17. data/app/views/refinery/admin/pages/_form_extra_fields_for_more_options.html.erb +1 -0
  18. data/app/views/refinery/admin/pages/_form_new_page_parts.html.erb +8 -0
  19. data/app/views/refinery/admin/pages/_form_page_parts.html.erb +10 -21
  20. data/app/views/refinery/admin/pages/_page.html.erb +31 -31
  21. data/app/views/refinery/admin/pages/_page_part_field.html.erb +2 -1
  22. data/app/views/refinery/admin/pages/_records.html.erb +3 -3
  23. data/app/views/refinery/admin/pages/_save_and_continue_callback.html.erb +1 -0
  24. data/app/views/refinery/admin/pages/index.html.erb +2 -4
  25. data/app/views/refinery/admin/pages_dialogs/link_to.html.erb +1 -1
  26. data/config/locales/bg.yml +1 -2
  27. data/config/locales/ca.yml +85 -0
  28. data/config/locales/cs.yml +13 -3
  29. data/config/locales/da.yml +1 -2
  30. data/config/locales/de.yml +0 -1
  31. data/config/locales/el.yml +41 -29
  32. data/config/locales/en.yml +2 -2
  33. data/config/locales/es.yml +2 -4
  34. data/config/locales/fi.yml +1 -4
  35. data/config/locales/fr.yml +2 -2
  36. data/config/locales/hu.yml +1 -2
  37. data/config/locales/it.yml +15 -16
  38. data/config/locales/ja.yml +1 -3
  39. data/config/locales/ko.yml +1 -4
  40. data/config/locales/lt.yml +1 -3
  41. data/config/locales/lv.yml +0 -2
  42. data/config/locales/nb.yml +1 -4
  43. data/config/locales/nl.yml +1 -2
  44. data/config/locales/pl.yml +1 -13
  45. data/config/locales/pt-BR.yml +1 -3
  46. data/config/locales/pt.yml +1 -2
  47. data/config/locales/rs.yml +1 -3
  48. data/config/locales/ru.yml +8 -5
  49. data/config/locales/sk.yml +1 -2
  50. data/config/locales/sl.yml +1 -3
  51. data/config/locales/sv.yml +1 -3
  52. data/config/locales/tr.yml +1 -2
  53. data/config/locales/uk.yml +1 -5
  54. data/config/locales/vi.yml +1 -3
  55. data/config/locales/zh-CN.yml +1 -4
  56. data/config/locales/zh-TW.yml +1 -3
  57. data/config/routes.rb +16 -16
  58. data/db/migrate/20100913234708_create_refinerycms_pages_schema.rb +28 -11
  59. data/db/migrate/20140105190324_add_custom_slug_to_refinery_pages.rb +20 -0
  60. data/db/migrate/20150130044643_add_slug_to_refinery_page_parts.rb +6 -0
  61. data/db/migrate/20150720155305_update_slug_and_title_in_refinery_page_parts.rb +13 -0
  62. data/db/seeds.rb +42 -36
  63. data/lib/generators/refinery/pages/templates/config/initializers/refinery/pages.rb.erb +12 -0
  64. data/lib/refinery/pages.rb +4 -3
  65. data/lib/refinery/pages/admin/instance_methods.rb +3 -3
  66. data/lib/refinery/pages/configuration.rb +7 -1
  67. data/lib/refinery/pages/engine.rb +19 -18
  68. data/lib/refinery/pages/finder.rb +179 -0
  69. data/lib/refinery/pages/instance_methods.rb +3 -14
  70. data/lib/refinery/pages/tab.rb +2 -2
  71. data/lib/refinery/pages/types.rb +1 -1
  72. data/lib/refinery/pages/url.rb +15 -3
  73. data/license.md +1 -1
  74. data/refinerycms-pages.gemspec +8 -5
  75. data/spec/controllers/refinery/admin/pages_controller_spec.rb +53 -0
  76. data/spec/controllers/refinery/pages_controller_spec.rb +1 -1
  77. data/spec/factories/pages.rb +1 -1
  78. data/spec/features/refinery/admin/pages_spec.rb +274 -290
  79. data/spec/features/refinery/pages_spec.rb +123 -138
  80. data/spec/helpers/refinery/pages/admin/pages_helper_spec.rb +15 -11
  81. data/spec/helpers/refinery/pages/content_pages_helper_spec.rb +11 -11
  82. data/spec/lib/generators/refinery/pages/pages_generator_spec.rb +1 -1
  83. data/spec/lib/pages_spec.rb +2 -2
  84. data/spec/lib/refinery/pages/tab_spec.rb +9 -9
  85. data/spec/lib/refinery/pages/url_spec.rb +12 -12
  86. data/spec/models/refinery/page_finder_spec.rb +72 -0
  87. data/spec/models/refinery/page_menu_spec.rb +107 -0
  88. data/spec/models/refinery/page_meta_data_spec.rb +49 -0
  89. data/spec/models/refinery/page_part_spec.rb +67 -0
  90. data/spec/models/refinery/page_spec.rb +16 -534
  91. data/spec/models/refinery/page_url_spec.rb +320 -0
  92. data/spec/presenters/refinery/pages/content_page_presenter_spec.rb +9 -9
  93. data/spec/presenters/refinery/pages/content_presenter_spec.rb +20 -20
  94. data/spec/presenters/refinery/pages/menu_presenter_spec.rb +44 -6
  95. data/spec/presenters/refinery/pages/page_part_section_presenter_spec.rb +12 -12
  96. data/spec/presenters/refinery/pages/section_presenter_spec.rb +22 -22
  97. data/spec/presenters/refinery/pages/title_section_presenter_spec.rb +4 -4
  98. data/spec/support/refinery/pages/caching_helpers.rb +2 -2
  99. metadata +55 -25
  100. data/app/helpers/refinery/admin/page_parts_helper.rb +0 -6
  101. data/app/helpers/refinery/admin/pages_dialogs_helper.rb +0 -6
  102. data/app/sweepers/refinery/pages/page_sweeper.rb +0 -29
  103. data/lib/refinery/pages/caching.rb +0 -60
  104. data/spec/lib/refinery/pages/caching_spec.rb +0 -90
  105. data/spec/support/refinery/pages/caching.rb +0 -19
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 7ca74af9b5248a8313eba585323664f7421241c9
4
- data.tar.gz: a6940067a4a5686a4045cea92e156185cb2ba370
3
+ metadata.gz: 2bdcbfb6ced22f015c509108b4cdf43b28a2fb99
4
+ data.tar.gz: f1c3faf991e0c23fa346ed0461d5cf3759139bb0
5
5
  SHA512:
6
- metadata.gz: a2b2acb0803208475af177019dc732e90ab5c29c6ea16ba8113bfe8c972f2b2918bed991de0bfbdec5c1b16dbb54b04b6c58ffb17f7cda8d38dc515f3216bdee
7
- data.tar.gz: fd54a6aaad887e98d79ce2485ba77d53294b7f7397cb2ee2be1da52182c05ce5ef35b80a0f93a01edc1fb86a690b7153b9f5627fd273828f1dc3d87efb0fc93b
6
+ metadata.gz: 7dbd0c97774895a794dd88fd3114f8d601ebfca7de61771b9b60714b7614a4482511bf4e123da675eac832802c222a1539013c46806dd86ea575e7cbebfe102b
7
+ data.tar.gz: fd285907905d48ccbeaaacbebe163f5d101d25b5198c347ba2a427ee9c123d1541f362afeb8d2eb42ab0a6c97ca1baf6aa8511adababa9e0bdddf42522b63f54
@@ -4,7 +4,7 @@ module Refinery
4
4
 
5
5
  def new
6
6
  render :partial => '/refinery/admin/pages/page_part_field', :locals => {
7
- :part => ::Refinery::PagePart.new(:title => params[:title], :body => params[:body]),
7
+ :part => ::Refinery::PagePart.new(new_page_part_params),
8
8
  :new_part => true,
9
9
  :part_index => params[:part_index]
10
10
  }
@@ -21,6 +21,11 @@ module Refinery
21
21
  end
22
22
  end
23
23
 
24
+ protected
25
+ def new_page_part_params
26
+ params.permit(:title, :slug, :body)
27
+ end
28
+
24
29
  end
25
30
  end
26
31
  end
@@ -1,22 +1,18 @@
1
1
  module Refinery
2
2
  module Admin
3
3
  class PagesController < Refinery::AdminController
4
- include Pages::InstanceMethods
5
- cache_sweeper Pages::PageSweeper
4
+ prepend Pages::InstanceMethods
6
5
 
7
6
  crudify :'refinery/page',
8
- :order => "lft ASC",
9
7
  :include => [:translations, :children],
10
8
  :paging => false
11
9
 
12
- before_filter :load_valid_templates, :only => [:edit, :new, :create]
13
- before_filter :restrict_access, :only => [:create, :update, :update_positions, :destroy]
14
- after_filter proc { Pages::Caching.new().expire! }, :only => :update_positions
10
+ helper_method :valid_layout_templates, :valid_view_templates
15
11
 
16
12
  def new
17
- @page = Page.new(params.except(:controller, :action, :switch_locale))
13
+ @page = Page.new(new_page_params)
18
14
  Pages.default_parts_for(@page).each_with_index do |page_part, index|
19
- @page.parts << PagePart.new(:title => page_part, :position => index)
15
+ @page.parts << PagePart.new(:title => page_part, :slug => page_part, :position => index)
20
16
  end
21
17
  end
22
18
 
@@ -26,43 +22,38 @@ module Refinery
26
22
  end
27
23
 
28
24
  def update
29
- if @page.update_attributes(params[:page])
30
- flash.notice = t(
31
- 'refinery.crudify.updated',
32
- :what => "'#{@page.title}'"
33
- )
34
-
35
- unless from_dialog?
36
- unless params[:continue_editing] =~ /true|on|1/
37
- redirect_back_or_default(refinery.admin_pages_path)
38
- else
39
- unless request.xhr?
40
- redirect_to :back
41
- else
42
- render :partial => 'save_and_continue_callback', :locals => {
43
- :new_refinery_page_path => refinery.admin_page_path(@page.nested_url),
44
- :new_page_path => refinery.pages_admin_preview_page_path(@page.nested_url)
45
- }
46
- end
47
- end
48
- else
25
+ if @page.update_attributes(page_params)
26
+ flash.notice = t('refinery.crudify.updated', what: "'#{@page.title}'")
27
+
28
+ if from_dialog?
49
29
  self.index
50
30
  @dialog_successful = true
51
31
  render :index
32
+ else
33
+ if params[:continue_editing] =~ /true|on|1/
34
+ if request.xhr?
35
+ render partial: 'save_and_continue_callback',
36
+ locals: save_and_continue_locals(@page)
37
+ else
38
+ redirect_to :back
39
+ end
40
+ else
41
+ redirect_back_or_default(refinery.admin_pages_path)
42
+ end
52
43
  end
53
44
  else
54
- unless request.xhr?
55
- render :action => 'edit'
56
- else
45
+ if request.xhr?
57
46
  render :partial => '/refinery/admin/error_messages', :locals => {
58
47
  :object => @page,
59
48
  :include_object_name => true
60
49
  }
50
+ else
51
+ render 'edit'
61
52
  end
62
53
  end
63
54
  end
64
55
 
65
- protected
56
+ protected
66
57
 
67
58
  def after_update_positions
68
59
  find_all_pages
@@ -70,12 +61,12 @@ module Refinery
70
61
  end
71
62
 
72
63
  def find_page
73
- @page = Page.find_by_path_or_id(params[:path], params[:id])
64
+ @page = Page.find_by_path_or_id!(params[:path], params[:id])
74
65
  end
75
66
  alias_method :page, :find_page
76
67
 
77
68
  # We can safely assume ::Refinery::I18n is defined because this method only gets
78
- # Invoked when the before_filter from the plugin is run.
69
+ # Invoked when the before_action from the plugin is run.
79
70
  def globalize!
80
71
  return super unless action_name.to_s == 'index'
81
72
 
@@ -87,23 +78,40 @@ module Refinery
87
78
  end
88
79
  end
89
80
 
90
- def load_valid_templates
91
- @valid_layout_templates = Pages.layout_template_whitelist &
92
- Pages.valid_templates('app', 'views', '{layouts,refinery/layouts}', '*html*')
81
+ def valid_layout_templates
82
+ Pages.layout_template_whitelist & Pages.valid_templates(*Pages.layout_templates_pattern)
83
+ end
84
+
85
+ def valid_view_templates
86
+ Pages.valid_templates(*Pages.view_templates_pattern)
87
+ end
93
88
 
94
- @valid_view_templates = Pages.valid_templates('app', 'views', '{pages,refinery/pages}', '*html*')
89
+ def page_params
90
+ params.require(:page).permit(permitted_page_params)
95
91
  end
96
92
 
97
- def restrict_access
98
- if current_refinery_user.has_role?(:translator) && !current_refinery_user.has_role?(:superuser) &&
99
- (params[:switch_locale].blank? || params[:switch_locale] == Refinery::I18n.default_frontend_locale.to_s)
100
- flash[:error] = t('translator_access', :scope => 'refinery.admin.pages')
101
- redirect_to refinery.admin_pages_path
102
- end
93
+ def new_page_params
94
+ params.permit(:parent_id, :view_template, :layout_template)
95
+ end
96
+
97
+ private
103
98
 
104
- return true
99
+ def permitted_page_params
100
+ [
101
+ :browser_title, :draft, :link_url, :menu_title, :meta_description,
102
+ :parent_id, :skip_to_first_child, :show_in_menu, :title, :view_template,
103
+ :layout_template, :custom_slug, parts_attributes: [:id, :title, :slug, :body, :position]
104
+ ]
105
105
  end
106
106
 
107
+ def save_and_continue_locals(page)
108
+ nested_url = page.nested_url
109
+ {
110
+ new_refinery_edit_page_path: refinery.admin_edit_page_path(nested_url),
111
+ new_refinery_page_path: refinery.admin_page_path(nested_url),
112
+ new_page_path: refinery.pages_admin_preview_page_path(nested_url)
113
+ }
114
+ end
107
115
  end
108
116
  end
109
117
  end
@@ -8,7 +8,7 @@ module Refinery
8
8
  include Refinery::Admin::BaseController
9
9
  include Pages::RenderOptions
10
10
 
11
- skip_before_filter :error_404, :set_canonical
11
+ skip_before_action :error_404, :set_canonical
12
12
 
13
13
  layout :layout
14
14
 
@@ -17,6 +17,7 @@ module Refinery
17
17
  end
18
18
 
19
19
  protected
20
+
20
21
  def admin?
21
22
  false
22
23
  end
@@ -24,10 +25,10 @@ module Refinery
24
25
  def find_page
25
26
  if @page = Refinery::Page.find_by_path_or_id(params[:path], params[:id])
26
27
  # Preview existing pages
27
- @page.attributes = params[:page]
28
+ @page.attributes = page_params
28
29
  elsif params[:page]
29
30
  # Preview a non-persisted page
30
- @page = Page.new params[:page]
31
+ @page = Page.new page_params
31
32
  end
32
33
  end
33
34
  alias_method :page, :find_page
@@ -35,6 +36,14 @@ module Refinery
35
36
  def layout
36
37
  'application'
37
38
  end
39
+
40
+ def page_params
41
+ params.require(:page).permit(
42
+ :browser_title, :draft, :link_url, :menu_title, :meta_description,
43
+ :parent_id, :skip_to_first_child, :show_in_menu, :title, :view_template,
44
+ :layout_template, parts_attributes: [:id, :title, :body, :position]
45
+ )
46
+ end
38
47
  end
39
48
  end
40
49
  end
@@ -2,11 +2,11 @@ module Refinery
2
2
  class PagesController < ::ApplicationController
3
3
  include Pages::RenderOptions
4
4
 
5
- before_filter :find_page, :set_canonical
6
- before_filter :error_404, :unless => :current_user_can_view_page?
5
+ before_action :find_page, :set_canonical
6
+ before_action :error_404, :unless => :current_user_can_view_page?
7
7
 
8
8
  # Save whole Page after delivery
9
- after_filter :write_cache?
9
+ after_action :write_cache?
10
10
 
11
11
  # This action is usually accessed with the root path, normally '/'
12
12
  def home
@@ -53,15 +53,14 @@ module Refinery
53
53
  end
54
54
 
55
55
  def should_redirect_to_friendly_url?
56
- requested_friendly_id != page.friendly_id || ::Refinery::Pages.scope_slug_by_parent && params[:path].present? && params[:path].match(page.root.slug).nil?
56
+ requested_friendly_id != page.friendly_id || (
57
+ ::Refinery::Pages.scope_slug_by_parent &&
58
+ params[:path].present? && params[:path].match(page.root.slug).nil?
59
+ )
57
60
  end
58
61
 
59
62
  def current_user_can_view_page?
60
- page.live? || current_refinery_user_can_access?("refinery_pages")
61
- end
62
-
63
- def current_refinery_user_can_access?(plugin)
64
- refinery_user? && current_refinery_user.authorized_plugins.include?(plugin)
63
+ page.live? || authorisation_manager.allow?(:plugin, "refinery_pages")
65
64
  end
66
65
 
67
66
  def first_live_child
@@ -71,9 +70,9 @@ module Refinery
71
70
  def find_page(fallback_to_404 = true)
72
71
  @page ||= case action_name
73
72
  when "home"
74
- Refinery::Page.where(:link_url => '/').first
73
+ Refinery::Page.find_by(link_url: '/')
75
74
  when "show"
76
- Refinery::Page.find_by_path_or_id(params[:path], params[:id])
75
+ Refinery::Page.friendly.find_by_path_or_id(params[:path], params[:id])
77
76
  end
78
77
  @page || (error_404 if fallback_to_404)
79
78
  end
@@ -85,7 +84,8 @@ module Refinery
85
84
  end
86
85
 
87
86
  def write_cache?
88
- if Refinery::Pages.cache_pages_full && !refinery_user?
87
+ # Don't cache the page with the site bar showing.
88
+ if Refinery::Pages.cache_pages_full && !authorisation_manager.allow?(:read, :site_bar)
89
89
  cache_page(response.body, File.join('', 'refinery', 'cache', 'pages', request.path).to_s)
90
90
  end
91
91
  end
@@ -3,12 +3,12 @@ module Refinery
3
3
  module PagesHelper
4
4
  def parent_id_nested_set_options(current_page)
5
5
  pages = []
6
- nested_set_options(::Refinery::Page, current_page) {|page| pages << page}
6
+ nested_set_options(::Refinery::Page, current_page) { |page| pages << page}
7
7
  # page.title needs the :translations association, doing something like
8
8
  # nested_set_options(::Refinery::Page.includes(:translations), page) doesn't work, yet.
9
9
  # See https://github.com/collectiveidea/awesome_nested_set/pull/123
10
- ActiveRecord::Associations::Preloader.new(pages, :translations).run
11
- pages.map {|page| ["#{'-' * page.level} #{page.title}", page.id]}
10
+ ActiveRecord::Associations::Preloader.new.preload(pages, :translations)
11
+ pages.map { |page| ["#{'-' * page.level} #{page.title}", page.id]}
12
12
  end
13
13
 
14
14
  def template_options(template_type, current_page)
@@ -50,8 +50,9 @@ module Refinery
50
50
  # We show the title from the next available locale
51
51
  # if there is no title for the current locale
52
52
  def page_title_with_translations(page)
53
- page.title.presence || page.translations.detect {|t| t.title.present?}.title
53
+ page.title.presence || page.translations.detect { |t| t.title.present?}.title
54
54
  end
55
+
55
56
  end
56
57
  end
57
58
  end
@@ -18,9 +18,14 @@ module Refinery
18
18
  # so avoid common layout names such as :header, :footer, etc.
19
19
  def render_content_presenter(content_page, options = {})
20
20
  content_page.hide_sections(options[:hide_sections]) if options[:hide_sections]
21
- content_page.fetch_template_overrides {|section_id| content_for(section_id)}
21
+ content_page.fetch_template_overrides { |section_id| content_for(section_id)}
22
22
  content_page.to_html(options[:can_use_fallback])
23
23
  end
24
+
25
+ # Compiles the default menu.
26
+ def refinery_menu_pages
27
+ Refinery::Menu.new Refinery::Page.fast_menu
28
+ end
24
29
  end
25
30
  end
26
31
  end
@@ -2,6 +2,7 @@
2
2
  require 'friendly_id'
3
3
  require 'refinery/core/base_model'
4
4
  require 'refinery/pages/url'
5
+ require 'refinery/pages/finder'
5
6
 
6
7
  module Refinery
7
8
  class Page < Core::BaseModel
@@ -11,18 +12,35 @@ module Refinery
11
12
 
12
13
  class Translation
13
14
  is_seo_meta
14
- attr_accessible *::SeoMeta.attributes.keys, :locale
15
+
16
+ def self.seo_fields
17
+ ::SeoMeta.attributes.keys.map{ |a| [a, :"#{a}="]}.flatten
18
+ end
15
19
  end
16
20
 
17
- # Delegate SEO Attributes to globalize translation
18
- seo_fields = ::SeoMeta.attributes.keys.map{|a| [a, :"#{a}="]}.flatten
19
- delegate(*(seo_fields << {:to => :translation}))
21
+ class FriendlyIdOptions
22
+ def self.options
23
+ # Docs for friendly_id https://github.com/norman/friendly_id
24
+ friendly_id_options = {
25
+ use: [:reserved],
26
+ reserved_words: Refinery::Pages.friendly_id_reserved_words
27
+ }
28
+ if ::Refinery::Pages.scope_slug_by_parent
29
+ friendly_id_options[:use] << :scoped
30
+ friendly_id_options.merge!(scope: :parent)
31
+ end
32
+ friendly_id_options[:use] << :globalize
33
+ friendly_id_options
34
+ end
35
+ end
20
36
 
21
- attr_accessible :id, :deletable, :link_url, :menu_match,
22
- :skip_to_first_child, :position, :show_in_menu, :draft,
23
- :parts_attributes, :parent_id, :menu_title, :page_id,
24
- :layout_template, :view_template, :custom_slug, :slug,
25
- :title, *::SeoMeta.attributes.keys
37
+ # If title changes tell friendly_id to regenerate slug when saving record
38
+ def should_generate_new_friendly_id?
39
+ changes.keys.include?("title") || changes.keys.include?("custom_slug")
40
+ end
41
+
42
+ # Delegate SEO Attributes to globalize translation
43
+ delegate(*(Translation.seo_fields << {:to => :translation}))
26
44
 
27
45
  validates :title, :presence => true
28
46
 
@@ -32,30 +50,19 @@ module Refinery
32
50
  # rather than :delete_all we want :destroy
33
51
  acts_as_nested_set :dependent => :destroy
34
52
 
35
- # Docs for friendly_id http://github.com/norman/friendly_id
36
- friendly_id_options = {:use => [:reserved, :globalize], :reserved_words => %w(index new session login logout users refinery admin images wymiframe)}
37
- if ::Refinery::Pages.scope_slug_by_parent
38
- friendly_id_options[:use] << :scoped
39
- friendly_id_options.merge!(:scope => :parent)
40
- end
41
-
42
- friendly_id :custom_slug_or_title, friendly_id_options
53
+ friendly_id :custom_slug_or_title, FriendlyIdOptions.options
43
54
 
44
- has_many :parts,
45
- :foreign_key => :refinery_page_id,
55
+ has_many :parts, -> {
56
+ scope = order('position ASC')
57
+ scope = scope.includes(:translations) if ::Refinery::PagePart.respond_to?(:translation_class)
58
+ scope
59
+ }, :foreign_key => :refinery_page_id,
46
60
  :class_name => '::Refinery::PagePart',
47
- :order => 'position ASC',
48
61
  :inverse_of => :page,
49
- :dependent => :destroy,
50
- :include => ((:translations) if ::Refinery::PagePart.respond_to?(:translation_class))
62
+ :dependent => :destroy
51
63
 
52
64
  accepts_nested_attributes_for :parts, :allow_destroy => true
53
65
 
54
- before_save do |m|
55
- m.translation.globalized_model = self
56
- m.translation.save if m.translation.new_record?
57
- end
58
- before_create :ensure_locale!
59
66
  before_destroy :deletable?
60
67
  after_save :reposition_parts!
61
68
 
@@ -68,36 +75,27 @@ module Refinery
68
75
 
69
76
  # Find page by path, checking for scoping rules
70
77
  def find_by_path(path)
71
- if ::Refinery::Pages.scope_slug_by_parent
72
- # With slugs scoped to the parent page we need to find a page by its full path.
73
- # For example with about/example we would need to find 'about' and then its child
74
- # called 'example' otherwise it may clash with another page called /example.
75
- path = path.split('/').select(&:present?)
76
- page = by_slug(path.shift, :parent_id => nil).first
77
- while page && path.any? do
78
- slug_or_id = path.shift
79
- page = page.children.by_slug(slug_or_id).first || page.children.find(slug_or_id)
80
- end
81
- else
82
- page = by_slug(path).first
83
- end
84
-
85
- page
78
+ Pages::Finder.by_path(path)
86
79
  end
87
80
 
88
81
  # Helps to resolve the situation where you have a path and an id
89
82
  # and if the path is unfriendly then a different finder method is required
90
83
  # than find_by_path.
91
84
  def find_by_path_or_id(path, id)
92
- if path.present?
93
- if path.friendly_id?
94
- find_by_path(path)
95
- else
96
- find(path)
97
- end
98
- elsif id.present?
99
- find(id)
100
- end
85
+ Pages::Finder.by_path_or_id(path, id)
86
+ end
87
+
88
+ # Helps to resolve the situation where you have a path and an id
89
+ # and if the path is unfriendly then a different finder method is required
90
+ # than find_by_path.
91
+ #
92
+ # raise ActiveRecord::RecordNotFound if not found.
93
+ def find_by_path_or_id!(path, id)
94
+ page = find_by_path_or_id(path, id)
95
+
96
+ raise ActiveRecord::RecordNotFound unless page
97
+
98
+ page
101
99
  end
102
100
 
103
101
  # Finds pages by their title. This method is necessary because pages
@@ -105,18 +103,15 @@ module Refinery
105
103
  # pages table thus requiring us to find the attribute on the translations table
106
104
  # and then join to the pages table again to return the associated record.
107
105
  def by_title(title)
108
- with_globalize(:title => title)
106
+ Pages::Finder.by_title(title)
109
107
  end
110
108
 
111
109
  # Finds pages by their slug. This method is necessary because pages
112
110
  # are translated which means the slug attribute does not exist on the
113
111
  # pages table thus requiring us to find the attribute on the translations table
114
112
  # and then join to the pages table again to return the associated record.
115
- def by_slug(slug, conditions={})
116
- with_globalize({
117
- :locale => Refinery::I18n.frontend_locales.map(&:to_s),
118
- :slug => slug
119
- }.merge(conditions))
113
+ def by_slug(slug, conditions = {})
114
+ Pages::Finder.by_slug(slug, conditions)
120
115
  end
121
116
 
122
117
  # Shows all pages with :show_in_menu set to true, but it also
@@ -134,19 +129,7 @@ module Refinery
134
129
 
135
130
  # Wrap up the logic of finding the pages based on the translations table.
136
131
  def with_globalize(conditions = {})
137
- conditions = {:locale => ::Globalize.locale.to_s}.merge(conditions)
138
- translations_conditions = {}
139
- translated_attrs = translated_attribute_names.map(&:to_s) | %w(locale)
140
-
141
- conditions.keys.each do |key|
142
- if translated_attrs.include? key.to_s
143
- translations_conditions["#{self.translation_class.table_name}.#{key}"] = conditions.delete(key)
144
- end
145
- end
146
-
147
- # A join implies readonly which we don't really want.
148
- where(conditions).joins(:translations).where(translations_conditions).
149
- readonly(false)
132
+ Pages::Finder.with_globalize(conditions)
150
133
  end
151
134
 
152
135
  # Returns how many pages per page should there be when paginating pages
@@ -161,6 +144,7 @@ module Refinery
161
144
  alias_method_chain :rebuild!, :slug_nullification
162
145
 
163
146
  protected
147
+
164
148
  def nullify_duplicate_slugs_under_the_same_parent!
165
149
  t_slug = translation_class.arel_table[:slug]
166
150
  joins(:translations).group(:locale, :parent_id, t_slug).having(t_slug.count.gt(1)).count.
@@ -174,7 +158,7 @@ module Refinery
174
158
  end
175
159
 
176
160
  def translated_to_default_locale?
177
- persisted? && translations.where(:locale => Refinery::I18n.default_frontend_locale).any?
161
+ persisted? && translations.any?{ |t| t.locale == Refinery::I18n.default_frontend_locale}
178
162
  end
179
163
 
180
164
  # The canonical page for this particular page.
@@ -193,7 +177,8 @@ module Refinery
193
177
  # Returns in cascading order: custom_slug or menu_title or title depending on
194
178
  # which attribute is first found to be present for this page.
195
179
  def custom_slug_or_title
196
- custom_slug.presence || menu_title.presence || title.presence
180
+ (Refinery::Pages.use_custom_slugs && custom_slug.presence) ||
181
+ menu_title.presence || title.presence
197
182
  end
198
183
 
199
184
  # Am I allowed to delete this page?
@@ -207,7 +192,7 @@ module Refinery
207
192
  # This ensures that they are in the correct 0,1,2,3,4... etc order.
208
193
  def reposition_parts!
209
194
  reload.parts.each_with_index do |part, index|
210
- part.update_attributes :position => index
195
+ part.update_columns position: index
211
196
  end
212
197
  end
213
198
 
@@ -223,56 +208,32 @@ module Refinery
223
208
 
224
209
  # If you want to destroy a page that is set to be not deletable this is the way to do it.
225
210
  def destroy!
226
- self.menu_match = nil
227
- self.link_url = nil
228
- self.deletable = true
211
+ self.update_attributes(:menu_match => nil, :link_url => nil, :deletable => true)
229
212
 
230
- destroy
213
+ self.destroy
231
214
  end
232
215
 
233
- # Used for the browser title to get the full path to this page
234
- # It automatically prints out this page title and all of it's parent page titles joined by a PATH_SEPARATOR
235
- def path(options = {})
236
- # Override default options with any supplied.
237
- options = {:reversed => true}.merge(options)
216
+ # Returns the full path to this page.
217
+ # This automatically prints out this page title and all parent page titles.
218
+ # The result is joined by the path_separator argument.
219
+ def path(path_separator: ' - ', ancestors_first: true)
220
+ return title if root?
238
221
 
239
- if parent_id
240
- parts = [title, parent.path(options)]
241
- parts.reverse! if options[:reversed]
242
- parts.join(' - ')
243
- else
244
- title
245
- end
222
+ chain = ancestors_first ? self_and_ancestors : self_and_ancestors.reverse
223
+ chain.map(&:title).join(path_separator)
246
224
  end
247
225
 
248
226
  def url
249
227
  Pages::Url.build(self)
250
228
  end
251
229
 
252
- def link_url_localised?
253
- Refinery.deprecate "Refinery::Page#link_url_localised?", :when => '2.2',
254
- :replacement => "Refinery::Pages::Url::Localised#url"
255
- Pages::Url::Localised.new(self).url
256
- end
257
-
258
- def url_normal
259
- Refinery.deprecate "Refinery::Page#url_normal", :when => '2.2',
260
- :replacement => "Refinery::Pages::Url::Normal#url"
261
- Pages::Url::Normal.new(self).url
262
- end
263
-
264
- def url_marketable
265
- Refinery.deprecate "Refinery::Page#url_marketable", :when => '2.2',
266
- :replacement => "Refinery::Pages::Url::Marketable#url"
267
- Pages::Url::Marketable.new(self).url
268
- end
269
-
270
230
  def nested_url
271
- globalized_slug = Globalize.with_locale(slug_locale) { to_param.to_s }
272
- if ::Refinery::Pages.scope_slug_by_parent
273
- [parent.try(:nested_url), globalized_slug].compact.flatten
274
- else
275
- [globalized_slug]
231
+ Globalize.with_locale(slug_locale) do
232
+ if ::Refinery::Pages.scope_slug_by_parent && !root?
233
+ self_and_ancestors.includes(:translations).map(&:to_param)
234
+ else
235
+ [to_param.to_s]
236
+ end
276
237
  end
277
238
  end
278
239
 
@@ -330,8 +291,8 @@ module Refinery
330
291
  # ::Refinery::Page.first.content_for(:body)
331
292
  #
332
293
  # Will return the body page part of the first page.
333
- def content_for(part_title)
334
- part_with_title(part_title).try(:body)
294
+ def content_for(part_slug)
295
+ part_with_slug(part_slug).try(:body)
335
296
  end
336
297
 
337
298
  # Accessor method to test whether a page part
@@ -341,34 +302,27 @@ module Refinery
341
302
  # ::Refinery::Page.first.content_for?(:body)
342
303
  #
343
304
  # Will return true if the page has a body page part and it is not blank.
344
- def content_for?(part_title)
345
- content_for(part_title).present?
305
+ def content_for?(part_slug)
306
+ content_for(part_slug).present?
346
307
  end
347
308
 
348
309
  # Accessor method to get a page part object from a page.
349
310
  # Example:
350
311
  #
351
- # ::Refinery::Page.first.part_with_title(:body)
312
+ # ::Refinery::Page.first.part_with_slug(:body)
352
313
  #
353
- # Will return the Refinery::PagePart object with that title using the first page.
354
- def part_with_title(part_title)
314
+ # Will return the Refinery::PagePart object with that slug using the first page.
315
+ def part_with_slug(part_slug)
355
316
  # self.parts is usually already eager loaded so we can now just grab
356
317
  # the first element matching the title we specified.
357
318
  self.parts.detect do |part|
358
- part.title.present? and # protecting against the problem that occurs when have nil title
359
- part.title == part_title.to_s or
360
- part.title.downcase.gsub(" ", "_") == part_title.to_s.downcase.gsub(" ", "_")
319
+ part.slug_matches?(part_slug)
361
320
  end
362
321
  end
363
322
 
364
- private
365
-
366
- # Make sures that a translation exists for this page.
367
- # The translation is set to the default frontend locale.
368
- def ensure_locale!
369
- if self.translations.empty?
370
- self.translations.build(:locale => Refinery::I18n.default_frontend_locale)
371
- end
323
+ def part_with_title(part_title)
324
+ Refinery.deprecate("Refinery::Page#part_with_title", when: "3.1", replacement: "part_with_slug")
325
+ part_with_slug(part_title.to_s.parameterize.underscore)
372
326
  end
373
327
 
374
328
  # Protects generated slugs from title if they are in the list of reserved words
@@ -377,21 +331,41 @@ module Refinery
377
331
  # Also check for global scoping, and if enabled, allow slashes in slug.
378
332
  #
379
333
  # Returns the sluggified string
380
- def normalize_friendly_id_with_marketable_urls(slug_string)
381
- # If we are scoping by parent, no slashes are allowed. Otherwise, slug is potentially
382
- # a custom slug that contains a custom route to the page.
383
- if !Pages.scope_slug_by_parent && slug_string.include?('/')
384
- slug_string.sub!(%r{^/*}, '').sub!(%r{/*$}, '') # Remove leading and trailing slashes, but allow internal
385
- slug_string.split('/').select(&:present?).map {|s| normalize_friendly_id_with_marketable_urls(s) }.join('/')
386
- else
334
+ def normalize_friendly_id(slug_string)
335
+ FriendlyIdPath.normalize_friendly_id(slug_string)
336
+ end
337
+
338
+ private
339
+
340
+ class FriendlyIdPath
341
+ def self.normalize_friendly_id_path(slug_string)
342
+ # Remove leading and trailing slashes, but allow internal
343
+ slug_string
344
+ .sub(%r{^/*}, '')
345
+ .sub(%r{/*$}, '')
346
+ .split('/')
347
+ .select(&:present?)
348
+ .map { |slug| self.normalize_friendly_id(slug) }.join('/')
349
+ end
350
+
351
+ def self.normalize_friendly_id(slug_string)
352
+ # If we are scoping by parent, no slashes are allowed. Otherwise, slug is
353
+ # potentially a custom slug that contains a custom route to the page.
354
+ if !Pages.scope_slug_by_parent && slug_string.include?('/')
355
+ self.normalize_friendly_id_path(slug_string)
356
+ else
357
+ self.protected_slug_string(slug_string)
358
+ end
359
+ end
360
+
361
+ def self.protected_slug_string(slug_string)
387
362
  sluggified = slug_string.to_slug.normalize!
388
- if Pages.marketable_urls && self.class.friendly_id_config.reserved_words.include?(sluggified)
363
+ if Pages.marketable_urls && Refinery::Pages.friendly_id_reserved_words.include?(sluggified)
389
364
  sluggified << "-page"
390
365
  end
391
366
  sluggified
392
367
  end
393
368
  end
394
- alias_method_chain :normalize_friendly_id, :marketable_urls
395
369
 
396
370
  def puts_destroy_help
397
371
  puts "This page is not deletable. Please use .destroy! if you really want it deleted "
@@ -401,9 +375,9 @@ module Refinery
401
375
  end
402
376
 
403
377
  def slug_locale
404
- return Globalize.locale if translation_for(Globalize.locale).try(:slug).present?
378
+ return Globalize.locale if translation_for(Globalize.locale, false).try(:slug).present?
405
379
 
406
- if translations.empty? || translation_for(Refinery::I18n.default_frontend_locale).present?
380
+ if translations.empty? || translation_for(Refinery::I18n.default_frontend_locale, false).present?
407
381
  Refinery::I18n.default_frontend_locale
408
382
  else
409
383
  translations.first.locale