refinerycms-pages 2.0.10 → 2.1.0

Sign up to get free protection for your applications and to get access to all the features.
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