aslakjo-aslakjo-comatose 2.0.5.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (93) hide show
  1. data/CHANGELOG +195 -0
  2. data/INSTALL +20 -0
  3. data/LICENSE +20 -0
  4. data/MANIFEST +91 -0
  5. data/README.markdown +159 -0
  6. data/Rakefile +176 -0
  7. data/SPECS +61 -0
  8. data/about.yml +7 -0
  9. data/bin/comatose +112 -0
  10. data/comatose.gemspec +113 -0
  11. data/generators/comatose_migration/USAGE +15 -0
  12. data/generators/comatose_migration/comatose_migration_generator.rb +74 -0
  13. data/generators/comatose_migration/templates/migration.rb +35 -0
  14. data/generators/comatose_migration/templates/v4_upgrade.rb +15 -0
  15. data/generators/comatose_migration/templates/v6_upgrade.rb +23 -0
  16. data/generators/comatose_migration/templates/v7_upgrade.rb +22 -0
  17. data/init.rb +2 -0
  18. data/install.rb +18 -0
  19. data/lib/acts_as_versioned.rb +543 -0
  20. data/lib/comatose.rb +33 -0
  21. data/lib/comatose/comatose_drop.rb +79 -0
  22. data/lib/comatose/configuration.rb +69 -0
  23. data/lib/comatose/page_wrapper.rb +119 -0
  24. data/lib/comatose/processing_context.rb +69 -0
  25. data/lib/comatose/tasks/admin.rb +60 -0
  26. data/lib/comatose/tasks/data.rb +82 -0
  27. data/lib/comatose/tasks/setup.rb +52 -0
  28. data/lib/comatose/version.rb +4 -0
  29. data/lib/comatose_admin_controller.rb +395 -0
  30. data/lib/comatose_admin_helper.rb +37 -0
  31. data/lib/comatose_controller.rb +138 -0
  32. data/lib/comatose_helper.rb +3 -0
  33. data/lib/comatose_page.rb +141 -0
  34. data/lib/liquid.rb +52 -0
  35. data/lib/liquid/block.rb +96 -0
  36. data/lib/liquid/context.rb +190 -0
  37. data/lib/liquid/document.rb +17 -0
  38. data/lib/liquid/drop.rb +48 -0
  39. data/lib/liquid/errors.rb +7 -0
  40. data/lib/liquid/extensions.rb +53 -0
  41. data/lib/liquid/file_system.rb +62 -0
  42. data/lib/liquid/htmltags.rb +64 -0
  43. data/lib/liquid/standardfilters.rb +111 -0
  44. data/lib/liquid/standardtags.rb +399 -0
  45. data/lib/liquid/strainer.rb +42 -0
  46. data/lib/liquid/tag.rb +25 -0
  47. data/lib/liquid/template.rb +88 -0
  48. data/lib/liquid/variable.rb +39 -0
  49. data/lib/redcloth.rb +1129 -0
  50. data/lib/support/class_options.rb +36 -0
  51. data/lib/support/inline_rendering.rb +48 -0
  52. data/lib/support/route_mapper.rb +50 -0
  53. data/lib/text_filters.rb +140 -0
  54. data/lib/text_filters/markdown.rb +14 -0
  55. data/lib/text_filters/markdown_smartypants.rb +15 -0
  56. data/lib/text_filters/none.rb +8 -0
  57. data/lib/text_filters/rdoc.rb +13 -0
  58. data/lib/text_filters/simple.rb +8 -0
  59. data/lib/text_filters/textile.rb +15 -0
  60. data/rails/init.rb +3 -0
  61. data/resources/layouts/comatose_admin_template.html.erb +28 -0
  62. data/resources/public/images/collapsed.gif +0 -0
  63. data/resources/public/images/expanded.gif +0 -0
  64. data/resources/public/images/no-children.gif +0 -0
  65. data/resources/public/images/page.gif +0 -0
  66. data/resources/public/images/spinner.gif +0 -0
  67. data/resources/public/images/title-hover-bg.gif +0 -0
  68. data/resources/public/javascripts/comatose_admin.js +401 -0
  69. data/resources/public/stylesheets/comatose_admin.css +404 -0
  70. data/tasks/comatose.rake +9 -0
  71. data/test/behaviors.rb +106 -0
  72. data/test/fixtures/comatose_pages.yml +96 -0
  73. data/test/functional/comatose_admin_controller_test.rb +114 -0
  74. data/test/functional/comatose_controller_test.rb +44 -0
  75. data/test/javascripts/test.html +26 -0
  76. data/test/javascripts/test_runner.js +307 -0
  77. data/test/test_helper.rb +55 -0
  78. data/test/unit/class_options_test.rb +52 -0
  79. data/test/unit/comatose_page_test.rb +136 -0
  80. data/test/unit/processing_context_test.rb +108 -0
  81. data/test/unit/text_filters_test.rb +52 -0
  82. data/views/comatose_admin/_form.html.erb +96 -0
  83. data/views/comatose_admin/_page_list_item.html.erb +60 -0
  84. data/views/comatose_admin/delete.html.erb +18 -0
  85. data/views/comatose_admin/edit.html.erb +5 -0
  86. data/views/comatose_admin/index.html.erb +29 -0
  87. data/views/comatose_admin/new.html.erb +5 -0
  88. data/views/comatose_admin/reorder.html.erb +30 -0
  89. data/views/comatose_admin/versions.html.erb +40 -0
  90. data/views/layouts/comatose_admin.html.erb +837 -0
  91. data/views/layouts/comatose_admin_customize.html.erb +28 -0
  92. data/views/layouts/comatose_content.html.erb +17 -0
  93. metadata +148 -0
