Pimki 1.7.092 → 1.8.092
Sign up to get free protection for your applications and to get access to all the features.
- data/README-PIMKI +182 -178
- data/app/controllers/wiki.rb +950 -942
- data/app/models/chunks/category.rb +33 -33
- data/app/models/chunks/category_test.rb +21 -21
- data/app/models/chunks/chunk.rb +20 -20
- data/app/models/chunks/engines.rb +48 -48
- data/app/models/chunks/include.rb +1 -1
- data/app/models/chunks/match.rb +1 -1
- data/app/models/chunks/nowiki.rb +1 -1
- data/app/models/chunks/nowiki_test.rb +5 -0
- data/app/models/chunks/todo.rb +1 -0
- data/app/models/chunks/wiki.rb +130 -130
- data/app/models/page.rb +124 -124
- data/app/models/revision.rb +92 -92
- data/app/models/web.rb +314 -316
- data/app/models/wiki_content.rb +2 -2
- data/app/models/wiki_service.rb +170 -166
- data/app/models/wiki_words.rb +28 -28
- data/app/views/error.rhtml +37 -37
- data/app/views/navigation.rhtml +1 -1
- data/app/views/static_style_sheet.rhtml +10 -5
- data/app/views/top.rhtml +1 -1
- data/app/views/wiki/adv_search.rhtml +61 -61
- data/app/views/wiki/bliki.rhtml +7 -6
- data/app/views/wiki/bliki_edit.rhtml +26 -37
- data/app/views/wiki/bliki_new.rhtml +58 -64
- data/app/views/wiki/bliki_revision.rhtml +5 -3
- data/app/views/wiki/edit.rhtml +44 -44
- data/app/views/wiki/edit_menu.rhtml +26 -19
- data/app/views/wiki/edit_web.rhtml +303 -305
- data/app/views/wiki/glossary.rhtml +35 -27
- data/app/views/wiki/list.rhtml +175 -174
- data/app/views/wiki/list.rhtml.bak +175 -0
- data/app/views/wiki/mind.rhtml +70 -70
- data/app/views/wiki/new.rhtml +34 -32
- data/app/views/wiki/published.rhtml +34 -49
- data/app/views/wiki/revision.rhtml +88 -87
- data/app/views/wiki/rollback.rhtml +36 -35
- data/app/views/wiki/todo.rhtml +2 -2
- data/app/views/wiki_words_help.rhtml +8 -8
- data/libraries/action_controller_servlet.rb +202 -202
- data/libraries/madeleine_service.rb +162 -162
- data/pimki.rb +181 -181
- metadata +11 -12
- data/README +0 -172
- data/app/models/chunks/acronym.rb +0 -19
- data/app/views/wiki/test.rhtml +0 -25
- data/libraries/secure_web_controller_server.rb +0 -106
@@ -1,163 +1,163 @@
|
|
1
|
-
require 'madeleine'
|
2
|
-
require 'madeleine/automatic'
|
3
|
-
require 'madeleine/zmarshal'
|
4
|
-
require 'singleton'
|
5
|
-
require 'yaml'
|
6
|
-
|
7
|
-
class MadeleineService
|
8
|
-
include Madeleine::Automatic::Interceptor
|
9
|
-
|
10
|
-
@@storage_path = self.name.downcase + "_storage"
|
11
|
-
automatic_read_only :snapshot_interval_hours, :take_snapshot, :clean_old_snapshots,
|
12
|
-
:restart, :request_stop
|
13
|
-
|
14
|
-
class << self
|
15
|
-
def storage_path
|
16
|
-
@@storage_path
|
17
|
-
end
|
18
|
-
|
19
|
-
def storage_path=(storage_path)
|
20
|
-
@@storage_path = storage_path
|
21
|
-
end
|
22
|
-
|
23
|
-
def instance
|
24
|
-
if @system.nil?
|
25
|
-
@madeleine_server = MadeleineServer.new(self)
|
26
|
-
@system = @madeleine_server.system
|
27
|
-
end
|
28
|
-
@system
|
29
|
-
end
|
30
|
-
|
31
|
-
def restart
|
32
|
-
MadeleineServer.delete_storage(self)
|
33
|
-
@system = nil
|
34
|
-
instance
|
35
|
-
end
|
36
|
-
|
37
|
-
def clean_old_snapshots
|
38
|
-
instance
|
39
|
-
@madeleine_server.clean_storage(self)
|
40
|
-
end
|
41
|
-
|
42
|
-
def take_snapshot
|
43
|
-
instance
|
44
|
-
@madeleine_server.force_snapshot
|
45
|
-
end
|
46
|
-
|
47
|
-
def snapshot_interval_hours
|
48
|
-
instance
|
49
|
-
@madeleine_server.snapshot_interval.div MadeleineServer::ONE_HOUR rescue 1
|
50
|
-
end
|
51
|
-
|
52
|
-
def snapshot_interval_hours= hours
|
53
|
-
instance
|
54
|
-
@madeleine_server.snapshot_interval = hours.to_i * MadeleineServer::ONE_HOUR rescue MadeleineServer::ONE_HOUR
|
55
|
-
end
|
56
|
-
|
57
|
-
def request_stop
|
58
|
-
instance
|
59
|
-
@madeleine_server.request_stop
|
60
|
-
end
|
61
|
-
|
62
|
-
end
|
63
|
-
end
|
64
|
-
|
65
|
-
require 'fileutils'
|
66
|
-
class MadeleineServer
|
67
|
-
|
68
|
-
attr_reader :storage_path
|
69
|
-
attr_accessor :snapshot_interval
|
70
|
-
|
71
|
-
# Clears all the command_log and snapshot files located in the storage directory, so the
|
72
|
-
# database is essentially dropped and recreated as blank. Used in tests.
|
73
|
-
def self.delete_storage(service)
|
74
|
-
if (File.directory?(service.storage_path))
|
75
|
-
FileUtils.rm_rf(Dir[service.storage_path + '/*.command_log'])
|
76
|
-
FileUtils.rm_rf(Dir[service.storage_path + '/*.snapshot'])
|
77
|
-
else
|
78
|
-
FileUtils.mkdir_p(service.storage_path)
|
79
|
-
end
|
80
|
-
end
|
81
|
-
|
82
|
-
def clean_storage(service)
|
83
|
-
force_snapshot
|
84
|
-
command_logs = Dir[service.storage_path + '/*.command_log']
|
85
|
-
raise 'Error: existing command_logs after snapshot' unless command_logs.empty?
|
86
|
-
|
87
|
-
snapshots = Dir[service.storage_path + '/*.snapshot']
|
88
|
-
FileUtils.rm_rf(snapshots.sort[0..-2])
|
89
|
-
end
|
90
|
-
|
91
|
-
|
92
|
-
def initialize(service)
|
93
|
-
@storage_path = service.storage_path
|
94
|
-
@snapshot_interval = ONE_HOUR
|
95
|
-
marshaller = Madeleine::ZMarshal.new()
|
96
|
-
@server = Madeleine::Automatic::AutomaticSnapshotMadeleine.new(service.storage_path,
|
97
|
-
marshaller) { service.new }
|
98
|
-
start_snapshot_thread
|
99
|
-
end
|
100
|
-
|
101
|
-
def system
|
102
|
-
@server.system
|
103
|
-
end
|
104
|
-
|
105
|
-
def command_log_present?
|
106
|
-
not Dir[File.join(File.expand_path(storage_path), '*.command_log')].empty?
|
107
|
-
end
|
108
|
-
|
109
|
-
def force_snapshot
|
110
|
-
begin
|
111
|
-
hours_since_last_snapshot = 0
|
112
|
-
@server.take_snapshot
|
113
|
-
rescue => e
|
114
|
-
sleep(ONE_MINUTE)
|
115
|
-
retry
|
116
|
-
end
|
117
|
-
end
|
118
|
-
|
119
|
-
|
120
|
-
ONE_MINUTE = 60
|
121
|
-
ONE_HOUR = ONE_MINUTE * 60
|
122
|
-
MAX_INTERVAL_HOURS = 24 * 2
|
123
|
-
|
124
|
-
def start_snapshot_thread
|
125
|
-
@snapshot_thread = Thread.new(@server) {
|
126
|
-
hours_since_last_snapshot = 0
|
127
|
-
while not @request_stop
|
128
|
-
sleep(snapshot_interval)
|
129
|
-
hours_since_last_snapshot += snapshot_interval.div ONE_HOUR
|
130
|
-
begin
|
131
|
-
# Take a snapshot if there is a command log
|
132
|
-
if command_log_present? or hours_since_last_snapshot > MAX_INTERVAL_HOURS
|
133
|
-
# 'Taking a Madeleine snapshot'
|
134
|
-
@server.take_snapshot
|
135
|
-
hours_since_last_snapshot = 0
|
136
|
-
puts "[#{DateTime.now.strftime '%F %T'}] INFO Taking snapshot"
|
137
|
-
else
|
138
|
-
puts "[#{DateTime.now.strftime '%F %T'}] INFO Skipping snapshot (no command logs)"
|
139
|
-
end
|
140
|
-
rescue => e
|
141
|
-
# wait for a minute (not to spoof the log with the same error)
|
142
|
-
# and go back into the loop, to keep trying
|
143
|
-
sleep(ONE_MINUTE)
|
144
|
-
retry
|
145
|
-
end
|
146
|
-
end
|
147
|
-
}
|
148
|
-
end
|
149
|
-
|
150
|
-
def request_stop
|
151
|
-
begin
|
152
|
-
@request_stop = true
|
153
|
-
if @snapshot_thread and @snapshot_thread.alive?
|
154
|
-
@snapshot_thread.wakeup
|
155
|
-
@snapshot_thread.join
|
156
|
-
end
|
157
|
-
@server.take_snapshot if command_log_present?
|
158
|
-
rescue => detail
|
159
|
-
puts detail
|
160
|
-
end
|
161
|
-
end
|
162
|
-
|
1
|
+
require 'madeleine'
|
2
|
+
require 'madeleine/automatic'
|
3
|
+
require 'madeleine/zmarshal'
|
4
|
+
require 'singleton'
|
5
|
+
require 'yaml'
|
6
|
+
|
7
|
+
class MadeleineService
|
8
|
+
include Madeleine::Automatic::Interceptor
|
9
|
+
|
10
|
+
@@storage_path = self.name.downcase + "_storage"
|
11
|
+
automatic_read_only :snapshot_interval_hours, :take_snapshot, :clean_old_snapshots,
|
12
|
+
:restart, :request_stop
|
13
|
+
|
14
|
+
class << self
|
15
|
+
def storage_path
|
16
|
+
@@storage_path
|
17
|
+
end
|
18
|
+
|
19
|
+
def storage_path=(storage_path)
|
20
|
+
@@storage_path = storage_path
|
21
|
+
end
|
22
|
+
|
23
|
+
def instance
|
24
|
+
if @system.nil?
|
25
|
+
@madeleine_server = MadeleineServer.new(self)
|
26
|
+
@system = @madeleine_server.system
|
27
|
+
end
|
28
|
+
@system
|
29
|
+
end
|
30
|
+
|
31
|
+
def restart
|
32
|
+
MadeleineServer.delete_storage(self)
|
33
|
+
@system = nil
|
34
|
+
instance
|
35
|
+
end
|
36
|
+
|
37
|
+
def clean_old_snapshots
|
38
|
+
instance
|
39
|
+
@madeleine_server.clean_storage(self)
|
40
|
+
end
|
41
|
+
|
42
|
+
def take_snapshot
|
43
|
+
instance
|
44
|
+
@madeleine_server.force_snapshot
|
45
|
+
end
|
46
|
+
|
47
|
+
def snapshot_interval_hours
|
48
|
+
instance
|
49
|
+
@madeleine_server.snapshot_interval.div MadeleineServer::ONE_HOUR rescue 1
|
50
|
+
end
|
51
|
+
|
52
|
+
def snapshot_interval_hours= hours
|
53
|
+
instance
|
54
|
+
@madeleine_server.snapshot_interval = hours.to_i * MadeleineServer::ONE_HOUR rescue MadeleineServer::ONE_HOUR
|
55
|
+
end
|
56
|
+
|
57
|
+
def request_stop
|
58
|
+
instance
|
59
|
+
@madeleine_server.request_stop
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
require 'fileutils'
|
66
|
+
class MadeleineServer
|
67
|
+
|
68
|
+
attr_reader :storage_path
|
69
|
+
attr_accessor :snapshot_interval
|
70
|
+
|
71
|
+
# Clears all the command_log and snapshot files located in the storage directory, so the
|
72
|
+
# database is essentially dropped and recreated as blank. Used in tests.
|
73
|
+
def self.delete_storage(service)
|
74
|
+
if (File.directory?(service.storage_path))
|
75
|
+
FileUtils.rm_rf(Dir[service.storage_path + '/*.command_log'])
|
76
|
+
FileUtils.rm_rf(Dir[service.storage_path + '/*.snapshot'])
|
77
|
+
else
|
78
|
+
FileUtils.mkdir_p(service.storage_path)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def clean_storage(service)
|
83
|
+
force_snapshot
|
84
|
+
command_logs = Dir[service.storage_path + '/*.command_log']
|
85
|
+
raise 'Error: existing command_logs after snapshot' unless command_logs.empty?
|
86
|
+
|
87
|
+
snapshots = Dir[service.storage_path + '/*.snapshot']
|
88
|
+
FileUtils.rm_rf(snapshots.sort[0..-2])
|
89
|
+
end
|
90
|
+
|
91
|
+
|
92
|
+
def initialize(service)
|
93
|
+
@storage_path = service.storage_path
|
94
|
+
@snapshot_interval = ONE_HOUR
|
95
|
+
marshaller = Madeleine::ZMarshal.new()
|
96
|
+
@server = Madeleine::Automatic::AutomaticSnapshotMadeleine.new(service.storage_path,
|
97
|
+
marshaller) { service.new }
|
98
|
+
start_snapshot_thread
|
99
|
+
end
|
100
|
+
|
101
|
+
def system
|
102
|
+
@server.system
|
103
|
+
end
|
104
|
+
|
105
|
+
def command_log_present?
|
106
|
+
not Dir[File.join(File.expand_path(storage_path), '*.command_log')].empty?
|
107
|
+
end
|
108
|
+
|
109
|
+
def force_snapshot
|
110
|
+
begin
|
111
|
+
hours_since_last_snapshot = 0
|
112
|
+
@server.take_snapshot
|
113
|
+
rescue => e
|
114
|
+
sleep(ONE_MINUTE)
|
115
|
+
retry
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
|
120
|
+
ONE_MINUTE = 60
|
121
|
+
ONE_HOUR = ONE_MINUTE * 60
|
122
|
+
MAX_INTERVAL_HOURS = 24 * 2
|
123
|
+
|
124
|
+
def start_snapshot_thread
|
125
|
+
@snapshot_thread = Thread.new(@server) {
|
126
|
+
hours_since_last_snapshot = 0
|
127
|
+
while not @request_stop
|
128
|
+
sleep(snapshot_interval)
|
129
|
+
hours_since_last_snapshot += snapshot_interval.div ONE_HOUR
|
130
|
+
begin
|
131
|
+
# Take a snapshot if there is a command log
|
132
|
+
if command_log_present? or hours_since_last_snapshot > MAX_INTERVAL_HOURS
|
133
|
+
# 'Taking a Madeleine snapshot'
|
134
|
+
@server.take_snapshot
|
135
|
+
hours_since_last_snapshot = 0
|
136
|
+
puts "[#{DateTime.now.strftime '%F %T'}] INFO Taking snapshot"
|
137
|
+
else
|
138
|
+
puts "[#{DateTime.now.strftime '%F %T'}] INFO Skipping snapshot (no command logs)"
|
139
|
+
end
|
140
|
+
rescue => e
|
141
|
+
# wait for a minute (not to spoof the log with the same error)
|
142
|
+
# and go back into the loop, to keep trying
|
143
|
+
sleep(ONE_MINUTE)
|
144
|
+
retry
|
145
|
+
end
|
146
|
+
end
|
147
|
+
}
|
148
|
+
end
|
149
|
+
|
150
|
+
def request_stop
|
151
|
+
begin
|
152
|
+
@request_stop = true
|
153
|
+
if @snapshot_thread and @snapshot_thread.alive?
|
154
|
+
@snapshot_thread.wakeup
|
155
|
+
@snapshot_thread.join
|
156
|
+
end
|
157
|
+
@server.take_snapshot if command_log_present?
|
158
|
+
rescue => detail
|
159
|
+
puts detail
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
163
|
end
|
data/pimki.rb
CHANGED
@@ -1,182 +1,182 @@
|
|
1
|
-
#!/usr/local/bin/ruby
|
2
|
-
|
3
|
-
if RUBY_VERSION < "1.8.1"
|
4
|
-
puts "Pimki requires Ruby 1.8.1+"
|
5
|
-
exit
|
6
|
-
end
|
7
|
-
|
8
|
-
# Handle rubygems support & loading libraries: {{{
|
9
|
-
$LOAD_PATH.unshift File.join(File.dirname(__FILE__), "libraries")
|
10
|
-
begin
|
11
|
-
require 'rubygems'
|
12
|
-
require_gem 'madeleine'
|
13
|
-
require_gem 'BlueCloth'
|
14
|
-
require_gem 'rubyzip'
|
15
|
-
require 'zip/zip'
|
16
|
-
|
17
|
-
rescue LoadError => detail
|
18
|
-
# no rubygems, so load from the libraries directory
|
19
|
-
p detail if $DEBUG
|
20
|
-
puts 'Unable to load libraries through RubyGems. Loading from ./libraries directory'
|
21
|
-
require 'bluecloth'
|
22
|
-
require 'zip/zip'
|
23
|
-
end
|
24
|
-
# }}}
|
25
|
-
|
26
|
-
# Handle command-line options: {{{
|
27
|
-
require 'optparse'
|
28
|
-
|
29
|
-
cdir = File.expand_path(File.dirname(__FILE__))
|
30
|
-
%w( /libraries/ /app/models /app/controllers ).each { |dir| $:.unshift(cdir + dir) }
|
31
|
-
%w( web_controller_server action_controller_servlet wiki_service wiki ).each { |lib| require lib }
|
32
|
-
|
33
|
-
fork_available = true
|
34
|
-
begin
|
35
|
-
exit unless fork
|
36
|
-
rescue NotImplementedError
|
37
|
-
fork_available = false
|
38
|
-
end
|
39
|
-
|
40
|
-
begin
|
41
|
-
pdflatex_available = system "pdflatex -version"
|
42
|
-
rescue Errno::ENOENT
|
43
|
-
pdflatex_available = false
|
44
|
-
end
|
45
|
-
|
46
|
-
graphviz_available = (`dot -V 2>&1` =~ /dot version/)
|
47
|
-
|
48
|
-
OPTIONS = {
|
49
|
-
:server_type => fork_available ? Daemon : SimpleServer,
|
50
|
-
:port => 2500,
|
51
|
-
:storage => "#{Dir.pwd}/storage",
|
52
|
-
:pdflatex => pdflatex_available,
|
53
|
-
:redcloth => '3',
|
54
|
-
:graphviz_available => graphviz_available
|
55
|
-
}
|
56
|
-
|
57
|
-
ARGV.options do |opts|
|
58
|
-
script_name = File.basename($0)
|
59
|
-
opts.banner = "Usage: ruby #{script_name} [options]"
|
60
|
-
|
61
|
-
opts.separator ""
|
62
|
-
|
63
|
-
opts.on("-p", "--port=port", Integer,
|
64
|
-
"Runs Instiki on the specified port.",
|
65
|
-
"Default: 2500\n") { |OPTIONS[:port]| }
|
66
|
-
opts.on("-s", "--simple", "--simple-server",
|
67
|
-
"Forces Instiki not to run as a Daemon if fork is available.\n"
|
68
|
-
) { OPTIONS[:server_type] = SimpleServer }
|
69
|
-
opts.on("-t", "--storage=storage", String,
|
70
|
-
"Makes Instiki use the specified directory for storage.",
|
71
|
-
"Default: [cwd]/storage/[port]\n") { |OPTIONS[:storage]| }
|
72
|
-
opts.on("-r", "--redcloth VERSION", String,
|
73
|
-
"Makes Instiki use the specified RedCloth version.",
|
74
|
-
"You can specify major version only (2 or 3)",
|
75
|
-
"Default: 2.0.11\n") { |OPTIONS[:redcloth]| }
|
76
|
-
|
77
|
-
|
78
|
-
opts.separator ""
|
79
|
-
|
80
|
-
opts.on("-h", "--help",
|
81
|
-
"Show this help message.") { puts opts; exit }
|
82
|
-
|
83
|
-
opts.parse!
|
84
|
-
end
|
85
|
-
|
86
|
-
Socket.do_not_reverse_lookup = true
|
87
|
-
#}}}
|
88
|
-
|
89
|
-
# RedCloth + Modifications: {{{
|
90
|
-
begin
|
91
|
-
if defined? Gem
|
92
|
-
if Gem.source_index.search('redcloth').map { |s| s.version.version }.include? OPTIONS[:redcloth]
|
93
|
-
require_gem 'RedCloth', "#{OPTIONS[:redcloth]}"
|
94
|
-
else
|
95
|
-
require_gem 'RedCloth', "~> #{OPTIONS[:redcloth]}"
|
96
|
-
end
|
97
|
-
end
|
98
|
-
|
99
|
-
rescue LoadError, NoMethodError => detail
|
100
|
-
if OPTIONS[:redcloth] < '3'
|
101
|
-
require 'redcloth_2.0.11'
|
102
|
-
else
|
103
|
-
require 'redcloth'
|
104
|
-
end
|
105
|
-
end
|
106
|
-
|
107
|
-
# Remove the "caps" spanning:
|
108
|
-
RedCloth::GLYPHS.delete_if { |glyph| glyph[1] =~ /class=\"caps\"/ }
|
109
|
-
|
110
|
-
# Fix missing hard_break in RedCloth 3.0.0/1
|
111
|
-
if ['3.0.0', '3.0.1'].include? RedCloth::VERSION
|
112
|
-
class RedCloth #{{{
|
113
|
-
# Attempts at fixing the list blocks recognition:
|
114
|
-
#BLOCKS_GROUP_RE = /(^([#*> ])(?:[^\n]|\n+|\n(?!\n|\Z))+)|((?:[^\n]+|\n+ +|\n(?![#*\n]|\Z))+)/m
|
115
|
-
|
116
|
-
def to_html( *rules ) #{{{
|
117
|
-
rules = @rules if rules.empty?
|
118
|
-
# make our working copy
|
119
|
-
text = self.dup
|
120
|
-
|
121
|
-
@urlrefs = {}
|
122
|
-
@shelf = []
|
123
|
-
textile_rules = [:refs_textile, :block_textile_table, :block_textile_lists,
|
124
|
-
:block_textile_prefix, :inline_textile_image, :inline_textile_link,
|
125
|
-
:inline_textile_code, :inline_textile_span, :inline_textile_glyphs]
|
126
|
-
markdown_rules = [:refs_markdown, :block_markdown_setext, :block_markdown_atx, :block_markdown_rule,
|
127
|
-
:block_markdown_bq, :block_markdown_lists,
|
128
|
-
:inline_markdown_reflink, :inline_markdown_link]
|
129
|
-
@rules = rules.collect do |rule|
|
130
|
-
case rule
|
131
|
-
when :markdown
|
132
|
-
markdown_rules
|
133
|
-
when :textile
|
134
|
-
textile_rules
|
135
|
-
else
|
136
|
-
rule
|
137
|
-
end
|
138
|
-
end.flatten
|
139
|
-
|
140
|
-
# standard clean up
|
141
|
-
incoming_entities text
|
142
|
-
clean_white_space text
|
143
|
-
|
144
|
-
# start processor
|
145
|
-
pre_list = rip_offtags text
|
146
|
-
refs text
|
147
|
-
blocks text
|
148
|
-
inline text
|
149
|
-
smooth_offtags text, pre_list
|
150
|
-
|
151
|
-
retrieve text
|
152
|
-
hard_break text # PIMKI: this is the missing bit!
|
153
|
-
|
154
|
-
text.gsub!( /<\/?notextile>/, '' )
|
155
|
-
text.gsub!( /x%x%/, '&' )
|
156
|
-
text.strip!
|
157
|
-
text
|
158
|
-
end #}}}
|
159
|
-
end #}}}
|
160
|
-
end #}}}
|
161
|
-
|
162
|
-
# Start the application: {{{
|
163
|
-
storage_dir = OPTIONS[:storage]
|
164
|
-
require 'fileutils'
|
165
|
-
FileUtils.mkdir_p(storage_dir)
|
166
|
-
WikiService.storage_path = storage_dir
|
167
|
-
|
168
|
-
WikiController.template_root = "#{cdir}/app/views/"
|
169
|
-
|
170
|
-
on_exit = lambda {
|
171
|
-
WebControllerServer::the_active_server.shutdown
|
172
|
-
WikiService.request_stop
|
173
|
-
}
|
174
|
-
|
175
|
-
trap "INT", on_exit
|
176
|
-
trap "TERM", on_exit
|
177
|
-
|
178
|
-
WebControllerServer.new(OPTIONS[:port], OPTIONS[:server_type], "#{cdir}/app/controllers/")
|
179
|
-
|
180
|
-
# }}}
|
181
|
-
|
1
|
+
#!/usr/local/bin/ruby
|
2
|
+
|
3
|
+
if RUBY_VERSION < "1.8.1"
|
4
|
+
puts "Pimki requires Ruby 1.8.1+"
|
5
|
+
exit
|
6
|
+
end
|
7
|
+
|
8
|
+
# Handle rubygems support & loading libraries: {{{
|
9
|
+
$LOAD_PATH.unshift File.join(File.dirname(__FILE__), "libraries")
|
10
|
+
begin
|
11
|
+
require 'rubygems'
|
12
|
+
require_gem 'madeleine'
|
13
|
+
require_gem 'BlueCloth'
|
14
|
+
require_gem 'rubyzip'
|
15
|
+
require 'zip/zip'
|
16
|
+
|
17
|
+
rescue LoadError => detail
|
18
|
+
# no rubygems, so load from the libraries directory
|
19
|
+
p detail if $DEBUG
|
20
|
+
puts 'Unable to load libraries through RubyGems. Loading from ./libraries directory'
|
21
|
+
require 'bluecloth'
|
22
|
+
require 'zip/zip'
|
23
|
+
end
|
24
|
+
# }}}
|
25
|
+
|
26
|
+
# Handle command-line options: {{{
|
27
|
+
require 'optparse'
|
28
|
+
|
29
|
+
cdir = File.expand_path(File.dirname(__FILE__))
|
30
|
+
%w( /libraries/ /app/models /app/controllers ).each { |dir| $:.unshift(cdir + dir) }
|
31
|
+
%w( web_controller_server action_controller_servlet wiki_service wiki ).each { |lib| require lib }
|
32
|
+
|
33
|
+
fork_available = true
|
34
|
+
begin
|
35
|
+
exit unless fork
|
36
|
+
rescue NotImplementedError
|
37
|
+
fork_available = false
|
38
|
+
end
|
39
|
+
|
40
|
+
begin
|
41
|
+
pdflatex_available = system "pdflatex -version"
|
42
|
+
rescue Errno::ENOENT
|
43
|
+
pdflatex_available = false
|
44
|
+
end
|
45
|
+
|
46
|
+
graphviz_available = (`dot -V 2>&1` =~ /dot version/)
|
47
|
+
|
48
|
+
OPTIONS = {
|
49
|
+
:server_type => fork_available ? Daemon : SimpleServer,
|
50
|
+
:port => 2500,
|
51
|
+
:storage => "#{Dir.pwd}/storage",
|
52
|
+
:pdflatex => pdflatex_available,
|
53
|
+
:redcloth => '3',
|
54
|
+
:graphviz_available => graphviz_available
|
55
|
+
}
|
56
|
+
|
57
|
+
ARGV.options do |opts|
|
58
|
+
script_name = File.basename($0)
|
59
|
+
opts.banner = "Usage: ruby #{script_name} [options]"
|
60
|
+
|
61
|
+
opts.separator ""
|
62
|
+
|
63
|
+
opts.on("-p", "--port=port", Integer,
|
64
|
+
"Runs Instiki on the specified port.",
|
65
|
+
"Default: 2500\n") { |OPTIONS[:port]| }
|
66
|
+
opts.on("-s", "--simple", "--simple-server",
|
67
|
+
"Forces Instiki not to run as a Daemon if fork is available.\n"
|
68
|
+
) { OPTIONS[:server_type] = SimpleServer }
|
69
|
+
opts.on("-t", "--storage=storage", String,
|
70
|
+
"Makes Instiki use the specified directory for storage.",
|
71
|
+
"Default: [cwd]/storage/[port]\n") { |OPTIONS[:storage]| }
|
72
|
+
opts.on("-r", "--redcloth VERSION", String,
|
73
|
+
"Makes Instiki use the specified RedCloth version.",
|
74
|
+
"You can specify major version only (2 or 3)",
|
75
|
+
"Default: 2.0.11\n") { |OPTIONS[:redcloth]| }
|
76
|
+
|
77
|
+
|
78
|
+
opts.separator ""
|
79
|
+
|
80
|
+
opts.on("-h", "--help",
|
81
|
+
"Show this help message.") { puts opts; exit }
|
82
|
+
|
83
|
+
opts.parse!
|
84
|
+
end
|
85
|
+
|
86
|
+
Socket.do_not_reverse_lookup = true
|
87
|
+
#}}}
|
88
|
+
|
89
|
+
# RedCloth + Modifications: {{{
|
90
|
+
begin
|
91
|
+
if defined? Gem
|
92
|
+
if Gem.source_index.search('redcloth').map { |s| s.version.version }.include? OPTIONS[:redcloth]
|
93
|
+
require_gem 'RedCloth', "#{OPTIONS[:redcloth]}"
|
94
|
+
else
|
95
|
+
require_gem 'RedCloth', "~> #{OPTIONS[:redcloth]}"
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
rescue LoadError, NoMethodError => detail
|
100
|
+
if OPTIONS[:redcloth] < '3'
|
101
|
+
require 'redcloth_2.0.11'
|
102
|
+
else
|
103
|
+
require 'redcloth'
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
# Remove the "caps" spanning:
|
108
|
+
RedCloth::GLYPHS.delete_if { |glyph| glyph[1] =~ /class=\"caps\"/ }
|
109
|
+
|
110
|
+
# Fix missing hard_break in RedCloth 3.0.0/1
|
111
|
+
if ['3.0.0', '3.0.1'].include? RedCloth::VERSION
|
112
|
+
class RedCloth #{{{
|
113
|
+
# Attempts at fixing the list blocks recognition:
|
114
|
+
#BLOCKS_GROUP_RE = /(^([#*> ])(?:[^\n]|\n+|\n(?!\n|\Z))+)|((?:[^\n]+|\n+ +|\n(?![#*\n]|\Z))+)/m
|
115
|
+
|
116
|
+
def to_html( *rules ) #{{{
|
117
|
+
rules = @rules if rules.empty?
|
118
|
+
# make our working copy
|
119
|
+
text = self.dup
|
120
|
+
|
121
|
+
@urlrefs = {}
|
122
|
+
@shelf = []
|
123
|
+
textile_rules = [:refs_textile, :block_textile_table, :block_textile_lists,
|
124
|
+
:block_textile_prefix, :inline_textile_image, :inline_textile_link,
|
125
|
+
:inline_textile_code, :inline_textile_span, :inline_textile_glyphs]
|
126
|
+
markdown_rules = [:refs_markdown, :block_markdown_setext, :block_markdown_atx, :block_markdown_rule,
|
127
|
+
:block_markdown_bq, :block_markdown_lists,
|
128
|
+
:inline_markdown_reflink, :inline_markdown_link]
|
129
|
+
@rules = rules.collect do |rule|
|
130
|
+
case rule
|
131
|
+
when :markdown
|
132
|
+
markdown_rules
|
133
|
+
when :textile
|
134
|
+
textile_rules
|
135
|
+
else
|
136
|
+
rule
|
137
|
+
end
|
138
|
+
end.flatten
|
139
|
+
|
140
|
+
# standard clean up
|
141
|
+
incoming_entities text
|
142
|
+
clean_white_space text
|
143
|
+
|
144
|
+
# start processor
|
145
|
+
pre_list = rip_offtags text
|
146
|
+
refs text
|
147
|
+
blocks text
|
148
|
+
inline text
|
149
|
+
smooth_offtags text, pre_list
|
150
|
+
|
151
|
+
retrieve text
|
152
|
+
hard_break text # PIMKI: this is the missing bit!
|
153
|
+
|
154
|
+
text.gsub!( /<\/?notextile>/, '' )
|
155
|
+
text.gsub!( /x%x%/, '&' )
|
156
|
+
text.strip!
|
157
|
+
text
|
158
|
+
end #}}}
|
159
|
+
end #}}}
|
160
|
+
end #}}}
|
161
|
+
|
162
|
+
# Start the application: {{{
|
163
|
+
storage_dir = File.join(OPTIONS[:storage], OPTIONS[:port].to_s)
|
164
|
+
require 'fileutils'
|
165
|
+
FileUtils.mkdir_p(storage_dir)
|
166
|
+
WikiService.storage_path = storage_dir
|
167
|
+
|
168
|
+
WikiController.template_root = "#{cdir}/app/views/"
|
169
|
+
|
170
|
+
on_exit = lambda {
|
171
|
+
WebControllerServer::the_active_server.shutdown
|
172
|
+
WikiService.request_stop
|
173
|
+
}
|
174
|
+
|
175
|
+
trap "INT", on_exit
|
176
|
+
trap "TERM", on_exit
|
177
|
+
|
178
|
+
WebControllerServer.new(OPTIONS[:port], OPTIONS[:server_type], "#{cdir}/app/controllers/")
|
179
|
+
|
180
|
+
# }}}
|
181
|
+
|
182
182
|
# jEdit :folding=explicit:collapseFolds=1:
|