comfortable_mexican_sofa 1.0.1 → 1.0.2

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 (71) hide show
  1. data/Gemfile +1 -1
  2. data/Gemfile.lock +1 -1
  3. data/README.md +83 -1
  4. data/VERSION +1 -1
  5. data/app/controllers/cms_admin/base_controller.rb +15 -0
  6. data/app/controllers/cms_admin/layouts_controller.rb +3 -3
  7. data/app/controllers/cms_admin/pages_controller.rb +8 -8
  8. data/app/controllers/cms_admin/sites_controller.rb +58 -0
  9. data/app/controllers/cms_admin/snippets_controller.rb +18 -14
  10. data/app/controllers/cms_admin/uploads_controller.rb +7 -5
  11. data/app/controllers/cms_content_controller.rb +17 -5
  12. data/app/models/cms_layout.rb +7 -7
  13. data/app/models/cms_page.rb +18 -4
  14. data/app/models/cms_site.rb +23 -0
  15. data/app/models/cms_snippet.rb +17 -12
  16. data/app/models/cms_upload.rb +4 -7
  17. data/app/views/cms_admin/layouts/_form.html.erb +1 -1
  18. data/app/views/cms_admin/layouts/index.html.erb +1 -1
  19. data/app/views/cms_admin/pages/_form.html.erb +3 -2
  20. data/app/views/cms_admin/pages/_form_blocks.html.erb +1 -1
  21. data/app/views/cms_admin/pages/_index_branch.html.erb +1 -1
  22. data/app/views/cms_admin/pages/index.html.erb +1 -1
  23. data/app/views/cms_admin/sites/_form.html.erb +2 -0
  24. data/app/views/cms_admin/sites/edit.html.erb +6 -0
  25. data/app/views/cms_admin/sites/index.html.erb +22 -0
  26. data/app/views/cms_admin/sites/new.html.erb +6 -0
  27. data/app/views/cms_admin/snippets/_form.html.erb +2 -1
  28. data/app/views/cms_admin/snippets/index.html.erb +20 -1
  29. data/app/views/layouts/cms_admin.html.erb +2 -1
  30. data/comfortable_mexican_sofa.gemspec +31 -9
  31. data/config/initializers/comfortable_mexican_sofa.rb +10 -0
  32. data/config/routes.rb +3 -1
  33. data/db/migrate/01_create_cms.rb +18 -4
  34. data/lib/comfortable_mexican_sofa.rb +28 -22
  35. data/lib/comfortable_mexican_sofa/acts_as_tree.rb +97 -0
  36. data/lib/comfortable_mexican_sofa/cms_tag/snippet.rb +4 -0
  37. data/lib/comfortable_mexican_sofa/configuration.rb +19 -0
  38. data/lib/comfortable_mexican_sofa/controller_methods.rb +41 -0
  39. data/lib/comfortable_mexican_sofa/{cms_engine.rb → engine.rb} +1 -1
  40. data/lib/comfortable_mexican_sofa/{cms_form_builder.rb → form_builder.rb} +1 -1
  41. data/lib/comfortable_mexican_sofa/http_auth.rb +17 -0
  42. data/lib/comfortable_mexican_sofa/rails_extensions.rb +11 -0
  43. data/lib/comfortable_mexican_sofa/view_methods.rb +33 -0
  44. data/lib/generators/cms_generator.rb +4 -0
  45. data/public/stylesheets/comfortable_mexican_sofa/structure.css +10 -12
  46. data/test/fixtures/cms_layouts.yml +3 -1
  47. data/test/fixtures/cms_pages.yml +6 -2
  48. data/test/fixtures/cms_sites.yml +3 -0
  49. data/test/fixtures/cms_snippets.yml +3 -1
  50. data/test/fixtures/cms_uploads.yml +1 -0
  51. data/test/functional/cms_admin/layouts_controller_test.rb +3 -1
  52. data/test/functional/cms_admin/pages_controller_test.rb +4 -2
  53. data/test/functional/cms_admin/sites_controller_test.rb +92 -0
  54. data/test/functional/cms_admin/snippets_controller_test.rb +62 -37
  55. data/test/functional/cms_content_controller_test.rb +32 -5
  56. data/test/integration/authentication_test.rb +27 -0
  57. data/test/integration/render_cms_test.rb +57 -0
  58. data/test/integration/sites_test.rb +30 -0
  59. data/test/test_helper.rb +46 -3
  60. data/test/unit/cms_block_test.rb +1 -0
  61. data/test/unit/cms_configuration_test.rb +16 -0
  62. data/test/unit/cms_layout_test.rb +3 -3
  63. data/test/unit/cms_page_test.rb +23 -13
  64. data/test/unit/cms_site_test.rb +41 -0
  65. data/test/unit/cms_snippet_test.rb +1 -1
  66. data/test/unit/cms_tags/snippet_test.rb +1 -1
  67. data/test/unit/cms_upload_test.rb +7 -6
  68. metadata +32 -10
  69. data/lib/comfortable_mexican_sofa/cms_acts_as_tree.rb +0 -101
  70. data/lib/comfortable_mexican_sofa/cms_rails_extensions.rb +0 -32
  71. data/test/functional/cms_admin/base_controller_test.rb +0 -9
