cortex-reaver 0.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/LICENSE +25 -0
- data/README +48 -0
- data/bin/console +11 -0
- data/bin/cortex_reaver +110 -0
- data/bin/import.rb +78 -0
- data/lib/cortex_reaver.rb +114 -0
- data/lib/cortex_reaver/config.rb +21 -0
- data/lib/cortex_reaver/controller/comment.rb +113 -0
- data/lib/cortex_reaver/controller/config.rb +14 -0
- data/lib/cortex_reaver/controller/journal.rb +40 -0
- data/lib/cortex_reaver/controller/main.rb +56 -0
- data/lib/cortex_reaver/controller/page.rb +34 -0
- data/lib/cortex_reaver/controller/photograph.rb +45 -0
- data/lib/cortex_reaver/controller/project.rb +40 -0
- data/lib/cortex_reaver/controller/tag.rb +67 -0
- data/lib/cortex_reaver/controller/user.rb +71 -0
- data/lib/cortex_reaver/helper/activity.rb +9 -0
- data/lib/cortex_reaver/helper/attachments.rb +139 -0
- data/lib/cortex_reaver/helper/auth.rb +45 -0
- data/lib/cortex_reaver/helper/canonical.rb +55 -0
- data/lib/cortex_reaver/helper/crud.rb +317 -0
- data/lib/cortex_reaver/helper/date.rb +29 -0
- data/lib/cortex_reaver/helper/error.rb +14 -0
- data/lib/cortex_reaver/helper/feeds.rb +88 -0
- data/lib/cortex_reaver/helper/form.rb +114 -0
- data/lib/cortex_reaver/helper/navigation.rb +142 -0
- data/lib/cortex_reaver/helper/photographs.rb +25 -0
- data/lib/cortex_reaver/helper/tags.rb +47 -0
- data/lib/cortex_reaver/helper/workflow.rb +27 -0
- data/lib/cortex_reaver/migrations/001_users.rb +24 -0
- data/lib/cortex_reaver/migrations/002_pages.rb +29 -0
- data/lib/cortex_reaver/migrations/003_journals.rb +26 -0
- data/lib/cortex_reaver/migrations/004_photographs.rb +24 -0
- data/lib/cortex_reaver/migrations/005_projects.rb +27 -0
- data/lib/cortex_reaver/migrations/006_tags.rb +64 -0
- data/lib/cortex_reaver/migrations/007_comments.rb +40 -0
- data/lib/cortex_reaver/migrations/008_config.rb +23 -0
- data/lib/cortex_reaver/model/comment.rb +109 -0
- data/lib/cortex_reaver/model/journal.rb +53 -0
- data/lib/cortex_reaver/model/page.rb +87 -0
- data/lib/cortex_reaver/model/photograph.rb +133 -0
- data/lib/cortex_reaver/model/project.rb +49 -0
- data/lib/cortex_reaver/model/tag.rb +72 -0
- data/lib/cortex_reaver/model/user.rb +147 -0
- data/lib/cortex_reaver/public/css/admin.css +45 -0
- data/lib/cortex_reaver/public/css/custom.css +0 -0
- data/lib/cortex_reaver/public/css/form.css +51 -0
- data/lib/cortex_reaver/public/css/main.css +325 -0
- data/lib/cortex_reaver/public/css/photo.css +113 -0
- data/lib/cortex_reaver/public/css/ramaze_error.css +90 -0
- data/lib/cortex_reaver/public/css/text.css +25 -0
- data/lib/cortex_reaver/public/dispatch.fcgi +11 -0
- data/lib/cortex_reaver/public/images/CortexReaver.gif +0 -0
- data/lib/cortex_reaver/public/images/atom-xml-icon.png +0 -0
- data/lib/cortex_reaver/public/images/body.png +0 -0
- data/lib/cortex_reaver/public/images/border_bottom.png +0 -0
- data/lib/cortex_reaver/public/images/border_bottom_left.png +0 -0
- data/lib/cortex_reaver/public/images/border_bottom_right.png +0 -0
- data/lib/cortex_reaver/public/images/border_left.png +0 -0
- data/lib/cortex_reaver/public/images/border_right.png +0 -0
- data/lib/cortex_reaver/public/images/border_top.png +0 -0
- data/lib/cortex_reaver/public/images/border_top_left.png +0 -0
- data/lib/cortex_reaver/public/images/border_top_right.png +0 -0
- data/lib/cortex_reaver/public/images/comment.gif +0 -0
- data/lib/cortex_reaver/public/images/dark_trans.png +0 -0
- data/lib/cortex_reaver/public/images/delete.gif +0 -0
- data/lib/cortex_reaver/public/images/edit.gif +0 -0
- data/lib/cortex_reaver/public/images/header.png +0 -0
- data/lib/cortex_reaver/public/images/header.xcf +0 -0
- data/lib/cortex_reaver/public/images/header_background.png +0 -0
- data/lib/cortex_reaver/public/images/parent.gif +0 -0
- data/lib/cortex_reaver/public/images/rss-xml-icon.png +0 -0
- data/lib/cortex_reaver/public/images/sections.png +0 -0
- data/lib/cortex_reaver/public/images/sections_highlight.png +0 -0
- data/lib/cortex_reaver/public/js/admin.js +36 -0
- data/lib/cortex_reaver/public/js/cookie.js +27 -0
- data/lib/cortex_reaver/public/js/jquery.js +32 -0
- data/lib/cortex_reaver/public/js/photo.js +33 -0
- data/lib/cortex_reaver/snippets/array.rb +7 -0
- data/lib/cortex_reaver/snippets/ramaze/dispatcher/file.rb +37 -0
- data/lib/cortex_reaver/support/attachments.rb +235 -0
- data/lib/cortex_reaver/support/cached_rendering.rb +79 -0
- data/lib/cortex_reaver/support/canonical.rb +107 -0
- data/lib/cortex_reaver/support/comments.rb +69 -0
- data/lib/cortex_reaver/support/pagination.rb +38 -0
- data/lib/cortex_reaver/support/renderer.rb +196 -0
- data/lib/cortex_reaver/support/sequenceable.rb +248 -0
- data/lib/cortex_reaver/support/tags.rb +108 -0
- data/lib/cortex_reaver/support/timestamps.rb +33 -0
- data/lib/cortex_reaver/version.rb +8 -0
- data/lib/cortex_reaver/view/adminbox.rhtml +56 -0
- data/lib/cortex_reaver/view/blank_layout.rhtml +46 -0
- data/lib/cortex_reaver/view/comments/comment.rhtml +34 -0
- data/lib/cortex_reaver/view/comments/form.rhtml +25 -0
- data/lib/cortex_reaver/view/comments/list.rhtml +5 -0
- data/lib/cortex_reaver/view/comments/post_form.rhtml +36 -0
- data/lib/cortex_reaver/view/config/form.rhtml +10 -0
- data/lib/cortex_reaver/view/error.rhtml +72 -0
- data/lib/cortex_reaver/view/journals/form.rhtml +12 -0
- data/lib/cortex_reaver/view/journals/journal.rhtml +39 -0
- data/lib/cortex_reaver/view/journals/list.rhtml +33 -0
- data/lib/cortex_reaver/view/journals/short.rhtml +3 -0
- data/lib/cortex_reaver/view/journals/show.rhtml +5 -0
- data/lib/cortex_reaver/view/pages/form.rhtml +12 -0
- data/lib/cortex_reaver/view/pages/list.rhtml +26 -0
- data/lib/cortex_reaver/view/pages/show.rhtml +12 -0
- data/lib/cortex_reaver/view/photographs/atom_fragment.rhtml +82 -0
- data/lib/cortex_reaver/view/photographs/form.rhtml +19 -0
- data/lib/cortex_reaver/view/photographs/grid.rhtml +36 -0
- data/lib/cortex_reaver/view/photographs/list.rhtml +9 -0
- data/lib/cortex_reaver/view/photographs/short.rhtml +3 -0
- data/lib/cortex_reaver/view/photographs/show.rhtml +114 -0
- data/lib/cortex_reaver/view/photographs/sidebar.rhtml +7 -0
- data/lib/cortex_reaver/view/projects/form.rhtml +13 -0
- data/lib/cortex_reaver/view/projects/list.rhtml +27 -0
- data/lib/cortex_reaver/view/projects/show.rhtml +38 -0
- data/lib/cortex_reaver/view/tags/form.rhtml +9 -0
- data/lib/cortex_reaver/view/tags/list.rhtml +28 -0
- data/lib/cortex_reaver/view/tags/show.rhtml +51 -0
- data/lib/cortex_reaver/view/text_layout.rhtml +78 -0
- data/lib/cortex_reaver/view/users/form.rhtml +16 -0
- data/lib/cortex_reaver/view/users/list.rhtml +38 -0
- data/lib/cortex_reaver/view/users/login.rhtml +13 -0
- data/lib/cortex_reaver/view/users/register.rhtml +13 -0
- data/lib/cortex_reaver/view/users/show.rhtml +37 -0
- data/lib/cortex_reaver/view/users/user.rhtml +35 -0
- data/lib/proto/cortex_reaver.yaml +28 -0
- metadata +285 -0
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
module CortexReaver
|
|
2
|
+
module Model
|
|
3
|
+
|
|
4
|
+
# On save, calls a special rendering method on configured attributes, and
|
|
5
|
+
# saves the results to their cache.
|
|
6
|
+
module CachedRendering
|
|
7
|
+
require 'ostruct'
|
|
8
|
+
|
|
9
|
+
def self.included(base)
|
|
10
|
+
base.class_eval do
|
|
11
|
+
# Before save, render all changed caching fields
|
|
12
|
+
before_save(:render_to_cache) do
|
|
13
|
+
# Get changed fields to render
|
|
14
|
+
if new?
|
|
15
|
+
changed = columns.map { |c| c.to_sym }
|
|
16
|
+
else
|
|
17
|
+
changed = changed_columns.map { |c| c.to_sym }
|
|
18
|
+
end
|
|
19
|
+
fields = render_fields.select do |k, v|
|
|
20
|
+
changed.include? k.to_sym
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
fields.each do |name, field|
|
|
24
|
+
# Render and cache
|
|
25
|
+
self[field.to] = self.send(field.with, self[name])
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# Refreshes all records with cached fields.
|
|
30
|
+
def self.refresh_render_caches
|
|
31
|
+
# TODO: inefficient, but Model.each breaks Sequel in validation
|
|
32
|
+
# "commands out of sync"
|
|
33
|
+
all.each do |record|
|
|
34
|
+
# Mark all caching columns as changed, so the before_save hook
|
|
35
|
+
# processes them.
|
|
36
|
+
record.skip_timestamp_update = true
|
|
37
|
+
render_fields.keys.each do |column|
|
|
38
|
+
record.changed_columns << column
|
|
39
|
+
end
|
|
40
|
+
record.save
|
|
41
|
+
end
|
|
42
|
+
nil
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# Assigns a field to cache
|
|
46
|
+
#
|
|
47
|
+
# render :body, :with => 'wikify', :to => 'cached_body'
|
|
48
|
+
#
|
|
49
|
+
# ... calls #wikify on the value of self.body, and stores the result
|
|
50
|
+
# in self.cached_body. :to defaults to the field name with _cache
|
|
51
|
+
# appended. :with defaults to :render.
|
|
52
|
+
def self.render(field, params = {})
|
|
53
|
+
# Assign parameters
|
|
54
|
+
params = {
|
|
55
|
+
:to => (field.to_s + '_cache').to_sym,
|
|
56
|
+
:with => :render
|
|
57
|
+
}.merge!(params)
|
|
58
|
+
|
|
59
|
+
# Store field
|
|
60
|
+
render_fields[field] = OpenStruct.new(params)
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def self.render_fields
|
|
64
|
+
@render_fields ||= {}
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# Default renderer
|
|
70
|
+
def render(value)
|
|
71
|
+
value
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def render_fields
|
|
75
|
+
self.class.render_fields
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
module CortexReaver
|
|
2
|
+
module Model
|
|
3
|
+
# Supports canonical, url-safe identifiers for records, inferred from other
|
|
4
|
+
# fields.
|
|
5
|
+
module Canonical
|
|
6
|
+
|
|
7
|
+
# The canonical name attribute
|
|
8
|
+
CANONICAL_NAME_ATTR = :name
|
|
9
|
+
# The attribute we infer the canonical name from, if not set.
|
|
10
|
+
CANONICAL_INFERENCE_ATTR = :title
|
|
11
|
+
|
|
12
|
+
def self.included(base)
|
|
13
|
+
base.class_eval do
|
|
14
|
+
# Canonical names which cannot be reserved.
|
|
15
|
+
def self.reserved_canonical_names
|
|
16
|
+
@reserved_canonical_names ||= []
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def self.reserved_canonical_names=(names)
|
|
20
|
+
@reserved_canonical_names = names
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# Canonicalize a string. Optionally, ignore conflicts with the record
|
|
24
|
+
# with id.
|
|
25
|
+
def self.canonicalize(string, id = nil)
|
|
26
|
+
# Lower case, remove special chars, and replace with hyphens.
|
|
27
|
+
proper = string.downcase.gsub(/[^a-z0-9_]/, '-').squeeze('-')[0..250].sub(/-$/, '')
|
|
28
|
+
|
|
29
|
+
# If proper is blank, just return it at this point.
|
|
30
|
+
if proper.blank?
|
|
31
|
+
return proper
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# Numeric suffix to append
|
|
35
|
+
suffix = nil
|
|
36
|
+
|
|
37
|
+
if proper != filter(:id => id).map(canonical_name_attr).first
|
|
38
|
+
# We don't already have this name.
|
|
39
|
+
|
|
40
|
+
similar = []
|
|
41
|
+
|
|
42
|
+
if filter(canonical_name_attr => proper).limit(1).count > 0
|
|
43
|
+
similar << proper
|
|
44
|
+
# This name already exists, and it's not ours!
|
|
45
|
+
similar += filter(canonical_name_attr.like(/^#{proper}\-[0-9]+$/)).map(canonical_name_attr)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# Check for reserved names
|
|
49
|
+
reserved_canonical_names.each do |name|
|
|
50
|
+
if name =~ /^#{proper}(-\d+)?$/
|
|
51
|
+
similar << name
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# Find possible conflicting names from actions on this model's controller.
|
|
56
|
+
# if self.respond_to? :url and controller = Ramaze::Controller.at(self.url)
|
|
57
|
+
# similar += controller.action_methods.select do |action|
|
|
58
|
+
# action =~ /^#{proper}(-\d+)?$/
|
|
59
|
+
# end
|
|
60
|
+
# end
|
|
61
|
+
|
|
62
|
+
# Extract numeric suffices
|
|
63
|
+
suffices = {}
|
|
64
|
+
similar.each do |name|
|
|
65
|
+
suffices[name[/\d$/].to_i] = true
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# Compute suffix
|
|
69
|
+
unless suffices.empty?
|
|
70
|
+
i = 1
|
|
71
|
+
while suffices.include? i
|
|
72
|
+
i += 1
|
|
73
|
+
end
|
|
74
|
+
suffix = i
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
if suffix
|
|
79
|
+
proper + '-' + suffix.to_s
|
|
80
|
+
else
|
|
81
|
+
proper
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
# Sets the attribute we infer the canonical name from to attr, or
|
|
86
|
+
# gets that attr if nil.
|
|
87
|
+
def self.canonical_inference_attr(attr = nil)
|
|
88
|
+
if attr
|
|
89
|
+
@canonical_inference_attr = attr.to_sym
|
|
90
|
+
else
|
|
91
|
+
@canonical_inference_attr || CANONICAL_INFERENCE_ATTR
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
# Sets the canonical name attribute to attr. Returns it if nil.
|
|
96
|
+
def self.canonical_name_attr(attr = nil)
|
|
97
|
+
if attr
|
|
98
|
+
@canonical_name_attr = attr.to_sym
|
|
99
|
+
else
|
|
100
|
+
@canonical_name_attr || CANONICAL_NAME_ATTR
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
end
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
module CortexReaver
|
|
2
|
+
module Model
|
|
3
|
+
# Support methods for comments on models
|
|
4
|
+
module Comments
|
|
5
|
+
def self.included(base)
|
|
6
|
+
base.class_eval do
|
|
7
|
+
# When we delete a model that has comments, remove the comments too.
|
|
8
|
+
before_delete(:drop_comments) do
|
|
9
|
+
comments = self.comments
|
|
10
|
+
remove_all_comments
|
|
11
|
+
comments.each do |comment|
|
|
12
|
+
comment.destroy
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# Refresh all comment counts
|
|
17
|
+
def self.refresh_comment_counts
|
|
18
|
+
all.each do |model|
|
|
19
|
+
model.refresh_comment_count
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# Recalculates the number of comments on this record (and all comments
|
|
26
|
+
# below it, recursively) and saves those values. Returns the comment
|
|
27
|
+
# count on this record.
|
|
28
|
+
def refresh_comment_count
|
|
29
|
+
count = 0
|
|
30
|
+
comments.each do |comment|
|
|
31
|
+
# Recalculate for sub-comments and sum.
|
|
32
|
+
count += comment.refresh_comment_count + 1
|
|
33
|
+
end
|
|
34
|
+
self[:comment_count] = count
|
|
35
|
+
self.skip_timestamp_update = true
|
|
36
|
+
|
|
37
|
+
# Save and return
|
|
38
|
+
self.save
|
|
39
|
+
self[:comment_count]
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# Returns the parent of a given comment. Caches, pass true to refresh.
|
|
43
|
+
def parent(refresh = false)
|
|
44
|
+
if refresh or @parent_cache.nil?
|
|
45
|
+
[:comment, :journal, :photograph, :project, :page].each do |p|
|
|
46
|
+
if self.respond_to?(p) and parent = self.send(p)
|
|
47
|
+
# We found an applicable parent.
|
|
48
|
+
@parent_cache = parent
|
|
49
|
+
return parent
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
# We didn't find any parent
|
|
53
|
+
nil
|
|
54
|
+
else
|
|
55
|
+
@parent_cache
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# Returns the top-level parent of a given comment.
|
|
60
|
+
def root_parent
|
|
61
|
+
if parent
|
|
62
|
+
parent.root_parent
|
|
63
|
+
else
|
|
64
|
+
self
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
module CortexReaver
|
|
2
|
+
module Model
|
|
3
|
+
# Defines class-level accessors for page size, order attribute, etc.
|
|
4
|
+
module Pagination
|
|
5
|
+
DEFAULT_SIZE = 16
|
|
6
|
+
DEFAULT_ORDER = 'created_on'
|
|
7
|
+
DEFAULT_REVERSE = false
|
|
8
|
+
|
|
9
|
+
def self.included(base)
|
|
10
|
+
base.class_eval do
|
|
11
|
+
class << self
|
|
12
|
+
attr_accessor :page_size, :page_order, :page_reverse
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
@page_size ||= CortexReaver::Model::Pagination::DEFAULT_SIZE
|
|
16
|
+
@page_order ||= CortexReaver::Model::Pagination::DEFAULT_ORDER
|
|
17
|
+
@page_reverse ||= CortexReaver::Model::Pagination::DEFAULT_REVERSE
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# Returns a paginated dataset at page number. Optionally, filters on
|
|
21
|
+
# dataset instead of the whole model.
|
|
22
|
+
def page(number, dataset = self.dataset)
|
|
23
|
+
if reverse
|
|
24
|
+
dataset = dataset.reverse
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
dataset.order(@order).paginate(number, @page_size)
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# Returns the page number for this model. Optionally, filters on dataset
|
|
32
|
+
# rather than the whole model.
|
|
33
|
+
def page_number(dataset = self.class.dataset)
|
|
34
|
+
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
module CortexReaver
|
|
2
|
+
module Model
|
|
3
|
+
# Some common rendering methods, wrapped up for your convenience. Use in your model
|
|
4
|
+
# with something like:
|
|
5
|
+
#
|
|
6
|
+
# render :body, :with => :render_comment
|
|
7
|
+
#
|
|
8
|
+
# See CortexReaver::Model::CachedRendering for more details.
|
|
9
|
+
module Renderer
|
|
10
|
+
require 'bluecloth'
|
|
11
|
+
require 'hpricot'
|
|
12
|
+
require 'syntax'
|
|
13
|
+
|
|
14
|
+
# Elements to allow in sanitized HTML.
|
|
15
|
+
ELEMENTS = [
|
|
16
|
+
'a', 'b', 'blockquote', 'br', 'code', 'dd', 'dl', 'dt', 'em', 'i', 'li',
|
|
17
|
+
'ol', 'p', 'pre', 'small', 'strike', 'strong', 'sub', 'sup', 'u', 'ul'
|
|
18
|
+
]
|
|
19
|
+
|
|
20
|
+
# Attributes to allow in sanitized HTML elements.
|
|
21
|
+
ATTRIBUTES = {
|
|
22
|
+
'a' => ['href', 'title'],
|
|
23
|
+
'pre' => ['class']
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
# Attributes to add to sanitized HTML elements.
|
|
27
|
+
ADD_ATTRIBUTES = {
|
|
28
|
+
'a' => {'rel' => 'nofollow'}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
# Attributes that should be checked for valid protocols.
|
|
32
|
+
PROTOCOL_ATTRIBUTES = {'a' => ['href']}
|
|
33
|
+
|
|
34
|
+
# Valid protocols.
|
|
35
|
+
PROTOCOLS = ['ftp', 'http', 'https', 'mailto']
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
# Renders plain text and html to html.
|
|
39
|
+
def bluecloth(text)
|
|
40
|
+
return text if text.nil?
|
|
41
|
+
|
|
42
|
+
BlueCloth::new(text).to_html
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# Replace <% and %> to prevent Erubis injection.
|
|
46
|
+
def erubis_filter(text)
|
|
47
|
+
return text if text.nil?
|
|
48
|
+
|
|
49
|
+
t = text.dup
|
|
50
|
+
t.gsub!('<%', '<%')
|
|
51
|
+
t.gsub!('%>', '%&rt;')
|
|
52
|
+
t
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# Macro substitutions
|
|
56
|
+
#
|
|
57
|
+
# Expands [[type:resource][name]] macros. Right now, resource is just an attachment.
|
|
58
|
+
# Included types are:
|
|
59
|
+
#
|
|
60
|
+
# url: returns the URL to an attachment
|
|
61
|
+
# image: returns an image tag
|
|
62
|
+
# link: returns a link to an attachment
|
|
63
|
+
#
|
|
64
|
+
# The default action is a link, so
|
|
65
|
+
#
|
|
66
|
+
# [[foo.jpg]] => <a href="/data/.../foo.jpg">foo.jpg</a>
|
|
67
|
+
def macro(text)
|
|
68
|
+
return text if text.nil?
|
|
69
|
+
|
|
70
|
+
copy = text.dup
|
|
71
|
+
|
|
72
|
+
# Links
|
|
73
|
+
#
|
|
74
|
+
# Example [[image:foo.png][name]]
|
|
75
|
+
# 1. the link type prefix image:
|
|
76
|
+
# 2. the link type, sans-colon image
|
|
77
|
+
# 3. the link itself foo.png
|
|
78
|
+
# 4. the second half of the link [name]
|
|
79
|
+
# 5. the name name
|
|
80
|
+
copy.gsub!(/\[\[(([^\]]+):)?([^\]]+)(\]\[([^\]]+))?\]\]/) do |match|
|
|
81
|
+
prefix = $2
|
|
82
|
+
path = $3
|
|
83
|
+
name = $5
|
|
84
|
+
|
|
85
|
+
# Find the link target
|
|
86
|
+
target = attachment(path)
|
|
87
|
+
|
|
88
|
+
if target.exists?
|
|
89
|
+
# Name of the link
|
|
90
|
+
name ||= path
|
|
91
|
+
|
|
92
|
+
# Create link to this target
|
|
93
|
+
case prefix
|
|
94
|
+
when 'image'
|
|
95
|
+
# Create an inline image
|
|
96
|
+
"<img src=\"#{target.public_path}\" alt=\"#{name.gsub('"', '"')}\" title=\"#{name.gsub('"', '"')}\" />"
|
|
97
|
+
when 'url'
|
|
98
|
+
# Create a URL
|
|
99
|
+
target.public_path
|
|
100
|
+
else
|
|
101
|
+
# Create a full link
|
|
102
|
+
"<a href=\"#{target.public_path}\">#{Rack::Utils.escape_html(name).gsub(/#([{@$]@?)/, '#\1')}</a>"
|
|
103
|
+
end
|
|
104
|
+
else
|
|
105
|
+
# Don't create a link
|
|
106
|
+
match
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
copy
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def syntax_highlight(text)
|
|
114
|
+
return text if text.nil?
|
|
115
|
+
|
|
116
|
+
h = Hpricot(text)
|
|
117
|
+
|
|
118
|
+
h.search('cr:code').replace do |code|
|
|
119
|
+
code[:lang]
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
# Stolen wholesale from Ryan's Thoth (http://github.com/rgrove/thoth/)
|
|
124
|
+
# Who adapted it from http://rid.onkulo.us/archives/14-sanitizing-html-with-ruby-and-hpricot
|
|
125
|
+
def sanitize_html(html)
|
|
126
|
+
return html if html.nil?
|
|
127
|
+
|
|
128
|
+
h = Hpricot(html)
|
|
129
|
+
|
|
130
|
+
h.search('*').each do |el|
|
|
131
|
+
if el.elem?
|
|
132
|
+
tag = el.name.downcase
|
|
133
|
+
|
|
134
|
+
if ELEMENTS.include?(tag)
|
|
135
|
+
if ATTRIBUTES.has_key?(tag)
|
|
136
|
+
# Delete any attribute that isn't in the whitelist for this
|
|
137
|
+
# particular element.
|
|
138
|
+
el.raw_attributes.delete_if do |key, val|
|
|
139
|
+
!ATTRIBUTES[tag].include?(key.downcase)
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
# Check applicable attributes for valid protocols.
|
|
143
|
+
if PROTOCOL_ATTRIBUTES.has_key?(tag)
|
|
144
|
+
el.raw_attributes.delete_if do |key, val|
|
|
145
|
+
PROTOCOL_ATTRIBUTES[tag].include?(key.downcase) &&
|
|
146
|
+
(!(val.downcase =~ /^([^:]+)\:/) || !PROTOCOLS.include?($1))
|
|
147
|
+
end
|
|
148
|
+
end
|
|
149
|
+
else
|
|
150
|
+
# Delete all attributes from elements with no whitelisted
|
|
151
|
+
# attributes.
|
|
152
|
+
el.raw_attributes = {}
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
# Add required attributes.
|
|
156
|
+
if ADD_ATTRIBUTES.has_key?(tag)
|
|
157
|
+
el.raw_attributes.merge!(ADD_ATTRIBUTES[tag])
|
|
158
|
+
end
|
|
159
|
+
else
|
|
160
|
+
# Delete any element that isn't in the whitelist.
|
|
161
|
+
el.parent.replace_child(el, el.children)
|
|
162
|
+
end
|
|
163
|
+
elsif el.comment?
|
|
164
|
+
# Delete all comments, since it's possible to make IE execute JS
|
|
165
|
+
# within conditional comments.
|
|
166
|
+
el.swap('')
|
|
167
|
+
end
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
h.to_s
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
# Default renderer
|
|
174
|
+
def render(text)
|
|
175
|
+
bluecloth(
|
|
176
|
+
macro(
|
|
177
|
+
erubis_filter(
|
|
178
|
+
text
|
|
179
|
+
)
|
|
180
|
+
)
|
|
181
|
+
) # (((Feeling) LISPish yet)?)
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
# Comments render
|
|
185
|
+
def render_comment(text)
|
|
186
|
+
bluecloth(
|
|
187
|
+
erubis_filter(
|
|
188
|
+
sanitize_html(
|
|
189
|
+
text
|
|
190
|
+
)
|
|
191
|
+
)
|
|
192
|
+
)
|
|
193
|
+
end
|
|
194
|
+
end
|
|
195
|
+
end
|
|
196
|
+
end
|