refinerycms-pages 2.0.10 → 2.1.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 (89) hide show
  1. checksums.yaml +4 -4
  2. data/app/controllers/refinery/admin/pages_controller.rb +23 -17
  3. data/app/controllers/refinery/admin/pages_dialogs_controller.rb +8 -45
  4. data/app/controllers/refinery/pages/admin/preview_controller.rb +38 -0
  5. data/app/controllers/refinery/pages_controller.rb +26 -43
  6. data/app/helpers/refinery/admin/pages_helper.rb +19 -14
  7. data/app/models/refinery/page.rb +133 -194
  8. data/app/models/refinery/page_part.rb +1 -1
  9. data/{lib → app/presenters}/refinery/pages/content_page_presenter.rb +1 -5
  10. data/{lib → app/presenters}/refinery/pages/content_presenter.rb +1 -15
  11. data/app/presenters/refinery/pages/menu_presenter.rb +118 -0
  12. data/{lib → app/presenters}/refinery/pages/page_part_section_presenter.rb +0 -0
  13. data/{lib → app/presenters}/refinery/pages/section_presenter.rb +0 -0
  14. data/{lib → app/presenters}/refinery/pages/title_section_presenter.rb +0 -0
  15. data/app/sweepers/refinery/pages/page_sweeper.rb +29 -0
  16. data/app/views/refinery/admin/pages/_form.html.erb +4 -4
  17. data/app/views/refinery/admin/pages/_form_advanced_options.html.erb +3 -18
  18. data/app/views/refinery/admin/pages/_form_page_parts.html.erb +2 -2
  19. data/app/views/refinery/admin/pages/_page.html.erb +5 -5
  20. data/app/views/refinery/admin/pages/_records.html.erb +1 -3
  21. data/app/views/refinery/admin/pages/_sortable_list.html.erb +1 -1
  22. data/app/views/refinery/admin/pages/index.html.erb +1 -1
  23. data/app/views/refinery/admin/pages_dialogs/link_to.html.erb +0 -2
  24. data/config/locales/bg.yml +0 -11
  25. data/config/locales/cs.yml +1 -3
  26. data/config/locales/da.yml +15 -5
  27. data/config/locales/de.yml +16 -5
  28. data/config/locales/el.yml +0 -3
  29. data/config/locales/en.yml +1 -12
  30. data/config/locales/es.yml +0 -3
  31. data/config/locales/fi.yml +0 -3
  32. data/config/locales/fr.yml +0 -11
  33. data/config/locales/hu.yml +85 -0
  34. data/config/locales/it.yml +0 -10
  35. data/config/locales/ja.yml +0 -3
  36. data/config/locales/ko.yml +0 -11
  37. data/config/locales/lt.yml +0 -3
  38. data/config/locales/lv.yml +0 -3
  39. data/config/locales/nb.yml +0 -3
  40. data/config/locales/nl.yml +51 -40
  41. data/config/locales/pl.yml +23 -4
  42. data/config/locales/pt-BR.yml +0 -3
  43. data/config/locales/pt.yml +85 -0
  44. data/config/locales/rs.yml +0 -3
  45. data/config/locales/ru.yml +0 -3
  46. data/config/locales/sk.yml +0 -11
  47. data/config/locales/sl.yml +0 -3
  48. data/config/locales/sv.yml +0 -3
  49. data/config/locales/tr.yml +85 -0
  50. data/config/locales/uk.yml +82 -0
  51. data/config/locales/vi.yml +0 -3
  52. data/config/locales/zh-CN.yml +8 -11
  53. data/config/locales/zh-TW.yml +0 -3
  54. data/config/routes.rb +11 -5
  55. data/db/seeds.rb +16 -14
  56. data/lib/generators/refinery/pages/templates/config/initializers/refinery/pages.rb.erb +8 -4
  57. data/lib/refinery/pages.rb +5 -9
  58. data/lib/refinery/pages/caching.rb +60 -0
  59. data/lib/refinery/pages/configuration.rb +11 -7
  60. data/lib/refinery/pages/engine.rb +7 -8
  61. data/lib/refinery/pages/instance_methods.rb +4 -11
  62. data/lib/refinery/pages/render_options.rb +27 -0
  63. data/lib/refinery/pages/tab.rb +15 -4
  64. data/lib/refinery/pages/url.rb +74 -0
  65. data/refinerycms-pages.gemspec +4 -3
  66. data/spec/controllers/refinery/pages_controller_spec.rb +24 -0
  67. data/spec/factories/pages.rb +1 -1
  68. data/spec/{requests → features}/refinery/admin/pages_spec.rb +125 -42
  69. data/spec/{requests → features}/refinery/pages_spec.rb +139 -23
  70. data/spec/helpers/refinery/pages/admin/pages_helper_spec.rb +25 -25
  71. data/spec/lib/refinery/pages/caching_spec.rb +90 -0
  72. data/spec/lib/refinery/pages/tab_spec.rb +89 -0
  73. data/spec/lib/refinery/pages/url_spec.rb +74 -0
  74. data/spec/models/refinery/page_spec.rb +196 -71
  75. data/spec/{lib → presenters/refinery}/pages/content_page_presenter_spec.rb +0 -0
  76. data/spec/{lib → presenters/refinery}/pages/content_presenter_spec.rb +0 -0
  77. data/spec/presenters/refinery/pages/menu_presenter_spec.rb +58 -0
  78. data/spec/{lib → presenters/refinery}/pages/page_part_section_presenter_spec.rb +0 -0
  79. data/spec/{lib → presenters/refinery}/pages/section_presenter_spec.rb +0 -0
  80. data/spec/{lib → presenters/refinery}/pages/title_section_presenter_spec.rb +0 -0
  81. data/spec/support/refinery/pages/caching.rb +19 -0
  82. data/spec/support/refinery/pages/caching_helpers.rb +22 -0
  83. metadata +77 -33
  84. data/app/controllers/refinery/page_sweeper.rb +0 -34
  85. data/app/helpers/refinery/pages_helper.rb +0 -20
  86. data/app/views/refinery/admin/pages/_locale_picker.html.erb +0 -11
  87. data/config/locales/pt-PT.yml +0 -75
  88. data/spec/controllers/refinery/admin/pages_controller_spec.rb +0 -17
  89. data/spec/helpers/refinery/pages/pages_helper_spec.rb +0 -30
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: afa1108c7b18ea0168413156287b4dbc685b6a97
4
- data.tar.gz: e24e4cc3c8d1b35b51474e7d4ff1e2e78ae0506d
3
+ metadata.gz: 4780e7414495d270398ae05f97f184a229536571
4
+ data.tar.gz: 570c24e94417d1eec341292220e105a8ee162261
5
5
  SHA512:
