instiki 0.9.2 → 0.10.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (102) hide show
  1. data/CHANGELOG +165 -0
  2. data/README +68 -172
  3. data/app/controllers/admin_controller.rb +94 -0
  4. data/app/controllers/application.rb +131 -0
  5. data/app/controllers/file_controller.rb +129 -0
  6. data/app/controllers/wiki_controller.rb +354 -0
  7. data/{libraries/view_helper.rb → app/helpers/application_helper.rb} +68 -33
  8. data/app/models/author.rb +3 -3
  9. data/app/models/chunks/category.rb +33 -31
  10. data/app/models/chunks/chunk.rb +86 -20
  11. data/app/models/chunks/engines.rb +54 -38
  12. data/app/models/chunks/include.rb +41 -29
  13. data/app/models/chunks/literal.rb +31 -19
  14. data/app/models/chunks/nowiki.rb +28 -31
  15. data/app/models/chunks/test.rb +18 -18
  16. data/app/models/chunks/uri.rb +182 -97
  17. data/app/models/chunks/wiki.rb +141 -82
  18. data/app/models/file_yard.rb +58 -0
  19. data/app/models/page.rb +112 -86
  20. data/app/models/page_lock.rb +22 -23
  21. data/app/models/page_set.rb +89 -64
  22. data/app/models/revision.rb +123 -90
  23. data/app/models/web.rb +176 -89
  24. data/app/models/wiki_content.rb +207 -105
  25. data/app/models/wiki_service.rb +233 -83
  26. data/app/models/wiki_words.rb +23 -25
  27. data/app/views/{wiki/new_system.rhtml → admin/create_system.rhtml} +83 -78
  28. data/app/views/{wiki/new_web.rhtml → admin/create_web.rhtml} +69 -64
  29. data/app/views/admin/edit_web.rhtml +136 -0
  30. data/app/views/file/file.rhtml +19 -0
  31. data/app/views/file/import.rhtml +23 -0
  32. data/app/views/layouts/default.rhtml +85 -0
  33. data/app/views/markdown_help.rhtml +12 -16
  34. data/app/views/mixed_help.rhtml +7 -0
  35. data/app/views/navigation.rhtml +30 -19
  36. data/app/views/rdoc_help.rhtml +12 -16
  37. data/app/views/textile_help.rhtml +24 -28
  38. data/app/views/wiki/authors.rhtml +11 -13
  39. data/app/views/wiki/edit.rhtml +39 -31
  40. data/app/views/wiki/export.rhtml +12 -14
  41. data/app/views/wiki/feeds.rhtml +14 -10
  42. data/app/views/wiki/list.rhtml +64 -57
  43. data/app/views/wiki/locked.rhtml +23 -14
  44. data/app/views/wiki/login.rhtml +14 -11
  45. data/app/views/wiki/new.rhtml +31 -27
  46. data/app/views/wiki/page.rhtml +115 -81
  47. data/app/views/wiki/print.rhtml +14 -16
  48. data/app/views/wiki/published.rhtml +9 -10
  49. data/app/views/wiki/recently_revised.rhtml +27 -30
  50. data/app/views/wiki/revision.rhtml +103 -81
  51. data/app/views/wiki/rollback.rhtml +14 -9
  52. data/app/views/wiki/rss_feed.rhtml +22 -22
  53. data/app/views/wiki/search.rhtml +38 -15
  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 -13
  57. data/app/views/wiki_words_help.rhtml +9 -8
  58. data/config/environment.rb +82 -0
  59. data/config/environments/development.rb +5 -0
  60. data/config/environments/production.rb +4 -0
  61. data/config/environments/test.rb +17 -0
  62. data/config/routes.rb +18 -0
  63. data/instiki +6 -67
  64. data/instiki.rb +3 -0
  65. data/lib/active_record_stub.rb +31 -0
  66. data/{libraries/diff → lib}/diff.rb +444 -475
  67. data/lib/instiki_errors.rb +15 -0
  68. data/{libraries → lib}/rdocsupport.rb +151 -155
  69. data/lib/redcloth_for_tex.rb +736 -0
  70. data/natives/osx/desktop_launcher/AppDelegate.h +18 -0
  71. data/natives/osx/desktop_launcher/AppDelegate.mm +109 -0
  72. data/natives/osx/desktop_launcher/Credits.html +16 -0
  73. data/natives/osx/desktop_launcher/English.lproj/InfoPlist.strings +0 -0
  74. data/natives/osx/desktop_launcher/English.lproj/MainMenu.nib/classes.nib +13 -0
  75. data/natives/osx/desktop_launcher/English.lproj/MainMenu.nib/info.nib +24 -0
  76. data/natives/osx/desktop_launcher/English.lproj/MainMenu.nib/objects.nib +0 -0
  77. data/natives/osx/desktop_launcher/Info.plist +13 -0
  78. data/natives/osx/desktop_launcher/Instiki.xcode/project.pbxproj +592 -0
  79. data/natives/osx/desktop_launcher/Instiki_Prefix.pch +7 -0
  80. data/natives/osx/desktop_launcher/MakeDMG.sh +9 -0
  81. data/natives/osx/desktop_launcher/main.mm +14 -0
  82. data/natives/osx/desktop_launcher/version.plist +16 -0
  83. data/public/404.html +6 -0
  84. data/public/500.html +6 -0
  85. data/public/dispatch.rb +10 -0
  86. data/public/favicon.ico +0 -0
  87. data/public/javascripts/edit_web.js +52 -0
  88. data/public/javascripts/prototype.js +336 -0
  89. data/{app/views/static_style_sheet.rhtml → public/stylesheets/instiki.css} +221 -198
  90. data/script/breakpointer +4 -0
  91. data/script/server +93 -0
  92. metadata +59 -32
  93. data/app/controllers/wiki.rb +0 -389
  94. data/app/models/chunks/match.rb +0 -19
  95. data/app/views/bottom.rhtml +0 -4
  96. data/app/views/top.rhtml +0 -49
  97. data/app/views/wiki/edit_web.rhtml +0 -138
  98. data/libraries/action_controller_servlet.rb +0 -177
  99. data/libraries/erb.rb +0 -490
  100. data/libraries/madeleine_service.rb +0 -68
  101. data/libraries/redcloth_for_tex.rb +0 -869
  102. data/libraries/web_controller_server.rb +0 -81
