instiki 0.10.0 → 0.10.1

Sign up to get free protection for your applications and to get access to all the features.
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
@@ -1,129 +1,129 @@
1
- require 'fileutils'
2
- require 'application'
3
- require 'instiki_errors'
4
-
5
- # Controller that is responsible for serving files and pictures.
6
- # Disabled in version 0.10
7
-
8
- class FileController < ApplicationController
9
-
10
- layout 'default'
11
-
12
- before_filter :check_allow_uploads
13
-
14
- def file
15
- check_path
16
- if @params['file']
17
- # form supplied
18
- file_yard.upload_file(@file_name, @params['file'])
19
- flash[:info] = "File '#{@file_name}' successfully uploaded"
20
- @web.refresh_pages_with_references(@file_name)
21
- return_to_last_remembered
22
- elsif file_yard.has_file?(@file_name)
23
- send_file(file_yard.file_path(@file_name))
24
- else
25
- logger.debug("File not found: #{file_yard.files_path}/#{@file_name}")
26
- # go to the template, which is a file upload form
27
- end
28
- end
29
-
30
- def cancel_upload
31
- return_to_last_remembered
32
- end
33
-
34
- def pic
35
- check_path
36
- if @params['file']
37
- # form supplied
38
- file_yard.upload_file(@file_name, @params['file'])
39
- flash[:info] = "Image '#{@file_name}' successfully uploaded"
40
- @web.refresh_pages_with_references(@file_name)
41
- return_to_last_remembered
42
- elsif file_yard.has_file?(@file_name)
43
- send_file(file_yard.file_path(@file_name))
44
- else
45
- logger.debug("Image not found: #{file_yard.files_path}/#{@file_name}")
46
- render_action 'file'
47
- end
48
- end
49
-
50
- def import
51
- return if file_uploads_disabled?
52
-
53
- check_authorization
54
- if @params['file']
55
- @problems = []
56
- import_file_name = "#{@web.address}-import-#{Time.now.strftime('%Y-%m-%d-%H-%M-%S')}.zip"
57
- file_yard.upload_file(import_file_name, @params['file'])
58
- import_from_archive(file_yard.file_path(import_file_name))
59
- if @problems.empty?
60
- flash[:info] = 'Import successfully finished'
61
- else
62
- flash[:info] = "Import finished, but some pages were not imported:<li>" +
63
- @problems.join('</li><li>') + '</li>'
64
- end
65
- return_to_last_remembered
66
- else
67
- # to template
68
- end
69
- end
70
-
71
- protected
72
-
73
- def check_allow_uploads
74
-
75
- # TODO enable file uploads again after 0.10 release
76
- unless RAILS_ENV == 'test'
77
- render_text 'File uploads are not ready for general use in Instiki 0.10', '403 Forbidden'
78
- return false
79
- end
80
-
81
- unless @web.allow_uploads
82
- render_text 'File uploads are blocked by the webmaster', '403 Forbidden'
83
- return false
84
- end
85
- end
86
-
87
-
88
- private
89
-
90
- def check_path
91
- raise Instiki::ValidationError.new("Invalid path: no file name") unless @file_name
92
- raise Instiki::ValidationError.new("Invalid path: no web name") unless @web_name
93
- raise Instiki::ValidationError.new("Invalid path: unknown web name") unless @web
94
- end
95
-
96
- def file_yard
97
- @wiki.file_yard(@web)
98
- end
99
-
100
- def import_from_archive(archive)
101
- logger.info "Importing pages from #{archive}"
102
- zip = Zip::ZipInputStream.open(archive)
103
- while (entry = zip.get_next_entry) do
104
- ext_length = File.extname(entry.name).length
105
- page_name = entry.name[0..-(ext_length + 1)]
106
- page_content = entry.get_input_stream.read
107
- logger.info "Processing page '#{page_name}'"
108
- begin
109
- existing_page = @wiki.read_page(@web.address, page_name)
110
- if existing_page
111
- if existing_page.content == page_content
112
- logger.info "Page '#{page_name}' with the same content already exists. Skipping."
113
- next
114
- else
115
- logger.info "Page '#{page_name}' already exists. Adding a new revision to it."
116
- wiki.revise_page(@web.address, page_name, page_content, Time.now, @author)
117
- end
118
- else
119
- wiki.write_page(@web.address, page_name, page_content, Time.now, @author)
120
- end
121
- rescue => e
122
- logger.error(e)
123
- @problems << "#{page_name} : #{e.message}"
124
- end
125
- end
126
- logger.info "Import from #{archive} finished"
127
- end
128
-
129
- end
1
+ require 'fileutils'
2
+ require 'application'
3
+ require 'instiki_errors'
4
+
5
+ # Controller that is responsible for serving files and pictures.
6
+ # Disabled in version 0.10
7
+
8
+ class FileController < ApplicationController
9
+
10
+ layout 'default'
11
+
12
+ before_filter :check_allow_uploads
13
+
14
+ def file
15
+ check_path
16
+ if @params['file']
17
+ # form supplied
18
+ file_yard.upload_file(@file_name, @params['file'])
19
+ flash[:info] = "File '#{@file_name}' successfully uploaded"
20
+ @web.refresh_pages_with_references(@file_name)
21
+ return_to_last_remembered
22
+ elsif file_yard.has_file?(@file_name)
23
+ send_file(file_yard.file_path(@file_name))
24
+ else
25
+ logger.debug("File not found: #{file_yard.files_path}/#{@file_name}")
26
+ # go to the template, which is a file upload form
27
+ end
28
+ end
29
+
30
+ def cancel_upload
31
+ return_to_last_remembered
32
+ end
33
+
34
+ def pic
35
+ check_path
36
+ if @params['file']
37
+ # form supplied
38
+ file_yard.upload_file(@file_name, @params['file'])
39
+ flash[:info] = "Image '#{@file_name}' successfully uploaded"
40
+ @web.refresh_pages_with_references(@file_name)
41
+ return_to_last_remembered
42
+ elsif file_yard.has_file?(@file_name)
43
+ send_file(file_yard.file_path(@file_name))
44
+ else
45
+ logger.debug("Image not found: #{file_yard.files_path}/#{@file_name}")
46
+ render_action 'file'
47
+ end
48
+ end
49
+
50
+ def import
51
+ return if file_uploads_disabled?
52
+
53
+ check_authorization
54
+ if @params['file']
55
+ @problems = []
56
+ import_file_name = "#{@web.address}-import-#{Time.now.strftime('%Y-%m-%d-%H-%M-%S')}.zip"
57
+ file_yard.upload_file(import_file_name, @params['file'])
58
+ import_from_archive(file_yard.file_path(import_file_name))
59
+ if @problems.empty?
60
+ flash[:info] = 'Import successfully finished'
61
+ else
62
+ flash[:info] = "Import finished, but some pages were not imported:<li>" +
63
+ @problems.join('</li><li>') + '</li>'
64
+ end
65
+ return_to_last_remembered
66
+ else
67
+ # to template
68
+ end
69
+ end
70
+
71
+ protected
72
+
73
+ def check_allow_uploads
74
+
75
+ # TODO enable file uploads again after 0.10 release
76
+ unless RAILS_ENV == 'test'
77
+ render_text 'File uploads are not ready for general use in Instiki 0.10', '403 Forbidden'
78
+ return false
79
+ end
80
+
81
+ unless @web.allow_uploads
82
+ render_text 'File uploads are blocked by the webmaster', '403 Forbidden'
83
+ return false
84
+ end
85
+ end
86
+
87
+
88
+ private
89
+
90
+ def check_path
91
+ raise Instiki::ValidationError.new("Invalid path: no file name") unless @file_name
92
+ raise Instiki::ValidationError.new("Invalid path: no web name") unless @web_name
93
+ raise Instiki::ValidationError.new("Invalid path: unknown web name") unless @web
94
+ end
95
+
96
+ def file_yard
97
+ @wiki.file_yard(@web)
98
+ end
99
+
100
+ def import_from_archive(archive)
101
+ logger.info "Importing pages from #{archive}"
102
+ zip = Zip::ZipInputStream.open(archive)
103
+ while (entry = zip.get_next_entry) do
104
+ ext_length = File.extname(entry.name).length
105
+ page_name = entry.name[0..-(ext_length + 1)]
106
+ page_content = entry.get_input_stream.read
107
+ logger.info "Processing page '#{page_name}'"
108
+ begin
109
+ existing_page = @wiki.read_page(@web.address, page_name)
110
+ if existing_page
111
+ if existing_page.content == page_content
112
+ logger.info "Page '#{page_name}' with the same content already exists. Skipping."
113
+ next
114
+ else
115
+ logger.info "Page '#{page_name}' already exists. Adding a new revision to it."
116
+ wiki.revise_page(@web.address, page_name, page_content, Time.now, @author)
117
+ end
118
+ else
119
+ wiki.write_page(@web.address, page_name, page_content, Time.now, @author)
120
+ end
121
+ rescue => e
122
+ logger.error(e)
123
+ @problems << "#{page_name} : #{e.message}"
124
+ end
125
+ end
126
+ logger.info "Import from #{archive} finished"
127
+ end
128
+
129
+ end
@@ -1,354 +1,354 @@
1
- require 'application'
2
- require 'fileutils'
3
- require 'redcloth_for_tex'
4
- require 'parsedate'
5
-
6
- class WikiController < ApplicationController
7
-
8
- layout 'default', :except => [:rss_feed, :rss_with_content, :rss_with_headlines, :tex, :export_tex, :export_html]
9
-
10
- def index
11
- if @web_name
12
- redirect_show 'HomePage'
13
- elsif not @wiki.setup?
14
- redirect_to :controller => 'admin', :action => 'create_system'
15
- elsif @wiki.webs.length == 1
16
- redirect_show 'HomePage', @wiki.webs.values.first.address
17
- else
18
- redirect_to :action => 'web_list'
19
- end
20
- end
21
-
22
- # Outside a single web --------------------------------------------------------
23
-
24
- def authenticate
25
- if password_check(@params['password'])
26
- redirect_show('HomePage')
27
- else
28
- flash[:info] = password_error(@params['password'])
29
- redirect_to :action => 'login', :web => @web_name
30
- end
31
- end
32
-
33
- def login
34
- # to template
35
- end
36
-
37
- def web_list
38
- @webs = wiki.webs.values.sort_by { |web| web.name }
39
- end
40
-
41
-
42
- # Within a single web ---------------------------------------------------------
43
-
44
- def authors
45
- @authors = @web.select.authors
46
- end
47
-
48
- def export_html
49
- export_pages_as_zip('html') do |page|
50
- @page = page
51
- @link_mode = :export
52
- render_to_string 'wiki/print'
53
- end
54
- end
55
-
56
- def export_markup
57
- export_pages_as_zip(@web.markup) { |page| page.content }
58
- end
59
-
60
- def export_pdf
61
- file_name = "#{@web.address}-tex-#{@web.revised_on.strftime('%Y-%m-%d-%H-%M-%S')}"
62
- file_path = File.join(@wiki.storage_path, file_name)
63
-
64
- export_web_to_tex "#{file_path}.tex" unless FileTest.exists? "#{file_path}.tex"
65
- convert_tex_to_pdf "#{file_path}.tex"
66
- send_file "#{file_path}.pdf"
67
- end
68
-
69
- def export_tex
70
- file_name = "#{@web.address}-tex-#{@web.revised_on.strftime('%Y-%m-%d-%H-%M-%S')}.tex"
71
- file_path = File.join(@wiki.storage_path, file_name)
72
- export_web_to_tex(file_path) unless FileTest.exists?(file_path)
73
- send_file file_path
74
- end
75
-
76
- def feeds
77
- @rss_with_content_allowed = rss_with_content_allowed?
78
- # show the template
79
- end
80
-
81
- def list
82
- parse_category
83
- @pages_by_name = @pages_in_category.by_name
84
- @page_names_that_are_wanted = @pages_in_category.wanted_pages
85
- @pages_that_are_orphaned = @pages_in_category.orphaned_pages
86
- end
87
-
88
- def recently_revised
89
- parse_category
90
- @pages_by_revision = @pages_in_category.by_revision
91
- end
92
-
93
- def rss_with_content
94
- if rss_with_content_allowed?
95
- render_rss(hide_description = false, *parse_rss_params)
96
- else
97
- render_text 'RSS feed with content for this web is blocked for security reasons. ' +
98
- 'The web is password-protected and not published', '403 Forbidden'
99
- end
100
- end
101
-
102
- def rss_with_headlines
103
- render_rss(hide_description = true, *parse_rss_params)
104
- end
105
-
106
- def search
107
- @query = @params['query']
108
- @title_results = @web.select { |page| page.name =~ /#{@query}/i }.sort
109
- @results = @web.select { |page| page.content =~ /#{@query}/i }.sort
110
- all_pages_found = (@results + @title_results).uniq
111
- if all_pages_found.size == 1
112
- redirect_show(all_pages_found.first.name)
113
- end
114
- end
115
-
116
- # Within a single page --------------------------------------------------------
117
-
118
- def cancel_edit
119
- @page.unlock
120
- redirect_show
121
- end
122
-
123
- def edit
124
- if @page.nil?
125
- redirect_to :action => 'index'
126
- elsif @page.locked?(Time.now) and not @params['break_lock']
127
- redirect_to :web => @web_name, :action => 'locked', :id => @page_name
128
- else
129
- @page.lock(Time.now, @author)
130
- end
131
- end
132
-
133
- def locked
134
- # to template
135
- end
136
-
137
- def new
138
- # to template
139
- end
140
-
141
- def pdf
142
- page = wiki.read_page(@web_name, @page_name)
143
- safe_page_name = @page.name.gsub(/\W/, '')
144
- file_name = "#{safe_page_name}-#{@web.address}-#{@page.created_at.strftime('%Y-%m-%d-%H-%M-%S')}"
145
- file_path = File.join(@wiki.storage_path, file_name)
146
-
147
- export_page_to_tex("#{file_path}.tex") unless FileTest.exists?("#{file_path}.tex")
148
- # NB: this is _very_ slow
149
- convert_tex_to_pdf("#{file_path}.tex")
150
- send_file "#{file_path}.pdf"
151
- end
152
-
153
- def print
154
- @link_mode ||= :show
155
- # to template
156
- end
157
-
158
- def published
159
- if @web.published
160
- @page = wiki.read_page(@web_name, @page_name || 'HomePage')
161
- else
162
- redirect_show('HomePage')
163
- end
164
- end
165
-
166
- def revision
167
- get_page_and_revision
168
- end
169
-
170
- def rollback
171
- get_page_and_revision
172
- end
173
-
174
- def save
175
- redirect_to :action => 'index' if @page_name.nil?
176
- cookies['author'] = @params['author']
177
-
178
- begin
179
- page = @web.pages[@page_name]
180
- if @web.pages[@page_name]
181
- wiki.revise_page(
182
- @web_name, @page_name, @params['content'], Time.now,
183
- Author.new(@params['author'], remote_ip)
184
- )
185
- page.unlock
186
- else
187
- wiki.write_page(
188
- @web_name, @page_name, @params['content'], Time.now,
189
- Author.new(@params['author'], remote_ip)
190
- )
191
- end
192
- redirect_show(@page_name)
193
- rescue Instiki::ValidationError => e
194
- page.unlock if defined? page
195
- flash[:error] = e
196
- return_to_last_remembered
197
- end
198
- end
199
-
200
- def show
201
- if @page
202
- begin
203
- render_action 'page'
204
- # TODO this rescue should differentiate between errors due to rendering and errors in
205
- # the application itself (for application errors, it's better not to rescue the error at all)
206
- rescue => e
207
- logger.error e
208
- if in_a_web?
209
- redirect_to :web => @web_name, :action => 'edit',
210
- :action_suffix => "#{@page_name}?msg=#{e.message}"
211
- else
212
- raise e
213
- end
214
- end
215
- else
216
- if not @page_name.nil? and not @page_name.empty?
217
- redirect_to :web => @web_name, :action => 'new', :id => @page_name
218
- else
219
- render_text 'Page name is not specified', '404 Not Found'
220
- end
221
- end
222
- end
223
-
224
- def tex
225
- @tex_content = RedClothForTex.new(@page.content).to_tex
226
- end
227
-
228
-
229
- private
230
-
231
- def convert_tex_to_pdf(tex_path)
232
- # TODO remove earlier PDF files with the same prefix
233
- # TODO handle gracefully situation where pdflatex is not available
234
- begin
235
- wd = Dir.getwd
236
- Dir.chdir(File.dirname(tex_path))
237
- logger.info `pdflatex --interaction=nonstopmode #{File.basename(tex_path)}`
238
- ensure
239
- Dir.chdir(wd)
240
- end
241
- end
242
-
243
- def export_page_to_tex(file_path)
244
- tex
245
- File.open(file_path, 'w') { |f| f.write(render_to_string('wiki/tex')) }
246
- end
247
-
248
- def export_pages_as_zip(file_type, &block)
249
-
250
- file_prefix = "#{@web.address}-#{file_type}-"
251
- timestamp = @web.revised_on.strftime('%Y-%m-%d-%H-%M-%S')
252
- file_path = File.join(@wiki.storage_path, file_prefix + timestamp + '.zip')
253
- tmp_path = "#{file_path}.tmp"
254
-
255
- Zip::ZipOutputStream.open(tmp_path) do |zip_out|
256
- @web.select.by_name.each do |page|
257
- zip_out.put_next_entry("#{CGI.escape(page.name)}.#{file_type}")
258
- zip_out.puts(block.call(page))
259
- end
260
- # add an index file, if exporting to HTML
261
- if file_type.to_s.downcase == 'html'
262
- zip_out.put_next_entry 'index.html'
263
- zip_out.puts <<-EOL
264
- <html>
265
- <head>
266
- <META HTTP-EQUIV="Refresh" CONTENT="0;URL=HomePage.#{file_type}">
267
- </head>
268
- </html>
269
- EOL
270
- end
271
- end
272
- FileUtils.rm_rf(Dir[File.join(@wiki.storage_path, file_prefix + '*.zip')])
273
- FileUtils.mv(tmp_path, file_path)
274
- send_file file_path
275
- end
276
-
277
- def export_web_to_tex(file_path)
278
- @tex_content = table_of_contents(@web.pages['HomePage'].content, render_tex_web)
279
- File.open(file_path, 'w') { |f| f.write(render_to_string('wiki/tex_web')) }
280
- end
281
-
282
- def get_page_and_revision
283
- @revision = @page.revisions[@params['rev'].to_i]
284
- end
285
-
286
- def parse_category
287
- @categories = @web.categories
288
- @category = @params['category']
289
- if @categories.include?(@category)
290
- @pages_in_category = @web.select { |page| page.in_category?(@category) }
291
- @set_name = "category '#{@category}'"
292
- else
293
- @pages_in_category = PageSet.new(@web).by_name
294
- @set_name = 'the web'
295
- end
296
- end
297
-
298
- def parse_rss_params
299
- if @params.include? 'limit'
300
- limit = @params['limit'].to_i rescue nil
301
- limit = nil if limit == 0
302
- else
303
- limit = 15
304
- end
305
- start_date = Time.local(*ParseDate::parsedate(@params['start'])) rescue nil
306
- end_date = Time.local(*ParseDate::parsedate(@params['end'])) rescue nil
307
- [ limit, start_date, end_date ]
308
- end
309
-
310
- def remote_ip
311
- ip = @request.remote_ip
312
- logger.info(ip)
313
- ip
314
- end
315
-
316
- def render_rss(hide_description = false, limit = 15, start_date = nil, end_date = nil)
317
- if limit && !start_date && !end_date
318
- @pages_by_revision = @web.select.by_revision.first(limit)
319
- else
320
- @pages_by_revision = @web.select.by_revision
321
- @pages_by_revision.reject! { |page| page.created_at < start_date } if start_date
322
- @pages_by_revision.reject! { |page| page.created_at > end_date } if end_date
323
- end
324
-
325
- @hide_description = hide_description
326
- @response.headers['Content-Type'] = 'text/xml'
327
- @link_action = @web.password ? 'published' : 'show'
328
-
329
- render 'wiki/rss_feed'
330
- end
331
-
332
- def render_tex_web
333
- @web.select.by_name.inject({}) do |tex_web, page|
334
- tex_web[page.name] = RedClothForTex.new(page.content).to_tex
335
- tex_web
336
- end
337
- end
338
-
339
- def render_to_string(template_name)
340
- add_variables_to_assigns
341
- render template_name
342
- @performed_render = false
343
- @template.render_file(template_name)
344
- end
345
-
346
- def rss_with_content_allowed?
347
- @web.password.nil? or @web.published
348
- end
349
-
350
- def truncate(text, length = 30, truncate_string = '...')
351
- if text.length > length then text[0..(length - 3)] + truncate_string else text end
352
- end
353
-
354
- end
1
+ require 'application'
2
+ require 'fileutils'
3
+ require 'redcloth_for_tex'
4
+ require 'parsedate'
5
+
6
+ class WikiController < ApplicationController
7
+
8
+ layout 'default', :except => [:rss_feed, :rss_with_content, :rss_with_headlines, :tex, :export_tex, :export_html]
9
+
10
+ def index
11
+ if @web_name
12
+ redirect_home
13
+ elsif not @wiki.setup?
14
+ redirect_to :controller => 'admin', :action => 'create_system'
15
+ elsif @wiki.webs.length == 1
16
+ redirect_home @wiki.webs.values.first.address
17
+ else
18
+ redirect_to :action => 'web_list'
19
+ end
20
+ end
21
+
22
+ # Outside a single web --------------------------------------------------------
23
+
24
+ def authenticate
25
+ if password_check(@params['password'])
26
+ redirect_home
27
+ else
28
+ flash[:info] = password_error(@params['password'])
29
+ redirect_to :action => 'login', :web => @web_name
30
+ end
31
+ end
32
+
33
+ def login
34
+ # to template
35
+ end
36
+
37
+ def web_list
38
+ @webs = wiki.webs.values.sort_by { |web| web.name }
39
+ end
40
+
41
+
42
+ # Within a single web ---------------------------------------------------------
43
+
44
+ def authors
45
+ @authors = @web.select.authors
46
+ end
47
+
48
+ def export_html
49
+ export_pages_as_zip('html') do |page|
50
+ @page = page
51
+ @link_mode = :export
52
+ render_to_string('wiki/print', use_layout = true)
53
+ end
54
+ end
55
+
56
+ def export_markup
57
+ export_pages_as_zip(@web.markup) { |page| page.content }
58
+ end
59
+
60
+ def export_pdf
61
+ file_name = "#{@web.address}-tex-#{@web.revised_on.strftime('%Y-%m-%d-%H-%M-%S')}"
62
+ file_path = File.join(@wiki.storage_path, file_name)
63
+
64
+ export_web_to_tex "#{file_path}.tex" unless FileTest.exists? "#{file_path}.tex"
65
+ convert_tex_to_pdf "#{file_path}.tex"
66
+ send_file "#{file_path}.pdf"
67
+ end
68
+
69
+ def export_tex
70
+ file_name = "#{@web.address}-tex-#{@web.revised_on.strftime('%Y-%m-%d-%H-%M-%S')}.tex"
71
+ file_path = File.join(@wiki.storage_path, file_name)
72
+ export_web_to_tex(file_path) unless FileTest.exists?(file_path)
73
+ send_file file_path
74
+ end
75
+
76
+ def feeds
77
+ @rss_with_content_allowed = rss_with_content_allowed?
78
+ # show the template
79
+ end
80
+
81
+ def list
82
+ parse_category
83
+ @pages_by_name = @pages_in_category.by_name
84
+ @page_names_that_are_wanted = @pages_in_category.wanted_pages
85
+ @pages_that_are_orphaned = @pages_in_category.orphaned_pages
86
+ end
87
+
88
+ def recently_revised
89
+ parse_category
90
+ @pages_by_revision = @pages_in_category.by_revision
91
+ end
92
+
93
+ def rss_with_content
94
+ if rss_with_content_allowed?
95
+ render_rss(hide_description = false, *parse_rss_params)
96
+ else
97
+ render_text 'RSS feed with content for this web is blocked for security reasons. ' +
98
+ 'The web is password-protected and not published', '403 Forbidden'
99
+ end
100
+ end
101
+
102
+ def rss_with_headlines
103
+ render_rss(hide_description = true, *parse_rss_params)
104
+ end
105
+
106
+ def search
107
+ @query = @params['query']
108
+ @title_results = @web.select { |page| page.name =~ /#{@query}/i }.sort
109
+ @results = @web.select { |page| page.content =~ /#{@query}/i }.sort
110
+ all_pages_found = (@results + @title_results).uniq
111
+ if all_pages_found.size == 1
112
+ redirect_to_page(all_pages_found.first.name)
113
+ end
114
+ end
115
+
116
+ # Within a single page --------------------------------------------------------
117
+
118
+ def cancel_edit
119
+ @page.unlock
120
+ redirect_to_page(@page_name)
121
+ end
122
+
123
+ def edit
124
+ if @page.nil?
125
+ redirect_to :action => 'index'
126
+ elsif @page.locked?(Time.now) and not @params['break_lock']
127
+ redirect_to :web => @web_name, :action => 'locked', :id => @page_name
128
+ else
129
+ @page.lock(Time.now, @author)
130
+ end
131
+ end
132
+
133
+ def locked
134
+ # to template
135
+ end
136
+
137
+ def new
138
+ # to template
139
+ end
140
+
141
+ def pdf
142
+ page = wiki.read_page(@web_name, @page_name)
143
+ safe_page_name = @page.name.gsub(/\W/, '')
144
+ file_name = "#{safe_page_name}-#{@web.address}-#{@page.created_at.strftime('%Y-%m-%d-%H-%M-%S')}"
145
+ file_path = File.join(@wiki.storage_path, file_name)
146
+
147
+ export_page_to_tex("#{file_path}.tex") unless FileTest.exists?("#{file_path}.tex")
148
+ # NB: this is _very_ slow
149
+ convert_tex_to_pdf("#{file_path}.tex")
150
+ send_file "#{file_path}.pdf"
151
+ end
152
+
153
+ def print
154
+ @link_mode ||= :show
155
+ # to template
156
+ end
157
+
158
+ def published
159
+ if @web.published
160
+ @page = wiki.read_page(@web_name, @page_name || 'HomePage')
161
+ else
162
+ redirect_home
163
+ end
164
+ end
165
+
166
+ def revision
167
+ get_page_and_revision
168
+ end
169
+
170
+ def rollback
171
+ get_page_and_revision
172
+ end
173
+
174
+ def save
175
+ redirect_to :action => 'index' if @page_name.nil?
176
+ cookies['author'] = @params['author']
177
+
178
+ begin
179
+ page = @web.pages[@page_name]
180
+ if @web.pages[@page_name]
181
+ wiki.revise_page(
182
+ @web_name, @page_name, @params['content'], Time.now,
183
+ Author.new(@params['author'], remote_ip)
184
+ )
185
+ page.unlock
186
+ else
187
+ wiki.write_page(
188
+ @web_name, @page_name, @params['content'], Time.now,
189
+ Author.new(@params['author'], remote_ip)
190
+ )
191
+ end
192
+ redirect_to_page @page_name
193
+ rescue Instiki::ValidationError => e
194
+ page.unlock if defined? page
195
+ flash[:error] = e
196
+ return_to_last_remembered
197
+ end
198
+ end
199
+
200
+ def show
201
+ if @page
202
+ begin
203
+ render_action 'page'
204
+ # TODO this rescue should differentiate between errors due to rendering and errors in
205
+ # the application itself (for application errors, it's better not to rescue the error at all)
206
+ rescue => e
207
+ logger.error e
208
+ if in_a_web?
209
+ redirect_to :web => @web_name, :action => 'edit',
210
+ :action_suffix => "#{@page_name}?msg=#{e.message}"
211
+ else
212
+ raise e
213
+ end
214
+ end
215
+ else
216
+ if not @page_name.nil? and not @page_name.empty?
217
+ redirect_to :web => @web_name, :action => 'new', :id => @page_name
218
+ else
219
+ render_text 'Page name is not specified', '404 Not Found'
220
+ end
221
+ end
222
+ end
223
+
224
+ def tex
225
+ @tex_content = RedClothForTex.new(@page.content).to_tex
226
+ end
227
+
228
+
229
+ private
230
+
231
+ def convert_tex_to_pdf(tex_path)
232
+ # TODO remove earlier PDF files with the same prefix
233
+ # TODO handle gracefully situation where pdflatex is not available
234
+ begin
235
+ wd = Dir.getwd
236
+ Dir.chdir(File.dirname(tex_path))
237
+ logger.info `pdflatex --interaction=nonstopmode #{File.basename(tex_path)}`
238
+ ensure
239
+ Dir.chdir(wd)
240
+ end
241
+ end
242
+
243
+ def export_page_to_tex(file_path)
244
+ tex
245
+ File.open(file_path, 'w') { |f| f.write(render_to_string('wiki/tex')) }
246
+ end
247
+
248
+ def export_pages_as_zip(file_type, &block)
249
+
250
+ file_prefix = "#{@web.address}-#{file_type}-"
251
+ timestamp = @web.revised_on.strftime('%Y-%m-%d-%H-%M-%S')
252
+ file_path = File.join(@wiki.storage_path, file_prefix + timestamp + '.zip')
253
+ tmp_path = "#{file_path}.tmp"
254
+
255
+ Zip::ZipOutputStream.open(tmp_path) do |zip_out|
256
+ @web.select.by_name.each do |page|
257
+ zip_out.put_next_entry("#{CGI.escape(page.name)}.#{file_type}")
258
+ zip_out.puts(block.call(page))
259
+ end
260
+ # add an index file, if exporting to HTML
261
+ if file_type.to_s.downcase == 'html'
262
+ zip_out.put_next_entry 'index.html'
263
+ zip_out.puts <<-EOL
264
+ <html>
265
+ <head>
266
+ <META HTTP-EQUIV="Refresh" CONTENT="0;URL=HomePage.#{file_type}">
267
+ </head>
268
+ </html>
269
+ EOL
270
+ end
271
+ end
272
+ FileUtils.rm_rf(Dir[File.join(@wiki.storage_path, file_prefix + '*.zip')])
273
+ FileUtils.mv(tmp_path, file_path)
274
+ send_file file_path
275
+ end
276
+
277
+ def export_web_to_tex(file_path)
278
+ @tex_content = table_of_contents(@web.pages['HomePage'].content, render_tex_web)
279
+ File.open(file_path, 'w') { |f| f.write(render_to_string('wiki/tex_web')) }
280
+ end
281
+
282
+ def get_page_and_revision
283
+ @revision = @page.revisions[@params['rev'].to_i]
284
+ end
285
+
286
+ def parse_category
287
+ @categories = @web.categories
288
+ @category = @params['category']
289
+ if @categories.include?(@category)
290
+ @pages_in_category = @web.select { |page| page.in_category?(@category) }
291
+ @set_name = "category '#{@category}'"
292
+ else
293
+ @pages_in_category = PageSet.new(@web).by_name
294
+ @set_name = 'the web'
295
+ end
296
+ end
297
+
298
+ def parse_rss_params
299
+ if @params.include? 'limit'
300
+ limit = @params['limit'].to_i rescue nil
301
+ limit = nil if limit == 0
302
+ else
303
+ limit = 15
304
+ end
305
+ start_date = Time.local(*ParseDate::parsedate(@params['start'])) rescue nil
306
+ end_date = Time.local(*ParseDate::parsedate(@params['end'])) rescue nil
307
+ [ limit, start_date, end_date ]
308
+ end
309
+
310
+ def remote_ip
311
+ ip = @request.remote_ip
312
+ logger.info(ip)
313
+ ip
314
+ end
315
+
316
+ def render_rss(hide_description = false, limit = 15, start_date = nil, end_date = nil)
317
+ if limit && !start_date && !end_date
318
+ @pages_by_revision = @web.select.by_revision.first(limit)
319
+ else
320
+ @pages_by_revision = @web.select.by_revision
321
+ @pages_by_revision.reject! { |page| page.created_at < start_date } if start_date
322
+ @pages_by_revision.reject! { |page| page.created_at > end_date } if end_date
323
+ end
324
+
325
+ @hide_description = hide_description
326
+ @response.headers['Content-Type'] = 'text/xml'
327
+ @link_action = @web.password ? 'published' : 'show'
328
+
329
+ render 'wiki/rss_feed'
330
+ end
331
+
332
+ def render_tex_web
333
+ @web.select.by_name.inject({}) do |tex_web, page|
334
+ tex_web[page.name] = RedClothForTex.new(page.content).to_tex
335
+ tex_web
336
+ end
337
+ end
338
+
339
+ def render_to_string(template_name, with_layout = false)
340
+ add_variables_to_assigns
341
+ @content_for_layout = @template.render_file(template_name)
342
+ if with_layout then @template.render_file('layouts/default');
343
+ else @content_for_layout; end
344
+ end
345
+
346
+ def rss_with_content_allowed?
347
+ @web.password.nil? or @web.published
348
+ end
349
+
350
+ def truncate(text, length = 30, truncate_string = '...')
351
+ if text.length > length then text[0..(length - 3)] + truncate_string else text end
352
+ end
353
+
354
+ end