data/Gemfile CHANGED
@@ -1,6 +1,6 @@
1
1
  source 'http://rubygems.org'
2
2
 
3
- gem 'rails', '3.0.0'
3
+ gem 'rails', '>=3.0.0'
4
4
  gem 'sqlite3-ruby', :require => 'sqlite3'
5
5
  gem 'active_link_to', '>=0.0.6'
6
6
  gem 'paperclip', '>=2.3.3'
data/Gemfile.lock CHANGED
@@ -87,5 +87,5 @@ DEPENDENCIES
87
87
  jeweler (>= 1.4.0)
88
88
  mime-types
89
89
  paperclip (>= 2.3.3)
90
- rails (= 3.0.0)
90
+ rails (>= 3.0.0)
91
91
  sqlite3-ruby
data/README.md CHANGED
@@ -1,4 +1,86 @@
1
1
  Comfortable Mexican Sofa (CMS)
2
2
  ==============================
3
3
 
4
- TODO
4
+ What is this?
5
+ -------------
6
+ Comfortable Mexican Sofa is a Content Management System with an obnoxious name. Also it's a Rails 3 Engine. This means that you can use it as a stand-alone application and also as an Engine for your existing application.
7
+
8
+ Installation
9
+ ------------
10
+
11
+ ### Stand-alone
12
+ TODO: Need to create some sort of setup, so you can simply run:
13
+
14
+ $ comfortable_mexican_sofa my_new_app
15
+
16
+ ### As a Rails Engine
17
+ Add gem definition to your Gemfile:
18
+
19
+ config.gem 'comfortable_mexican_sofa'
20
+
21
+ Then from the Rails project's root run:
22
+
23
+ bundle install
24
+ rails g cms
25
+ rake db:migrate
26
+
27
+ At this point you should have database structure created, some assets copied to /public directory and initializer set up:
28
+
29
+ ComfortableMexicanSofa.configure do |config|
30
+ config.cms_title = 'ComfortableMexicanSofa'
31
+ config.authentication = 'ComfortableMexicanSofa::HttpAuth'
32
+ end
33
+
34
+ # Credentials for CmsHttpAuthentication
35
+ ComfortableMexicanSofa::HttpAuth.username = 'username'
36
+ ComfortableMexicanSofa::HttpAuth.password = 'password'
37
+
38
+ Usage
39
+ -----
40
+
41
+ Now you should be able to navigate to http://yoursite/cms-admin
42
+
43
+ ### Step 1: Create Site
44
+ CMS allows you to run multiple sites from a single installation. Each site is attached to a hostname. For the first time you'll be prompted to set up the initial site. Hostname will be pre-populated so just choose a label.
45
+
46
+ ### Step 2: Create Layout
47
+ Before creating pages and populating them with content we need to create a layout. Layout is the template of your pages. It defines some reusable content (like header and footer, for example) and places where the content goes. A very simple layout can look like this:
48
+
49
+ <html>
50
+ <body>
51
+ <h1>My Awesome Site</h1>
52
+ <cms:page:content>
53
+ </body>
54
+ </html>
55
+
56
+ So there's your layout and the `<cms:page:content>` defines a place where renderable `content` will go. There's just a handful of tags that you can use.
57
+
58
+ *Page Blocks* are pieces of content that will be output on the page:
59
+
60
+ <cms:page:some_label:text> # same as <cms:page:some_label>, will render a text area during page creation
61
+ <cms:page:some_label:string> # will render a text field during page creation
62
+ <cms:page:some_label:datetime> # datetime select widget
63
+ <cms:page:some_label:integer> # a number field
64
+
65
+ *Page Fields* are pieces of content that are not rendered. They are useful for hidden values you want to use inside your app.
66
+
67
+ <cms:field:some_label:text> # text area for the page creation form
68
+ <cms:field:some_label:string> # same as <cms:field:some_label>, this is a text field
69
+ <cms:field:some_label:datetime> # datetime
70
+ <cms:field:some_label:integer> # a number field
71
+
72
+ *Snippets* bits of reusable content that can be used in pages and layouts
73
+
74
+ <cms:snippet:snippet_slug>
75
+
76
+ *Partials* are exactly that. You don't want to do IRB inside CMS so there's a handy tag:
77
+
78
+ <cms:partial:path/to/partial>
79
+
80
+ You don't have to define entire html layout, however. You can simply re-use your application one. Page content will be yielded into it like any normal view.
81
+
82
+ TODO: more stuff
83
+
84
+ ### Step 3: Create Page
85
+
86
+ TODO: You pres butan page is created. Yay!
data/VERSION CHANGED
@@ -1 +1 @@
1
- 1.0.1
1
+ 1.0.2
@@ -1,5 +1,20 @@
1
+ # Authentication module must have #authenticate method
2
+ include ComfortableMexicanSofa.config.authentication.to_s.constantize
3
+
1
4
  class CmsAdmin::BaseController < ApplicationController