6
- metadata.gz: 31fbc30c1a4666743cde7cbbd4d1ac9f7ebc9127504a11bdec22d93854f112a20c9706c5bed4a3222ef4762c2c64b3fea0369ab7b667e2a833cbd130ea2b4ca6
7
- data.tar.gz: 0d920af2461126b770741dac6a9d0aee9afb71ced6d3cef20888f725fc94a2786702c1052b04b13338ce653f6bf5961e05538946362050263dd90d98fa2456d8
6
+ metadata.gz: 69fd5760bf9f85414fe6fbc03bb099b0a48009add74178c43fba435e5264ef2a1234d1b29608a2d7a32dd7a82c2710df35c46d526d907db3c0849542d91c0ce5
7
+ data.tar.gz: f99adadf7d2ee66c289f89789a2d0b54c1a80d1511f6a1a026dc085330b0872cdd2e4e07e91ff42eebfafb5057464cf3bf65c89b2d0926590c603c22432ec1df
@@ -1,24 +1,22 @@
1
1
  module Refinery
2
2
  module Admin
3
3
  class PagesController < Refinery::AdminController
4
- cache_sweeper Refinery::PageSweeper
4
+ include Pages::InstanceMethods
5
+ cache_sweeper Pages::PageSweeper
5
6
 
6
7
  crudify :'refinery/page',
7
8
  :order => "lft ASC",
8
9
  :include => [:translations, :children],
9
10
  :paging => false
10
11
 
11
- after_filter lambda{::Refinery::Page.expire_page_caching}, :only => [:update_positions]
12
-
13
12
  before_filter :load_valid_templates, :only => [:edit, :new]
14
-
15
- before_filter :restrict_access, :only => [:create, :update, :update_positions, :destroy],
16
- :if => proc { Refinery.i18n_enabled? }
13
+ before_filter :restrict_access, :only => [:create, :update, :update_positions, :destroy]
14
+ after_filter proc { Pages::Caching.new().expire! }, :only => :update_positions
17
15
 
18
16
  def new
19
- @page = Refinery::Page.new(params.except(:controller, :action, :switch_locale))
20
- Refinery::Pages.default_parts_for(@page).each_with_index do |page_part, index|
21
- @page.parts << Refinery::PagePart.new(:title => page_part, :position => index)
17
+ @page = Page.new(params.except(:controller, :action, :switch_locale))
18
+ Pages.default_parts_for(@page).each_with_index do |page_part, index|
19
+ @page.parts << PagePart.new(:title => page_part, :position => index)
22
20
  end
23
21
  end
24
22
 
@@ -42,8 +40,8 @@ module Refinery
42
40
  redirect_to :back
43
41
  else
44
42
  render :partial => 'save_and_continue_callback', :locals => {
45
- :new_refinery_page_path => refinery.admin_page_path(@page.uncached_nested_url),
46
- :new_page_path => refinery.preview_page_path(@page.uncached_nested_url)
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)
47
45
  }
48
46
  end
49
47
  end
@@ -66,8 +64,13 @@ module Refinery
66
64
 
67
65
  protected
68
66
 
67
+ def after_update_positions
68
+ find_all_pages
69
+ render :partial => '/refinery/admin/pages/sortable_list' and return
70
+ end
71
+
69
72
  def find_page
70
- @page = Refinery::Page.find_by_path_or_id(params[:path], params[:id])
73
+ @page = Page.find_by_path_or_id(params[:path], params[:id])
71
74
  end
72
75
  alias_method :page, :find_page
73
76
 
@@ -77,15 +80,18 @@ module Refinery
77
80
  return super unless action_name.to_s == 'index'
78
81
 
79
82
  # Always display the tree of pages from the default frontend locale.
80
- Globalize.locale = params[:switch_locale].try(:to_sym) || Refinery::I18n.default_frontend_locale
83
+ if Refinery::I18n.built_in_locales.keys.map(&:to_s).include?(params[:switch_locale])
84
+ Globalize.locale = params[:switch_locale].try(:to_sym)
85
+ else
86
+ Globalize.locale = Refinery::I18n.default_frontend_locale
87
+ end
81
88
  end
82
89
 
83
90
  def load_valid_templates
84
- @valid_layout_templates = Refinery::Pages.layout_template_whitelist &
85
- Refinery::Pages.valid_templates('app', 'views', '{layouts,refinery/layouts}', '*html*')
91
+ @valid_layout_templates = Pages.layout_template_whitelist &
92
+ Pages.valid_templates('app', 'views', '{layouts,refinery/layouts}', '*html*')
86
93
 
87
- @valid_view_templates = Refinery::Pages.view_template_whitelist &
88
- Refinery::Pages.valid_templates('app', 'views', '{pages,refinery/pages}', '*html*')
94
+ @valid_view_templates = Pages.valid_templates('app', 'views', '{pages,refinery/pages}', '*html*')
89
95
  end
