refinerycms-pages 0.9.9.1

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 (66) hide show
  1. data/app/controllers/admin/page_parts_controller.rb +24 -0
  2. data/app/controllers/admin/pages_controller.rb +37 -0
  3. data/app/controllers/admin/pages_dialogs_controller.rb +87 -0
  4. data/app/controllers/pages_controller.rb +31 -0
  5. data/app/helpers/pages_helper.rb +2 -0
  6. data/app/models/page.rb +274 -0
  7. data/app/models/page_part.rb +23 -0
  8. data/app/presenters/page_presenter.rb +7 -0
  9. data/app/views/admin/pages/_form.html.erb +61 -0
  10. data/app/views/admin/pages/_form_advanced_options.html.erb +79 -0
  11. data/app/views/admin/pages/_form_advanced_options_seo.html.erb +24 -0
  12. data/app/views/admin/pages/_form_fields_after_title.html.erb +1 -0
  13. data/app/views/admin/pages/_form_new_page_parts.html.erb +14 -0
  14. data/app/views/admin/pages/_form_page_parts.html.erb +47 -0
  15. data/app/views/admin/pages/_locale_picker.html.erb +11 -0
  16. data/app/views/admin/pages/_page.html.erb +35 -0
  17. data/app/views/admin/pages/_page_part_field.html.erb +5 -0
  18. data/app/views/admin/pages/_sortable_list.html.erb +5 -0
  19. data/app/views/admin/pages/edit.html.erb +1 -0
  20. data/app/views/admin/pages/index.html.erb +40 -0
  21. data/app/views/admin/pages/new.html.erb +1 -0
  22. data/app/views/admin/pages_dialogs/_page_link.html.erb +13 -0
  23. data/app/views/admin/pages_dialogs/link_to.html.erb +141 -0
  24. data/app/views/pages/home.html.erb +1 -0
  25. data/app/views/pages/show.html.erb +1 -0
  26. data/config/locales/cs.yml +84 -0
  27. data/config/locales/da.yml +84 -0
  28. data/config/locales/de.yml +84 -0
  29. data/config/locales/el.yml +84 -0
  30. data/config/locales/en.yml +84 -0
  31. data/config/locales/es.yml +83 -0
  32. data/config/locales/fr.yml +84 -0
  33. data/config/locales/it.yml +98 -0
  34. data/config/locales/lolcat.yml +83 -0
  35. data/config/locales/lt.yml +85 -0
  36. data/config/locales/lv.yml +86 -0
  37. data/config/locales/nb.yml +85 -0
  38. data/config/locales/nl.yml +81 -0
  39. data/config/locales/pl.yml +85 -0
  40. data/config/locales/pt-BR.yml +85 -0
  41. data/config/locales/rs.yml +84 -0
  42. data/config/locales/ru.yml +108 -0
  43. data/config/locales/sl.yml +83 -0
  44. data/config/locales/sv.yml +84 -0
  45. data/config/locales/vi.yml +84 -0
  46. data/config/locales/zh-CN.yml +84 -0
  47. data/config/locales/zh-TW.yml +84 -0
  48. data/config/routes.rb +21 -0
  49. data/db/migrate/20100913234708_create_refinerycms_pages_schema.rb +53 -0
  50. data/db/migrate/20101214040815_translate_page_plugin.rb +29 -0
  51. data/db/migrate/20101216194133_remove_cached_slug_from_pages.rb +9 -0
  52. data/db/seeds/pages.rb +43 -0
  53. data/features/manage_pages.feature +47 -0
  54. data/features/step_definitions/page_steps.rb +53 -0
  55. data/features/support/paths.rb +26 -0
  56. data/features/visit_pages.feature +47 -0
  57. data/lib/gemspec.rb +33 -0
  58. data/lib/generators/refinerycms_pages_generator.rb +8 -0
  59. data/lib/pages/marketable_routes.rb +10 -0
  60. data/lib/pages/tabs.rb +30 -0
  61. data/lib/refinerycms-pages.rb +45 -0
  62. data/license.md +21 -0
  63. data/readme.md +156 -0
  64. data/refinerycms-pages.gemspec +110 -0
  65. data/spec/models/page_spec.rb +144 -0
  66. metadata +133 -0
