instiki 0.10.0 → 0.10.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/CHANGELOG +174 -165
- data/README +68 -68
- data/app/controllers/admin_controller.rb +94 -94
- data/app/controllers/application.rb +135 -131
- data/app/controllers/file_controller.rb +129 -129
- data/app/controllers/wiki_controller.rb +354 -354
- data/app/helpers/application_helper.rb +68 -68
- data/app/models/author.rb +3 -3
- data/app/models/chunks/category.rb +33 -33
- data/app/models/chunks/chunk.rb +86 -86
- data/app/models/chunks/engines.rb +61 -54
- data/app/models/chunks/include.rb +41 -41
- data/app/models/chunks/literal.rb +31 -31
- data/app/models/chunks/nowiki.rb +28 -28
- data/app/models/chunks/test.rb +18 -18
- data/app/models/chunks/uri.rb +182 -182
- data/app/models/chunks/wiki.rb +141 -141
- data/app/models/file_yard.rb +58 -58
- data/app/models/page.rb +112 -112
- data/app/models/page_lock.rb +22 -22
- data/app/models/page_set.rb +89 -89
- data/app/models/revision.rb +123 -123
- data/app/models/web.rb +182 -176
- data/app/models/wiki_content.rb +207 -207
- data/app/models/wiki_service.rb +233 -233
- data/app/models/wiki_words.rb +23 -23
- data/app/views/admin/create_system.rhtml +83 -83
- data/app/views/admin/create_web.rhtml +69 -69
- data/app/views/admin/edit_web.rhtml +137 -136
- data/app/views/file/file.rhtml +18 -18
- data/app/views/file/import.rhtml +22 -22
- data/app/views/layouts/default.rhtml +86 -85
- data/app/views/markdown_help.rhtml +12 -12
- data/app/views/mixed_help.rhtml +6 -6
- data/app/views/navigation.rhtml +30 -30
- data/app/views/rdoc_help.rhtml +12 -12
- data/app/views/textile_help.rhtml +24 -24
- data/app/views/wiki/authors.rhtml +11 -11
- data/app/views/wiki/edit.rhtml +39 -39
- data/app/views/wiki/export.rhtml +12 -12
- data/app/views/wiki/feeds.rhtml +14 -14
- data/app/views/wiki/list.rhtml +64 -64
- data/app/views/wiki/locked.rhtml +23 -23
- data/app/views/wiki/login.rhtml +14 -14
- data/app/views/wiki/new.rhtml +31 -31
- data/app/views/wiki/page.rhtml +115 -115
- data/app/views/wiki/print.rhtml +14 -14
- data/app/views/wiki/published.rhtml +9 -9
- data/app/views/wiki/recently_revised.rhtml +26 -26
- data/app/views/wiki/revision.rhtml +103 -103
- data/app/views/wiki/rollback.rhtml +36 -36
- data/app/views/wiki/rss_feed.rhtml +22 -22
- data/app/views/wiki/search.rhtml +38 -38
- data/app/views/wiki/tex.rhtml +22 -22
- data/app/views/wiki/tex_web.rhtml +34 -34
- data/app/views/wiki/web_list.rhtml +18 -18
- data/app/views/wiki_words_help.rhtml +9 -9
- data/config/environment.rb +82 -82
- data/config/environments/development.rb +5 -5
- data/config/environments/production.rb +4 -4
- data/config/environments/test.rb +17 -17
- data/config/routes.rb +18 -18
- data/lib/active_record_stub.rb +31 -31
- data/lib/bluecloth_tweaked.rb +1127 -0
- data/lib/diff.rb +444 -444
- data/lib/instiki_errors.rb +14 -14
- data/lib/rdocsupport.rb +151 -151
- data/lib/redcloth_for_tex.rb +736 -736
- data/natives/osx/desktop_launcher/AppDelegate.h +18 -18
- data/natives/osx/desktop_launcher/AppDelegate.mm +109 -109
- data/natives/osx/desktop_launcher/Credits.html +15 -15
- data/natives/osx/desktop_launcher/English.lproj/MainMenu.nib/classes.nib +12 -12
- data/natives/osx/desktop_launcher/English.lproj/MainMenu.nib/info.nib +24 -24
- data/natives/osx/desktop_launcher/Info.plist +12 -12
- data/natives/osx/desktop_launcher/Instiki.xcode/project.pbxproj +592 -592
- data/natives/osx/desktop_launcher/Instiki_Prefix.pch +7 -7
- data/natives/osx/desktop_launcher/MakeDMG.sh +9 -9
- data/natives/osx/desktop_launcher/main.mm +14 -14
- data/natives/osx/desktop_launcher/version.plist +16 -16
- data/public/404.html +5 -5
- data/public/500.html +5 -5
- data/public/dispatch.rb +9 -9
- data/public/javascripts/edit_web.js +52 -52
- data/public/javascripts/prototype.js +336 -336
- data/public/stylesheets/instiki.css +222 -222
- data/script/breakpointer +4 -4
- data/script/server +93 -93
- metadata +4 -3
@@ -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
|
-
|
13
|
-
elsif not @wiki.setup?
|
14
|
-
redirect_to :controller => 'admin', :action => 'create_system'
|
15
|
-
elsif @wiki.webs.length == 1
|
16
|
-
|
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
|
-
|
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
|
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
|
-
|
113
|
-
end
|
114
|
-
end
|
115
|
-
|
116
|
-
# Within a single page --------------------------------------------------------
|
117
|
-
|
118
|
-
def cancel_edit
|
119
|
-
@page.unlock
|
120
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
342
|
-
|
343
|
-
@
|
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
|