90
96
 
91
97
  def restrict_access
@@ -1,5 +1,3 @@
1
- require 'net/http'
2
-
3
1
  module Refinery
4
2
  module Admin
5
3
  class PagesDialogsController < ::Refinery::Admin::DialogsController
@@ -9,21 +7,23 @@ module Refinery
9
7
  def link_to
10
8
  # Get the switch_local variable to determine the locale we're currently editing
11
9
  # Set up Globalize with our current locale
12
- if ::Refinery.i18n_enabled?
13
- Thread.current[:globalize_locale] = params[:switch_locale] || Refinery::I18n.default_locale
10
+ Globalize.locale = if params[:switch_locale].present? && Refinery::I18n.built_in_locales.keys.map(&:to_s).include?(params[:switch_locale])
11
+ Globalize.locale = params[:switch_locale]
12
+ else
13
+ Refinery::I18n.default_locale
14
14
  end
15
15
 
16
16
  @pages = ::Refinery::Page.roots.paginate(:page => params[:page], :per_page => ::Refinery::Page.per_page(true))
17
17
 
18
- @pages = @pages.with_globalize if ::Refinery.i18n_enabled?
18
+ @pages = @pages.with_globalize
19
19
 
20
20
  if ::Refinery::Plugins.registered.names.include?('refinery_files')
21
- @resources = Resource.paginate(:page => params[:resource_page], :per_page => Resource.per_page(true)).
22
- order('created_at DESC')
21
+ @resources = Resource.paginate(:page => params[:resource_page], :per_page => Resource.per_page(true)).
22
+ order('created_at DESC')
23
23
 
24
24
  # resource link
25
25
  if params[:current_link].present?
26
- is_resource_link = params[:current_link].include?("/system/resources")
26
+ is_resource_link = params[:current_link].include?("/system/resources")
27
27
  end
28
28
  end
29
29
 
@@ -52,43 +52,6 @@ module Refinery
52
52
  end
53
53
  end
54
54
 
55
- def test_url
56
- result = 'failure'
57
- begin
58
- timeout(5) do
59
- unless params[:url].blank?
60
- url = URI.parse(params[:url])
61
- if url.host.nil? && params[:url].start_with?('/')
62
- url.host = URI.parse(request.url).host
63
- end
64
-
65
- result = case Net::HTTP.get_response(url)
66
- when Net::HTTPSuccess, Net::HTTPRedirection
67
- 'success'
68
- end
69
-
70
- end
71
- end
72
-
73
- rescue
74
- # be quiet
75
- end
76
-
77
- render :json => {:result => result}
78
- end
79
-
80
- def test_email
81
- if params[:email].present?
82
- valid = params[:email] =~ /^([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})$/i
83
-
84
- render :json => if valid
85
- {:result => 'success'}
86
- else
87
- {:result => 'failure'}
88
- end
89
- end
90
- end
91
-
92
55
  end
93
56
  end
94
57
  end
@@ -0,0 +1,38 @@
1
+ module Refinery
2
+ module Pages
3
+ module Admin
4
+ class PreviewController < AdminController
5
+ include Pages::InstanceMethods
6
+ include Pages::RenderOptions
7
+
8
+ before_filter :find_page
9
+
10
+ layout :layout
11
+
12
+ def show
13
+ render_with_templates?
14
+ end
15
+
16
+ protected
17
+ def admin?
18
+ false
19
+ end
20
+
21
+ def find_page
22
+ if @page = Refinery::Page.find_by_path_or_id(params[:path], params[:id])
23
+ # Preview existing pages
24
+ @page.attributes = params[:page]
25
+ elsif params[:page]
26
+ # Preview a non-persisted page
27
+ @page = Page.new params[:page]
28
+ end
29
+ end
30
+ alias_method :page, :find_page
31
+
32
+ def layout
33
+ 'application'
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -1,10 +1,12 @@
1
1
  module Refinery
2
2
  class PagesController < ::ApplicationController
3
- before_filter :find_page, :set_canonical, :except => [:preview]
4
- before_filter :find_page_for_preview, :only => [:preview]
3
+ include Pages::RenderOptions
4
+
5
+ before_filter :find_page, :set_canonical
6
+ before_filter :error_404, :unless => :current_user_can_view_page?
5
7
 
6
8
  # Save whole Page after delivery
7
- after_filter { |c| c.write_cache? }
9
+ after_filter :write_cache?
8
10
 
9
11
  # This action is usually accessed with the root path, normally '/'
10
12
  def home
@@ -22,37 +24,38 @@ module Refinery
22
24
  # GET /about/mission
23
25
  #
24
26
  def show
25
- if current_user_can_view_page?
26
- if should_skip_to_first_child?
27
- redirect_to refinery.url_for(first_live_child.url)
28
- elsif page.link_url.present?
29
- redirect_to page.link_url
30
- else
31
- if requested_friendly_id != page.friendly_id
32
- redirect_to refinery.url_for(page.url), :status => 301
33
- else
34
- render_with_templates?
35
- end
36
- end
37
- else
38
- error_404
27
+ if should_skip_to_first_child?
28
+ redirect_to refinery.url_for(first_live_child.url) and return
29
+ elsif page.link_url.present?
30
+ redirect_to page.link_url and return
31
+ elsif should_redirect_to_friendly_url?
32
+ redirect_to refinery.url_for(page.url), :status => 301 and return
39
33
  end
40
- end
41
34
 
42
- def preview
43
- render_with_templates?(:action => :show)
35
+ render_with_templates?
44
36
  end
45
37
 
46
38
  protected
47
39
 
48
40
  def requested_friendly_id
