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.
Files changed (88) hide show
  1. data/CHANGELOG +174 -165
  2. data/README +68 -68
  3. data/app/controllers/admin_controller.rb +94 -94
  4. data/app/controllers/application.rb +135 -131
  5. data/app/controllers/file_controller.rb +129 -129
  6. data/app/controllers/wiki_controller.rb +354 -354
  7. data/app/helpers/application_helper.rb +68 -68
  8. data/app/models/author.rb +3 -3
  9. data/app/models/chunks/category.rb +33 -33
  10. data/app/models/chunks/chunk.rb +86 -86
  11. data/app/models/chunks/engines.rb +61 -54
  12. data/app/models/chunks/include.rb +41 -41
  13. data/app/models/chunks/literal.rb +31 -31
  14. data/app/models/chunks/nowiki.rb +28 -28
  15. data/app/models/chunks/test.rb +18 -18
  16. data/app/models/chunks/uri.rb +182 -182
  17. data/app/models/chunks/wiki.rb +141 -141
  18. data/app/models/file_yard.rb +58 -58
  19. data/app/models/page.rb +112 -112
  20. data/app/models/page_lock.rb +22 -22
  21. data/app/models/page_set.rb +89 -89
  22. data/app/models/revision.rb +123 -123
  23. data/app/models/web.rb +182 -176
  24. data/app/models/wiki_content.rb +207 -207
  25. data/app/models/wiki_service.rb +233 -233
  26. data/app/models/wiki_words.rb +23 -23
  27. data/app/views/admin/create_system.rhtml +83 -83
  28. data/app/views/admin/create_web.rhtml +69 -69
  29. data/app/views/admin/edit_web.rhtml +137 -136
  30. data/app/views/file/file.rhtml +18 -18
  31. data/app/views/file/import.rhtml +22 -22
  32. data/app/views/layouts/default.rhtml +86 -85
  33. data/app/views/markdown_help.rhtml +12 -12
  34. data/app/views/mixed_help.rhtml +6 -6
  35. data/app/views/navigation.rhtml +30 -30
  36. data/app/views/rdoc_help.rhtml +12 -12
  37. data/app/views/textile_help.rhtml +24 -24
  38. data/app/views/wiki/authors.rhtml +11 -11
  39. data/app/views/wiki/edit.rhtml +39 -39
  40. data/app/views/wiki/export.rhtml +12 -12
  41. data/app/views/wiki/feeds.rhtml +14 -14
  42. data/app/views/wiki/list.rhtml +64 -64
  43. data/app/views/wiki/locked.rhtml +23 -23
  44. data/app/views/wiki/login.rhtml +14 -14
  45. data/app/views/wiki/new.rhtml +31 -31
  46. data/app/views/wiki/page.rhtml +115 -115
  47. data/app/views/wiki/print.rhtml +14 -14
  48. data/app/views/wiki/published.rhtml +9 -9
  49. data/app/views/wiki/recently_revised.rhtml +26 -26
  50. data/app/views/wiki/revision.rhtml +103 -103
  51. data/app/views/wiki/rollback.rhtml +36 -36
  52. data/app/views/wiki/rss_feed.rhtml +22 -22
  53. data/app/views/wiki/search.rhtml +38 -38
  54. data/app/views/wiki/tex.rhtml +22 -22
  55. data/app/views/wiki/tex_web.rhtml +34 -34
  56. data/app/views/wiki/web_list.rhtml +18 -18
  57. data/app/views/wiki_words_help.rhtml +9 -9
  58. data/config/environment.rb +82 -82
  59. data/config/environments/development.rb +5 -5
  60. data/config/environments/production.rb +4 -4
  61. data/config/environments/test.rb +17 -17
  62. data/config/routes.rb +18 -18
  63. data/lib/active_record_stub.rb +31 -31
  64. data/lib/bluecloth_tweaked.rb +1127 -0
  65. data/lib/diff.rb +444 -444
  66. data/lib/instiki_errors.rb +14 -14
  67. data/lib/rdocsupport.rb +151 -151
  68. data/lib/redcloth_for_tex.rb +736 -736
  69. data/natives/osx/desktop_launcher/AppDelegate.h +18 -18
  70. data/natives/osx/desktop_launcher/AppDelegate.mm +109 -109
  71. data/natives/osx/desktop_launcher/Credits.html +15 -15
  72. data/natives/osx/desktop_launcher/English.lproj/MainMenu.nib/classes.nib +12 -12
  73. data/natives/osx/desktop_launcher/English.lproj/MainMenu.nib/info.nib +24 -24
  74. data/natives/osx/desktop_launcher/Info.plist +12 -12
  75. data/natives/osx/desktop_launcher/Instiki.xcode/project.pbxproj +592 -592
  76. data/natives/osx/desktop_launcher/Instiki_Prefix.pch +7 -7
  77. data/natives/osx/desktop_launcher/MakeDMG.sh +9 -9
  78. data/natives/osx/desktop_launcher/main.mm +14 -14
  79. data/natives/osx/desktop_launcher/version.plist +16 -16
  80. data/public/404.html +5 -5
  81. data/public/500.html +5 -5
  82. data/public/dispatch.rb +9 -9
  83. data/public/javascripts/edit_web.js +52 -52
  84. data/public/javascripts/prototype.js +336 -336
  85. data/public/stylesheets/instiki.css +222 -222
  86. data/script/breakpointer +4 -4
  87. data/script/server +93 -93
  88. 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, :markup, :color, :safe_mode, :pages