@@ -0,0 +1,131 @@
1
+ # The filters added to this controller will be run for all controllers in the application.
2
+ # Likewise will all the methods added be available for all controllers.
3
+ class ApplicationController < ActionController::Base
4
+
5
+ before_filter :set_utf8_http_header, :connect_to_model
6
+ after_filter :remember_location
7
+
8
+ # For injecting a different wiki model implementation. Intended for use in tests
9
+ def self.wiki=(the_wiki)
10
+ # a global variable is used here because Rails reloads controller and model classes in the
11
+ # development environment; therefore, storing it as a class variable does not work
12
+ # class variable is, anyway, not much different from a global variable
13
+ $instiki_wiki_service = the_wiki
14
+ logger.debug("Wiki service: #{the_wiki.to_s}")
15
+ end
16
+
17
+ def self.wiki
18
+ $instiki_wiki_service
19
+ end
20
+
21
+ protected
22
+
23
+ def authorized?
24
+ @web.nil? ||
25
+ @web.password.nil? ||
26
+ cookies['web_address'] == @web.password ||
27
+ password_check(@params['password'])
28
+ end
29
+
30
+ def check_authorization
31
+ if in_a_web? and needs_authorization?(@action_name) and not authorized? and
32
+ redirect_to :controller => 'wiki', :action => 'login', :web => @web_name
33
+ return false
34
+ end
35
+ end
36
+
37
+ def connect_to_model
38
+ @action_name = @params['action'] || 'index'
39
+ @web_name = @params['web']
40
+ @wiki = wiki
41
+ if @web_name
42
+ @web = @wiki.webs[@web_name]
43
+ if @web.nil?
44
+ render_text "Unknown web '#{@web_name}'", '404 Not Found'
45
+ return false
46
+ end
47
+ end
48
+ @page_name = @file_name = @params['id']
49
+ @page = @wiki.read_page(@web_name, @page_name) unless @page_name.nil?
50
+ @author = cookies['author'] || 'AnonymousCoward'
51
+ check_authorization
52
+ end
53
+
54
+ FILE_TYPES = {
55
+ '.exe' => 'application/octet-stream',
56
+ '.gif' => 'image/gif',
57
+ '.jpg' => 'image/jpeg',
58
+ '.pdf' => 'application/pdf',
59
+ '.png' => 'image/png',
60
+ '.txt' => 'text/plain',
61
+ '.zip' => 'application/zip'
62
+ } unless defined? FILE_TYPES
63
+
64
+ def send_file(file, options = {})
65
+ options[:type] ||= (FILE_TYPES[File.extname(file)] || 'application/octet-stream')
66
+ super(file, options)
67
+ end
68
+
69
+ def in_a_web?
70
+ not @web_name.nil?
71
+ end
72
+
73
+ def password_check(password)
74
+ if password == @web.password
75
+ cookies['web_address'] = password
76
+ true
77
+ else
78
+ false
79
+ end
80
+ end
81
+
82
+ def password_error(password)
83
+ if password.nil? or password.empty?
84
+ 'Please enter the password.'
85
+ else
86
+ 'You entered a wrong password. Please enter the right one.'
87
+ end
88
+ end
89
+
90
+ def redirect_show(page_name = @page_name, web = @web_name)
91
+ redirect_to :web => web, :controller => 'wiki', :action => 'show',
92
+ :id => (page_name || 'HomePage')
93
+ end
94
+
95
+ @@REMEMBER_NOT = ['locked', 'save', 'back', 'file', 'pic', 'import']
96
+ def remember_location
97
+ if @response.headers['Status'] == '200 OK'
98
+ unless @@REMEMBER_NOT.include? action_name or @request.method != :get
99
+ @session[:return_to] = @request.request_uri
100
+ logger.debug("Session ##{session.object_id}: remembered URL '#{@session[:return_to]}'")
101
+ end
102
+ end
103
+ end
104
+
105
+ def return_to_last_remembered
106
+ # Forget the redirect location
107
+ redirect_target, @session[:return_to] = @session[:return_to], nil
108
+ # then try to redirect to it
109
+ if redirect_target.nil?
110
+ logger.debug("Session ##{session.object_id}: no remembered redirect location, trying /")
111
+ redirect_to_url '/'
112
+ else
113
+ logger.debug("Session ##{session.object_id}: " +
114
+ "redirect to the last remembered URL #{redirect_target}")
115
+ redirect_to_url(redirect_target)
116
+ end
117
+ end
118
+
119
+ def set_utf8_http_header
120
+ @response.headers['Content-Type'] = 'text/html; charset=UTF-8'
121
+ end
122
+
123
+ def wiki
124
+ $instiki_wiki_service
125
+ end
126
+
127
+ def needs_authorization?(action)
128
+ not %w( login authenticate published rss_with_content rss_with_headlines ).include?(action)
129
+ end
130
+
131
+ end
@@ -0,0 +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
@@ -0,0 +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