49
- "#{params[:path]}/#{params[:id]}".split('/').last
41
+ if ::Refinery::Pages.scope_slug_by_parent
42
+ # Pick out last path component, or id if present
43
+ "#{params[:path]}/#{params[:id]}".split('/').last
44
+ else
45
+ # Remove leading and trailing slashes in path, but leave internal
46
+ # ones for global slug scoping
47
+ params[:path].to_s.gsub(%r{\A/+}, '').presence || params[:id]
48
+ end
50
49
  end
51
50
 
52
51
  def should_skip_to_first_child?
53
52
  page.skip_to_first_child && first_live_child
54
53
  end
55
54
 
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?
57
+ end
58
+
56
59
  def current_user_can_view_page?
57
60
  page.live? || current_refinery_user_can_access?("refinery_pages")
58
61
  end
@@ -65,21 +68,11 @@ module Refinery
65
68
  page.children.order('lft ASC').live.first
66
69
  end
67
70
 
68
- def find_page_for_preview
69
- if page(fallback_to_404 = false)
70
- # Preview existing pages
71
- @page.attributes = view_context.sanitize_hash params[:page]
72
- elsif params[:page]
73
- # Preview a non-persisted page
74
- @page = Page.new params[:page]
75
- end
76
- end
77
-
78
71
  def find_page(fallback_to_404 = true)
79
72
  @page ||= case action_name
80
73
  when "home"
81
74
  Refinery::Page.where(:link_url => '/').first
82
- when "show", "preview"
75
+ when "show"
83
76
  Refinery::Page.find_by_path_or_id(params[:path], params[:id])
84
77
  end
85
78
  @page || (error_404 if fallback_to_404)
@@ -87,23 +80,13 @@ module Refinery
87
80
 
88
81
  alias_method :page, :find_page
89
82
 
90
- def render_with_templates?(render_options = {})
91
- if Refinery::Pages.use_layout_templates && page.layout_template.present?
92
- render_options[:layout] = page.layout_template
93
- end
94
- if Refinery::Pages.use_view_templates && page.view_template.present?
95
- render_options[:action] = page.view_template
96
- end
97
- render render_options if render_options.any?
98
- end
99
-
100
83
  def set_canonical
101
84
  @canonical = refinery.url_for @page.canonical if @page.present?
102
85
  end
103
86
 
104
87
  def write_cache?
105
88
  if Refinery::Pages.cache_pages_full && !refinery_user?
106
- cache_page(response.body, File.join('', 'refinery', 'cache', 'pages', request.path.sub("//", "/")).to_s)
89
+ cache_page(response.body, File.join('', 'refinery', 'cache', 'pages', request.path).to_s)
107
90
  end
108
91
  end
109
92
  end
@@ -12,15 +12,24 @@ module Refinery
12
12
  end
13
13
 
14
14
  def template_options(template_type, current_page)
15
- return {} if current_page.send(template_type)
16
-
17
- if current_page.parent_id?
18
- # Use Parent Template by default.
19
- { :selected => current_page.parent.send(template_type) }
20
- else
21
- # Use Default Template (First in whitelist)
22
- { :selected => Refinery::Pages.send("#{template_type}_whitelist").first }
15
+ html_options = { :selected => send("default_#{template_type}", current_page) }
16
+
17
+ if (template = current_page.send(template_type).presence)
18
+ html_options.update :selected => template
19
+ elsif current_page.parent_id? && !current_page.send(template_type).presence
20
+ template = current_page.parent.send(template_type).presence
21
+ html_options.update :selected => template if template
23
22
  end
23
+
24
+ html_options
25
+ end
26
+
27
+ def default_view_template(current_page)
28
+ current_page.link_url == "/" ? "home" : "show"
29
+ end
30
+
31
+ def default_layout_template(current_page)
32
+ "application"
24
33
  end
25
34
 
26
35
  # In the admin area we use a slightly different title
@@ -35,17 +44,13 @@ module Refinery
35
44
  ::I18n.t('draft', :scope => 'refinery.admin.pages.page')
36
45
  end if page.draft?
37
46
 
38
- meta_information.html_safe
47
+ meta_information
39
48
  end
40
49
 
41
50
  # We show the title from the next available locale
42
51
  # if there is no title for the current locale
43
52
  def page_title_with_translations(page)
44
- if page.title.present?
45
- page.title
46
- else
47
- page.translations.detect {|t| t.title.present?}.title
48
- end
53
+ page.title.presence || page.translations.detect {|t| t.title.present?}.title
49
54
  end
50
55
  end
51
56
  end
@@ -1,49 +1,44 @@
1
1
  # Encoding: utf-8
2
- require 'refinerycms-core'
3
- require 'acts_as_indexed'
4
2
  require 'friendly_id'
3
+ require 'refinery/core/base_model'
5
4
 
6
5
  module Refinery
7
6
  class Page < Core::BaseModel
8
7
  extend FriendlyId
9
8
 
10
- # when collecting the pages path how is each of the pages seperated?
11
- PATH_SEPARATOR = " - "
12
-
13
9
  translates :title, :menu_title, :custom_slug, :slug, :include => :seo_meta
14
10
 
15
11
  class Translation
16
12
  is_seo_meta
17
- attr_accessible :browser_title, :meta_description, :meta_keywords
13
+ attr_accessible *::SeoMeta.attributes.keys, :locale
18
14
  end
19
15
 
20
- attr_accessible :title
21
-
22
16
  # Delegate SEO Attributes to globalize3 translation
23
17
  seo_fields = ::SeoMeta.attributes.keys.map{|a| [a, :"#{a}="]}.flatten
24
18
  delegate(*(seo_fields << {:to => :translation}))
25
19
 
26
- attr_accessible :id, :deletable, :link_url, :menu_match, :meta_keywords,
20
+ attr_accessible :id, :deletable, :link_url, :menu_match,
27
21
  :skip_to_first_child, :position, :show_in_menu, :draft,
