instiki 0.10.0 → 0.10.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 +174 -165
- data/README +68 -68
- data/app/controllers/admin_controller.rb +94 -94
- data/app/controllers/application.rb +135 -131
- data/app/controllers/file_controller.rb +129 -129
- data/app/controllers/wiki_controller.rb +354 -354
- data/app/helpers/application_helper.rb +68 -68
- data/app/models/author.rb +3 -3
- data/app/models/chunks/category.rb +33 -33
- data/app/models/chunks/chunk.rb +86 -86
- data/app/models/chunks/engines.rb +61 -54
- data/app/models/chunks/include.rb +41 -41
- data/app/models/chunks/literal.rb +31 -31
- data/app/models/chunks/nowiki.rb +28 -28
- data/app/models/chunks/test.rb +18 -18
- data/app/models/chunks/uri.rb +182 -182
- data/app/models/chunks/wiki.rb +141 -141
- data/app/models/file_yard.rb +58 -58
- data/app/models/page.rb +112 -112
- data/app/models/page_lock.rb +22 -22
- data/app/models/page_set.rb +89 -89
- data/app/models/revision.rb +123 -123
- data/app/models/web.rb +182 -176
- data/app/models/wiki_content.rb +207 -207
- data/app/models/wiki_service.rb +233 -233
- data/app/models/wiki_words.rb +23 -23
- data/app/views/admin/create_system.rhtml +83 -83
- data/app/views/admin/create_web.rhtml +69 -69
- data/app/views/admin/edit_web.rhtml +137 -136
- data/app/views/file/file.rhtml +18 -18
- data/app/views/file/import.rhtml +22 -22
- data/app/views/layouts/default.rhtml +86 -85
- data/app/views/markdown_help.rhtml +12 -12
- data/app/views/mixed_help.rhtml +6 -6
- data/app/views/navigation.rhtml +30 -30
- data/app/views/rdoc_help.rhtml +12 -12
- data/app/views/textile_help.rhtml +24 -24
- data/app/views/wiki/authors.rhtml +11 -11
- data/app/views/wiki/edit.rhtml +39 -39
- data/app/views/wiki/export.rhtml +12 -12
- data/app/views/wiki/feeds.rhtml +14 -14
- data/app/views/wiki/list.rhtml +64 -64
- data/app/views/wiki/locked.rhtml +23 -23
- data/app/views/wiki/login.rhtml +14 -14
- data/app/views/wiki/new.rhtml +31 -31
- data/app/views/wiki/page.rhtml +115 -115
- data/app/views/wiki/print.rhtml +14 -14
- data/app/views/wiki/published.rhtml +9 -9
- data/app/views/wiki/recently_revised.rhtml +26 -26
- data/app/views/wiki/revision.rhtml +103 -103
- data/app/views/wiki/rollback.rhtml +36 -36
- data/app/views/wiki/rss_feed.rhtml +22 -22
- data/app/views/wiki/search.rhtml +38 -38
- data/app/views/wiki/tex.rhtml +22 -22
- data/app/views/wiki/tex_web.rhtml +34 -34
- data/app/views/wiki/web_list.rhtml +18 -18
- data/app/views/wiki_words_help.rhtml +9 -9
- data/config/environment.rb +82 -82
- data/config/environments/development.rb +5 -5
- data/config/environments/production.rb +4 -4
- data/config/environments/test.rb +17 -17
- data/config/routes.rb +18 -18
- data/lib/active_record_stub.rb +31 -31
- data/lib/bluecloth_tweaked.rb +1127 -0
- data/lib/diff.rb +444 -444
- data/lib/instiki_errors.rb +14 -14
- data/lib/rdocsupport.rb +151 -151
- data/lib/redcloth_for_tex.rb +736 -736
- data/natives/osx/desktop_launcher/AppDelegate.h +18 -18
- data/natives/osx/desktop_launcher/AppDelegate.mm +109 -109
- data/natives/osx/desktop_launcher/Credits.html +15 -15
- data/natives/osx/desktop_launcher/English.lproj/MainMenu.nib/classes.nib +12 -12
- data/natives/osx/desktop_launcher/English.lproj/MainMenu.nib/info.nib +24 -24
- data/natives/osx/desktop_launcher/Info.plist +12 -12
- data/natives/osx/desktop_launcher/Instiki.xcode/project.pbxproj +592 -592
- data/natives/osx/desktop_launcher/Instiki_Prefix.pch +7 -7
- data/natives/osx/desktop_launcher/MakeDMG.sh +9 -9
- data/natives/osx/desktop_launcher/main.mm +14 -14
- data/natives/osx/desktop_launcher/version.plist +16 -16
- data/public/404.html +5 -5
- data/public/500.html +5 -5
- data/public/dispatch.rb +9 -9
- data/public/javascripts/edit_web.js +52 -52
- data/public/javascripts/prototype.js +336 -336
- data/public/stylesheets/instiki.css +222 -222
- data/script/breakpointer +4 -4
- data/script/server +93 -93
- metadata +4 -3
data/app/models/web.rb
CHANGED
@@ -1,176 +1,182 @@
|
|
1
|
-
require 'cgi'
|
2
|
-
require 'page'
|
3
|
-
require 'page_set'
|
4
|
-
require 'wiki_words'
|
5
|
-
require 'zip/zip'
|
6
|
-
|
7
|
-
class Web
|
8
|
-
attr_accessor :name, :password, :
|
9
|
-
attr_accessor :additional_style, :
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
@
|
22
|
-
@
|
23
|
-
@
|
24
|
-
@
|
25
|
-
@
|
26
|
-
@
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
@
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
end
|
42
|
-
|
43
|
-
def
|
44
|
-
|
45
|
-
end
|
46
|
-
|
47
|
-
def
|
48
|
-
|
49
|
-
end
|
50
|
-
|
51
|
-
def
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
when :
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
when :
|
121
|
-
if
|
122
|
-
else %{<
|
123
|
-
|
124
|
-
if
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
end
|
130
|
-
end
|
131
|
-
|
132
|
-
def
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
end
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
1
|
+
require 'cgi'
|
2
|
+
require 'page'
|
3
|
+
require 'page_set'
|
4
|
+
require 'wiki_words'
|
5
|
+
require 'zip/zip'
|
6
|
+
|
7
|
+
class Web
|
8
|
+
attr_accessor :name, :password, :safe_mode, :pages
|
9
|
+
attr_accessor :additional_style, :allow_uploads, :published
|
10
|
+
attr_reader :address
|
11
|
+
|
12
|
+
# there are getters for all these attributes, too
|
13
|
+
attr_writer :markup, :color, :brackets_only, :count_pages, :max_upload_size
|
14
|
+
|
15
|
+
def initialize(parent_wiki, name, address, password = nil)
|
16
|
+
self.address = address
|
17
|
+
@wiki, @name, @password = parent_wiki, name, password
|
18
|
+
|
19
|
+
set_compatible_defaults
|
20
|
+
|
21
|
+
@pages = {}
|
22
|
+
@allow_uploads = true
|
23
|
+
@additional_style = nil
|
24
|
+
@published = false
|
25
|
+
@count_pages = false
|
26
|
+
@allow_uploads = true
|
27
|
+
end
|
28
|
+
|
29
|
+
# Explicitly sets value of some web attributes to defaults, unless they are already set
|
30
|
+
def set_compatible_defaults
|
31
|
+
@markup = markup()
|
32
|
+
@color = color()
|
33
|
+
@safe_mode = safe_mode()
|
34
|
+
@brackets_only = brackets_only()
|
35
|
+
@max_upload_size = max_upload_size()
|
36
|
+
@wiki = wiki
|
37
|
+
end
|
38
|
+
# All below getters know their default values. This is necessary to ensure compatibility with
|
39
|
+
# 0.9 storages, where they were not defined.
|
40
|
+
def brackets_only() @brackets_only || false end
|
41
|
+
def color() @color ||= '008B26' end
|
42
|
+
def count_pages() @count_pages || false end
|
43
|
+
def markup() @markup ||= :textile end
|
44
|
+
def max_upload_size() @max_upload_size || 100; end
|
45
|
+
def wiki() @wiki ||= WikiService.instance; end
|
46
|
+
|
47
|
+
def add_page(page)
|
48
|
+
@pages[page.name] = page
|
49
|
+
end
|
50
|
+
|
51
|
+
def address=(the_address)
|
52
|
+
if the_address != CGI.escape(the_address)
|
53
|
+
raise Instiki::ValidationError.new('Web name should contain only valid URI characters')
|
54
|
+
end
|
55
|
+
@address = the_address
|
56
|
+
end
|
57
|
+
|
58
|
+
def authors
|
59
|
+
select.authors
|
60
|
+
end
|
61
|
+
|
62
|
+
def categories
|
63
|
+
select.map { |page| page.categories }.flatten.uniq.sort
|
64
|
+
end
|
65
|
+
|
66
|
+
def has_page?(name)
|
67
|
+
pages[name]
|
68
|
+
end
|
69
|
+
|
70
|
+
def has_file?(name)
|
71
|
+
wiki.file_yard(self).has_file?(name)
|
72
|
+
end
|
73
|
+
|
74
|
+
def make_file_link(mode, name, text, base_url)
|
75
|
+
link = CGI.escape(name)
|
76
|
+
case mode
|
77
|
+
when :export
|
78
|
+
if has_file?(name) then "<a class=\"existingWikiWord\" href=\"#{link}.html\">#{text}</a>"
|
79
|
+
else "<span class=\"newWikiWord\">#{text}</span>" end
|
80
|
+
when :publish
|
81
|
+
if has_file?(name) then "<a class=\"existingWikiWord\" href=\"#{base_url}/published/#{link}\">#{text}</a>"
|
82
|
+
else "<span class=\"newWikiWord\">#{text}</span>" end
|
83
|
+
else
|
84
|
+
if has_file?(name)
|
85
|
+
"<a class=\"existingWikiWord\" href=\"#{base_url}/file/#{link}\">#{text}</a>"
|
86
|
+
else
|
87
|
+
"<span class=\"newWikiWord\">#{text}<a href=\"#{base_url}/file/#{link}\">?</a></span>"
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
# Create a link for the given page name and link text based
|
93
|
+
# on the render mode in options and whether the page exists
|
94
|
+
# in the this web.
|
95
|
+
# The links a relative, and will work only if displayed on another WikiPage.
|
96
|
+
# It should not be used in menus, templates and such - instead, use link_to_page helper
|
97
|
+
def make_link(name, text = nil, options = {})
|
98
|
+
text = CGI.escapeHTML(text || WikiWords.separate(name))
|
99
|
+
mode = options[:mode] || :show
|
100
|
+
base_url = options[:base_url] || '..'
|
101
|
+
link_type = options[:link_type] || :show
|
102
|
+
case link_type.to_sym
|
103
|
+
when :show
|
104
|
+
make_page_link(mode, name, text, base_url)
|
105
|
+
when :file
|
106
|
+
make_file_link(mode, name, text, base_url)
|
107
|
+
when :pic
|
108
|
+
make_pic_link(mode, name, text, base_url)
|
109
|
+
else
|
110
|
+
raise "Unknown link type: #{link_type}"
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
def make_page_link(mode, name, text, base_url)
|
115
|
+
link = CGI.escape(name)
|
116
|
+
case mode.to_sym
|
117
|
+
when :export
|
118
|
+
if has_page?(name) then %{<a class="existingWikiWord" href="#{link}.html">#{text}</a>}
|
119
|
+
else %{<span class="newWikiWord">#{text}</span>} end
|
120
|
+
when :publish
|
121
|
+
if has_page?(name) then %{<a class="existingWikiWord" href="#{base_url}/published/#{link}">#{text}</a>}
|
122
|
+
else %{<span class="newWikiWord">#{text}</span>} end
|
123
|
+
else
|
124
|
+
if has_page?(name)
|
125
|
+
%{<a class="existingWikiWord" href="#{base_url}/show/#{link}">#{text}</a>}
|
126
|
+
else
|
127
|
+
%{<span class="newWikiWord">#{text}<a href="#{base_url}/show/#{link}">?</a></span>}
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
def make_pic_link(mode, name, text, base_url)
|
133
|
+
link = CGI.escape(name)
|
134
|
+
case mode.to_sym
|
135
|
+
when :export
|
136
|
+
if has_file?(name) then %{<img alt="#{text}" src="#{link}" />}
|
137
|
+
else %{<img alt="#{text}" src="no image" />} end
|
138
|
+
when :publish
|
139
|
+
if has_file?(name) then %{<img alt="#{text}" src="#{link}" />}
|
140
|
+
else %{<span class="newWikiWord">#{text}</span>} end
|
141
|
+
else
|
142
|
+
if has_file?(name) then %{<img alt="#{text}" src="#{base_url}/pic/#{link}" />}
|
143
|
+
else %{<span class="newWikiWord">#{text}<a href="#{base_url}/pic/#{link}">?</a></span>} end
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
# Clears the display cache for all the pages with references to
|
148
|
+
def refresh_pages_with_references(page_name)
|
149
|
+
select.pages_that_reference(page_name).each { |page|
|
150
|
+
page.revisions.each { |revision| revision.clear_display_cache }
|
151
|
+
}
|
152
|
+
end
|
153
|
+
|
154
|
+
def refresh_revisions
|
155
|
+
select.each { |page| page.revisions.each { |revision| revision.clear_display_cache } }
|
156
|
+
end
|
157
|
+
|
158
|
+
def remove_pages(pages_to_be_removed)
|
159
|
+
pages.delete_if { |page_name, page| pages_to_be_removed.include?(page) }
|
160
|
+
end
|
161
|
+
|
162
|
+
def revised_on
|
163
|
+
select.most_recent_revision
|
164
|
+
end
|
165
|
+
|
166
|
+
def select(&condition)
|
167
|
+
PageSet.new(self, @pages.values, condition)
|
168
|
+
end
|
169
|
+
|
170
|
+
private
|
171
|
+
|
172
|
+
# Returns an array of all the wiki words in any current revision
|
173
|
+
def wiki_words
|
174
|
+
pages.values.inject([]) { |wiki_words, page| wiki_words << page.wiki_words }.flatten.uniq
|
175
|
+
end
|
176
|
+
|
177
|
+
# Returns an array of all the page names on this web
|
178
|
+
def page_names
|
179
|
+
pages.keys
|
180
|
+
end
|
181
|
+
|
182
|
+
end
|
data/app/models/wiki_content.rb
CHANGED
@@ -1,207 +1,207 @@
|
|
1
|
-
require 'cgi'
|
2
|
-
require 'chunks/engines'
|
3
|
-
require 'chunks/category'
|
4
|
-
require 'chunks/include'
|
5
|
-
require 'chunks/wiki'
|
6
|
-
require 'chunks/literal'
|
7
|
-
require 'chunks/uri'
|
8
|
-
require 'chunks/nowiki'
|
9
|
-
|
10
|
-
# Wiki content is just a string that can process itself with a chain of
|
11
|
-
# actions. The actions can modify wiki content so that certain parts of
|
12
|
-
# it are protected from being rendered by later actions.
|
13
|
-
#
|
14
|
-
# When wiki content is rendered, it can be interrogated to find out
|
15
|
-
# which chunks were rendered. This means things like categories, wiki
|
16
|
-
# links, can be determined.
|
17
|
-
#
|
18
|
-
# Exactly how wiki content is rendered is determined by a number of
|
19
|
-
# settings that are optionally passed in to a constructor. The current
|
20
|
-
# options are:
|
21
|
-
# * :engine
|
22
|
-
# => The structural markup engine to use (Textile, Markdown, RDoc)
|
23
|
-
# * :engine_opts
|
24
|
-
# => A list of options to pass to the markup engines (safe modes, etc)
|
25
|
-
# * :pre_engine_actions
|
26
|
-
# => A list of render actions or chunks to be processed before the
|
27
|
-
# markup engine is applied. By default this is:
|
28
|
-
# Category, Include, URIChunk, WikiChunk::Link, WikiChunk::Word
|
29
|
-
# * :post_engine_actions
|
30
|
-
# => A list of render actions or chunks to apply after the markup
|
31
|
-
# engine. By default these are:
|
32
|
-
# Literal::Pre, Literal::Tags
|
33
|
-
# * :mode
|
34
|
-
# => How should the content be rendered? For normal display (show),
|
35
|
-
# publishing (:publish) or export (:export)?
|
36
|
-
#
|
37
|
-
# AUTHOR: Mark Reid <mark @ threewordslong . com>
|
38
|
-
# CREATED: 15th May 2004
|
39
|
-
# UPDATED: 22nd May 2004
|
40
|
-
|
41
|
-
module ChunkManager
|
42
|
-
attr_reader :chunks_by_type, :chunks_by_id, :chunks, :chunk_id
|
43
|
-
|
44
|
-
ACTIVE_CHUNKS = [ NoWiki, Category, WikiChunk::Link, URIChunk, LocalURIChunk,
|
45
|
-
WikiChunk::Word ]
|
46
|
-
|
47
|
-
HIDE_CHUNKS = [ Literal::Pre, Literal::Tags ]
|
48
|
-
|
49
|
-
MASK_RE = {
|
50
|
-
ACTIVE_CHUNKS => Chunk::Abstract.mask_re(ACTIVE_CHUNKS),
|
51
|
-
HIDE_CHUNKS => Chunk::Abstract.mask_re(HIDE_CHUNKS)
|
52
|
-
}
|
53
|
-
|
54
|
-
def init_chunk_manager
|
55
|
-
@chunks_by_type = Hash.new
|
56
|
-
Chunk::Abstract::derivatives.each{|chunk_type|
|
57
|
-
@chunks_by_type[chunk_type] = Array.new
|
58
|
-
}
|
59
|
-
@chunks_by_id = Hash.new
|
60
|
-
@chunks = []
|
61
|
-
@chunk_id = 0
|
62
|
-
end
|
63
|
-
|
64
|
-
def add_chunk(c)
|
65
|
-
@chunks_by_type[c.class] << c
|
66
|
-
@chunks_by_id[c.id] = c
|
67
|
-
@chunks << c
|
68
|
-
@chunk_id += 1
|
69
|
-
end
|
70
|
-
|
71
|
-
def delete_chunk(c)
|
72
|
-
@chunks_by_type[c.class].delete(c)
|
73
|
-
@chunks_by_id.delete(c.id)
|
74
|
-
@chunks.delete(c)
|
75
|
-
end
|
76
|
-
|
77
|
-
def merge_chunks(other)
|
78
|
-
other.chunks.each{|c| add_chunk(c)}
|
79
|
-
end
|
80
|
-
|
81
|
-
def scan_chunkid(text)
|
82
|
-
text.scan(MASK_RE[ACTIVE_CHUNKS]){|a| yield a[0] }
|
83
|
-
end
|
84
|
-
|
85
|
-
def find_chunks(chunk_type)
|
86
|
-
@chunks.select { |chunk| chunk.kind_of?(chunk_type) and chunk.rendered? }
|
87
|
-
end
|
88
|
-
|
89
|
-
# for testing and WikiContentStub; we need a page_id even if we have no page
|
90
|
-
def page_id
|
91
|
-
0
|
92
|
-
end
|
93
|
-
|
94
|
-
end
|
95
|
-
|
96
|
-
# A simplified version of WikiContent. Useful to avoid recursion problems in
|
97
|
-
# WikiContent.new
|
98
|
-
class WikiContentStub < String
|
99
|
-
attr_reader :options
|
100
|
-
include ChunkManager
|
101
|
-
def initialize(content, options)
|
102
|
-
super(content)
|
103
|
-
@options = options
|
104
|
-
init_chunk_manager
|
105
|
-
end
|
106
|
-
|
107
|
-
# Detects the mask strings contained in the text of chunks of type chunk_types
|
108
|
-
# and yields the corresponding chunk ids
|
109
|
-
# example: content = "chunk123categorychunk <pre>chunk456categorychunk</pre>"
|
110
|
-
# inside_chunks(Literal::Pre) ==> yield 456
|
111
|
-
def inside_chunks(chunk_types)
|
112
|
-
chunk_types.each{|chunk_type| chunk_type.apply_to(self) }
|
113
|
-
|
114
|
-
chunk_types.each{|chunk_type| @chunks_by_type[chunk_type].each{|hide_chunk|
|
115
|
-
scan_chunkid(hide_chunk.text){|id| yield id }
|
116
|
-
}
|
117
|
-
}
|
118
|
-
end
|
119
|
-
end
|
120
|
-
|
121
|
-
class WikiContent < String
|
122
|
-
|
123
|
-
include ChunkManager
|
124
|
-
|
125
|
-
DEFAULT_OPTS = {
|
126
|
-
:active_chunks => ACTIVE_CHUNKS,
|
127
|
-
:engine => Engines::Textile,
|
128
|
-
:engine_opts => [],
|
129
|
-
:mode => :show
|
130
|
-
}.freeze
|
131
|
-
|
132
|
-
attr_reader :web, :options, :revision, :not_rendered, :pre_rendered
|
133
|
-
|
134
|
-
# Create a new wiki content string from the given one.
|
135
|
-
# The options are explained at the top of this file.
|
136
|
-
def initialize(revision, options = {})
|
137
|
-
@revision = revision
|
138
|
-
@web = @revision.page.web
|
139
|
-
|
140
|
-
@options = DEFAULT_OPTS.dup.merge(options)
|
141
|
-
@options[:engine] = Engines::MAP[@web.markup]
|
142
|
-
@options[:engine_opts] = [:filter_html, :filter_styles] if @web.safe_mode
|
143
|
-
@options[:active_chunks] = (ACTIVE_CHUNKS - [WikiChunk::Word] ) if @web.brackets_only
|
144
|
-
|
145
|
-
super(@revision.content)
|
146
|
-
init_chunk_manager
|
147
|
-
build_chunks
|
148
|
-
@not_rendered = String.new(self)
|
149
|
-
end
|
150
|
-
|
151
|
-
# Call @web.page_link using current options.
|
152
|
-
def page_link(name, text, link_type)
|
153
|
-
@options[:link_type] = (link_type || :show)
|
154
|
-
@web.make_link(name, text, @options)
|
155
|
-
end
|
156
|
-
|
157
|
-
def build_chunks
|
158
|
-
# create and mask Includes and "active_chunks" chunks
|
159
|
-
Include.apply_to(self)
|
160
|
-
@options[:active_chunks].each{|chunk_type| chunk_type.apply_to(self)}
|
161
|
-
|
162
|
-
# Handle hiding contexts like "pre" and "code" etc..
|
163
|
-
# The markup (textile, rdoc etc) can produce such contexts with its own syntax.
|
164
|
-
# To reveal them, we work on a copy of the content.
|
165
|
-
# The copy is rendered and used to detect the chunks that are inside protecting context
|
166
|
-
# These chunks are reverted on the original content string.
|
167
|
-
|
168
|
-
copy = WikiContentStub.new(self, @options)
|
169
|
-
@options[:engine].apply_to(copy)
|
170
|
-
|
171
|
-
copy.inside_chunks(HIDE_CHUNKS) do |id|
|
172
|
-
@chunks_by_id[id].revert
|
173
|
-
end
|
174
|
-
end
|
175
|
-
|
176
|
-
def pre_render!
|
177
|
-
unless @pre_rendered
|
178
|
-
@chunks_by_type[Include].each{|chunk| chunk.unmask }
|
179
|
-
@pre_rendered = String.new(self)
|
180
|
-
end
|
181
|
-
@pre_rendered
|
182
|
-
end
|
183
|
-
|
184
|
-
def render!
|
185
|
-
pre_render!
|
186
|
-
@options[:engine].apply_to(self)
|
187
|
-
# unmask in one go. $~[1] is the chunk id
|
188
|
-
gsub!(MASK_RE[ACTIVE_CHUNKS]){
|
189
|
-
if chunk = @chunks_by_id[$~[1]]
|
190
|
-
chunk.unmask_text
|
191
|
-
# if we match a chunkmask that existed in the original content string
|
192
|
-
# just keep it as it is
|
193
|
-
else
|
194
|
-
$~[0]
|
195
|
-
end}
|
196
|
-
self
|
197
|
-
end
|
198
|
-
|
199
|
-
def page_name
|
200
|
-
@revision.page.name
|
201
|
-
end
|
202
|
-
|
203
|
-
def page_id
|
204
|
-
@revision.page.id
|
205
|
-
end
|
206
|
-
|
207
|
-
end
|
1
|
+
require 'cgi'
|
2
|
+
require 'chunks/engines'
|
3
|
+
require 'chunks/category'
|
4
|
+
require 'chunks/include'
|
5
|
+
require 'chunks/wiki'
|
6
|
+
require 'chunks/literal'
|
7
|
+
require 'chunks/uri'
|
8
|
+
require 'chunks/nowiki'
|
9
|
+
|
10
|
+
# Wiki content is just a string that can process itself with a chain of
|
11
|
+
# actions. The actions can modify wiki content so that certain parts of
|
12
|
+
# it are protected from being rendered by later actions.
|
13
|
+
#
|
14
|
+
# When wiki content is rendered, it can be interrogated to find out
|
15
|
+
# which chunks were rendered. This means things like categories, wiki
|
16
|
+
# links, can be determined.
|
17
|
+
#
|
18
|
+
# Exactly how wiki content is rendered is determined by a number of
|
19
|
+
# settings that are optionally passed in to a constructor. The current
|
20
|
+
# options are:
|
21
|
+
# * :engine
|
22
|
+
# => The structural markup engine to use (Textile, Markdown, RDoc)
|
23
|
+
# * :engine_opts
|
24
|
+
# => A list of options to pass to the markup engines (safe modes, etc)
|
25
|
+
# * :pre_engine_actions
|
26
|
+
# => A list of render actions or chunks to be processed before the
|
27
|
+
# markup engine is applied. By default this is:
|
28
|
+
# Category, Include, URIChunk, WikiChunk::Link, WikiChunk::Word
|
29
|
+
# * :post_engine_actions
|
30
|
+
# => A list of render actions or chunks to apply after the markup
|
31
|
+
# engine. By default these are:
|
32
|
+
# Literal::Pre, Literal::Tags
|
33
|
+
# * :mode
|
34
|
+
# => How should the content be rendered? For normal display (show),
|
35
|
+
# publishing (:publish) or export (:export)?
|
36
|
+
#
|
37
|
+
# AUTHOR: Mark Reid <mark @ threewordslong . com>
|
38
|
+
# CREATED: 15th May 2004
|
39
|
+
# UPDATED: 22nd May 2004
|
40
|
+
|
41
|
+
module ChunkManager
|
42
|
+
attr_reader :chunks_by_type, :chunks_by_id, :chunks, :chunk_id
|
43
|
+
|
44
|
+
ACTIVE_CHUNKS = [ NoWiki, Category, WikiChunk::Link, URIChunk, LocalURIChunk,
|
45
|
+
WikiChunk::Word ]
|
46
|
+
|
47
|
+
HIDE_CHUNKS = [ Literal::Pre, Literal::Tags ]
|
48
|
+
|
49
|
+
MASK_RE = {
|
50
|
+
ACTIVE_CHUNKS => Chunk::Abstract.mask_re(ACTIVE_CHUNKS),
|
51
|
+
HIDE_CHUNKS => Chunk::Abstract.mask_re(HIDE_CHUNKS)
|
52
|
+
}
|
53
|
+
|
54
|
+
def init_chunk_manager
|
55
|
+
@chunks_by_type = Hash.new
|
56
|
+
Chunk::Abstract::derivatives.each{|chunk_type|
|
57
|
+
@chunks_by_type[chunk_type] = Array.new
|
58
|
+
}
|
59
|
+
@chunks_by_id = Hash.new
|
60
|
+
@chunks = []
|
61
|
+
@chunk_id = 0
|
62
|
+
end
|
63
|
+
|
64
|
+
def add_chunk(c)
|
65
|
+
@chunks_by_type[c.class] << c
|
66
|
+
@chunks_by_id[c.id] = c
|
67
|
+
@chunks << c
|
68
|
+
@chunk_id += 1
|
69
|
+
end
|
70
|
+
|
71
|
+
def delete_chunk(c)
|
72
|
+
@chunks_by_type[c.class].delete(c)
|
73
|
+
@chunks_by_id.delete(c.id)
|
74
|
+
@chunks.delete(c)
|
75
|
+
end
|
76
|
+
|
77
|
+
def merge_chunks(other)
|
78
|
+
other.chunks.each{|c| add_chunk(c)}
|
79
|
+
end
|
80
|
+
|
81
|
+
def scan_chunkid(text)
|
82
|
+
text.scan(MASK_RE[ACTIVE_CHUNKS]){|a| yield a[0] }
|
83
|
+
end
|
84
|
+
|
85
|
+
def find_chunks(chunk_type)
|
86
|
+
@chunks.select { |chunk| chunk.kind_of?(chunk_type) and chunk.rendered? }
|
87
|
+
end
|
88
|
+
|
89
|
+
# for testing and WikiContentStub; we need a page_id even if we have no page
|
90
|
+
def page_id
|
91
|
+
0
|
92
|
+
end
|
93
|
+
|
94
|
+
end
|
95
|
+
|
96
|
+
# A simplified version of WikiContent. Useful to avoid recursion problems in
|
97
|
+
# WikiContent.new
|
98
|
+
class WikiContentStub < String
|
99
|
+
attr_reader :options
|
100
|
+
include ChunkManager
|
101
|
+
def initialize(content, options)
|
102
|
+
super(content)
|
103
|
+
@options = options
|
104
|
+
init_chunk_manager
|
105
|
+
end
|
106
|
+
|
107
|
+
# Detects the mask strings contained in the text of chunks of type chunk_types
|
108
|
+
# and yields the corresponding chunk ids
|
109
|
+
# example: content = "chunk123categorychunk <pre>chunk456categorychunk</pre>"
|
110
|
+
# inside_chunks(Literal::Pre) ==> yield 456
|
111
|
+
def inside_chunks(chunk_types)
|
112
|
+
chunk_types.each{|chunk_type| chunk_type.apply_to(self) }
|
113
|
+
|
114
|
+
chunk_types.each{|chunk_type| @chunks_by_type[chunk_type].each{|hide_chunk|
|
115
|
+
scan_chunkid(hide_chunk.text){|id| yield id }
|
116
|
+
}
|
117
|
+
}
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
class WikiContent < String
|
122
|
+
|
123
|
+
include ChunkManager
|
124
|
+
|
125
|
+
DEFAULT_OPTS = {
|
126
|
+
:active_chunks => ACTIVE_CHUNKS,
|
127
|
+
:engine => Engines::Textile,
|
128
|
+
:engine_opts => [],
|
129
|
+
:mode => :show
|
130
|
+
}.freeze
|
131
|
+
|
132
|
+
attr_reader :web, :options, :revision, :not_rendered, :pre_rendered
|
133
|
+
|
134
|
+
# Create a new wiki content string from the given one.
|
135
|
+
# The options are explained at the top of this file.
|
136
|
+
def initialize(revision, options = {})
|
137
|
+
@revision = revision
|
138
|
+
@web = @revision.page.web
|
139
|
+
|
140
|
+
@options = DEFAULT_OPTS.dup.merge(options)
|
141
|
+
@options[:engine] = Engines::MAP[@web.markup]
|
142
|
+
@options[:engine_opts] = [:filter_html, :filter_styles] if @web.safe_mode
|
143
|
+
@options[:active_chunks] = (ACTIVE_CHUNKS - [WikiChunk::Word] ) if @web.brackets_only
|
144
|
+
|
145
|
+
super(@revision.content)
|
146
|
+
init_chunk_manager
|
147
|
+
build_chunks
|
148
|
+
@not_rendered = String.new(self)
|
149
|
+
end
|
150
|
+
|
151
|
+
# Call @web.page_link using current options.
|
152
|
+
def page_link(name, text, link_type)
|
153
|
+
@options[:link_type] = (link_type || :show)
|
154
|
+
@web.make_link(name, text, @options)
|
155
|
+
end
|
156
|
+
|
157
|
+
def build_chunks
|
158
|
+
# create and mask Includes and "active_chunks" chunks
|
159
|
+
Include.apply_to(self)
|
160
|
+
@options[:active_chunks].each{|chunk_type| chunk_type.apply_to(self)}
|
161
|
+
|
162
|
+
# Handle hiding contexts like "pre" and "code" etc..
|
163
|
+
# The markup (textile, rdoc etc) can produce such contexts with its own syntax.
|
164
|
+
# To reveal them, we work on a copy of the content.
|
165
|
+
# The copy is rendered and used to detect the chunks that are inside protecting context
|
166
|
+
# These chunks are reverted on the original content string.
|
167
|
+
|
168
|
+
copy = WikiContentStub.new(self, @options)
|
169
|
+
@options[:engine].apply_to(copy)
|
170
|
+
|
171
|
+
copy.inside_chunks(HIDE_CHUNKS) do |id|
|
172
|
+
@chunks_by_id[id].revert
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
def pre_render!
|
177
|
+
unless @pre_rendered
|
178
|
+
@chunks_by_type[Include].each{|chunk| chunk.unmask }
|
179
|
+
@pre_rendered = String.new(self)
|
180
|
+
end
|
181
|
+
@pre_rendered
|
182
|
+
end
|
183
|
+
|
184
|
+
def render!
|
185
|
+
pre_render!
|
186
|
+
@options[:engine].apply_to(self)
|
187
|
+
# unmask in one go. $~[1] is the chunk id
|
188
|
+
gsub!(MASK_RE[ACTIVE_CHUNKS]){
|
189
|
+
if chunk = @chunks_by_id[$~[1]]
|
190
|
+
chunk.unmask_text
|
191
|
+
# if we match a chunkmask that existed in the original content string
|
192
|
+
# just keep it as it is
|
193
|
+
else
|
194
|
+
$~[0]
|
195
|
+
end}
|
196
|
+
self
|
197
|
+
end
|
198
|
+
|
199
|
+
def page_name
|
200
|
+
@revision.page.name
|
201
|
+
end
|
202
|
+
|
203
|
+
def page_id
|
204
|
+
@revision.page.id
|
205
|
+
end
|
206
|
+
|
207
|
+
end
|