@@ -0,0 +1,24 @@
1
+ module Admin
2
+ class PagePartsController < Admin::BaseController
3
+
4
+ def new
5
+ render :partial => "/admin/pages/page_part_field", :locals => {
6
+ :part => PagePart.new(:title => params[:title], :body => params[:body]),
7
+ :new_part => true,
8
+ :part_index => params[:part_index]
9
+ }
10
+ end
11
+
12
+ def destroy
13
+ part = PagePart.find(params[:id])
14
+ page = part.page
15
+ if part.destroy
16
+ page.reposition_parts!
17
+ render :text => "'#{part.title}' deleted."
18
+ else
19
+ render :text => "'#{part.title}' not deleted."
20
+ end
21
+ end
22
+
23
+ end
24
+ end
@@ -0,0 +1,37 @@
1
+ module Admin
2
+ class PagesController < Admin::BaseController
3
+
4
+ crudify :page,
5
+ :conditions => {:parent_id => nil},
6
+ :order => "lft ASC",
7
+ :include => [:parts, :slugs, :children, :parent, :translations],
8
+ :paging => false
9
+
10
+ rescue_from FriendlyId::ReservedError, :with => :show_errors_for_reserved_slug
11
+
12
+ def new
13
+ @page = Page.new
14
+ Page.default_parts.each_with_index do |page_part, index|
15
+ @page.parts << PagePart.new(:title => page_part, :position => index)
16
+ end
17
+ end
18
+
19
+ protected
20
+
21
+ def globalize!
22
+ Thread.current[:globalize_locale] = (params[:switch_locale] || (@page.present? && @page.slug.present? && @page.slug.locale) || ::Refinery::I18n.default_frontend_locale)
23
+ end
24
+
25
+ def show_errors_for_reserved_slug(exception)
26
+ flash[:error] = t('reserved_system_word', :scope => 'admin.pages')
27
+ if params[:action] == 'update'
28
+ find_page
29
+ render :edit
30
+ else
31
+ @page = Page.new(params[:page])
32
+ render :new
33
+ end
34
+ end
35
+
36
+ end
37
+ end
@@ -0,0 +1,87 @@
1
+ require 'net/http'
2
+
3
+ module Admin
4
+ class PagesDialogsController < Admin::DialogsController
5
+
6
+ crudify :page
7
+
8
+ def link_to
9
+ @pages = Page.paginate :page => params[:page],
10
+ :conditions => {:parent_id => nil},
11
+ :order => 'position ASC',
12
+ :per_page => Page.per_page(dialog=true)
13
+
14
+ if ::Refinery::Plugins.registered.names.include?('refinery_files')
15
+ @resources = Resource.paginate :page => params[:resource_page],
16
+ :order => 'created_at DESC',
17
+ :per_page => Resource.per_page(dialog=true)
18
+
19
+ # resource link
20
+ if params[:current_link].present?
21
+ is_resource_link = params[:current_link].include?("/system/resources")
22
+ end
23
+ end
24
+
25
+ # web address link
26
+ @web_address_text = "http://"
27
+ @web_address_text = params[:current_link] if params[:current_link].to_s =~ /^http:\/\//
28
+ @web_address_target_blank = (params[:target_blank] == "true")
29
+
30
+ # mailto link
31
+ if params[:current_link].present?
32
+ if params[:current_link] =~ /^mailto:/
33
+ @email_address_text = params[:current_link].split("mailto:")[1].split('?')[0]
34
+ end
35
+ @email_default_subject_text = params[:current_link].split('?subject=')[1] || params[:subject]
36
+ @email_default_body_text = params[:current_link].split('?body=')[1] || params[:body]
37
+ end
38
+
39
+ if params[:paginating].present?
40
+ @page_area_selected = (params[:paginating] == "your_page")
41
+ @resource_area_selected = (params[:paginating] == "resource_file")
42
+ else
43
+ @page_area_selected = (!is_resource_link and @web_address_text == "http://" and @email_address_text.blank?)
44
+ @web_address_area_selected = (@web_address_text != "http://")
45
+ @email_address_area_selected = @email_address_text.present?
46
+ @resource_area_selected = is_resource_link
47
+ end
48
+ end
49
+
50
+ def test_url
51
+ unless params[:url].blank?
52
+ url = URI.parse(params[:url])
53
+ if url.host.nil? && params[:url].start_with?('/')
54
+ url.host = URI.parse(request.url).host
55
+ end
56
+
57
+ http = Net::HTTP.new(url.host)
58
+ request = Net::HTTP::Get.new(url.path.blank? ? "/" : url.path)
59
+
60
+ response = http.request request
61
+
62
+ render :json => {:result => case response
63
+ when Net::HTTPSuccess, Net::HTTPRedirection
64
+ 'success'
65
+ else
66
+ 'failure'
67
+ end }
68
+ end
69
+
70
+ rescue
71
+ render :json => {:result => 'failure'}
72
+ end
73
+
74
+ def test_email
75
+ unless params[:email].blank?
76
+ valid = params[:email] =~ /^([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})$/i
77
+
78
+ render :json => if valid
79
+ {:result => 'success'}
80
+ else
81
+ {:result => 'failure'}
82
+ end
83
+ end
84
+ end
85
+
86
+ end
87
+ end
@@ -0,0 +1,31 @@
1
+ class PagesController < ApplicationController
2
+
3
+ # This action is usually accessed with the root path, normally '/'
4
+ def home
5
+ error_404 unless (@page = Page.where(:link_url => '/').first).present?
6
+ end
7
+
8
+ # This action can be accessed normally, or as nested pages.
9
+ # Assuming a page named "mission" that is a child of "about",
10
+ # you can access the pages with the following URLs:
11
+ #
12
+ # GET /pages/about
13
+ # GET /about
14
+ #
15
+ # GET /pages/mission
16
+ # GET /about/mission
17
+ #
18
+ def show
19
+ @page = Page.find("#{params[:path]}/#{params[:id]}".split('/').last)
20
+
21
+ if @page.try(:live?) || (refinery_user? && current_user.authorized_plugins.include?("refinery_pages"))
22
+ # if the admin wants this to be a "placeholder" page which goes to its first child, go to that instead.
23
+ if @page.skip_to_first_child && (first_live_child = @page.children.order('lft ASC').where(:draft=>false).first).present?
24
+ redirect_to first_live_child.url
25
+ end
26
+ else
27
+ error_404
28
+ end
29
+ end
30
+
31
+ end
@@ -0,0 +1,2 @@
1
+ module PagesHelper
2
+ end
@@ -0,0 +1,274 @@
1
+ require 'globalize3'
2
+
3
+ class Page < ActiveRecord::Base
4
+
5
+ translates :title, :meta_keywords, :meta_description, :browser_title if self.respond_to?(:translates)
6
+ attr_accessor :locale # to hold temporarily
7
+ validates :title, :presence => true
8
+
9
+ acts_as_nested_set
10
+
11
+ # Docs for friendly_id http://github.com/norman/friendly_id
12
+ has_friendly_id :title, :use_slug => true,
13
+ :default_locale => (::Refinery::I18n.default_frontend_locale rescue :en),
14
+ :reserved_words => %w(index new session login logout users refinery admin images wymiframe),
15
+ :approximate_ascii => RefinerySetting.find_or_set(:approximate_ascii, false, :scoping => "pages")
16
+
17
+ has_many :parts,
18
+ :class_name => "PagePart",
19
+ :order => "position ASC",
20
+ :inverse_of => :page,
21
+ :dependent => :destroy
22
+
23
+ accepts_nested_attributes_for :parts, :allow_destroy => true
24
+
25
+ # Docs for acts_as_indexed http://github.com/dougal/acts_as_indexed
26
+ acts_as_indexed :fields => [:title, :meta_keywords, :meta_description,
27
+ :custom_title, :browser_title, :all_page_part_content]
28
+
29
+ before_destroy :deletable?
30
+ after_save :reposition_parts!
31
+ after_save :invalidate_child_cached_url
32
+
33
+ scope :live, where(:draft => false)
34
+
35
+ # shows all pages with :show_in_menu set to true, but it also
36
+ # rejects any page that has not been translated to the current locale.
37
+ scope :in_menu, lambda {
38
+ pages = Arel::Table.new(Page.table_name)
39
+ translations = Arel::Table.new(Page.translations_table_name)
40
+
41
+ includes(:translations).where(:show_in_menu => true).where(
42
+ translations[:locale].eq(Globalize.locale)).where(pages[:id].eq(translations[:page_id]))
43
+ }
44
+
45
+ # when a dialog pops up to link to a page, how many pages per page should there be
46
+ PAGES_PER_DIALOG = 14
47
+
48
+ # when listing pages out in the admin area, how many pages should show per page
49
+ PAGES_PER_ADMIN_INDEX = 20
50
+
51
+ # when collecting the pages path how is each of the pages seperated?
52
+ PATH_SEPARATOR = " - "
53
+
54
+ # Am I allowed to delete this page?
55
+ # If a link_url is set we don't want to break the link so we don't allow them to delete
56
+ # If deletable is set to false then we don't allow this page to be deleted. These are often Refinery system pages
57
+ def deletable?
58
+ deletable && link_url.blank? and menu_match.blank?
59
+ end
60
+
61
+ # Repositions the child page_parts that belong to this page.
62
+ # This ensures that they are in the correct 0,1,2,3,4... etc order.
63
+ def reposition_parts!
64
+ parts.each_with_index do |part, index|
65
+ part.update_attribute(:position, index)
66
+ end
67
+ end
68
+
69
+ # Before destroying a page we check to see if it's a deletable page or not
70
+ # Refinery system pages are not deletable.
71
+ def destroy
72
+ if deletable?
73
+ super
74
+ else
75
+ unless Rails.env.test?
76
+ # give useful feedback when trying to delete from console
77
+ puts "This page is not deletable. Please use .destroy! if you really want it deleted "
78
+ puts "unset .link_url," if link_url.present?
79
+ puts "unset .menu_match," if menu_match.present?
80
+ puts "set .deletable to true" unless deletable
81
+ end
82
+
83
+ return false
84
+ end
85
+ end
86
+
87
+ # If you want to destroy a page that is set to be not deletable this is the way to do it.
88
+ def destroy!
89
+ self.menu_match = nil
90
+ self.link_url = nil
91
+ self.deletable = true
92
+
93
+ destroy
94
+ end
95
+
96
+ # Used for the browser title to get the full path to this page
97
+ # It automatically prints out this page title and all of it's parent page titles joined by a PATH_SEPARATOR
98
+ def path(options = {})
99
+ # Override default options with any supplied.
100
+ options = {:reversed => true}.merge(options)
101
+
102
+ unless parent.nil?
103
+ parts = [title, parent.path(options)]
104
+ parts.reverse! if options[:reversed]
105
+ parts.join(PATH_SEPARATOR)
106
+ else
107
+ title
108
+ end
109
+ end
110
+
111
+ # When this page is rendered in the navigation, where should it link?
112
+ # If a custom "link_url" is set, it uses that otherwise it defaults to a normal page URL.
113
+ # The "link_url" is often used to link to a plugin rather than a page.
114
+ #
115
+ # For example if I had a "Contact" page I don't want it to just render a contact us page
116
+ # I want it to show the Inquiries form so I can collect inquiries. So I would set the "link_url"
117
+ # to "/contact"
118
+ def url
119
+ if link_url.present?
120
+ link_url_localised?
121
+ elsif self.class.use_marketable_urls?
122
+ url_marketable
123
+ elsif to_param.present?
124
+ url_normal
125
+ end
126
+ end
127
+
128
+ def link_url_localised?
129
+ if link_url =~ %r{^/} and defined?(::Refinery::I18n) and ::Refinery::I18n.enabled? and
130
+ ::I18n.locale != ::Refinery::I18n.default_frontend_locale
131
+ "/#{::I18n.locale}#{link_url}"
132
+ else
133
+ link_url
134
+ end
135
+ end
136
+
137
+ def url_marketable
138
+ # :id => nil is important to prevent any other params[:id] from interfering with this route.
139
+ {:controller => '/pages', :action => 'show', :path => nested_url, :id => nil}
140
+ end
141
+
142
+ def url_normal
143
+ {:controller => '/pages', :action => 'show', :path => nil, :id => to_param}
144
+ end
145
+
146
+ # Returns an array with all ancestors to_param, allow with its own
147
+ # Ex: with an About page and a Mission underneath,
148
+ # Page.find('mission').nested_url would return:
149
+ #
150
+ # ['about', 'mission']
151
+ #
152
+ def nested_url
153
+ Rails.cache.fetch(url_cache_key) { uncached_nested_url }
154
+ end
155
+
156
+ def uncached_nested_url
157
+ [parent.try(:nested_url), to_param].compact.flatten
158
+ end
159
+
160
+ # Returns the string version of nested_url, i.e., the path that should be generated
161
+ # by the router
162
+ def nested_path
163
+ Rails.cache.fetch(path_cache_key) { ['', nested_url].join('/') }
164
+ end
165
+
166
+ def path_cache_key
167
+ [cache_key, 'nested_path'].join('#')
168
+ end
169
+
170
+ def url_cache_key
171
+ [cache_key, 'nested_url'].join('#')
172
+ end
173
+
174
+ def cache_key
175
+ [Refinery.base_cache_key, super].join('/')
176
+ end
177
+
178
+ # Returns true if this page is "published"
179
+ def live?
180
+ not draft?
181
+ end
182
+
183
+ # Return true if this page can be shown in the navigation.
184
+ # If it's a draft or is set to not show in the menu it will return false.
185
+ def in_menu?
186
+ live? && show_in_menu?
187
+ end
188
+
189
+ # Returns true if this page is the home page or links to it.
190
+ def home?
191
+ link_url == "/"
192
+ end
193
+
194
+ # Returns all visible sibling pages that can be rendered for the menu
195
+ def shown_siblings
196
+ siblings.reject { |sibling| not sibling.in_menu? }
197
+ end
198
+
199
+ class << self
200
+ # Accessor to find out the default page parts created for each new page
201
+ def default_parts
202
+ RefinerySetting.find_or_set(:default_page_parts, ["Body", "Side Body"])
203
+ end
204
+
205
+ # Returns how many pages per page should there be when paginating pages
206
+ def per_page(dialog = false)
207
+ dialog ? PAGES_PER_DIALOG : PAGES_PER_ADMIN_INDEX
208
+ end
209
+
210
+ def use_marketable_urls?
211
+ RefinerySetting.find_or_set(:use_marketable_urls, true, :scoping => 'pages')
212
+ end
213
+ end
214
+
215
+ # Accessor method to get a page part from a page.
216
+ # Example:
217
+ #
218
+ # Page.first[:body]
219
+ #
220
+ # Will return the body page part of the first page.
221
+ def [](part_title)
222
+ # don't want to override a super method when trying to call a page part.
223
+ # the way that we call page parts seems flawed, will probably revert to page.parts[:title] in a future release.
224
+ if (super_value = super).blank?
225
+ # self.parts is already eager loaded so we can now just grab the first element matching the title we specified.
226
+ part = self.parts.detect do |part|
227
+ part.title.present? and #protecting against the problem that occurs when have nil title
228
+ part.title == part_title.to_s or
229
+ part.title.downcase.gsub(" ", "_") == part_title.to_s.downcase.gsub(" ", "_")
230
+ end
231
+
232
+ return part.body unless part.nil?
233
+ end
234
+
235
+ super_value
236
+ end
237
+
238
+ # In the admin area we use a slightly different title to inform the which pages are draft or hidden pages
239
+ def title_with_meta
240
+ title = [self.title.to_s]
241
+ title << "<em>(#{::I18n.t('hidden', :scope => 'admin.pages.page')})</em>" unless show_in_menu?
242
+ title << "<em>(#{::I18n.t('draft', :scope => 'admin.pages.page')})</em>" if draft?
243
+
244
+ title.join(' ')
245
+ end
246
+
247
+ # Used to index all the content on this page so it can be easily searched.
248
+ def all_page_part_content
249
+ parts.collect {|p| p.body}.join(" ")
250
+ end
251
+
252
+ ##
253
+ # Protects generated slugs from title if they are in the list of reserved words
254
+ # This applies mostly to plugin-generated pages.
255
+ #
256
+ # Returns the sluggified string
257
+ def normalize_friendly_id(slug_string)
258
+ sluggified = super
259
+ if self.class.use_marketable_urls? && self.class.friendly_id_config.reserved_words.include?(sluggified)
260
+ sluggified << "-page"
261
+ end
262
+ sluggified
263
+ end
264
+
265
+ private
266
+
267
+ def invalidate_child_cached_url
268
+ return true unless self.class.use_marketable_urls?
269
+ children.each do |child|
270
+ Rails.cache.delete(child.url_cache_key)
271
+ Rails.cache.delete(child.path_cache_key)
272
+ end
273
+ end
274
+ end