2
5
 
6
+ before_filter :authenticate,
7
+ :load_admin_cms_site
8
+
3
9
  layout 'cms_admin'
10
+
11
+ protected
4
12
 
13
+ def load_admin_cms_site
14
+ @cms_site = CmsSite.find_by_hostname!(request.host.downcase)
15
+ rescue ActiveRecord::RecordNotFound
16
+ flash[:error] = 'No Site defined for this hostname. Create it now.'
17
+ redirect_to new_cms_admin_site_path
18
+ end
19
+
5
20
  end
@@ -4,7 +4,7 @@ class CmsAdmin::LayoutsController < CmsAdmin::BaseController
4
4
  before_filter :load_cms_layout, :only => [:edit, :update, :destroy]
5
5
 
6
6
  def index
7
- @cms_layouts = CmsLayout.roots
7
+ @cms_layouts = @cms_site.cms_layouts.roots
8
8
  end
9
9
 
10
10
  def new
@@ -42,12 +42,12 @@ class CmsAdmin::LayoutsController < CmsAdmin::BaseController
42
42
  protected
43
43
 
44
44
  def build_cms_layout
45
- @cms_layout = CmsLayout.new(params[:cms_layout])
45
+ @cms_layout = @cms_site.cms_layouts.new(params[:cms_layout])
46
46
  @cms_layout.parent ||= CmsLayout.find_by_id(params[:parent_id])
47
47
  end
48
48
 
49
49
  def load_cms_layout
50
- @cms_layout = CmsLayout.find(params[:id])
50
+ @cms_layout = @cms_site.cms_layouts.find(params[:id])
51
51
  rescue ActiveRecord::RecordNotFound
52
52
  flash[:error] = 'Layout not found'
53
53
  redirect_to :action => :index
@@ -4,7 +4,7 @@ class CmsAdmin::PagesController < CmsAdmin::BaseController
4
4
  before_filter :load_cms_page, :only => [:edit, :update, :destroy]
5
5
 
6
6
  def index
7
- @cms_pages = [CmsPage.root].compact
7
+ @cms_pages = [@cms_site.cms_pages.root].compact
8
8
  end
9
9
 
10
10
  def new
@@ -40,21 +40,21 @@ class CmsAdmin::PagesController < CmsAdmin::BaseController
40
40
  end
41
41
 
42
42
  def form_blocks
43
- @cms_page = CmsPage.find_by_id(params[:id]) || CmsPage.new
44
- @cms_page.cms_layout = CmsLayout.find_by_id(params[:layout_id])
43
+ @cms_page = @cms_site.cms_pages.find_by_id(params[:id]) || CmsPage.new
44
+ @cms_page.cms_layout = @cms_site.cms_layouts.find_by_id(params[:layout_id])
45
45
  end
46
46
 
47
47
  protected
48
48
 
49
49
  def build_cms_page
