comatose 2.0.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.
- data/CHANGELOG +176 -0
- data/INSTALL +19 -0
- data/LICENSE +20 -0
- data/MANIFEST +91 -0
- data/README.rdoc +148 -0
- data/Rakefile +122 -0
- data/SPECS +61 -0
- data/about.yml +7 -0
- data/bin/comatose +109 -0
- data/comatose.gemspec +113 -0
- data/generators/comatose_migration/USAGE +15 -0
- data/generators/comatose_migration/comatose_migration_generator.rb +59 -0
- data/generators/comatose_migration/templates/migration.rb +35 -0
- data/generators/comatose_migration/templates/v4_upgrade.rb +15 -0
- data/generators/comatose_migration/templates/v6_upgrade.rb +23 -0
- data/generators/comatose_migration/templates/v7_upgrade.rb +22 -0
- data/init.rb +2 -0
- data/install.rb +16 -0
- data/lib/acts_as_versioned.rb +543 -0
- data/lib/comatose/comatose_drop.rb +79 -0
- data/lib/comatose/configuration.rb +68 -0
- data/lib/comatose/page_wrapper.rb +119 -0
- data/lib/comatose/processing_context.rb +69 -0
- data/lib/comatose/tasks/admin.rb +60 -0
- data/lib/comatose/tasks/data.rb +82 -0
- data/lib/comatose/tasks/setup.rb +52 -0
- data/lib/comatose/version.rb +4 -0
- data/lib/comatose.rb +33 -0
- data/lib/comatose_admin_controller.rb +349 -0
- data/lib/comatose_admin_helper.rb +37 -0
- data/lib/comatose_controller.rb +141 -0
- data/lib/comatose_helper.rb +3 -0
- data/lib/comatose_page.rb +141 -0
- data/lib/liquid/block.rb +96 -0
- data/lib/liquid/context.rb +190 -0
- data/lib/liquid/document.rb +17 -0
- data/lib/liquid/drop.rb +48 -0
- data/lib/liquid/errors.rb +7 -0
- data/lib/liquid/extensions.rb +53 -0
- data/lib/liquid/file_system.rb +62 -0
- data/lib/liquid/htmltags.rb +64 -0
- data/lib/liquid/standardfilters.rb +111 -0
- data/lib/liquid/standardtags.rb +399 -0
- data/lib/liquid/strainer.rb +42 -0
- data/lib/liquid/tag.rb +25 -0
- data/lib/liquid/template.rb +88 -0
- data/lib/liquid/variable.rb +39 -0
- data/lib/liquid.rb +52 -0
- data/lib/redcloth.rb +1129 -0
- data/lib/support/class_options.rb +36 -0
- data/lib/support/inline_rendering.rb +48 -0
- data/lib/support/route_mapper.rb +50 -0
- data/lib/text_filters/markdown.rb +14 -0
- data/lib/text_filters/markdown_smartypants.rb +15 -0
- data/lib/text_filters/none.rb +8 -0
- data/lib/text_filters/rdoc.rb +13 -0
- data/lib/text_filters/simple.rb +8 -0
- data/lib/text_filters/textile.rb +15 -0
- data/lib/text_filters.rb +138 -0
- data/rails/init.rb +3 -0
- data/resources/layouts/comatose_admin_template.html.erb +28 -0
- data/resources/public/images/collapsed.gif +0 -0
- data/resources/public/images/expanded.gif +0 -0
- data/resources/public/images/no-children.gif +0 -0
- data/resources/public/images/page.gif +0 -0
- data/resources/public/images/spinner.gif +0 -0
- data/resources/public/images/title-hover-bg.gif +0 -0
- data/resources/public/javascripts/comatose_admin.js +401 -0
- data/resources/public/stylesheets/comatose_admin.css +381 -0
- data/tasks/comatose.rake +9 -0
- data/test/behaviors.rb +106 -0
- data/test/fixtures/comatose_pages.yml +96 -0
- data/test/functional/comatose_admin_controller_test.rb +112 -0
- data/test/functional/comatose_controller_test.rb +44 -0
- data/test/javascripts/test.html +26 -0
- data/test/javascripts/test_runner.js +307 -0
- data/test/test_helper.rb +43 -0
- data/test/unit/class_options_test.rb +52 -0
- data/test/unit/comatose_page_test.rb +128 -0
- data/test/unit/processing_context_test.rb +108 -0
- data/test/unit/text_filters_test.rb +52 -0
- data/views/comatose_admin/_form.html.erb +96 -0
- data/views/comatose_admin/_page_list_item.html.erb +60 -0
- data/views/comatose_admin/delete.html.erb +18 -0
- data/views/comatose_admin/edit.html.erb +5 -0
- data/views/comatose_admin/index.html.erb +18 -0
- data/views/comatose_admin/new.html.erb +5 -0
- data/views/comatose_admin/reorder.html.erb +30 -0
- data/views/comatose_admin/versions.html.erb +40 -0
- data/views/layouts/comatose_admin.html.erb +814 -0
- data/views/layouts/comatose_admin_customize.html.erb +28 -0
- data/views/layouts/comatose_content.html.erb +17 -0
- metadata +156 -0
|
@@ -0,0 +1,349 @@
|
|
|
1
|
+
# The controller for serving cms content...
|
|
2
|
+
class ComatoseAdminController < ActionController::Base
|
|
3
|
+
unloadable
|
|
4
|
+
|
|
5
|
+
define_option :original_template_root, nil
|
|
6
|
+
define_option :plugin_layout_path, File.join( '..', '..', '..', 'vendor', 'plugins', 'comatose', 'views', 'layouts' )
|
|
7
|
+
|
|
8
|
+
before_filter :handle_authorization
|
|
9
|
+
before_filter :set_content_type
|
|
10
|
+
layout 'comatose_admin'
|
|
11
|
+
|
|
12
|
+
# Shows the page tree
|
|
13
|
+
def index
|
|
14
|
+
@root_pages = [fetch_root_page].flatten
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
# Edit a specfic page (posts back)
|
|
18
|
+
def edit
|
|
19
|
+
# Clear the page cache for this page... ?
|
|
20
|
+
@page = ComatosePage.find params[:id]
|
|
21
|
+
@root_pages = [fetch_root_page].flatten
|
|
22
|
+
if request.post?
|
|
23
|
+
@page.update_attributes(params[:page])
|
|
24
|
+
@page.updated_on = Time.now
|
|
25
|
+
@page.author = fetch_author_name
|
|
26
|
+
if @page.save
|
|
27
|
+
expire_cms_page @page
|
|
28
|
+
expire_cms_fragment @page
|
|
29
|
+
flash[:notice] = "Saved changes to '#{@page.title}'"
|
|
30
|
+
redirect_to :controller=>self.controller_name, :action=>'index'
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# Create a new page (posts back)
|
|
36
|
+
def new
|
|
37
|
+
@root_pages = [fetch_root_page].flatten
|
|
38
|
+
if request.post?
|
|
39
|
+
@page = ComatosePage.new params[:page]
|
|
40
|
+
@page.author = fetch_author_name
|
|
41
|
+
if @page.save
|
|
42
|
+
flash[:notice] = "Created page '#{@page.title}'"
|
|
43
|
+
redirect_to :controller=>self.controller_name, :action=>'index'
|
|
44
|
+
end
|
|
45
|
+
else
|
|
46
|
+
@page = ComatosePage.new :title=>'New Page', :parent_id=>(params[:parent] || nil)
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# Saves position of child pages
|
|
51
|
+
def reorder
|
|
52
|
+
# If it's AJAX, do our thing and move on...
|
|
53
|
+
if request.xhr?
|
|
54
|
+
params["page_list_#{params[:id]}"].each_with_index { |id,idx| ComatosePage.update(id, :position => idx) }
|
|
55
|
+
expire_cms_page ComatosePage.find(params[:id])
|
|
56
|
+
render :text=>'Updated sort order', :layout=>false
|
|
57
|
+
else
|
|
58
|
+
@page = ComatosePage.find params[:id]
|
|
59
|
+
if params.has_key? :cmd
|
|
60
|
+
@target = ComatosePage.find params[:page]
|
|
61
|
+
case params[:cmd]
|
|
62
|
+
when 'up' then @target.move_higher
|
|
63
|
+
when 'down' then @target.move_lower
|
|
64
|
+
end
|
|
65
|
+
redirect_to :action=>'reorder', :id=>@page
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# Allows comparing between two versions of a page's content
|
|
71
|
+
def versions
|
|
72
|
+
@page = ComatosePage.find params[:id]
|
|
73
|
+
@version_num = (params[:version] || @page.versions.length).to_i
|
|
74
|
+
@version = @page.find_version(@version_num)
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
# Reverts a page to a specific version...
|
|
78
|
+
def set_version
|
|
79
|
+
if request.post?
|
|
80
|
+
@page = ComatosePage.find params[:id]
|
|
81
|
+
@version_num = params[:version]
|
|
82
|
+
@page.revert_to!(@version_num)
|
|
83
|
+
end
|
|
84
|
+
redirect_to :controller=>self.controller_name, :action=>'index'
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# Deletes the specified page
|
|
88
|
+
def delete
|
|
89
|
+
@page = ComatosePage.find params[:id]
|
|
90
|
+
if request.post?
|
|
91
|
+
expire_cms_pages_from_bottom @page
|
|
92
|
+
expire_cms_fragments_from_bottom @page
|
|
93
|
+
@page.destroy
|
|
94
|
+
flash[:notice] = "Deleted page '#{@page.title}'"
|
|
95
|
+
redirect_to :controller=>self.controller_name, :action=>'index'
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
# Returns a preview of the page content...
|
|
100
|
+
def preview
|
|
101
|
+
begin
|
|
102
|
+
page = ComatosePage.new(params[:page])
|
|
103
|
+
page.author = fetch_author_name
|
|
104
|
+
if params.has_key? :version
|
|
105
|
+
content = page.to_html( {'params'=>params.stringify_keys, 'version'=>params[:version]} )
|
|
106
|
+
else
|
|
107
|
+
content = page.to_html( {'params'=>params.stringify_keys} )
|
|
108
|
+
end
|
|
109
|
+
rescue SyntaxError
|
|
110
|
+
content = "<p>There was an error generating the preview.</p><p><pre>#{$!.to_s.gsub(/\</, '<')}</pre></p>"
|
|
111
|
+
rescue
|
|
112
|
+
content = "<p>There was an error generating the preview.</p><p><pre>#{$!.to_s.gsub(/\</, '<')}</pre></p>"
|
|
113
|
+
end
|
|
114
|
+
render :text=>content, :layout => false
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
# Expires the entire page cache
|
|
118
|
+
def expire_page_cache
|
|
119
|
+
expire_cms_pages_from_bottom( fetch_root_page )
|
|
120
|
+
expire_cms_fragments_from_bottom( fetch_root_page )
|
|
121
|
+
flash[:notice] = "Page cache has been flushed"
|
|
122
|
+
redirect_to :controller=>self.controller_name, :action=>'index'
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
# Walks the page tree and generates HTML files in your /public
|
|
126
|
+
# folder... It will skip pages that have a 'nocache' keyword
|
|
127
|
+
# TODO: Make page cache generation work when in :plugin mode
|
|
128
|
+
def generate_page_cache
|
|
129
|
+
if runtime_mode == :plugin
|
|
130
|
+
@errors = ["Page cache cannot be generated in plugin mode"]
|
|
131
|
+
else
|
|
132
|
+
@errors = generate_all_pages_html(params)
|
|
133
|
+
end
|
|
134
|
+
if @errors.length == 0
|
|
135
|
+
flash[:notice] = "Pages Cached Successfully"
|
|
136
|
+
else
|
|
137
|
+
flash[:notice] = "Pages Cache Error(s): #{@errors.join(', ')}"
|
|
138
|
+
flash[:cache_errors] = @errors
|
|
139
|
+
end
|
|
140
|
+
redirect_to :controller=>self.controller_name, :action=>'index'
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
protected
|
|
145
|
+
|
|
146
|
+
def handle_authorization
|
|
147
|
+
if Comatose.config.admin_authorization.is_a? Proc
|
|
148
|
+
instance_eval &Comatose.config.admin_authorization
|
|
149
|
+
elsif Comatose.config.admin_authorization.is_a? Symbol
|
|
150
|
+
send(Comatose.config.admin_authorization)
|
|
151
|
+
elsif defined? authorize
|
|
152
|
+
authorize
|
|
153
|
+
else
|
|
154
|
+
true
|
|
155
|
+
end
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
def fetch_author_name
|
|
159
|
+
if Comatose.config.admin_get_author.is_a? Proc
|
|
160
|
+
instance_eval &Comatose.config.admin_get_author
|
|
161
|
+
elsif Comatose.config.admin_get_author.is_a? Symbol
|
|
162
|
+
send(Comatose.config.admin_get_author)
|
|
163
|
+
elsif defined? get_author
|
|
164
|
+
get_author
|
|
165
|
+
end
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
# Can be overridden -- return your root comtase page
|
|
169
|
+
def fetch_root_page
|
|
170
|
+
if Comatose.config.admin_get_root_page.is_a? Proc
|
|
171
|
+
instance_eval &Comatose.config.admin_get_root_page
|
|
172
|
+
elsif Comatose.config.admin_get_root_page.is_a? Symbol
|
|
173
|
+
send(Comatose.config.admin_get_root_page)
|
|
174
|
+
elsif defined? get_root_page
|
|
175
|
+
get_root_page
|
|
176
|
+
end
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
# Sets the HTTP content-type header based on what's configured
|
|
180
|
+
# in Comatose.config.content_type
|
|
181
|
+
def set_content_type
|
|
182
|
+
response.headers["Content-Type"] = "text/html; charset=#{Comatose.config.content_type}" unless Comatose.config.content_type.nil?
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
# Calls generate_page_html for each mount point..
|
|
186
|
+
def generate_all_pages_html(params={})
|
|
187
|
+
@errors = []
|
|
188
|
+
@been_cached = []
|
|
189
|
+
Comatose.mount_points.each do |root_info|
|
|
190
|
+
ComatosePage.active_mount_info = root_info
|
|
191
|
+
generate_page_html(ComatosePage.find_by_path( root_info[:index] ), root_info, params)
|
|
192
|
+
end
|
|
193
|
+
@errors
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
# Accepts a Comatose Page and a root_info object to generate
|
|
197
|
+
# the page as a static HTML page -- using the layout that was
|
|
198
|
+
# defined on the mount point
|
|
199
|
+
def generate_page_html(page, root_info, params={})
|
|
200
|
+
@been_cached ||= []
|
|
201
|
+
unless page.has_keyword? :nocache or @been_cached.include? page.id
|
|
202
|
+
uri = page.uri
|
|
203
|
+
uri = "#{uri}/index".split('/').flatten.join('/') if page.full_path == root_info[:index]
|
|
204
|
+
@page = Comatose::PageWrapper.new(page)
|
|
205
|
+
begin
|
|
206
|
+
page_layout = get_page_layout(root_info)
|
|
207
|
+
#puts "mode = #{runtime_mode}, layout = #{page_layout}, template_root = #{template_root}, original_template_root = #{original_template_root}"
|
|
208
|
+
html = render_to_string( :text=>page.to_html({'params'=>params.stringify_keys}), :layout=>page_layout )
|
|
209
|
+
cache_page( html, uri )
|
|
210
|
+
rescue
|
|
211
|
+
logger.error "Comatose CMS Page Cache Exception: #{$!}"
|
|
212
|
+
@errors << "(#{page}/#{page.slug}) - #{$!}"
|
|
213
|
+
end
|
|
214
|
+
@been_cached << page.id
|
|
215
|
+
# recurse...
|
|
216
|
+
page.children.each do |child|
|
|
217
|
+
generate_page_html(child, root_info)
|
|
218
|
+
end
|
|
219
|
+
end
|
|
220
|
+
end
|
|
221
|
+
|
|
222
|
+
# Calls the class methods of the same name...
|
|
223
|
+
def expire_cms_page(page)
|
|
224
|
+
self.class.expire_cms_page(page)
|
|
225
|
+
end
|
|
226
|
+
def expire_cms_pages_from_bottom(page)
|
|
227
|
+
self.class.expire_cms_pages_from_bottom(page)
|
|
228
|
+
end
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
# expire the page from the fragment cache
|
|
232
|
+
def expire_cms_fragment(page)
|
|
233
|
+
key = page.full_path.gsub(/\//, '+')
|
|
234
|
+
expire_fragment(key)
|
|
235
|
+
end
|
|
236
|
+
|
|
237
|
+
# expire pages starting at a specific node
|
|
238
|
+
def expire_cms_fragments_from_bottom(page)
|
|
239
|
+
pages = page.is_a?(Array) ? page : [page]
|
|
240
|
+
pages.each do |page|
|
|
241
|
+
page.children.each {|c| expire_cms_fragments_from_bottom( c ) } if !page.children.empty?
|
|
242
|
+
expire_cms_fragment( page )
|
|
243
|
+
end
|
|
244
|
+
end
|
|
245
|
+
|
|
246
|
+
# Class Methods...
|
|
247
|
+
class << self
|
|
248
|
+
|
|
249
|
+
# Walks all the way down, and back up the tree -- the allows the expire_cms_page
|
|
250
|
+
# to delete empty directories better
|
|
251
|
+
def expire_cms_pages_from_bottom(page)
|
|
252
|
+
pages = page.is_a?(Array) ? page : [page]
|
|
253
|
+
pages.each do |page|
|
|
254
|
+
page.children.each {|c| expire_cms_pages_from_bottom( c ) } if !page.children.empty?
|
|
255
|
+
expire_cms_page( page )
|
|
256
|
+
end
|
|
257
|
+
end
|
|
258
|
+
|
|
259
|
+
# Expire the page from all the mount points...
|
|
260
|
+
def expire_cms_page(page)
|
|
261
|
+
Comatose.mount_points.each do |path_info|
|
|
262
|
+
ComatosePage.active_mount_info = path_info
|
|
263
|
+
expire_page(page.uri)
|
|
264
|
+
# If the page is the index page for the root, expire it too
|
|
265
|
+
if path_info[:root] == page.uri
|
|
266
|
+
expire_page("#{path_info[:root]}/index")
|
|
267
|
+
end
|
|
268
|
+
begin # I'm not sure this matters too much -- but it keeps things clean
|
|
269
|
+
dir_path = File.join(RAILS_ROOT, 'public', page.uri[1..-1])
|
|
270
|
+
Dir.delete( dir_path ) if FileTest.directory?( dir_path ) and !page.parent.nil?
|
|
271
|
+
rescue
|
|
272
|
+
# It probably isn't empty -- just as well we leave it be
|
|
273
|
+
#STDERR.puts " - Couldn't delete dir #{dir_path} -> #{$!}"
|
|
274
|
+
end
|
|
275
|
+
end
|
|
276
|
+
end
|
|
277
|
+
|
|
278
|
+
# Returns a path to plugin layout, if it's unspecified, otherwise
|
|
279
|
+
# a path to an application layout...
|
|
280
|
+
def get_page_layout(params)
|
|
281
|
+
if params[:layout] == 'comatose_content'
|
|
282
|
+
File.join(plugin_layout_path, params[:layout])
|
|
283
|
+
else
|
|
284
|
+
params[:layout]
|
|
285
|
+
end
|
|
286
|
+
end
|
|
287
|
+
|
|
288
|
+
def configure_template_root
|
|
289
|
+
if self.runtime_mode == :unknown
|
|
290
|
+
if FileTest.exist? File.join(RAILS_ROOT, 'public', 'javascripts', 'comatose_admin.js')
|
|
291
|
+
self.runtime_mode = :application
|
|
292
|
+
else
|
|
293
|
+
self.runtime_mode = :plugin
|
|
294
|
+
end
|
|
295
|
+
end
|
|
296
|
+
end
|
|
297
|
+
|
|
298
|
+
def runtime_mode
|
|
299
|
+
@@runtime_mode ||= :unknown
|
|
300
|
+
end
|
|
301
|
+
|
|
302
|
+
def runtime_mode=(mode)
|
|
303
|
+
admin_view_path = File.expand_path(File.join( File.dirname(__FILE__), '..', 'views'))
|
|
304
|
+
if self.respond_to?(:template_root)
|
|
305
|
+
case mode
|
|
306
|
+
when :plugin
|
|
307
|
+
self.original_template_root = self.template_root
|
|
308
|
+
self.template_root = admin_view_path
|
|
309
|
+
when :application
|
|
310
|
+
self.template_root = self.original_template_root if self.original_template_root
|
|
311
|
+
end
|
|
312
|
+
else
|
|
313
|
+
ActionController::Base.append_view_path(admin_view_path) unless ActionController::Base.view_paths.include?(admin_view_path)
|
|
314
|
+
end
|
|
315
|
+
@@runtime_mode = mode
|
|
316
|
+
end
|
|
317
|
+
|
|
318
|
+
end
|
|
319
|
+
|
|
320
|
+
# Check to see if we are in 'embedded' mode, or are being 'customized'
|
|
321
|
+
# embedded = runtime_mode of :plugin
|
|
322
|
+
# customized = runtime_mode of :application
|
|
323
|
+
configure_template_root
|
|
324
|
+
|
|
325
|
+
#
|
|
326
|
+
# Include any modules...
|
|
327
|
+
Comatose.config.admin_includes.each do |mod|
|
|
328
|
+
if mod.is_a? String
|
|
329
|
+
include mod.constantize
|
|
330
|
+
elsif mod.is_a? Symbol
|
|
331
|
+
include mod.to_s.classify.constantize
|
|
332
|
+
else
|
|
333
|
+
include mod
|
|
334
|
+
end
|
|
335
|
+
end
|
|
336
|
+
|
|
337
|
+
# Include any helpers...
|
|
338
|
+
Comatose.config.admin_helpers.each do |mod|
|
|
339
|
+
if mod.is_a? String
|
|
340
|
+
helper mod.constantize
|
|
341
|
+
elsif mod.is_a? Symbol
|
|
342
|
+
helper mod.to_s.classify.constantize
|
|
343
|
+
else
|
|
344
|
+
helper mod
|
|
345
|
+
end
|
|
346
|
+
end
|
|
347
|
+
|
|
348
|
+
|
|
349
|
+
end
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
module ComatoseAdminHelper
|
|
2
|
+
|
|
3
|
+
# Checks the hidden_meta_fields class variable for a specified field name...
|
|
4
|
+
def show_field?(key)
|
|
5
|
+
!Comatose.config.hidden_meta_fields.include? key
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
# Used in the Page Form to build an indented drop-down list of pages
|
|
9
|
+
def tree_select_box(nodes, selected= nil, hide= nil, label="Parent", add_initial=false)
|
|
10
|
+
level = 0
|
|
11
|
+
select_box = add_initial ? "<option value=0>No #{label}</option>\n" : ""
|
|
12
|
+
selected = nodes[0].id if selected.nil? and not add_initial
|
|
13
|
+
nodes.each {|node| select_box += add_select_tree_node(node, selected, level, hide) }
|
|
14
|
+
select_box += ''
|
|
15
|
+
end
|
|
16
|
+
# Called by tree_select_box
|
|
17
|
+
def add_select_tree_node(node, selected, level, hide)
|
|
18
|
+
padding = " " * level * 4
|
|
19
|
+
padding += '» ' unless level==0
|
|
20
|
+
hide_values = Array.new
|
|
21
|
+
hide_values << hide if hide
|
|
22
|
+
if node.id == selected
|
|
23
|
+
select_box = %Q|<option value="#{node.id}" selected="true">#{padding}#{node.title}</option>\n|
|
|
24
|
+
else
|
|
25
|
+
if hide_values.include?(node.id)
|
|
26
|
+
select_box = ''
|
|
27
|
+
else
|
|
28
|
+
select_box = %Q|<option value="#{node.id}">#{padding}#{node.title}</option>\n|
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
node.children.each do |child|
|
|
32
|
+
select_box += add_select_tree_node(child, selected, level + 1, hide) unless hide_values.include?(node.id)
|
|
33
|
+
end
|
|
34
|
+
select_box
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
end
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
# The controller for serving cms content...
|
|
2
|
+
class ComatoseController < ActionController::Base
|
|
3
|
+
|
|
4
|
+
before_filter :handle_authorization, :set_content_type
|
|
5
|
+
after_filter :cache_cms_page
|
|
6
|
+
|
|
7
|
+
# Render a specific page
|
|
8
|
+
def show
|
|
9
|
+
page_name, page_ext = get_page_path
|
|
10
|
+
page = ComatosePage.find_by_path( page_name )
|
|
11
|
+
status = nil
|
|
12
|
+
if page.nil?
|
|
13
|
+
page = ComatosePage.find_by_path( '404' )
|
|
14
|
+
status = 404
|
|
15
|
+
end
|
|
16
|
+
# if it's still nil, well, send a 404 status
|
|
17
|
+
if page.nil?
|
|
18
|
+
render :nothing=>true, :status=>status
|
|
19
|
+
#raise ActiveRecord::RecordNotFound.new("Comatose page not found ")
|
|
20
|
+
else
|
|
21
|
+
# Make the page access 'safe'
|
|
22
|
+
@page = Comatose::PageWrapper.new(page)
|
|
23
|
+
# For accurate uri creation, tell the page class which is the active mount point...
|
|
24
|
+
ComatosePage.active_mount_info = get_active_mount_point(params[:index])
|
|
25
|
+
render :text=>page.to_html({'params'=>params.stringify_keys}), :layout=>get_page_layout, :status=>status
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
protected
|
|
30
|
+
|
|
31
|
+
def handle_authorization
|
|
32
|
+
if Comatose.config.authorization.is_a? Proc
|
|
33
|
+
instance_eval &Comatose.config.authorization
|
|
34
|
+
elsif Comatose.config.authorization.is_a? Symbol
|
|
35
|
+
send(Comatose.config.authorization)
|
|
36
|
+
elsif defined? authorize
|
|
37
|
+
authorize
|
|
38
|
+
else
|
|
39
|
+
true
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def allow_page_cache?
|
|
44
|
+
# You should have access to the @page being rendered
|
|
45
|
+
true
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# For use in the #show method... determines the current mount point
|
|
49
|
+
def get_active_mount_point( index )
|
|
50
|
+
Comatose.mount_points.each do |path_info|
|
|
51
|
+
if path_info[:index] == index
|
|
52
|
+
return path_info
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
{:root=>"", :index=>index}
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# For use in the #show method... determines the current page path
|
|
59
|
+
def get_page_path
|
|
60
|
+
|
|
61
|
+
#in rails 2.0, params[:page] comes back as just an Array, so to_s doesn't do join('/')
|
|
62
|
+
if params[:page].is_a? Array
|
|
63
|
+
page_name = params[:page].join("/")
|
|
64
|
+
#in rails 1.x, params[:page] comes back as ActionController::Routing::PathSegment::Result
|
|
65
|
+
elsif params[:page].is_a? ActionController::Routing::PathSegment::Result
|
|
66
|
+
page_name = params[:page].to_s
|
|
67
|
+
else
|
|
68
|
+
logger.debug "get_page_path - params[:page] is an unrecognized type, may cause problems: #{params[:page].class}"
|
|
69
|
+
page_name = params[:page].to_s
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
page_ext = page_name.split('.')[1] unless page_name.empty?
|
|
73
|
+
# TODO: Automatic support for page RSS feeds... ????
|
|
74
|
+
if page_name.nil? or page_name.empty?
|
|
75
|
+
page_name = params[:index]
|
|
76
|
+
params[:cache_path] = "#{request.request_uri}/index"
|
|
77
|
+
elsif !params[:index].empty?
|
|
78
|
+
page_name = "#{params[:index]}/#{page_name}"
|
|
79
|
+
end
|
|
80
|
+
return page_name, page_ext
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
# Returns a path to plugin layout, if it's unspecified, otherwise
|
|
84
|
+
# a path to an application layout...
|
|
85
|
+
def get_page_layout
|
|
86
|
+
if params[:layout] == 'comatose_content'
|
|
87
|
+
File.join(plugin_layout_path, params[:layout])
|
|
88
|
+
else
|
|
89
|
+
params[:layout]
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
# An after_filter implementing page caching if it's enabled, globally,
|
|
94
|
+
# and is allowed by #allow_page_cache?
|
|
95
|
+
def cache_cms_page
|
|
96
|
+
unless Comatose.config.disable_caching or response.headers['Status'] == '404 Not Found'
|
|
97
|
+
return unless params[:use_cache].to_s == 'true' and allow_page_cache?
|
|
98
|
+
path = params[:cache_path] || request.request_uri
|
|
99
|
+
begin
|
|
100
|
+
# TODO: Don't cache pages rendering '404' content...
|
|
101
|
+
self.class.cache_page( response.body, path )
|
|
102
|
+
rescue
|
|
103
|
+
logger.error "Comatose CMS Page Cache Exception: #{$!}"
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
# An after_filter that sets the HTTP header for Content-Type to
|
|
109
|
+
# what's defined in Comatose.config.content_type. Defaults to utf-8.
|
|
110
|
+
def set_content_type
|
|
111
|
+
response.headers["Content-Type"] = "text/html; charset=#{Comatose.config.content_type}" unless Comatose.config.content_type.nil? or response.headers['Status'] == '404 Not Found'
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
# Path to layouts within the plugin... Assumes the plugin directory name is 'comatose'
|
|
115
|
+
define_option :plugin_layout_path, File.join( '..', '..', '..', 'vendor', 'plugins', 'comatose', 'views', 'layouts' )
|
|
116
|
+
|
|
117
|
+
# Include any, well, includes...
|
|
118
|
+
Comatose.config.includes.each do |mod|
|
|
119
|
+
mod_klass = if mod.is_a? String
|
|
120
|
+
mod.constantize
|
|
121
|
+
elsif mod.is_a? Symbol
|
|
122
|
+
mod.to_s.classify.constantize
|
|
123
|
+
else
|
|
124
|
+
mod
|
|
125
|
+
end
|
|
126
|
+
include mod_klass
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
# Include any helpers...
|
|
130
|
+
Comatose.config.helpers.each do |mod|
|
|
131
|
+
mod_klass = if mod.is_a? String
|
|
132
|
+
mod.constantize
|
|
133
|
+
elsif mod.is_a? Symbol
|
|
134
|
+
mod.to_s.classify.constantize
|
|
135
|
+
else
|
|
136
|
+
mod
|
|
137
|
+
end
|
|
138
|
+
helper mod_klass
|
|
139
|
+
end
|
|
140
|
+
end
|
|
141
|
+
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
# ComatosePage attributes
|
|
2
|
+
# - parent_id
|
|
3
|
+
# - title
|
|
4
|
+
# - full_path
|
|
5
|
+
# - slug
|
|
6
|
+
# - keywords
|
|
7
|
+
# - body
|
|
8
|
+
# - author
|
|
9
|
+
# - filter_type
|
|
10
|
+
# - position
|
|
11
|
+
# - version
|
|
12
|
+
# - updated_on
|
|
13
|
+
# - created_on
|
|
14
|
+
class ComatosePage < ActiveRecord::Base
|
|
15
|
+
|
|
16
|
+
set_table_name 'comatose_pages'
|
|
17
|
+
|
|
18
|
+
# Only versions the content... Not all of the meta data or position
|
|
19
|
+
acts_as_versioned :table_name=>'comatose_page_versions', :if_changed => [:title, :slug, :keywords, :body]
|
|
20
|
+
|
|
21
|
+
define_option :active_mount_info, {:root=>'', :index=>''}
|
|
22
|
+
|
|
23
|
+
acts_as_tree :order => "position, title"
|
|
24
|
+
acts_as_list :scope => :parent_id
|
|
25
|
+
|
|
26
|
+
#before_create :create_full_path
|
|
27
|
+
before_save :cache_full_path, :create_full_path
|
|
28
|
+
after_save :update_children_full_path
|
|
29
|
+
|
|
30
|
+
# Using before_validation so we can default the slug from the title
|
|
31
|
+
before_validation do |record|
|
|
32
|
+
# Create slug from title
|
|
33
|
+
if record.slug.blank? and !record.title.blank?
|
|
34
|
+
record.slug = record.title.downcase.lstrip.rstrip.gsub( /[^-a-z0-9~\s\.:;+=_]/, '').gsub(/[\s\.:;=_+]+/, '-').gsub(/[\-]{2,}/, '-').to_s
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# Manually set these, because record_timestamps = false
|
|
39
|
+
before_create do |record|
|
|
40
|
+
record.created_on = record.updated_on = Time.now
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
validates_presence_of :title, :on => :save, :message => "must be present"
|
|
44
|
+
validates_uniqueness_of :slug, :on => :save, :scope=>'parent_id', :message => "is already in use"
|
|
45
|
+
validates_presence_of :parent_id, :on=>:create, :message=>"must be present"
|
|
46
|
+
|
|
47
|
+
# Tests ERB/Liquid content...
|
|
48
|
+
validates_each :body, :allow_nil=>true, :allow_blank=>true do |record, attr, value|
|
|
49
|
+
begin
|
|
50
|
+
body_html = record.to_html
|
|
51
|
+
rescue SyntaxError
|
|
52
|
+
record.errors.add :body, "syntax error: #{$!.to_s.gsub('<', '<')}"
|
|
53
|
+
rescue
|
|
54
|
+
record.errors.add :body, "content error: #{$!.to_s.gsub('<', '<')}"
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# Returns a pages URI dynamically, based on the active mount point
|
|
59
|
+
def uri
|
|
60
|
+
if full_path == ''
|
|
61
|
+
active_mount_info[:root]
|
|
62
|
+
else
|
|
63
|
+
page_path = (full_path || '').split('/')
|
|
64
|
+
idx_path = active_mount_info[:index].split('/')
|
|
65
|
+
uri_root = active_mount_info[:root].split('/')
|
|
66
|
+
uri_path = ( uri_root + (page_path - idx_path) ).flatten.delete_if {|i| i == "" }
|
|
67
|
+
['',uri_path].join('/')
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
# Check if a page has a selected keyword... NOT case sensitive.
|
|
72
|
+
# So the keyword McCray is the same as mccray
|
|
73
|
+
def has_keyword?(keyword)
|
|
74
|
+
@key_list ||= (self.keywords || '').downcase.split(',').map {|k| k.strip }
|
|
75
|
+
@key_list.include? keyword.to_s.downcase
|
|
76
|
+
rescue
|
|
77
|
+
false
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
# Returns the page's content, transformed and filtered...
|
|
81
|
+
def to_html(options={})
|
|
82
|
+
#version = options.delete(:version)
|
|
83
|
+
text = self.body
|
|
84
|
+
binding = Comatose::ProcessingContext.new(self, options)
|
|
85
|
+
filter_type = self.filter_type || '[No Filter]'
|
|
86
|
+
TextFilters.transform(text, binding, filter_type, Comatose.config.default_processor)
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
# Static helpers...
|
|
90
|
+
|
|
91
|
+
# Returns a Page with a matching path.
|
|
92
|
+
def self.find_by_path( path )
|
|
93
|
+
path = path.split('.')[0] unless path.empty? # Will ignore file extension...
|
|
94
|
+
path = path[1..-1] if path.starts_with? "/"
|
|
95
|
+
find( :first, :conditions=>[ 'full_path = ?', path ] )
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
# Overrides...
|
|
99
|
+
|
|
100
|
+
# I don't want the AR magic timestamping support for this class...
|
|
101
|
+
def record_timestamps
|
|
102
|
+
false
|
|
103
|
+
end
|
|
104
|
+
def self.record_timestamps
|
|
105
|
+
false
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
protected
|
|
109
|
+
|
|
110
|
+
# Creates a URI path based on the Page tree
|
|
111
|
+
def create_full_path
|
|
112
|
+
if parent_node = self.parent
|
|
113
|
+
# Build URI Path
|
|
114
|
+
path = "#{parent_node.full_path}/#{self.slug}"
|
|
115
|
+
# strip leading space, if there is one...
|
|
116
|
+
path = path[1..-1] if path.starts_with? "/"
|
|
117
|
+
self.full_path = path || ""
|
|
118
|
+
else
|
|
119
|
+
# I'm the root -- My path is blank
|
|
120
|
+
self.full_path = ""
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
def create_full_path!
|
|
124
|
+
create_full_path
|
|
125
|
+
save
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
# Caches old path (before save) for comparison later
|
|
129
|
+
def cache_full_path
|
|
130
|
+
@old_full_path = self.full_path
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
# Updates all this content's child URI paths
|
|
134
|
+
def update_children_full_path(should_save=true)
|
|
135
|
+
# OPTIMIZE: Only update all the children if the :slug/:fullpath is different
|
|
136
|
+
for child in self.children
|
|
137
|
+
child.create_full_path! unless child.frozen?
|
|
138
|
+
child.update_children_full_path(should_save)
|
|
139
|
+
end
|
|
140
|
+
end
|
|
141
|
+
end
|