refinerycms-pages 0.9.9.1

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