50
- @cms_page = CmsPage.new(params[:cms_page])
51
- @cms_page.parent ||= (CmsPage.find_by_id(params[:parent_id]) || CmsPage.root)
52
- @cms_page.cms_layout ||= (@cms_page.parent && @cms_page.parent.cms_layout || CmsLayout.first)
50
+ @cms_page = @cms_site.cms_pages.new(params[:cms_page])
51
+ @cms_page.parent ||= (CmsPage.find_by_id(params[:parent_id]) || @cms_site.cms_pages.root)
52
+ @cms_page.cms_layout ||= (@cms_page.parent && @cms_page.parent.cms_layout || @cms_site.cms_layouts.first)
53
53
  end
54
54
 
55
55
  def load_cms_page
56
- @cms_page = CmsPage.find(params[:id])
57
- @cms_page.cms_layout ||= (@cms_page.parent && @cms_page.parent.cms_layout || CmsLayout.first)
56
+ @cms_page = @cms_site.cms_pages.find(params[:id])
57
+ @cms_page.cms_layout ||= (@cms_page.parent && @cms_page.parent.cms_layout || @cms_site.cms_layouts.first)
58
58
  rescue ActiveRecord::RecordNotFound
59
59
  flash[:error] = 'Page not found'
60
60
  redirect_to :action => :index
@@ -0,0 +1,58 @@
1
+ class CmsAdmin::SitesController < CmsAdmin::BaseController
2
+
3
+ skip_before_filter :load_admin_cms_site
4
+
5
+ before_filter :build_cms_site, :only => [:new, :create]
6
+ before_filter :load_cms_site, :only => [:edit, :update, :destroy]
7
+
8
+ def index
9
+ @cms_sites = CmsSite.all
10
+ end
11
+
12
+ def new
13
+ render
14
+ end
15
+
16
+ def edit
17
+ render
18
+ end
19
+
20
+ def create
21
+ @cms_site.save!
22
+ flash[:notice] = 'Site created'
23
+ redirect_to :action => :edit, :id => @cms_site
24
+ rescue ActiveRecord::RecordInvalid
25
+ flash.now[:error] = 'Failed to create site'
26
+ render :action => :new
27
+ end
28
+
29
+ def update
30
+ @cms_site.update_attributes!(params[:cms_site])
31
+ flash[:notice] = 'Site updated'
32
+ redirect_to :action => :edit, :id => @cms_site
33
+ rescue ActiveRecord::RecordInvalid
34
+ flash.now[:error] = 'Failed to update site'
35
+ render :action => :edit
36
+ end
37
+
38
+ def destroy
39
+ @cms_site.destroy
40
+ flash[:notice] = 'Site deleted'
41
+ redirect_to :action => :index
42
+ end
43
+
44
+ protected
45
+
46
+ def build_cms_site
47
+ @cms_site = CmsSite.new(params[:cms_site])
48
+ @cms_site.hostname ||= request.host.downcase
49
+ end
50
+
51
+ def load_cms_site
52
+ @cms_site = CmsSite.find(params[:id])
53
+ rescue ActiveRecord::RecordNotFound
54
+ flash[:error] = 'Site not found'
55
+ redirect_to :action => :index
56
+ end
57
+
58
+ end
@@ -1,32 +1,35 @@
1
1
  class CmsAdmin::SnippetsController < CmsAdmin::BaseController
2
- before_filter :build_cms_snippet,
3
- :only => [:new, :create]
4
- before_filter :load_cms_snippet,
5
- :only => [:edit, :update, :destroy]
6
-
2
+
3
+ before_filter :build_cms_snippet, :only => [:new, :create]
4
+ before_filter :load_cms_snippet, :only => [:edit, :update, :destroy]
5
+
7
6
  def index
8
- @cms_snippets = CmsSnippet.all(:order => 'label')
7
+ @cms_snippets = @cms_site.cms_snippets.all(:order => 'label')
9
8
  end
10
9
 
11
10
  def new
11
+ render
12
+ end
13
+
14
+ def edit
15
+ render
12
16
  end
13
17
 
14
18
  def create
15
19
  @cms_snippet.save!
16
- flash[:notice] = 'Snippet saved'
20
+ flash[:notice] = 'Snippet created'
17
21
  redirect_to :action => :edit, :id => @cms_snippet
18
22
  rescue ActiveRecord::RecordInvalid
23
+ flash.now[:error] = 'Failed to create snippet'
19
24
  render :action => :new
20
25
  end
21
26
 
22
- def edit
23
- end
24
-
25
27
  def update
26
28
  @cms_snippet.update_attributes!(params[:cms_snippet])
