radiant-vhost-extension 2.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (69) hide show
  1. data/.gitmodules +3 -0
  2. data/README +64 -0
  3. data/Rakefile +125 -0
  4. data/VERSION +1 -0
  5. data/app/controllers/admin/sites_controller.rb +16 -0
  6. data/app/models/site.rb +26 -0
  7. data/app/models/site_association_observer.rb +8 -0
  8. data/app/views/admin/sites/_form.html.haml +19 -0
  9. data/app/views/admin/sites/edit.html.haml +5 -0
  10. data/app/views/admin/sites/index.html.haml +24 -0
  11. data/app/views/admin/sites/new.html.haml +5 -0
  12. data/app/views/admin/sites/remove.html.haml +19 -0
  13. data/app/views/admin/users/_edit_sites.html.haml +4 -0
  14. data/app/views/admin/users/_site_admin_roles.html.haml +5 -0
  15. data/app/views/admin/users/_sites_td.html.haml +2 -0
  16. data/app/views/admin/users/_sites_th.html.haml +2 -0
  17. data/config/routes.rb +5 -0
  18. data/db/migrate/001_create_sites.rb +11 -0
  19. data/db/migrate/002_add_sites_users.rb +18 -0
  20. data/db/migrate/003_replace_snippet_name_unique_index.rb +10 -0
  21. data/db/migrate/004_add_site_admin_to_users.rb +9 -0
  22. data/db/templates/empty.yml +7 -0
  23. data/db/templates/simple-blog.yml +213 -0
  24. data/lib/bootstrap_with_site_id.rb +50 -0
  25. data/lib/radiant-vhost-extension.rb +0 -0
  26. data/lib/site_scope.rb +65 -0
  27. data/lib/tasks/add_site_columns.rb +44 -0
  28. data/lib/tasks/vhost_extension_tasks.rake +115 -0
  29. data/lib/vhost/admin_users_controller_extensions.rb +22 -0
  30. data/lib/vhost/admin_users_helper_extensions.rb +17 -0
  31. data/lib/vhost/application_controller_extensions.rb +36 -0
  32. data/lib/vhost/application_helper_extensions.rb +15 -0
  33. data/lib/vhost/controller_access_extensions.rb +16 -0
  34. data/lib/vhost/pages_controller_extensions.rb +11 -0
  35. data/lib/vhost/radiant_cache_extensions.rb +53 -0
  36. data/lib/vhost/site_scoped_model_extensions.rb +46 -0
  37. data/lib/vhost_default_config.yml +22 -0
  38. data/spec/controllers/admin/pages_controller_spec.rb +173 -0
  39. data/spec/controllers/admin/sites_controller_spec.rb +33 -0
  40. data/spec/controllers/site_controller_spec.rb +33 -0
  41. data/spec/datasets/site_home_pages_dataset.rb +76 -0
  42. data/spec/datasets/site_pages_dataset.rb +31 -0
  43. data/spec/datasets/site_users_dataset.rb +50 -0
  44. data/spec/datasets/sites_dataset.rb +10 -0
  45. data/spec/datasets/sites_site_users_and_site_pages_dataset.rb +8 -0
  46. data/spec/datasets/sites_site_users_dataset.rb +13 -0
  47. data/spec/fixtures/page_parts.yml +11 -0
  48. data/spec/fixtures/pages.yml +19 -0
  49. data/spec/fixtures/sites.yml +7 -0
  50. data/spec/fixtures/sites_users.yml +6 -0
  51. data/spec/fixtures/users.yml +35 -0
  52. data/spec/models/page_spec.rb +22 -0
  53. data/spec/models/site_spec.rb +19 -0
  54. data/spec/models/user_spec.rb +16 -0
  55. data/spec/spec.opts +6 -0
  56. data/spec/spec_helper.rb +42 -0
  57. data/test/fixtures/page_parts.yml +11 -0
  58. data/test/fixtures/pages.yml +19 -0
  59. data/test/fixtures/sites.yml +7 -0
  60. data/test/fixtures/sites_users.yml +6 -0
  61. data/test/fixtures/users.yml +35 -0
  62. data/test/functional/admin/pages_controller_test.rb +142 -0
  63. data/test/functional/admin/site_controller_test.rb +53 -0
  64. data/test/functional/vhost_extension_test.rb +37 -0
  65. data/test/helpers/page_part_test_helper.rb +49 -0
  66. data/test/test_helper.rb +17 -0
  67. data/test/unit/site_test.rb +26 -0
  68. data/vhost_extension.rb +154 -0
  69. metadata +167 -0