9
- attr_accessor :additional_style, :published, :brackets_only, :count_pages, :allow_uploads
10
- attr_accessor :max_upload_size
11
-
12
- attr_reader :address
13
-
14
- def initialize(parent_wiki, name, address, password = nil)
15
- self.address = address
16
- @wiki, @name, @password = parent_wiki, name, password
17
-
18
- # default values
19
- @markup = :textile
20
- @color = '008B26'
21
- @safe_mode = false
22
- @pages = {}
23
- @allow_uploads = true
24
- @additional_style = nil
25
- @published = false
26
- @brackets_only = false
27
- @count_pages = false
28
- @allow_uploads = true
29
- @max_upload_size = 100
30
- end
31
-
32
- def add_page(page)
33
- @pages[page.name] = page
34
- end
35
-
36
- def address=(the_address)
37
- if the_address != CGI.escape(the_address)
38
- raise Instiki::ValidationError.new('Web name should contain only valid URI characters')
39
- end
40
- @address = the_address
41
- end
42
-
43
- def authors
44
- select.authors
45
- end
46
-
47
- def categories
48
- select.map { |page| page.categories }.flatten.uniq.sort
49
- end
50
-
51
- def has_page?(name)
52
- pages[name]
53
- end
54
-
55
- def has_file?(name)
56
- wiki.file_yard(self).has_file?(name)
57
- end
58
-
59
- def make_file_link(mode, name, text, base_url)
60
- link = CGI.escape(name)
61
- case mode
62
- when :export
63
- if has_file?(name) then "<a class=\"existingWikiWord\" href=\"#{link}.html\">#{text}</a>"
64
- else "<span class=\"newWikiWord\">#{text}</span>" end
65
- when :publish
66
- if has_file?(name) then "<a class=\"existingWikiWord\" href=\"#{base_url}/published/#{link}\">#{text}</a>"
67
- else "<span class=\"newWikiWord\">#{text}</span>" end
68
- else
69
- if has_file?(name)
70
- "<a class=\"existingWikiWord\" href=\"#{base_url}/file/#{link}\">#{text}</a>"
71
- else
72
- "<span class=\"newWikiWord\">#{text}<a href=\"#{base_url}/file/#{link}\">?</a></span>"
73
- end
74
- end
75
- end
76
-
77
- # Create a link for the given page name and link text based
78
- # on the render mode in options and whether the page exists
79
- # in the this web.
80
- # The links a relative, and will work only if displayed on another WikiPage.
81
- # It should not be used in menus, templates and such - instead, use link_to_page helper
82
- def make_link(name, text = nil, options = {})
83
- text = CGI.escapeHTML(text || WikiWords.separate(name))
84
- mode = options[:mode] || :show
85
- base_url = options[:base_url] || '..'
86
- link_type = options[:link_type] || :show
87
- case link_type.to_sym
88
- when :show
89
- make_page_link(mode, name, text, base_url)
90
- when :file
91
- make_file_link(mode, name, text, base_url)
92
- when :pic
93
- make_pic_link(mode, name, text, base_url)
94
- else
95
- raise "Unknown link type: #{link_type}"
96
- end
97
- end
98
-
99
- def make_page_link(mode, name, text, base_url)
100
- link = CGI.escape(name)
101
- case mode.to_sym
102
- when :export
103
- if has_page?(name) then %{<a class="existingWikiWord" href="#{link}.html">#{text}</a>}
104
- else %{<span class="newWikiWord">#{text}</span>} end
105
- when :publish
106
- if has_page?(name) then %{<a class="existingWikiWord" href="#{base_url}/published/#{link}">#{text}</a>}
107
- else %{<span class="newWikiWord">#{text}</span>} end
108
- else
109
- if has_page?(name)
110
- %{<a class="existingWikiWord" href="#{base_url}/show/#{link}">#{text}</a>}
111
- else
112
- %{<span class="newWikiWord">#{text}<a href="#{base_url}/show/#{link}">?</a></span>}
113
- end
114
- end
115
- end
116
-
117
- def make_pic_link(mode, name, text, base_url)
118
- link = CGI.escape(name)
119
- case mode.to_sym
120
- when :export
121
- if has_file?(name) then %{<img alt="#{text}" src="#{link}" />}
122
- else %{<img alt="#{text}" src="no image" />} end
123
- when :publish
124
- if has_file?(name) then %{<img alt="#{text}" src="#{link}" />}
125
- else %{<span class="newWikiWord">#{text}</span>} end
126
- else
127
- if has_file?(name) then %{<img alt="#{text}" src="#{base_url}/pic/#{link}" />}
128
- else %{<span class="newWikiWord">#{text}<a href="#{base_url}/pic/#{link}">?</a></span>} end
129
- end
130
- end
131
-
132
- def max_upload_size
133
- @max_upload_size || 100
134
- end
135
-
136
- # Clears the display cache for all the pages with references to
137
- def refresh_pages_with_references(page_name)
138
- select.pages_that_reference(page_name).each { |page|
139
- page.revisions.each { |revision| revision.clear_display_cache }
140
- }
141
- end
142
-
143
- def refresh_revisions
144
- select.each { |page| page.revisions.each { |revision| revision.clear_display_cache } }
145
- end
146
-
147
- def remove_pages(pages_to_be_removed)
148
- pages.delete_if { |page_name, page| pages_to_be_removed.include?(page) }
149
- end
150
-
151
- def revised_on
152
- select.most_recent_revision
153
- end
154
-
155
- def select(&condition)
156
- PageSet.new(self, @pages.values, condition)
157
- end
158
-
159
- # This ensures compatibility with 0.9 storages
160
- def wiki
161
- @wiki ||= WikiService.instance
162
- end
163
-
164
- private
165
-
166
- # Returns an array of all the wiki words in any current revision
167
- def wiki_words
168
- pages.values.inject([]) { |wiki_words, page| wiki_words << page.wiki_words }.flatten.uniq
169
- end
170
-
171
- # Returns an array of all the page names on this web
172
- def page_names
173
- pages.keys
174
- end
175
-
176
- end
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
@@ -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