27
- flash[:notice] = 'Snippet saved'
29
+ flash[:notice] = 'Snippet updated'
28
30
  redirect_to :action => :edit, :id => @cms_snippet
29
31
  rescue ActiveRecord::RecordInvalid
32
+ flash.now[:error] = 'Failed to update snippet'
30
33
  render :action => :edit
31
34
  end
32
35
 
@@ -35,14 +38,15 @@ class CmsAdmin::SnippetsController < CmsAdmin::BaseController
35
38
  flash[:notice] = 'Snippet deleted'
36
39
  redirect_to :action => :index
37
40
  end
38
-
41
+
39
42
  protected
43
+
40
44
  def build_cms_snippet
41
- @cms_snippet = CmsSnippet.new(params[:cms_snippet])
45
+ @cms_snippet = @cms_site.cms_snippets.new(params[:cms_snippet])
42
46
  end
43
47
 
44
48
  def load_cms_snippet
45
- @cms_snippet = CmsSnippet.find(params[:id])
49
+ @cms_snippet = @cms_site.cms_snippets.find(params[:id])
46
50
  rescue ActiveRecord::RecordNotFound
47
51
  flash[:error] = 'Snippet not found'
48
52
  redirect_to :action => :index
@@ -1,12 +1,13 @@
1
1
  class CmsAdmin::UploadsController < CmsAdmin::BaseController
2
- before_filter :load_cms_upload,
3
- :only => :destroy
2
+
3
+ before_filter :load_cms_upload, :only => :destroy
4
4
 
5
5
  def index
6
+ render
6
7
  end
7
8
 
8
9
  def create
9
- @cms_upload = CmsUpload.new(:uploaded_file => params[:file])
10
+ @cms_upload = @cms_site.cms_uploads.new(:uploaded_file => params[:file])
10
11
  if @cms_upload.save
11
12
  render(:partial => 'cms_admin/uploads/upload', :object => @cms_upload)
12
13
  else
@@ -17,10 +18,11 @@ class CmsAdmin::UploadsController < CmsAdmin::BaseController
17
18
  def destroy
18
19
  @cms_upload.destroy
19
20
  end
20
-
21
+
21
22
  protected
23
+
22
24
  def load_cms_upload
23
- @cms_upload = CmsUpload.find(params[:id])
25
+ @cms_upload = @cms_site.cms_uploads.find(params[:id])
24
26
  rescue ActiveRecord::RecordNotFound
25
27
  render :nothing => true
26
28
  end
@@ -1,11 +1,12 @@
1
1
  class CmsContentController < ApplicationController
2
2
 
3
+ before_filter :load_cms_site
3
4
  before_filter :load_cms_page, :only => :render_html
4
5
  before_filter :load_cms_layout, :only => [:render_css, :render_js]
5
6
 
6
- def render_html
7
+ def render_html(status = 200)
7
8
  layout = @cms_page.cms_layout.app_layout.blank?? false : @cms_page.cms_layout.app_layout
8
- render :inline => @cms_page.content, :layout => layout
9
+ render :inline => @cms_page.content, :layout => layout, :status => status
9
10
  end
10
11
 
11
12
  def render_css
@@ -18,14 +19,25 @@ class CmsContentController < ApplicationController
18
19
 
19
20
  protected
20
21
 
22
+ def load_cms_site
23
+ @cms_site = CmsSite.find_by_hostname!(request.host.downcase)
24
+ rescue ActiveRecord::RecordNotFound
25
+ render :text => 'Site Not Found', :status => 404
26
+ end
27
+
21
28
  def load_cms_page
22
- @cms_page = CmsPage.find_by_full_path!("/#{params[:cms_path]}")
29
+ @cms_page = @cms_site.cms_pages.find_by_full_path!("/#{params[:cms_path]}")
30
+ return redirect_to(@cms_page.target_page.full_path) if @cms_page.target_page
23
31
  rescue ActiveRecord::RecordNotFound
24
- render :text => 'Page not found', :status => 404
32
+ if @cms_page = @cms_site.cms_pages.find_by_full_path('/404')
33
+ render_html(404)
34
+ else
35
+ render :text => 'Page Not Found', :status => 404
36
+ end
25
37
  end
26
38
 
27
39
  def load_cms_layout
28
- @cms_layout = CmsLayout.find(params[:id])
40
+ @cms_layout = @cms_site.cms_layouts.find(params[:id])
29
41
  rescue ActiveRecord::RecordNotFound