28
- :parts_attributes, :browser_title, :meta_description,
29
- :parent_id, :menu_title, :page_id, :layout_template,
30
- :view_template, :custom_slug, :slug
22
+ :parts_attributes, :parent_id, :menu_title, :page_id,
23
+ :layout_template, :view_template, :custom_slug, :slug,
24
+ :title, *::SeoMeta.attributes.keys
31
25
 
32
- attr_accessor :locale, :page_title, :page_menu_title # to hold temporarily
33
26
  validates :title, :presence => true
34
27
 
28
+ validates :custom_slug, :uniqueness => true, :allow_blank => true
29
+
35
30
  # Docs for acts_as_nested_set https://github.com/collectiveidea/awesome_nested_set
36
31
  # rather than :delete_all we want :destroy
37
32
  acts_as_nested_set :dependent => :destroy
38
33
 
39
34
  # Docs for friendly_id http://github.com/norman/friendly_id
40
- friendly_id :custom_slug_or_title, :use => [:reserved, :globalize, :scoped],
41
- :reserved_words => %w(index new session login logout users refinery admin images wymiframe),
42
- :scope => :parent
35
+ friendly_id_options = {:use => [:reserved, :globalize], :reserved_words => %w(index new session login logout users refinery admin images wymiframe)}
36
+ if ::Refinery::Pages.scope_slug_by_parent
37
+ friendly_id_options[:use] << :scoped
38
+ friendly_id_options.merge!(:scope => :parent)
39
+ end
43
40
 
44
- # Docs for acts_as_indexed http://github.com/dougal/acts_as_indexed
45
- acts_as_indexed :fields => [:title, :meta_keywords, :meta_description,
46
- :menu_title, :browser_title, :all_page_part_content]
41
+ friendly_id :custom_slug_or_title, friendly_id_options
47
42
 
48
43
  has_many :parts,
49
44
  :foreign_key => :refinery_page_id,
@@ -56,10 +51,9 @@ module Refinery
56
51
  accepts_nested_attributes_for :parts, :allow_destroy => true
57
52
 
58
53
  before_save { |m| m.translation.save }
59
- before_create :ensure_locale, :if => proc { ::Refinery.i18n_enabled? }
54
+ before_create :ensure_locale!
60
55
  before_destroy :deletable?
61
- after_save :reposition_parts!, :expire_page_caching
62
- after_destroy :expire_page_caching
56
+ after_save :reposition_parts!
63
57
 
64
58
  class << self
65
59
  # Live pages are 'allowed' to be shown in the frontend of your website.
@@ -68,13 +62,18 @@ module Refinery
68
62
  where(:draft => false)
69
63
  end
70
64
 
71
- # With slugs scoped to the parent page we need to find a page by its full path.
72
- # For example with about/example we would need to find 'about' and then its child
73
- # called 'example' otherwise it may clash with another page called /example.
65
+ # Find page by path, checking for scoping rules
74
66
  def find_by_path(path)
75
- split_path = path.to_s.split('/').reject(&:blank?)
76
- page = ::Refinery::Page.by_slug(split_path.shift, :parent_id => nil).first
77
- page = page.children.by_slug(split_path.shift).first until page.nil? || split_path.empty?
67
+ if ::Refinery::Pages.scope_slug_by_parent
68
+ # With slugs scoped to the parent page we need to find a page by its full path.
69
+ # For example with about/example we would need to find 'about' and then its child
70
+ # called 'example' otherwise it may clash with another page called /example.
71
+ path = path.split('/').select(&:present?)
72
+ page = by_slug(path.shift, :parent_id => nil).first
73
+ page = page.children.by_slug(path.shift).first while page && path.any?
74
+ else
75
+ page = by_slug(path).first
76
+ end
78
77
 
79
78
  page
80
79
  end
@@ -83,7 +82,7 @@ module Refinery
83
82
  # and if the path is unfriendly then a different finder method is required
84
83
  # than find_by_path.
85
84
  def find_by_path_or_id(path, id)
86
- if Refinery::Pages.marketable_urls && path.present?
85
+ if path.present?
87
86
  if path.friendly_id?
88
87
  find_by_path(path)
89
88
  else
@@ -102,10 +101,15 @@ module Refinery
102
101
  with_globalize(:title => title)
103
102
  end
104
103
 
105
- # Finds pages by their slug. See by_title
104
+ # Finds pages by their slug. This method is necessary because pages
105
+ # are translated which means the slug attribute does not exist on the
106
+ # pages table thus requiring us to find the attribute on the translations table
107
+ # and then join to the pages table again to return the associated record.
106
108
  def by_slug(slug, conditions={})
107
- locales = Refinery.i18n_enabled? ? Refinery::I18n.frontend_locales.map(&:to_s) : ::I18n.locale.to_s
108
- with_globalize({ :locale => locales, :slug => slug }.merge(conditions))
109
+ with_globalize({
110
+ :locale => Refinery::I18n.frontend_locales.map(&:to_s),
111
+ :slug => slug
112
+ }.merge(conditions))
109
113
  end
110
114
 
111
115
  # Shows all pages with :show_in_menu set to true, but it also
@@ -116,59 +120,26 @@ module Refinery
116
120
  where(:show_in_menu => true).with_globalize
117
121
  end
118
122
 
