instiki 0.10.0 → 0.10.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|