qcms 1.3.3
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +14 -0
- data/README +70 -0
- data/Rakefile +39 -0
- data/VERSION +1 -0
- data/app/controllers/admin/admin_controller.rb +12 -0
- data/app/controllers/admin/documents_controller.rb +133 -0
- data/app/controllers/admin/meta_definitions_controller.rb +36 -0
- data/app/controllers/documents_controller.rb +187 -0
- data/app/controllers/sendmail_controller.rb +87 -0
- data/app/helpers/admin/documents_helper.rb +71 -0
- data/app/helpers/documents_helper.rb +30 -0
- data/app/helpers/sendmail_helper.rb +2 -0
- data/app/models/document.rb +292 -0
- data/app/models/document_mailer.rb +13 -0
- data/app/models/document_sweeper.rb +28 -0
- data/app/models/meta_definition.rb +57 -0
- data/app/models/sendmail.rb +18 -0
- data/app/views/admin/admin/system.html.erb +34 -0
- data/app/views/admin/documents/_form.html.erb +60 -0
- data/app/views/admin/documents/default.edit.html.erb +10 -0
- data/app/views/admin/documents/default.new.html.erb +10 -0
- data/app/views/admin/documents/default.show.html.erb +77 -0
- data/app/views/admin/documents/index.html.erb +32 -0
- data/app/views/admin/documents/shared/_resource_link.html.erb +6 -0
- data/app/views/document_mailer/new_document.erb +12 -0
- data/app/views/layouts/admin.html.erb +43 -0
- data/app/views/layouts/application.html.erb +44 -0
- data/app/views/pages/404.html.erb +28 -0
- data/app/views/pages/contact.html.erb +42 -0
- data/app/views/pages/default.html.erb +12 -0
- data/app/views/pages/feed.rss.builder +17 -0
- data/app/views/pages/home.html.erb +5 -0
- data/app/views/pages/search.html.erb +19 -0
- data/app/views/pages/shared/_archived_pages.erb +16 -0
- data/app/views/pages/shared/_related_pages.html.erb +13 -0
- data/app/views/pages/sitemap.html.erb +9 -0
- data/app/views/pages/template.erb +51 -0
- data/app/views/pages/thank_you.html.erb +1 -0
- data/app/views/sendmail/default.erb +12 -0
- data/config/sitemap.example.yml +39 -0
- data/db/migrate/20090824150210_create_documents.rb +37 -0
- data/db/migrate/20091208124512_create_meta_definition.rb +26 -0
- data/init.rb +1 -0
- data/install.rb +1 -0
- data/lib/qcms.rb +4 -0
- data/lib/tasks/cms.rake +237 -0
- data/qcms.gemspec +91 -0
- data/rails/init.rb +3 -0
- data/tasks/qcms_tasks.rake +223 -0
- data/test/qwerty_test.rb +8 -0
- data/test/test_helper.rb +3 -0
- data/uninstall.rb +1 -0
- metadata +107 -0
@@ -0,0 +1,13 @@
|
|
1
|
+
class DocumentMailer < ActionMailer::Base
|
2
|
+
|
3
|
+
def new_document(document)
|
4
|
+
subject "[#{Settings.site.name}] New #{document.label}"
|
5
|
+
from Settings.mailer.from
|
6
|
+
recipients User.all.select { |u| u.has_role? 'admin'}.collect { |u| u.email }.join(', ')
|
7
|
+
body :document => document
|
8
|
+
content_type "text/html"
|
9
|
+
end
|
10
|
+
|
11
|
+
|
12
|
+
|
13
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
class DocumentSweeper < ActionController::Caching::Sweeper
|
2
|
+
observe Document
|
3
|
+
|
4
|
+
def after_save(document)
|
5
|
+
clear_document_cache(document)
|
6
|
+
end
|
7
|
+
|
8
|
+
def after_destroy(document)
|
9
|
+
clear_document_cache(document)
|
10
|
+
end
|
11
|
+
|
12
|
+
# We dont need to expire the cache for the parent as a document always
|
13
|
+
# touches its parent after_save, which will cause this method to be run
|
14
|
+
# for the parent as well.
|
15
|
+
|
16
|
+
def clear_document_cache(document)
|
17
|
+
expire_fragment :recent_posts
|
18
|
+
expire_fragment :menu
|
19
|
+
|
20
|
+
cache_paths = []
|
21
|
+
cache_paths << File.join(RAILS_ROOT, 'public', 'cache', document.path)
|
22
|
+
|
23
|
+
cache_paths.each do | cache_path |
|
24
|
+
Rails.logger.debug 'Deleting CACHE: ' + cache_path
|
25
|
+
FileUtils.rm_rf cache_path if File.exists? cache_path
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
class MetaDefinition < ActiveRecord::Base
|
2
|
+
has_many :documents, :dependent => :destroy
|
3
|
+
|
4
|
+
validates_presence_of :label_path, :label
|
5
|
+
validates_uniqueness_of :label_path
|
6
|
+
|
7
|
+
acts_as_list
|
8
|
+
acts_as_tree
|
9
|
+
path_finder :uid => 'label', :column => 'label_path'
|
10
|
+
|
11
|
+
default_scope :order => 'position DESC'
|
12
|
+
|
13
|
+
named_scope :by_label, lambda { |label| { :conditions => ['label = ?', label] } }
|
14
|
+
|
15
|
+
before_save :nullify_empty_columns
|
16
|
+
|
17
|
+
serialize :autherisation, Hash
|
18
|
+
serialize :field_map, Hash
|
19
|
+
serialize :flash_messages, Hash
|
20
|
+
|
21
|
+
|
22
|
+
def autherisation
|
23
|
+
super || {}
|
24
|
+
end
|
25
|
+
|
26
|
+
def field_map
|
27
|
+
super || {}
|
28
|
+
end
|
29
|
+
|
30
|
+
def flash_messages
|
31
|
+
super || {}
|
32
|
+
end
|
33
|
+
|
34
|
+
|
35
|
+
def allowed?(user, action)
|
36
|
+
tokens = (autherisation[action] || Settings.documents.autherisation.send(action)).split(' ')
|
37
|
+
tokens.each do | token |
|
38
|
+
return true if token == 'all'
|
39
|
+
return true if token == 'author' && self.author == user
|
40
|
+
return true if user.has_role? token
|
41
|
+
end
|
42
|
+
false
|
43
|
+
end
|
44
|
+
|
45
|
+
# Used to find template named after label_path
|
46
|
+
# eg. blog.post.html.erb
|
47
|
+
def template_filename(include_extension = true)
|
48
|
+
label_path.gsub('/', '.') + (include_extension ? '.html.erb' : '')
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
# Prevent empty strings being saved
|
54
|
+
def nullify_empty_columns
|
55
|
+
%w(sort_by per_page).each { |column| self.send(column+'=', nil) if self.send(column) == '' }
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
class Sendmail < ActionMailer::Base
|
2
|
+
|
3
|
+
def form(params, meta, template)
|
4
|
+
subject meta[:subject]
|
5
|
+
from set_from(params, meta)
|
6
|
+
recipients meta[:recipients]
|
7
|
+
body :data => params
|
8
|
+
content_type "text/html"
|
9
|
+
template template
|
10
|
+
end
|
11
|
+
|
12
|
+
private
|
13
|
+
|
14
|
+
def set_from(params, meta)
|
15
|
+
params[:email].nil? ? params[:email] : meta[:from]
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
<style>
|
2
|
+
ul.info li { margin-left: 20px;
|
3
|
+
list-style: none;
|
4
|
+
padding: 5px;
|
5
|
+
background-color: #CFDBA0;
|
6
|
+
margin-bottom: 4px;
|
7
|
+
}
|
8
|
+
</style>
|
9
|
+
|
10
|
+
<h3>Qwerty</h3>
|
11
|
+
<ul class="info">
|
12
|
+
<li>Qcore: <%= Qcore::VERSION %></li>
|
13
|
+
<li>Qcms: <%= Qcms::VERSION %></li>
|
14
|
+
</ul>
|
15
|
+
|
16
|
+
<h3>Rails</h3>
|
17
|
+
<ul class="info">
|
18
|
+
<li>Version: <%= Rails.version %></li>
|
19
|
+
<li>User: <%= Etc.getpwuid(Process.euid).name %></li>
|
20
|
+
<li>Root: <%= RAILS_ROOT %></li>
|
21
|
+
<li>Env: <%= RAILS_ENV %></li>
|
22
|
+
</ul>
|
23
|
+
|
24
|
+
<h3>Gem</h3>
|
25
|
+
<ul class="info">
|
26
|
+
<li>Config: <%= Gem.config_file %></li>
|
27
|
+
<li>Default path: <%= Gem.default_path %></li>
|
28
|
+
</ul>
|
29
|
+
|
30
|
+
<h3>Ruby</h3>
|
31
|
+
<ul class="info">
|
32
|
+
<li>version <%= Gem.ruby_version.to_s %></li>
|
33
|
+
<li>path <%= Gem.ruby %></li>
|
34
|
+
</ul>
|
@@ -0,0 +1,60 @@
|
|
1
|
+
<p>Title<BR/>
|
2
|
+
<%= f.text_field :title %>
|
3
|
+
</p>
|
4
|
+
|
5
|
+
<p>Permalink<BR/>
|
6
|
+
<% if @document.new_record? %>
|
7
|
+
<%= f.text_field :permalink, :value => '' %>
|
8
|
+
<small>This will be generated automatically if left blank</small>
|
9
|
+
<% else %>
|
10
|
+
<%= f.text_field :permalink, :readonly => 'true' %>
|
11
|
+
<small>Not editable</small>
|
12
|
+
<% end %>
|
13
|
+
</p>
|
14
|
+
|
15
|
+
<p>State<BR/>
|
16
|
+
<%= f.select :state, [['Published','published'], ['Draft', 'draft']] %>
|
17
|
+
</p>
|
18
|
+
|
19
|
+
<p>Published <BR><%= f.date_select :published_at, :order => [:day, :month, :year] %></p>
|
20
|
+
|
21
|
+
<p>Body<BR/>
|
22
|
+
<%= f.text_area :body %>
|
23
|
+
</p>
|
24
|
+
|
25
|
+
<p>Summary<BR/>
|
26
|
+
<%= f.text_field :summary %>
|
27
|
+
<small>This will be generated automatically if left blank</small>
|
28
|
+
</p>
|
29
|
+
|
30
|
+
<p>Image<BR/>
|
31
|
+
<%= f.file_field :resource %>
|
32
|
+
<%= render :partial => 'admin/documents/shared/resource_link' %>
|
33
|
+
<% if @document.resource.exists? %>
|
34
|
+
<div>Delete image: <%= f.check_box :delete_resource %></div>
|
35
|
+
<% end %>
|
36
|
+
</p>
|
37
|
+
|
38
|
+
<p>
|
39
|
+
Image Description<br/>
|
40
|
+
<%= f.text_field :resource_description %>
|
41
|
+
</p>
|
42
|
+
|
43
|
+
<fieldset>
|
44
|
+
<legend>Meta</legend>
|
45
|
+
<p>Page Title<BR/>
|
46
|
+
<%= f.text_field :meta_title %>
|
47
|
+
</p>
|
48
|
+
|
49
|
+
<p>Description<BR/>
|
50
|
+
<%= f.text_field :meta_description %>
|
51
|
+
</p>
|
52
|
+
|
53
|
+
<p>Keywords<BR/>
|
54
|
+
<%= f.text_field :meta_keywords %>
|
55
|
+
</p>
|
56
|
+
</fieldset>
|
57
|
+
|
58
|
+
<%= f.hidden_field :parent_id %>
|
59
|
+
<%= f.hidden_field :label %>
|
60
|
+
<%= f.hidden_field :meta_definition_id %>
|
@@ -0,0 +1,10 @@
|
|
1
|
+
<h4>Edit <%= @document.label.titleize %></h4>
|
2
|
+
<%= error_messages_for :document %>
|
3
|
+
<% form_for @document.becomes(Document), :url => admin_document_path(@document.id), :html => { :multipart => true } do |f| %>
|
4
|
+
<%= render :partial => form_partial, :locals => { :f => f } %>
|
5
|
+
<%= f.submit 'Save' %> or <%= link_to 'Cancel', admin_cancel_document_path(@document), :class => 'button' %>
|
6
|
+
<% end %>
|
7
|
+
|
8
|
+
<% if session[:system] %>
|
9
|
+
<%= form_partial %>
|
10
|
+
<% end %>
|
@@ -0,0 +1,10 @@
|
|
1
|
+
<h4>New <%= @document.label.titleize %></h4>
|
2
|
+
<%= error_messages_for :document %>
|
3
|
+
<% form_for @document.becomes(Document), :url => admin_documents_path, :html => { :multipart => true } do |f| %>
|
4
|
+
<%= render :partial => form_partial, :locals => { :f => f } %>
|
5
|
+
<%= f.submit 'Save' %> or <%= link_to 'Cancel', admin_cancel_document_path(@document), :class => 'button' %>
|
6
|
+
<% end %>
|
7
|
+
|
8
|
+
<% if session[:system] %>
|
9
|
+
<%= form_partial %>
|
10
|
+
<% end %>
|
@@ -0,0 +1,77 @@
|
|
1
|
+
<h4><%= @document.title %></h4>
|
2
|
+
|
3
|
+
<div style="margin-bottom: 20px;"><%= textilize @document.summary %></div>
|
4
|
+
|
5
|
+
<% @document.meta_definition.children.each do |md| %>
|
6
|
+
|
7
|
+
<h4 style="margin-top: 20px; border-bottom: 1px solid #ADBCBC;"><%= md.label.titleize.pluralize.capitalize %></h4>
|
8
|
+
|
9
|
+
<% unless md.documents.empty? %>
|
10
|
+
<table>
|
11
|
+
<tr>
|
12
|
+
<% md.field_map.each do |k,v| %>
|
13
|
+
<td><%= k.titleize %></td>
|
14
|
+
<% end %>
|
15
|
+
<th>Title</th>
|
16
|
+
<th>State</th>
|
17
|
+
<th>Author</th>
|
18
|
+
<% if md.record_hits? %>
|
19
|
+
<th>Hits</th>
|
20
|
+
<% end %>
|
21
|
+
</tr>
|
22
|
+
<% documents = @document.children.by_label(md.label).order_by(md.sort_by).paginate(:per_page => 20, :page => params["#{md.label}_page".to_sym]) %>
|
23
|
+
<% documents.each do |page| %>
|
24
|
+
<tr>
|
25
|
+
<% md.field_map.each do |k,v| %>
|
26
|
+
<td><%= page.send(v) %></td>
|
27
|
+
<% end %>
|
28
|
+
<td><%= truncate(page.title, :length => 40) %></td>
|
29
|
+
<td>
|
30
|
+
<% form_for page.becomes(Document), :url => admin_document_path(page.id) do |f| %>
|
31
|
+
<%= f.select :state, [['Published','published'], ['Draft', 'draft'], ['Pending', 'pending']] %>
|
32
|
+
<%= f.submit 'Change' %>
|
33
|
+
<% end %>
|
34
|
+
</td>
|
35
|
+
<td><%= page.author.name %></td>
|
36
|
+
<% if md.record_hits? %>
|
37
|
+
<td><%= page.hits %></td>
|
38
|
+
<% end %>
|
39
|
+
<td>
|
40
|
+
<%= link_to 'Open', admin_document_path(page.id), :class => 'button' %>
|
41
|
+
</td>
|
42
|
+
<td><%= link_to 'Edit', edit_admin_document_path(page.id), :class => 'button' %></td>
|
43
|
+
<% if md.sort_by == 'position ASC' %>
|
44
|
+
<td>
|
45
|
+
<%= link_to 'Up', up_admin_document_path(page.id), :class => 'button' unless page.first? %>
|
46
|
+
<%= link_to 'Down', down_admin_document_path(page.id), :class => 'button' unless page.last? %>
|
47
|
+
</td>
|
48
|
+
<% end %>
|
49
|
+
<td>
|
50
|
+
<% unless page.system? %>
|
51
|
+
<%= link_to 'Delete', admin_document_path(page.id), :method => 'delete', :confirm => 'Are you sure?', :class => 'button' %>
|
52
|
+
<% end %>
|
53
|
+
</td>
|
54
|
+
</tr>
|
55
|
+
<% end %>
|
56
|
+
</table>
|
57
|
+
<%= will_paginate documents, :param_name => "#{md.label}_page".to_sym %>
|
58
|
+
<% else %>
|
59
|
+
<p>There are no <%= md.label.titleize.pluralize %> yet!</p>
|
60
|
+
<% end %>
|
61
|
+
<p><%= link_to 'New ', new_admin_document_path + '?parent=' + @document.id.to_s + '&label=' + md.label, :class => 'button' %></p>
|
62
|
+
|
63
|
+
<% form_for md, :url => admin_meta_definition_path(md.id) do | f | %>
|
64
|
+
<p><label>Sort by</label> <%= f.select :sort_by, sort_by_options, :include_blank => true %></p>
|
65
|
+
<p><label>Per page</label> <%= f.select :per_page, per_page_options, :include_blank => true %></p>
|
66
|
+
<input type="hidden" name="document_id" value ="<%= @document.id %>" />
|
67
|
+
<%= f.submit 'Save' %>
|
68
|
+
<% end %>
|
69
|
+
<% end %>
|
70
|
+
|
71
|
+
|
72
|
+
|
73
|
+
<style>
|
74
|
+
form.edit_meta_definition { font-size: 1em; }
|
75
|
+
</style>
|
76
|
+
|
77
|
+
|
@@ -0,0 +1,32 @@
|
|
1
|
+
<h2>Sections</h2>
|
2
|
+
|
3
|
+
<table>
|
4
|
+
<tr>
|
5
|
+
<th>Title</th>
|
6
|
+
<th></th>
|
7
|
+
<th></th>
|
8
|
+
</tr>
|
9
|
+
<% @documents.each do |document| %>
|
10
|
+
<tr>
|
11
|
+
<td>
|
12
|
+
<%= document.title %><br />
|
13
|
+
<small><%= truncate(document.summary, :length => 30) %></small>
|
14
|
+
</td>
|
15
|
+
<td>
|
16
|
+
<a href="<%= admin_document_path(document.id) %>" class="button">Open</a>
|
17
|
+
</td>
|
18
|
+
<td><a href="<%= edit_admin_document_path(document.id) %>" class="button">Edit</a></td>
|
19
|
+
<td><a href="<%= new_admin_document_path + '?parent_id=' + document.id.to_s %>" class="button">Add</a></td>
|
20
|
+
<td>
|
21
|
+
<%= link_to 'Up', up_admin_document_path(document.id), :class => 'button' unless document.first? %>
|
22
|
+
<%= link_to 'Down', down_admin_document_path(document.id), :class => 'button' unless document.last? %>
|
23
|
+
</td>
|
24
|
+
<td><%= link_to 'Delete', admin_document_path(document.id), :method => 'delete', :class => 'button' %></td>
|
25
|
+
<td><a href="<%= document_path(document) %>" class="button">Live</a></td>
|
26
|
+
</tr>
|
27
|
+
<% end %>
|
28
|
+
</table>
|
29
|
+
|
30
|
+
<p><%= link_to 'New Page', new_admin_document_path + '?label_path=page', :class => 'button' %>
|
31
|
+
|
32
|
+
|
@@ -0,0 +1,12 @@
|
|
1
|
+
<p>You have a new <%= @document.label %></p>
|
2
|
+
|
3
|
+
<% unless @document.published? %>
|
4
|
+
<p>This <%= @document.label %> is currently <%= @document.state.capitalize %></p>
|
5
|
+
<% end %>
|
6
|
+
|
7
|
+
<p><%= link_to 'Edit', 'http://' + Settings.domain + admin_document_path(@document.parent_id) %></p>
|
8
|
+
|
9
|
+
|
10
|
+
|
11
|
+
|
12
|
+
|
@@ -0,0 +1,43 @@
|
|
1
|
+
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
|
2
|
+
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
3
|
+
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
|
4
|
+
<head>
|
5
|
+
<title><%= @page_title || 'untitled' %></title>
|
6
|
+
<meta http-equiv="content-type" content="text/xhtml; charset=utf-8" />
|
7
|
+
<link rel="stylesheet" href="/stylesheets/admin/styles.css" type="text/css" media="screen" />
|
8
|
+
<%= javascript_include_tag 'jquery-1.3.1.min.js' %>
|
9
|
+
<%= javascript_include_tag 'application' %>
|
10
|
+
<%= meta_tags -%>
|
11
|
+
<%= yield :head %>
|
12
|
+
</head>
|
13
|
+
<body>
|
14
|
+
|
15
|
+
<% if flash[:notice] %>
|
16
|
+
<div id="flash-notice"><%= flash[:notice] %> <a href="#">Close</a></div>
|
17
|
+
<% end %>
|
18
|
+
|
19
|
+
<div id="header">QWERTY+ Control Panel</div>
|
20
|
+
|
21
|
+
<ul id="top_menu">
|
22
|
+
<li><a href="<%= admin_dashboard_path %>">Dashboard</a></li>
|
23
|
+
<li><a href="<%= admin_users_path %>">Users</a></li>
|
24
|
+
<li><a href="<%= root_path %>">Site Homepage</a></li>
|
25
|
+
</ul>
|
26
|
+
|
27
|
+
<% if @document %>
|
28
|
+
<%= admin_bread_crumb(@document) %>
|
29
|
+
<% end %>
|
30
|
+
|
31
|
+
<div id="container">
|
32
|
+
|
33
|
+
<div id="content">
|
34
|
+
<%= yield %>
|
35
|
+
</div>
|
36
|
+
|
37
|
+
<div id="sidebar">
|
38
|
+
<%= yield :sidebar %>
|
39
|
+
</div>
|
40
|
+
|
41
|
+
</div> <!-- container -->
|
42
|
+
</body>
|
43
|
+
</html>
|
@@ -0,0 +1,44 @@
|
|
1
|
+
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
|
2
|
+
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
3
|
+
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
|
4
|
+
<head>
|
5
|
+
<title><%= page_title %></title>
|
6
|
+
<meta http-equiv="content-type" content="text/xhtml; charset=utf-8" />
|
7
|
+
<link rel="stylesheet" href="/stylesheets/styles.css" type="text/css" media="screen" />
|
8
|
+
<%= javascript_include_tag 'jquery-1.3.1.min.js' %>
|
9
|
+
<%= javascript_include_tag 'application' %>
|
10
|
+
<%= meta_tags -%>
|
11
|
+
<%= yield :head %>
|
12
|
+
</head>
|
13
|
+
<body>
|
14
|
+
<div id="user_nav">
|
15
|
+
<% if current_user %>
|
16
|
+
<a href="<%= logout_path %>">Logout</a>
|
17
|
+
<a href="/admin">Admin</a>
|
18
|
+
<% else %>
|
19
|
+
<a href="<%= login_path %>">Login</a>
|
20
|
+
<% end %>
|
21
|
+
</div>
|
22
|
+
|
23
|
+
<% if flash[:notice] %>
|
24
|
+
<div id="flash-notice"><%= flash[:notice] %> <a href="#">Close</a></div>
|
25
|
+
<% end %>
|
26
|
+
|
27
|
+
<div id="header"></div>
|
28
|
+
<ul id="nav">
|
29
|
+
<% Document.public.roots.order_by('position').each do | doc | %>
|
30
|
+
<li style=""><a href="<%= document_path(doc) %>"><%= doc.title %></a></li>
|
31
|
+
<% end %>
|
32
|
+
</ul>
|
33
|
+
<div style="clear:both;"></div>
|
34
|
+
<div id="container">
|
35
|
+
<div id="content">
|
36
|
+
<%= yield %>
|
37
|
+
</div>
|
38
|
+
|
39
|
+
<div id="sidebar">
|
40
|
+
<%= yield :sidebar %>
|
41
|
+
</div>
|
42
|
+
</div> <!-- container -->
|
43
|
+
</body>
|
44
|
+
</html>
|