119
- # Because pages are translated this can have a negative performance impact
120
- # on your website and can introduce scaling issues. What fast_menu does is
121
- # finds all of the columns necessary to render a +Refinery::Menu+ structure
122
- # using only one SQL query. This has limitations, including not being able
123
- # to access any other attributes of the pages but you can specify more columns
124
- # by passing in an array e.g. fast_menu([:column1, :column2])
125
- def fast_menu(columns = [])
126
- # First, apply a filter to determine which pages to show.
127
- pages = live.in_menu.order('lft ASC').includes(:translations)
128
-
129
- # Now we only want to select particular columns to avoid any further queries.
130
- # Title and menu_title are retrieved in the next block below so they are not here.
131
- (menu_columns | columns).each do |column|
132
- pages = pages.select(arel_table[column.to_sym])
133
- end
134
-
135
- # We have to get title and menu_title from the translations table.
136
- # To avoid calling globalize3 an extra time, we get title as page_title
137
- # and we get menu_title as page_menu_title.
138
- # These is used in 'to_refinery_menu_item' in the Page model.
139
- %w(title menu_title).each do |column|
140
- pages = pages.joins(:translations).select(
141
- "#{translation_class.table_name}.#{column} as page_#{column}"
142
- )
143
- end
144
-
145
- pages
123
+ # An optimised scope containing only live pages ordered for display in a menu.
124
+ def fast_menu
125
+ live.in_menu.order(arel_table[:lft]).includes(:parent, :translations)
146
126
  end
147
127
 
148
128
  # Wrap up the logic of finding the pages based on the translations table.
149
129
  def with_globalize(conditions = {})
150
130
  conditions = {:locale => ::Globalize.locale.to_s}.merge(conditions)
151
- globalized_conditions = {}
131
+ translations_conditions = {}
132
+ translated_attrs = translated_attribute_names.map(&:to_s) | %w(locale)
133
+
152
134
  conditions.keys.each do |key|
153
- if (translated_attribute_names.map(&:to_s) | %w(locale)).include?(key.to_s)
154
- globalized_conditions["#{self.translation_class.table_name}.#{key}"] = conditions.delete(key)
135
+ if translated_attrs.include? key.to_s
136
+ translations_conditions["#{self.translation_class.table_name}.#{key}"] = conditions.delete(key)
155
137
  end
156
138
  end
157
- # A join implies readonly which we don't really want.
158
- joins(:translations).where(globalized_conditions).where(conditions).readonly(false)
159
- end
160
-
161
- # Wraps up all the checks that we need to do to figure out whether
162
- # the current frontend locale is different to the current one set by ::I18n.locale.
163
- # This terminates in a false if i18n extension is not defined or enabled.
164
- def different_frontend_locale?
165
- ::Refinery.i18n_enabled? && ::Refinery::I18n.current_frontend_locale != ::I18n.locale
166
- end
167
139
 
168
- # Override this method to change which columns you want to select to render your menu.
169
- # title and menu_title are always retrieved so omit these.
170
- def menu_columns
171
- %w(id depth parent_id lft rgt link_url menu_match slug)
140
+ # A join implies readonly which we don't really want.
141
+ where(conditions).joins(:translations).where(translations_conditions).
142
+ readonly(false)
172
143
  end
173
144
 
174
145
  # Returns how many pages per page should there be when paginating pages
@@ -176,46 +147,53 @@ module Refinery
176
147
  dialog ? Pages.pages_per_dialog : Pages.pages_per_admin_index
177
148
  end
178
149
 
179
- def expire_page_caching
180
- begin
181
- Rails.cache.delete_matched(/.*pages.*/)
182
- rescue NotImplementedError
183
- Rails.cache.clear
184
- warn "**** [REFINERY] The cache store you are using is not compatible with Rails.cache#delete_matched - clearing entire cache instead ***"
185
- ensure
186
- return true # so that other callbacks process.
150
+ def rebuild_with_slug_nullification!
151
+ rebuild_without_slug_nullification!
152
+ nullify_duplicate_slugs_under_the_same_parent!
153
+ end
154
+ alias_method_chain :rebuild!, :slug_nullification
155
+
156
+ protected
157
+ def nullify_duplicate_slugs_under_the_same_parent!
158
+ t_slug = translation_class.arel_table[:slug]
159
+ joins(:translations).group(:locale, :parent_id, t_slug).having(t_slug.count.gt(1)).count.
160
+ each do |(locale, parent_id, slug), count|
161
+ by_slug(slug, :locale => locale).where(:parent_id => parent_id).drop(1).each do |page|
162
+ page.slug = nil # kill the duplicate slug
163
+ page.save # regenerate the slug
164
+ end
187
165
  end
188
166
  end
189
167
  end
190
168
 
191
169
  def translated_to_default_locale?
192
- persisted? && (!Refinery.i18n_enabled? || translations.where(:locale => Refinery::I18n.default_frontend_locale).any?)
170
+ persisted? && translations.where(:locale => Refinery::I18n.default_frontend_locale).any?
193
171
  end
194
172
 
195
173
  # The canonical page for this particular page.
196
174
  # Consists of:
197
175
  # * The default locale's translated slug
198
176
  def canonical
199
- Globalize.with_locale(Refinery.i18n_enabled? && Refinery::I18n.default_frontend_locale || ::I18n.locale) { url }
177
+ Globalize.with_locale(::Refinery::I18n.default_frontend_locale) { url }
200
178
  end
201
179
 
202
180
  # The canonical slug for this particular page.
203
181
  # This is the slug for the default frontend locale.
204
182
  def canonical_slug
205
- Globalize.with_locale(Refinery.i18n_enabled? && Refinery::I18n.default_frontend_locale || ::I18n.locale) { slug }
183
+ Globalize.with_locale(::Refinery::I18n.default_frontend_locale) { slug }
206
184
  end
207
185
 
208
186
  # Returns in cascading order: custom_slug or menu_title or title depending on
209
187
  # which attribute is first found to be present for this page.
210
188
  def custom_slug_or_title
211
- custom_slug.presence || menu_title.presence || title
189
+ custom_slug.presence || menu_title.presence || title.presence
212
190
  end