data/.gitmodules ADDED
@@ -0,0 +1,3 @@
1
+ [submodule "vendor/scoped_access"]
2
+ path = vendor/scoped_access
3
+ url = git://github.com/maiha/scoped_access.git
data/README ADDED
@@ -0,0 +1,64 @@
1
+ Host multiple separate websites on the same Radiant installation each with their
2
+ own users, pages, snippets and layouts (soon to be supporting other extensions).
3
+ Administer users and sites from any domain using your administrative accounts
4
+ and manage website content by logging into each domain.
5
+
6
+ IMPORTANT NOTES ABOUT VHOST
7
+ - Not compatible with the multi_site extension
8
+ - Uses the scoped_access plugin, make sure none of your extensions have a
9
+ conflicting version of the plugin.
10
+ - Hooks into the Radiant 'bootstrap' functionality and currently overwrites the
11
+ standard templates with a modified version of the Simple Blog (i.e. no Styled
12
+ Blog or Coffee template)
13
+
14
+ INSTRUCTIONS
15
+ Currently (April 26th, 2009) you need the freshest version of Radiant (0.7.1)
16
+ which includes Rails 2.2.2 to run vhost. The following instructions use git
17
+ to directly check out the latest version of Radiant so you may need to adjust
18
+ them slightly. Once the Radiant gem supports Rails 2.2.2 these instructions
19
+ will reflect the proper way to run and install vhost.
20
+
21
+ * Run 'git clone git://github.com/radiant/radiant.git yoursite'
22
+ * In /yoursite run:
23
+ * git submodule init
24
+ * git submodule update
25
+ * Create production, development and test database schemas
26
+ * Update /yoursite/config/database.yml to point to the schemas
27
+ * In /yoursite/vendor/extensions run:
28
+ * git clone git://github.com/saturnflyer/radiant-vhost-extension.git vhost
29
+ * In /yoursite/vendor/extensions/vhost run:
30
+ * git submodule init
31
+ * git submodule update
32
+ * In /yoursite run:
33
+ * rake development db:bootstrap
34
+ * rake db:test:clone_structure (this one may be unnecessary)
35
+ * rake db:test:clone
36
+ * Finally, in /yoursite run:
37
+ * rake spec:extensions EXT=vhost
38
+
39
+ You should see a number of spec examples running and passing. If you don't,
40
+ or they're failing, something has gone wrong!
41
+
42
+ VHOST SUPPORT FOR OTHER EXTENSIONS
43
+ Vhost support for other extensions is enabled by creating a /config/vhost.yml
44
+ file containing the names of the models that should be scoped down per site. If
45
+ site scoping for an extension cannot be specified through the model (i.e. it
46
+ uses the file system to present data or otherwise doesn't use an ActiveRecord)
47
+ then currently you cannot enable site scoping.
48
+
49
+ Example vhost.yml:
50
+
51
+ models:
52
+ # Class name
53
+ ManagedFile:
54
+ # Property requiring definition of validates_uniqueness_of
55
+ filename:
56
+ # Parameters to pass to validates_uniqueness_of
57
+ scope:
58
+ - site_id
59
+ message:
60
+ 'file name is already in use'
61
+ # Any classes used in Single Table Inheritance(STI)
62
+ sti_classes:
63
+ - OneClass
64
+ - TwoClass
data/Rakefile ADDED
@@ -0,0 +1,125 @@
1
+ require 'rake'
2
+
3
+ begin
4
+ require 'jeweler'
5
+ Jeweler::Tasks.new do |gem|
6
+ gem.name = "radiant-vhost-extension"
7
+ gem.summary = %Q{Vhost Extension for Radiant CMS}
8
+ gem.description = %Q{Host more than one site in a single instance of Radiant.}
9
+ gem.email = "jim@saturnflyer.com"
10
+ gem.homepage = "http://github.com/saturnflyer/radiant-vhost-extension"
11
+ gem.authors = ["Jason Garber",'Kaleb Walton', 'Jim Gay']
12
+ gem.add_development_dependency "radiant"
13
+ end
14
+ rescue LoadError
15
+ puts "Jeweler (or a dependency) not available. This is only required if you plan to package vhost as a gem."
16
+ end
17
+
18
+
19
+ # Determine where the RSpec plugin is by loading the boot
20
+ unless defined? RADIANT_ROOT
21
+ ENV["RAILS_ENV"] = "test"
22
+ case
23
+ when ENV["RADIANT_ENV_FILE"]
24
+ require File.dirname(ENV["RADIANT_ENV_FILE"]) + "/boot"
25
+ when File.dirname(__FILE__) =~ %r{vendor/radiant/vendor/extensions}
26
+ require "#{File.expand_path(File.dirname(__FILE__) + "/../../../../../")}/config/boot"
27
+ else
28
+ require "#{File.expand_path(File.dirname(__FILE__) + "/../../../")}/config/boot"
29
+ end
30
+ end
31
+
32
+ require 'rake/rdoctask'
33
+ require 'rake/testtask'
34
+
35
+ if defined? RADIANT_ROOT
36
+ rspec_base = File.expand_path(RADIANT_ROOT + '/vendor/plugins/rspec/lib')
37
+ $LOAD_PATH.unshift(rspec_base) if File.exist?(rspec_base)
38
+ end
39
+
40
+ require 'spec/rake/spectask'
41
+
42
+ # Cleanup the RADIANT_ROOT constant so specs will load the environment
43
+ Object.send(:remove_const, :RADIANT_ROOT)
44
+
45
+ extension_root = File.expand_path(File.dirname(__FILE__))
46
+
47
+ task :default => :spec
48
+ task :stats => "spec:statsetup"
49
+
50
+ desc "Run all specs in spec directory"
51
+ Spec::Rake::SpecTask.new(:spec) do |t|
52
+ t.spec_opts = ['--options', "\"#{extension_root}/spec/spec.opts\""]
53
+ t.spec_files = FileList['spec/**/*_spec.rb']
54
+ end
55
+
56
+ namespace :spec do
57
+ desc "Run all specs in spec directory with RCov"
58
+ Spec::Rake::SpecTask.new(:rcov) do |t|
59
+ t.spec_opts = ['--options', "\"#{extension_root}/spec/spec.opts\""]
60
+ t.spec_files = FileList['spec/**/*_spec.rb']
61
+ t.rcov = true
62
+ t.rcov_opts = ['--exclude', 'spec', '--rails']
63
+ end
64
+
65
+ desc "Print Specdoc for all specs"
66
+ Spec::Rake::SpecTask.new(:doc) do |t|
67
+ t.spec_opts = ["--format", "specdoc", "--dry-run"]
68
+ t.spec_files = FileList['spec/**/*_spec.rb']
69
+ end
70
+
71
+ [:models, :controllers, :views, :helpers].each do |sub|
72
+ desc "Run the specs under spec/#{sub}"
73
+ Spec::Rake::SpecTask.new(sub) do |t|
74
+ t.spec_opts = ['--options', "\"#{extension_root}/spec/spec.opts\""]
75
+ t.spec_files = FileList["spec/#{sub}/**/*_spec.rb"]
76
+ end
77
+ end
78
+
79
+ # Setup specs for stats
80
+ task :statsetup do
81
+ require 'code_statistics'
82
+ ::STATS_DIRECTORIES << %w(Model\ specs spec/models)
83
+ ::STATS_DIRECTORIES << %w(View\ specs spec/views)
84
+ ::STATS_DIRECTORIES << %w(Controller\ specs spec/controllers)
85
+ ::STATS_DIRECTORIES << %w(Helper\ specs spec/views)
86
+ ::CodeStatistics::TEST_TYPES << "Model specs"
87
+ ::CodeStatistics::TEST_TYPES << "View specs"
88
+ ::CodeStatistics::TEST_TYPES << "Controller specs"
89
+ ::CodeStatistics::TEST_TYPES << "Helper specs"
90
+ ::STATS_DIRECTORIES.delete_if {|a| a[0] =~ /test/}
91
+ end
92
+
93
+ namespace :db do
94
+ namespace :fixtures do
95
+ desc "Load fixtures (from spec/fixtures) into the current environment's database. Load specific fixtures using FIXTURES=x,y"
96
+ task :load => :environment do
97
+ require 'active_record/fixtures'
98
+ ActiveRecord::Base.establish_connection(RAILS_ENV.to_sym)
99
+ (ENV['FIXTURES'] ? ENV['FIXTURES'].split(/,/) : Dir.glob(File.join(RAILS_ROOT, 'spec', 'fixtures', '*.{yml,csv}'))).each do |fixture_file|
100
+ Fixtures.create_fixtures('spec/fixtures', File.basename(fixture_file, '.*'))
101
+ end
102
+ end
103
+ end
104
+ end
105
+ end
106
+
107
+ desc 'Generate documentation for the vhost extension.'
108
+ Rake::RDocTask.new(:rdoc) do |rdoc|
109
+ rdoc.rdoc_dir = 'rdoc'
110
+ rdoc.title = 'VhostExtension'
111
+ rdoc.options << '--line-numbers' << '--inline-source'
112
+ rdoc.rdoc_files.include('README')
113
+ rdoc.rdoc_files.include('lib/**/*.rb')
114
+ end
115
+
116
+ # For extensions that are in transition
117
+ desc 'Test the vhost extension.'
118
+ Rake::TestTask.new(:test) do |t|
119
+ t.libs << 'lib'
120
+ t.pattern = 'test/**/*_test.rb'
121
+ t.verbose = true
122
+ end
123
+
124
+ # Load any custom rakefiles for extension
125
+ Dir[File.dirname(__FILE__) + '/tasks/*.rake'].sort.each { |f| require f }
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 2.1.0
@@ -0,0 +1,16 @@
1
+ class Admin::SitesController < Admin::ResourceController
2
+ only_allow_access_to :index, :show, :new, :create, :edit, :update, :remove, :destroy, :switch_to,
3
+ :when => :site_admin,
4
+ :denied_url => { :controller => 'pages', :action => 'index' },
5
+ :denied_message => 'You must have administrative privileges to perform this action.'
6
+
7
+ def switch_to
8
+ site = Site.find(params[:id])
9
+ if site
10
+ redirect_to "http://#{site.hostname}#{request.port.to_s == '80' ? '' : ":#{request.port}"}/admin"
11
+ else
12
+ render :index
13
+ end
14
+ end
15
+
16
+ end
@@ -0,0 +1,26 @@
1
+ class Site < ActiveRecord::Base
2
+ has_and_belongs_to_many :users
3
+ has_many :pages
4
+
5
+ accepts_nested_attributes_for :users
6
+ VhostExtension.MODELS.each do |model|
7
+ has_many model.tableize.to_sym unless model.tableize.match(/pages|users/) # unless already defined
8
+ end
9
+
10
+ def allow_access_for(user)
11
+ # Site Admins can access all sites. Users can only access sites to which they belong
12
+ user.site_admin? || self.users.include?(user)
13
+ end
14
+
15
+ def homepage
16
+ self.pages.find(:first, :conditions => {:parent_id => nil})
17
+ end
18
+
19
+ def self.find_by_hostname(hostname)
20
+ # allow vhost to be added to existing sites
21
+ if Site.count == 0
22
+ Site.create!(:hostname => hostname)
23
+ end
24
+ self.find(:first, :conditions => ["hostname LIKE ?", "%#{hostname}%"])
25
+ end
26
+ end
@@ -0,0 +1,8 @@
1
+ class SiteAssociationObserver < ActiveRecord::Observer
2
+ observe *VhostExtension::SITE_SPECIFIC_MODELS.collect(&:constantize)
3
+
4
+ def before_validation(model)
5
+ # Set the site_id automatically if it's not already set
6
+ model.site_id ||= model.class.current_site.id
7
+ end
8
+ end
@@ -0,0 +1,19 @@
1
+ - form_for [:admin, @site] do |f|
2
+ = render_region :form_top
3
+ .form-area
4
+ - render_region :form do |form|
5
+ - form.edit_hostname do
6
+ %p.hostname
7
+ %label{:for=>"site_hostname"} Hostname
8
+ = f.text_field "hostname", :class => 'textbox', :maxlength => 100
9
+ - form.edit_users do
10
+ %p.users
11
+ %label{:for=>"site_users"} Users
12
+ = f.collection_select :user_ids, User.find(:all), :id, :name, {}, :multiple => true, :name => 'site[user_ids][]', :size => '10'
13
+ - render_region :form_bottom do |form_bottom|
14
+ - form_bottom.edit_buttons do
15
+ %p.buttons
16
+ = save_model_button(@site)
17
+ = save_model_and_continue_editing_button(@site)
18
+ or
19
+ = link_to 'Cancel', admin_sites_url
@@ -0,0 +1,5 @@
1
+ - render_region :main do |main|
2
+ - main.edit_header do
3
+ %h1 Edit Site
4
+ - main.edit_form do
5
+ = render :partial => 'form'
@@ -0,0 +1,24 @@
1
+ .outset
2
+ %table#sites.index{:cellpadding=>"0", :cellspacing=>"0", :border=>"0"}
3
+ %thead
4
+ %tr
5
+ %th.hostname Host
6
+ %th Administration
7
+ %th.users Users
8
+ %th.modify Modify
9
+ %tbody
10
+ - unless @sites.empty?
11
+ - for site in @sites
12
+ %tr.node.level-1
13
+ %td.site
14
+ = link_to site.hostname, edit_admin_site_url(:id => site)
15
+ %td.site
16
+ = link_to 'Go', switch_to_admin_site_url(site)
17
+ %td.users= site.users.collect(&:name).join(', ')
18
+ %td.remove= link_to('Remove Site', remove_admin_site_url(:id => site))
19
+ - else
20
+ %tr
21
+ %td.note{:colspan=>3} No Sites
22
+ #actions
23
+ %ul
24
+ %li= link_to 'New Site', new_admin_site_url
@@ -0,0 +1,5 @@
1
+ - render_region :main do |main|
2
+ - main.edit_header do
3
+ %h1 New Site
4
+ - main.edit_form do
5
+ = render :partial => 'form'
@@ -0,0 +1,19 @@
1
+ %h1 Remove Site
2
+
3
+ %p
4
+ Are you sure you want to
5
+ %strong.warning permanently remove
6
+ the following site?
7
+
8
+ %table.index#sites{:cellpadding=>0, :cellspacing=>0, :border=>0}
9
+ %tbody
10
+ %tr.node.level-1
11
+ %td.site
12
+ = image('page', :alt => "page-icon")
13
+ = @site.hostname
14
+
15
+ - form_for [:admin, @site], :html => {:method => :delete} do
16
+ %p.buttons
17
+ %input.button{:type=>"submit", :value=>"Delete Site"}/
18
+ or
19
+ = link_to 'Cancel', admin_sites_url
@@ -0,0 +1,4 @@
1
+ - if current_user.site_admin?
2
+ %p
3
+ = f.label :site_ids, 'Sites', :class => "optional"
4
+ ~ f.collection_select :site_ids, current_user.site_admin? ? Site.find(:all) : current_user.sites, :id, :hostname, {}, :multiple => true, :name => 'user[site_ids][]', :size => '10', :class => "textarea"
@@ -0,0 +1,5 @@
1
+ - if current_user.site_admin?
2
+ %p
3
+ = f.check_box "site_admin", :class => "checkbox"
4
+ = f.label :site_admin, 'Site admin', :class => "checkbox"
5
+ A site admin can make changes to all sites.
@@ -0,0 +1,2 @@
1
+ - if current_user.site_admin?
2
+ %td.sites= sites(user)
@@ -0,0 +1,2 @@
1
+ - if current_user.site_admin?
2
+ %th Sites
data/config/routes.rb ADDED
@@ -0,0 +1,5 @@
1
+ ActionController::Routing::Routes.draw do |map|
2
+ map.namespace :admin, :member => { :remove => :get } do |admin|
3
+ admin.resources :sites, :member => {:switch_to => :get}
4
+ end
5
+ end
@@ -0,0 +1,11 @@
1
+ class CreateSites < ActiveRecord::Migration
2
+ def self.up
3
+ create_table :sites do |t|
4
+ t.column :hostname, :string
5
+ end
6
+ end
7
+
8
+ def self.down
9
+ drop_table :sites
10
+ end
11
+ end
@@ -0,0 +1,18 @@
1
+ class AddSitesUsers < ActiveRecord::Migration
2
+ def self.up
3
+ create_table :sites_users, :id => false do |t|
4
+ t.column :site_id, :integer
5
+ t.column :user_id, :integer
6
+ end
7
+
8
+ add_index :sites_users, [:site_id, :user_id], :unique => true
9
+ add_index :sites_users, :user_id
10
+ end
11
+
12
+
13
+ def self.down
14
+ remove_index :sites_users, [:site_id, :user_id]
15
+ remove_index :sites_users, :user_id
16
+ drop_table :sites_users
17
+ end
18
+ end
@@ -0,0 +1,10 @@
1
+ class ReplaceSnippetNameUniqueIndex < ActiveRecord::Migration
2
+ def self.up
3
+ remove_index :snippets, :name => :name
4
+ end
5
+
6
+
7
+ def self.down
8
+ add_index "snippets", ["name"], :name => "name", :unique => true
9
+ end
10
+ end
@@ -0,0 +1,9 @@
1
+ class AddSiteAdminToUsers < ActiveRecord::Migration
2
+ def self.up
3
+ add_column :users, :site_admin, :boolean
4
+ end
5
+
6
+ def self.down
7
+ remove_column :users, :site_admin
8
+ end
9
+ end
@@ -0,0 +1,7 @@
1
+ name: Empty
2
+ description: Don't create any pages, layouts, or snippets.
3
+ records:
4
+ Sites:
5
+ main:
6
+ id: 1
7
+ hostname: '*'
@@ -0,0 +1,213 @@
1
+ name: Simple Blog
2
+ description: Setup Radiant as a simple weblog.
3
+ records:
4
+ Sites:
5
+ main:
6
+ id: 1
7
+ hostname: '*'
8
+ Layouts:
9
+ main:
10
+ id: 1
11
+ site_id: 1
12
+ name: Normal
13
+ content: |
14
+ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
15
+ "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
16
+ <html>
17
+ <head>
18
+ <title><r:title /></title>
19
+ <link href="/rss/" rel="alternate" title="RSS" type="application/rss+xml" />
20
+ <link rel="stylesheet" type="text/css" href="/styles.css" />
21
+ </head>
22
+ <body>
23
+ <r:snippet name="header" />
24
+ <div id="content">
25
+ <r:content />
26
+ <div id="extended">
27
+ <r:content part="extended" />
28
+ </div>
29
+ </div>
30
+ <r:snippet name="footer" />
31
+ </body>
32
+ </html>
33
+ css:
34
+ id: 2
35
+ site_id: 1
36
+ name: Stylesheet
37
+ content_type: text/css
38
+ content: |
39
+ <r:content />
40
+ xml_feed:
41
+ id: 3
42
+ site_id: 1
43
+ name: XML Feed
44
+ content_type: text/xml
45
+ content: |
46
+ <r:content />
47
+ Snippets:
48
+ header:
49
+ id: 1
50
+ site_id: 1
51
+ name: header
52
+ content: |
53
+ <div id="header">
54
+ <h1><r:title /></h1>
55
+ </div>
56
+ <hr />
57
+ footer:
58
+ id: 2
59
+ site_id: 1
60
+ name: footer
61
+ content: |
62
+ <hr />
63
+ <div id="footer">
64
+ <p>Powered by <a href="http://radiantcms.org/">Radiant CMS</a></p>
65
+ </div>
66
+ Pages:
67
+ home_page:
68
+ id: 1
69
+ site_id: 1
70
+ title: Home Page
71
+ breadcrumb: Home
72
+ slug: /
73
+ layout_id: 1
74
+ status_id: 100
75
+ file_not_found:
76
+ id: 2
77
+ site_id: 1
78
+ title: File Not Found
79
+ breadcrumb: File Not Found
80
+ slug: file-not-found
81
+ class_name: FileNotFoundPage
82
+ parent_id: 1
83
+ status_id: 100
84
+ rss_feed:
85
+ id: 3
86
+ site_id: 1
87
+ title: RSS Feed
88
+ breadcrumb: RSS Feed
89
+ slug: rss
90
+ layout_id: 3
91
+ parent_id: 1
92
+ status_id: 100
93
+ articles:
94
+ id: 4
95
+ site_id: 1
96
+ title: Articles
97
+ breadcrumb: Articles
98
+ slug: articles
99
+ class_name: ArchivePage
100
+ parent_id: 1
101
+ status_id: 100
102
+ first_post:
103
+ id: 5
104
+ site_id: 1
105
+ title: First Post
106
+ breadcrumb: First Post
107
+ slug: first-post
108
+ parent_id: 4
109
+ status_id: 100
110
+ second_post:
111
+ id: 6
112
+ site_id: 1
113
+ title: Second Post
114
+ breadcrumb: Second Post
115
+ slug: second-post
116
+ parent_id: 4
117
+ status_id: 100
118
+ styles:
119
+ id: 7
120
+ site_id: 1
121
+ title: Styles
122
+ breadcrumb: Styles
123
+ slug: styles.css
124
+ parent_id: 1
125
+ layout_id: 2
126
+ status_id: 100
127
+ PageParts:
128
+ home_page:
129
+ id: 1
130
+ name: body
131
+ page_id: 1
132
+ content: |
133
+ <r:find url="/articles/">
134
+ <r:children:each limit="5" order="desc">
135
+ <div class="entry">
136
+ <h3><r:link /> <small><r:author /></small></h3>
137
+ <r:content />
138
+ <r:if_content part="extended"><r:link anchor="extended">Continue Reading&#8230;</r:link></r:if_content>
139
+ </div>
140
+ </r:children:each>
141
+ </r:find>
142
+ file_not_found:
143
+ id: 2
144
+ name: body
145
+ page_id: 2
146
+ filter_id: Textile
147
+ content: |
148
+ The file you were looking for could not be found.
149
+
150
+ Attempted URL: @<r:attempted_url />@
151
+
152
+ It is possible that you typed the URL incorrectly or that you clicked on a bad link.
153
+
154
+ "<< Back to Home Page":/
155
+ rss_feed:
156
+ id: 3
157
+ name: body
158
+ page_id: 3
159
+ content: |
160
+ <?xml version="1.0" encoding="UTF-8"?>
161
+ <rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/">
162
+ <channel>
163
+ <title>Article RSS Feed</title>
164
+ <link>http://your-web-site.com<r:url /></link>
165
+ <language>en-us</language>
166
+ <ttl>40</ttl>
167
+ <description>The main blog feed for my Web site.</description>
168
+ <r:find url="/articles/">
169
+ <r:children:each limit="10">
170
+ <item>
171
+ <title><r:title /></title>
172
+ <description><r:escape_html><r:content /></r:escape_html></description>
173
+ <pubDate><r:rfc1123_date /></pubDate>
174
+ <guid>http://your-web-site.com<r:url /></guid>
175
+ <link>http://your-web-site.com<r:url /></link>
176
+ </item>
177
+ </r:children:each>
178
+ </r:find>
179
+ </channel>
180
+ </rss>
181
+ articles:
182
+ id: 4
183
+ name: body
184
+ page_id: 4
185
+ content: |
186
+ <ul>
187
+ <r:children:each order="desc">
188
+ <li><r:link /></li>
189
+ </r:children:each>
190
+ </ul>
191
+ first_post:
192
+ id: 5
193
+ name: body
194
+ page_id: 5
195
+ filter_id: Textile
196
+ content: |
197
+ This post uses "textile":http://www.textism.com/tools/textile/.
198
+ second_post:
199
+ id: 6
200
+ name: body
201
+ page_id: 6
202
+ filter_id: Markdown
203
+ content: |
204
+ This post uses **Markdown**.
205
+ styles:
206
+ id: 7
207
+ name: body
208
+ page_id: 7
209
+ content: |
210
+ body {
211
+ font-family: "Trebuchet MS", Arial, Helvetica, sans-serif;
212
+ font-size: 80%;
213
+ }