30
42
  render :nothing => true, :status => 404
31
43
  end
@@ -3,23 +3,23 @@ class CmsLayout < ActiveRecord::Base
3
3
  acts_as_tree
4
4
 
5
5
  # -- Relationships --------------------------------------------------------
6
+ belongs_to :cms_site
6
7
  has_many :cms_pages, :dependent => :nullify
7
8
 
8
9
  # -- Validations ----------------------------------------------------------
9
- validates :label,
10
- :presence => true
11
- validates :content,
12
- :presence => true
10
+ validates :cms_site_id, :presence => true
11
+ validates :label, :presence => true
12
+ validates :content, :presence => true
13
13
 
14
14
  # -- Class Methods --------------------------------------------------------
15
15
  # Tree-like structure for layouts
16
- def self.options_for_select(cms_layout = nil, current_layout = nil, depth = 0, spacer = '. . ')
16
+ def self.options_for_select(cms_site, cms_layout = nil, current_layout = nil, depth = 0, spacer = '. . ')
17
17
  out = []
18
- [current_layout || CmsLayout.roots].flatten.each do |layout|
18
+ [current_layout || cms_site.cms_layouts.roots].flatten.each do |layout|
19
19
  next if cms_layout == layout
20
20
  out << [ "#{spacer*depth}#{layout.label}", layout.id ]
21
21
  layout.children.each do |child|
22
- out += options_for_select(cms_layout, child, depth + 1, spacer)
22
+ out += options_for_select(cms_site, cms_layout, child, depth + 1, spacer)
23
23
  end
24
24
  end
25
25
  return out.compact
@@ -6,7 +6,10 @@ class CmsPage < ActiveRecord::Base
6
6
  attr_accessor :cms_tags
7
7
 
8
8
  # -- Relationships --------------------------------------------------------
9
+ belongs_to :cms_site
9
10
  belongs_to :cms_layout
11
+ belongs_to :target_page,
12
+ :class_name => 'CmsPage'
10
13
  has_many :cms_blocks,
11
14
  :dependent => :destroy
12
15
  accepts_nested_attributes_for :cms_blocks
@@ -16,6 +19,8 @@ class CmsPage < ActiveRecord::Base
16
19
  after_save :sync_child_pages
17
20
 
18
21
  # -- Validations ----------------------------------------------------------
22
+ validates :cms_site_id,
23
+ :presence => true
19
24
  validates :label,
20
25
  :presence => true
21
26
  validates :slug,
@@ -26,15 +31,16 @@ class CmsPage < ActiveRecord::Base
26
31
  :presence => true
27
32
  validates :full_path,
28
33
  :presence => true,
29
- :uniqueness => true
34
+ :uniqueness => { :scope => :cms_site_id }
35
+ validate :validate_target_page
30
36
 
31
37
  # -- Class Methods --------------------------------------------------------
32
38
  # Tree-like structure for pages
33
- def self.options_for_select(cms_page = nil, current_page = nil, depth = 0, spacer = '. . ')
34
- return [] if (current_page ||= CmsPage.root) == cms_page || !current_page
39
+ def self.options_for_select(cms_site, cms_page = nil, current_page = nil, depth = 0, exclude_self = true, spacer = '. . ')
40
+ return [] if (current_page ||= cms_site.cms_pages.root) == cms_page && exclude_self || !current_page
35
41
  out = [[ "#{spacer*depth}#{current_page.label}", current_page.id ]]
36
42
  current_page.children.each do |child|
37
- out += options_for_select(cms_page, child, depth + 1, spacer)
43
+ out += options_for_select(cms_site, cms_page, child, depth + 1, exclude_self, spacer)
38
44
  end
39
45
  return out.compact
40
46
  end
@@ -59,6 +65,14 @@ protected
59
65
  self.full_path = self.parent ? "#{self.parent.full_path}/#{self.slug}".squeeze('/') : '/'
60
66
  end
61
67
 
68
+ def validate_target_page
69
+ return unless self.target_page
70
+ p = self
71
+ while p.target_page do
72
+ return self.errors.add(:target_page_id, 'Invalid Redirect') if (p = p.target_page) == self
73
+ end
74
+ end
75
+
62
76
  # Forcing re-saves for child pages so they can update full_paths
63
77
  def sync_child_pages
64
78
  children.each{ |p| p.save! } if full_path_changed?