@@ -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 = "&nbsp;" * level * 4
19
+ padding += '&raquo; ' 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,138 @@
1
+ # The controller for serving cms content...
2
+ class ComatoseController < ActionController::Base
3
+ unloadable
4
+
5
+ before_filter :handle_authorization, :set_content_type
6
+ after_filter :cache_cms_page
7
+
8
+ # Render a specific page
9
+ def show
10
+ page_name, page_ext = get_page_path
11
+ page = ComatosePage.find_by_path( page_name )
12
+ status = nil
13
+ if page.nil?
14
+ page = ComatosePage.find_by_path( '404' )
15
+ status = 404
16
+ end
17
+ # if it's still nil, well, send a 404 status
18
+ if page.nil?
19
+ render :nothing=>true, :status=>status
20
+ #raise ActiveRecord::RecordNotFound.new("Comatose page not found ")
21
+ else
22
+ # Make the page access 'safe'
23
+ @page = Comatose::PageWrapper.new(page)
24
+ # For accurate uri creation, tell the page class which is the active mount point...
25
+ ComatosePage.active_mount_info = get_active_mount_point(params[:index])
26
+ render :text=>page.to_html({'params'=>params.stringify_keys}), :layout=>get_page_layout, :status=>status
27
+ end
28
+ end
29
+
30
+ protected
31
+
32
+ def handle_authorization
33
+ if Comatose.config.authorization.is_a? Proc
34
+ instance_eval &Comatose.config.authorization
35
+ elsif Comatose.config.authorization.is_a? Symbol
36
+ send(Comatose.config.authorization)
37
+ elsif defined? authorize
38
+ authorize
39
+ else
40
+ true
41
+ end
42
+ end
43
+
44
+ def allow_page_cache?
45
+ # You should have access to the @page being rendered
46
+ true
47
+ end
48
+
49
+ # For use in the #show method... determines the current mount point
50
+ def get_active_mount_point( index )
51
+ Comatose.mount_points.each do |path_info|
52
+ if path_info[:index] == index
53
+ return path_info
54
+ end
55
+ end
56
+ {:root=>"", :index=>index}
57
+ end
58
+
59
+ # For use in the #show method... determines the current page path
60
+ def get_page_path
61
+
62
+ #in rails 2.0, params[:page] comes back as just an Array, so to_s doesn't do join('/')
63
+ if params[:page].is_a? Array
64
+ page_name = params[:page].join("/")
65
+ #in rails 1.x, params[:page] comes back as ActionController::Routing::PathSegment::Result
66
+ elsif params[:page].is_a? ActionController::Routing::PathSegment::Result
67
+ page_name = params[:page].to_s
68
+ else
69
+ logger.debug "get_page_path - params[:page] is an unrecognized type, may cause problems: #{params[:page].class}"
70
+ page_name = params[:page].to_s
71
+ end
72
+
73
+ page_ext = page_name.split('.')[1] unless page_name.empty?
74
+ # TODO: Automatic support for page RSS feeds... ????
75
+ if page_name.nil? or page_name.empty?
76
+ page_name = params[:index]
77
+ params[:cache_path] = "#{request.request_uri}/index"
78
+ elsif !params[:index].empty?
79
+ page_name = "#{params[:index]}/#{page_name}"
80
+ end
81
+ return page_name, page_ext
82
+ end
83
+
84
+ # Returns a path to plugin layout, if it's unspecified, otherwise
85
+ # a path to an application layout...
86
+ def get_page_layout
87
+ params[:layout]
88
+ end
89
+
90
+ # An after_filter implementing page caching if it's enabled, globally,
91
+ # and is allowed by #allow_page_cache?
92
+ def cache_cms_page
93
+ unless Comatose.config.disable_caching or response.headers['Status'] == '404 Not Found'
94
+ return unless params[:use_cache].to_s == 'true' and allow_page_cache?
95
+ path = params[:cache_path] || request.request_uri
96
+ begin
97
+ # TODO: Don't cache pages rendering '404' content...
98
+ self.class.cache_page( response.body, path )
99
+ rescue
100
+ logger.error "Comatose CMS Page Cache Exception: #{$!}"
101
+ end
102
+ end
103
+ end
104
+
105
+ # An after_filter that sets the HTTP header for Content-Type to
106
+ # what's defined in Comatose.config.content_type. Defaults to utf-8.
107
+ def set_content_type
108
+ response.headers["Content-Type"] = "text/html; charset=#{Comatose.config.content_type}" unless Comatose.config.content_type.nil? or response.headers['Status'] == '404 Not Found'
109
+ end
110
+
111
+ COMATOSE_VIEW_PATH = File.join(RAILS_ROOT, 'vendor', 'plugins', 'comatose', 'views')
112
+ ActionController::Base.append_view_path(COMATOSE_VIEW_PATH) unless ActionController::Base.view_paths.include?(COMATOSE_VIEW_PATH)
113
+
114
+ # Include any, well, includes...
115
+ Comatose.config.includes.each do |mod|
116
+ mod_klass = if mod.is_a? String
117
+ mod.constantize
118
+ elsif mod.is_a? Symbol
119
+ mod.to_s.classify.constantize
120
+ else
121
+ mod
122
+ end
123
+ include mod_klass
124
+ end
125
+
126
+ # Include any helpers...
127
+ Comatose.config.helpers.each do |mod|
128
+ mod_klass = if mod.is_a? String
129
+ mod.constantize
130
+ elsif mod.is_a? Symbol
131
+ mod.to_s.classify.constantize
132
+ else
133
+ mod
134
+ end
135
+ helper mod_klass
136
+ end
137
+ end
138
+
@@ -0,0 +1,3 @@
1
+ module ComatoseHelper
2
+
3
+ end
@@ -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('<', '&lt;')}"
53
+ rescue
54
+ record.errors.add :body, "content error: #{$!.to_s.gsub('<', '&lt;')}"
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 || Comatose.config.default_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
@@ -0,0 +1,52 @@
1
+ # Copyright (c) 2005 Tobias Luetke
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining
4
+ # a copy of this software and associated documentation files (the
5
+ # "Software"), to deal in the Software without restriction, including
6
+ # without limitation the rights to use, copy, modify, merge, publish,
7
+ # distribute, sublicense, and/or sell copies of the Software, and to
8
+ # permit persons to whom the Software is furnished to do so, subject to
9
+ # the following conditions:
10
+ #
11
+ # The above copyright notice and this permission notice shall be
12
+ # included in all copies or substantial portions of the Software.
13
+ #
14
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOa AND
17
+ # NONINFRINGEMENT. IN NO EVENT SaALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
+
22
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
23
+
24
+ module Liquid
25
+ FilterSperator = /\|/
26
+ ArgumentSeparator = ','
27
+ FilterArgumentSeparator = ':'
28
+ VariableAttributeSeparator = '.'
29
+ TagStart = /\{\%/
30
+ TagEnd = /\%\}/
31
+ VariableStart = /\{\{/
32
+ VariableEnd = /\}\}/
33
+ AllowedVariableCharacters = /[a-zA-Z_.-]/
34
+ QuotedFragment = /"[^"]+"|'[^']+'|[^\s,|]+/
35
+ TagAttributes = /(\w+)\s*\:\s*(#{QuotedFragment})/
36
+ TokenizationRegexp = /(#{TagStart}.*?#{TagEnd}|#{VariableStart}.*?#{VariableEnd})/
37
+ end
38
+
39
+ require 'liquid/drop'
40
+ require 'liquid/extensions'
41
+ require 'liquid/errors'
42
+ require 'liquid/strainer'
43
+ require 'liquid/context'
44
+ require 'liquid/tag'
45
+ require 'liquid/block'
46
+ require 'liquid/document'
47
+ require 'liquid/variable'
48
+ require 'liquid/file_system'
49
+ require 'liquid/template'
50
+ require 'liquid/standardtags'
51
+ require 'liquid/htmltags'
52
+ require 'liquid/standardfilters'
@@ -0,0 +1,96 @@
1
+ module Liquid
2
+
3
+ class Block < Tag
4
+ def parse(tokens)
5
+ @nodelist ||= []
6
+ @nodelist.clear
7
+
8
+ while token = tokens.shift
9
+
10
+ case token
11
+ when /^#{TagStart}/
12
+ if token =~ /^#{TagStart}\s*(\w+)\s*(.*)?#{TagEnd}$/
13
+
14
+ # if we found the proper block delimitor just end parsing here and let the outer block
15
+ # proceed
16
+ if block_delimiter == $1
17
+ end_tag
18
+ return
19
+ end
20
+
21
+ # fetch the tag from registered blocks
22
+ if tag = Template.tags[$1]
23
+ @nodelist << tag.new($2, tokens)
24
+ else
25
+ # this tag is not registered with the system
26
+ # pass it to the current block for special handling or error reporting
27
+ unknown_tag($1, $2, tokens)
28
+ end
29
+ else
30
+ raise SyntaxError, "Tag '#{token}' was not properly terminated with regexp: #{TagEnd.inspect} "
31
+ end
32
+ when /^#{VariableStart}/
33
+ @nodelist << create_variable(token)
34
+ when ''
35
+ # pass
36
+ else
37
+ @nodelist << token
38
+ end
39
+ end
40
+
41
+ # Make sure that its ok to end parsing in the current block.
42
+ # Effectively this method will throw and exception unless the current block is
43
+ # of type Document
44
+ assert_missing_delimitation!
45
+ end
46
+
47
+ def end_tag
48
+ end
49
+
50
+ def unknown_tag(tag, params, tokens)
51
+ case tag
52
+ when 'else'
53
+ raise SyntaxError, "#{block_name} tag does not expect else tag"
54
+ when 'end'
55
+ raise SyntaxError, "'end' is not a valid delimiter for #{block_name} tags. use #{block_delimiter}"
56
+ else
57
+ raise SyntaxError, "Unknown tag '#{tag}'"
58
+ end
59
+ end
60
+
61
+ def block_delimiter
62
+ "end#{block_name}"
63
+ end
64
+
65
+ def block_name
66
+ self.class.name.scan(/\w+$/).first.downcase
67
+ end
68
+
69
+ def create_variable(token)
70
+ token.scan(/^#{VariableStart}(.*)#{VariableEnd}$/) do |content|
71
+ return Variable.new(content.first)
72
+ end
73
+ raise SyntaxError.new("Variable '#{token}' was not properly terminated with regexp: #{VariableEnd.inspect} ")
74
+ end
75
+
76
+ def render(context)
77
+ render_all(@nodelist, context)
78
+ end
79
+
80
+ protected
81
+
82
+ def assert_missing_delimitation!
83
+ raise SyntaxError.new("#{block_name} tag was never closed")
84
+ end
85
+
86
+ def render_all(list, context)
87
+ list.collect do |token|
88
+ if token.respond_to?(:render)
89
+ token.render(context)
90
+ else
91
+ token.to_s
92
+ end
93
+ end
94
+ end
95
+ end
96
+ end