213
191
 
214
192
  # Am I allowed to delete this page?
215
193
  # If a link_url is set we don't want to break the link so we don't allow them to delete
216
194
  # If deletable is set to false then we don't allow this page to be deleted. These are often Refinery system pages
217
195
  def deletable?
218
- deletable && link_url.blank? and menu_match.blank?
196
+ deletable && link_url.blank? && menu_match.blank?
219
197
  end
220
198
 
221
199
  # Repositions the child page_parts that belong to this page.
@@ -231,13 +209,7 @@ module Refinery
231
209
  def destroy
232
210
  return super if deletable?
233
211
 
234
- unless Rails.env.test?
235
- # give useful feedback when trying to delete from console
236
- puts "This page is not deletable. Please use .destroy! if you really want it deleted "
237
- puts "unset .link_url," if link_url.present?
238
- puts "unset .menu_match," if menu_match.present?
239
- puts "set .deletable to true" unless deletable
240
- end
212
+ puts_destroy_help
241
213
 
242
214
  false
243
215
  end
@@ -257,73 +229,44 @@ module Refinery
257
229
  # Override default options with any supplied.
258
230
  options = {:reversed => true}.merge(options)
259
231
 
260
- unless parent_id.nil?
232
+ if parent_id
261
233
  parts = [title, parent.path(options)]
262
234
  parts.reverse! if options[:reversed]
263
- parts.join(PATH_SEPARATOR)
235
+ parts.join(' - ')
264
236
  else
265
237
  title
266
238
  end
267
239
  end
268
240
 
269
- # When this page is rendered in the navigation, where should it link?
270
- # If a custom "link_url" is set, it uses that otherwise it defaults to a normal page URL.
271
- # The "link_url" is often used to link to a plugin rather than a page.
272
- #
273
- # For example if I had a "Contact" page I don't want it to just render a contact us page
274
- # I want it to show the Inquiries form so I can collect inquiries. So I would set the "link_url"
275
- # to "/contact"
276
241
  def url
277
- if link_url.present?
278
- link_url_localised?
279
- elsif Refinery::Pages.marketable_urls
280
- with_locale_param url_marketable
281
- elsif to_param.present?
282
- with_locale_param url_normal
283
- end
242
+ Pages::Url.build(self)
284
243
  end
285
244
 
286
- # Adds the locale key into the URI for this page's link_url attribute, unless
287
- # the current locale is set as the default locale.
288
245
  def link_url_localised?
289
- return link_url unless ::Refinery.i18n_enabled?
290
-
291
- current_url = link_url
292
-
293
- if current_url =~ %r{^/} && ::Refinery::I18n.current_frontend_locale != ::Refinery::I18n.default_frontend_locale
294
- current_url = "/#{::Refinery::I18n.current_frontend_locale}#{current_url}"
295
- end
296
-
297
- current_url
246
+ Refinery.deprecate "Refinery::Page#link_url_localised?", :when => '2.2',
247
+ :replacement => "Refinery::Pages::Url::Localised#url"
248
+ Pages::Url::Localised.new(self).url
298
249
  end
299
250
 
300
- # Add 'marketable url' attributes into this page's url.
301
- # This sets 'path' as the nested_url value and sets 'id' to nil.
302
- # For example, this might evaluate to /about for the "About" page.
303
- def url_marketable
304
- # :id => nil is important to prevent any other params[:id] from interfering with this route.
305
- url_normal.merge :path => nested_url, :id => nil
306
- end
307
-
308
- # Returns a url suitable to be used in url_for in Rails (such as link_to).
309
- # For example, this might evaluate to /pages/about for the "About" page.
310
251
  def url_normal
311
- {:controller => '/refinery/pages', :action => 'show', :path => nil, :id => to_param, :only_path => true}
252
+ Refinery.deprecate "Refinery::Page#url_normal", :when => '2.2',
253
+ :replacement => "Refinery::Pages::Url::Normal#url"
254
+ Pages::Url::Normal.new(self).url
312
255
  end
313
256
 
314
- # If the current locale is set to something other than the default locale
315
- # then the :locale attribute will be set on the url hash, otherwise it won't be.
316
- def with_locale_param(url_hash, locale = nil)
317
- locale ||= ::Refinery::I18n.current_frontend_locale if self.class.different_frontend_locale?
318
- url_hash.update :locale => locale if locale
319
- url_hash
257
+ def url_marketable
258
+ Refinery.deprecate "Refinery::Page#url_marketable", :when => '2.2',
259
+ :replacement => "Refinery::Pages::Url::Marketable#url"
260
+ Pages::Url::Marketable.new(self).url
320
261
  end
321
262
 
322
- def uncached_nested_url
323
- [
324
- parent.try(:uncached_nested_url),
325
- Globalize.with_locale(slug_locale) { to_param.to_s }
326
- ].compact.flatten
263
+ def nested_url
264
+ globalized_slug = Globalize.with_locale(slug_locale) { to_param.to_s }
265
+ if ::Refinery::Pages.scope_slug_by_parent
266
+ [parent.try(:nested_url), globalized_slug].compact.flatten
267
+ else
268
+ [globalized_slug]
269
+ end
327
270
  end
328
271
 
329
272
  # Returns an array with all ancestors to_param, allow with its own
@@ -332,29 +275,17 @@ module Refinery
332
275
  #
333
276
  # ['about', 'mission']
334
277
  #
335
- alias_method :nested_url, :uncached_nested_url
278
+ alias_method :uncached_nested_url, :nested_url
336
279
 
337
- # Returns the string version of nested_url, i.e., the path that should be generated
338
- # by the router
280
+ # Returns the string version of nested_url, i.e., the path that should be
281
+ # generated by the router
339
282
  def nested_path
340
- Rails.cache.fetch(path_cache_key) { ['', nested_url].join('/') }
341
- end
342
-
343
- def path_cache_key(locale = Globalize.locale)
344
- [cache_key(locale), 'nested_path'].join('#')
345
- end
346
-
347
- def url_cache_key(locale = Globalize.locale)
348
- [cache_key(locale), 'nested_url'].join('#')
349
- end
350
-
351
- def cache_key(locale)
352
- [Refinery::Core.base_cache_key, 'page', locale, id].compact.join('/')
283
+ ['', nested_url].join('/')
353
284
  end
354
285
 
355
286
  # Returns true if this page is "published"
356
287
  def live?
357
- not draft?
288
+ !draft?
358
289
  end
359
290
 
360
291
  # Return true if this page can be shown in the navigation.
@@ -364,12 +295,7 @@ module Refinery
364
295
  end
365
296
 
366
297
  def not_in_menu?
367
- not in_menu?
368
- end
369
-
370
- # Returns true if this page is the home page or links to it.
371
- def home?
372
- link_url == '/'
298
+ !in_menu?
373
299
  end
374
300
 
375
301
  # Returns all visible sibling pages that can be rendered for the menu
@@ -377,18 +303,15 @@ module Refinery
377
303
  siblings.reject(&:not_in_menu?)
378
304
  end
379
305
 
380
- def refinery_menu_title
381
- [page_menu_title, page_title, menu_title, title].detect(&:present?)
382
- end
383
-
384
306
  def to_refinery_menu_item
385
307
  {
386
308
  :id => id,
387
309
  :lft => lft,
310
+ :depth => depth,
388
311
  :menu_match => menu_match,
389
312
  :parent_id => parent_id,
390
313
  :rgt => rgt,
391
- :title => refinery_menu_title,
314
+ :title => menu_title.presence || title.presence,
392
315
  :type => self.class.name,
393
316
  :url => url
394
317
  }
@@ -404,6 +327,17 @@ module Refinery
404
327
  part_with_title(part_title).try(:body)
405
328
  end
406
329
 
330
+ # Accessor method to test whether a page part
331
+ # exists and has content for this page.
332
+ # Example:
333
+ #
334
+ # ::Refinery::Page.first.content_for?(:body)
335
+ #
336
+ # Will return true if the page has a body page part and it is not blank.
337
+ def content_for?(part_title)
338
+ content_for(part_title).present?
339
+ end
340
+
407
341
  # Accessor method to get a page part object from a page.
408
342
  # Example:
409
343
  #
@@ -420,44 +354,49 @@ module Refinery
420
354
  end
421
355
  end
422
356
 
423
- # Used to index all the content on this page so it can be easily searched.
424
- def all_page_part_content
425
- parts.map(&:body).join(" ")
357
+ private
358
+
359
+ # Make sures that a translation exists for this page.
360
+ # The translation is set to the default frontend locale.
361
+ def ensure_locale!
362
+ if self.translations.empty?
363
+ self.translations.build(:locale => Refinery::I18n.default_frontend_locale)
364
+ end
426
365
  end
427
366
 
428
- ##
429
367
  # Protects generated slugs from title if they are in the list of reserved words
430
368
  # This applies mostly to plugin-generated pages.
431
369
  # This only kicks in when Refinery::Pages.marketable_urls is enabled.
370
+ # Also check for global scoping, and if enabled, allow slashes in slug.
432
371
  #
433
372
  # Returns the sluggified string
434
373
  def normalize_friendly_id_with_marketable_urls(slug_string)
435
- sluggified = slug_string.to_slug.normalize!
436
- if Refinery::Pages.marketable_urls && self.class.friendly_id_config.reserved_words.include?(sluggified)
437
- sluggified << "-page"
374
+ # If we are scoping by parent, no slashes are allowed. Otherwise, slug is potentially
375
+ # a custom slug that contains a custom route to the page.
376
+ if !Pages.scope_slug_by_parent && slug_string.include?('/')
377
+ slug_string.sub!(%r{^/*}, '').sub!(%r{/*$}, '') # Remove leading and trailing slashes, but allow internal
378
+ slug_string.split('/').select(&:present?).map {|s| normalize_friendly_id_with_marketable_urls(s) }.join('/')
379
+ else
380
+ sluggified = slug_string.to_slug.normalize!
381
+ if Pages.marketable_urls && self.class.friendly_id_config.reserved_words.include?(sluggified)
382
+ sluggified << "-page"
383
+ end
384
+ sluggified
438
385
  end
439
- sluggified
440
386
  end
441
387
  alias_method_chain :normalize_friendly_id, :marketable_urls
442
388
 
443
- private
444
-
445
- # Make sures that a translation exists for this page.
446
- # The translation is set to the default frontend locale.
447
- def ensure_locale
448
- if self.translations.empty?
449
- self.translations.build :locale => ::Refinery::I18n.default_frontend_locale
450
- end
451
- end
452
-
453
- def expire_page_caching
454
- self.class.expire_page_caching
389
+ def puts_destroy_help
390
+ puts "This page is not deletable. Please use .destroy! if you really want it deleted "
391
+ puts "unset .link_url," if link_url.present?
392
+ puts "unset .menu_match," if menu_match.present?
393
+ puts "set .deletable to true" unless deletable
455
394
  end
456
395
 
457
396
  def slug_locale
458
397
  return Globalize.locale if translation_for(Globalize.locale).try(:slug).present?
459
398
 
460
- if Refinery.i18n_enabled? && (translations.empty? || translation_for(Refinery::I18n.default_frontend_locale).present?)
399
+ if translations.empty? || translation_for(Refinery::I18n.default_frontend_locale).present?
461
400
  Refinery::I18n.default_frontend_locale
462
401
  else
463
402
  translations.first.locale