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
data/app/models/wiki_service.rb
CHANGED
@@ -1,233 +1,233 @@
|
|
1
|
-
require 'open-uri'
|
2
|
-
require 'yaml'
|
3
|
-
require 'madeleine'
|
4
|
-
require 'madeleine/automatic'
|
5
|
-
require 'madeleine/zmarshal'
|
6
|
-
|
7
|
-
require 'web'
|
8
|
-
require 'page'
|
9
|
-
require 'author'
|
10
|
-
require 'file_yard'
|
11
|
-
require 'instiki_errors'
|
12
|
-
|
13
|
-
module AbstractWikiService
|
14
|
-
|
15
|
-
attr_reader :webs, :system
|
16
|
-
|
17
|
-
def authenticate(password)
|
18
|
-
# system['password'] variant is for compatibility with storages from older versions
|
19
|
-
password == (@system[:password] || @system['password'] || 'instiki')
|
20
|
-
end
|
21
|
-
|
22
|
-
def create_web(name, address, password = nil)
|
23
|
-
@webs[address] = Web.new(self, name, address, password) unless @webs[address]
|
24
|
-
end
|
25
|
-
|
26
|
-
def delete_web(address)
|
27
|
-
@webs[address] = nil
|
28
|
-
end
|
29
|
-
|
30
|
-
def file_yard(web)
|
31
|
-
raise "Web #{@web.name} does not belong to this wiki service" unless @webs.values.include?(web)
|
32
|
-
# TODO cache FileYards
|
33
|
-
FileYard.new("#{self.storage_path}/#{web.address}", web.max_upload_size)
|
34
|
-
end
|
35
|
-
|
36
|
-
def init_wiki_service
|
37
|
-
@webs = {}
|
38
|
-
@system = {}
|
39
|
-
end
|
40
|
-
|
41
|
-
def read_page(web_address, page_name)
|
42
|
-
ApplicationController.logger.debug "Reading page '#{page_name}' from web '#{web_address}'"
|
43
|
-
web = @webs[web_address]
|
44
|
-
if web.nil?
|
45
|
-
ApplicationController.logger.debug "Web '#{web_address}' not found"
|
46
|
-
return nil
|
47
|
-
else
|
48
|
-
page = web.pages[page_name]
|
49
|
-
ApplicationController.logger.debug "Page '#{page_name}' #{page.nil? ? 'not' : ''} found"
|
50
|
-
return page
|
51
|
-
end
|
52
|
-
end
|
53
|
-
|
54
|
-
def remove_orphaned_pages(web_address)
|
55
|
-
@webs[web_address].remove_pages(@webs[web_address].select.orphaned_pages)
|
56
|
-
end
|
57
|
-
|
58
|
-
def revise_page(web_address, page_name, content, revised_on, author)
|
59
|
-
page = read_page(web_address, page_name)
|
60
|
-
page.revise(content, revised_on, author)
|
61
|
-
page
|
62
|
-
end
|
63
|
-
|
64
|
-
def rollback_page(web_address, page_name, revision_number, created_at, author_id = nil)
|
65
|
-
page = read_page(web_address, page_name)
|
66
|
-
page.rollback(revision_number, created_at, author_id)
|
67
|
-
page
|
68
|
-
end
|
69
|
-
|
70
|
-
def setup(password, web_name, web_address)
|
71
|
-
@system[:password] = password
|
72
|
-
create_web(web_name, web_address)
|
73
|
-
end
|
74
|
-
|
75
|
-
def setup?
|
76
|
-
not (@webs.empty?)
|
77
|
-
end
|
78
|
-
|
79
|
-
def edit_web(old_address, new_address, name, markup, color, additional_style, safe_mode = false,
|
80
|
-
password = nil, published = false, brackets_only = false, count_pages = false,
|
81
|
-
allow_uploads = true, max_upload_size = nil)
|
82
|
-
|
83
|
-
if not @webs.key? old_address
|
84
|
-
raise Instiki::ValidationError.new("Web with address '#{old_address}' does not exist")
|
85
|
-
end
|
86
|
-
|
87
|
-
if old_address != new_address
|
88
|
-
if @webs.key? new_address
|
89
|
-
raise Instiki::ValidationError.new("There is already a web with address '#{new_address}'")
|
90
|
-
end
|
91
|
-
@webs[new_address] = @webs[old_address]
|
92
|
-
@webs.delete(old_address)
|
93
|
-
@webs[new_address].address = new_address
|
94
|
-
end
|
95
|
-
|
96
|
-
web = @webs[new_address]
|
97
|
-
web.refresh_revisions if settings_changed?(web, markup, safe_mode, brackets_only)
|
98
|
-
|
99
|
-
web.name, web.markup, web.color, web.additional_style, web.safe_mode =
|
100
|
-
name, markup, color, additional_style, safe_mode
|
101
|
-
|
102
|
-
web.password, web.published, web.brackets_only, web.count_pages =
|
103
|
-
password, published, brackets_only, count_pages, allow_uploads
|
104
|
-
web.allow_uploads, web.max_upload_size = allow_uploads, max_upload_size.to_i
|
105
|
-
end
|
106
|
-
|
107
|
-
def write_page(web_address, page_name, content, written_on, author)
|
108
|
-
page = Page.new(@webs[web_address], page_name, content, written_on, author)
|
109
|
-
@webs[web_address].add_page(page)
|
110
|
-
page
|
111
|
-
end
|
112
|
-
|
113
|
-
def storage_path
|
114
|
-
self.class.storage_path
|
115
|
-
end
|
116
|
-
|
117
|
-
private
|
118
|
-
def settings_changed?(web, markup, safe_mode, brackets_only)
|
119
|
-
web.markup != markup ||
|
120
|
-
web.safe_mode != safe_mode ||
|
121
|
-
web.brackets_only != brackets_only
|
122
|
-
end
|
123
|
-
end
|
124
|
-
|
125
|
-
class WikiService
|
126
|
-
|
127
|
-
include AbstractWikiService
|
128
|
-
include Madeleine::Automatic::Interceptor
|
129
|
-
|
130
|
-
# These methods do not change the state of persistent objects, and
|
131
|
-
# should not be
|
132
|
-
automatic_read_only :authenticate, :read_page, :setup?, :webs, :storage_path, :file_yard
|
133
|
-
|
134
|
-
@@storage_path = './storage/'
|
135
|
-
|
136
|
-
class << self
|
137
|
-
|
138
|
-
def storage_path=(storage_path)
|
139
|
-
@@storage_path = storage_path
|
140
|
-
end
|
141
|
-
|
142
|
-
def storage_path
|
143
|
-
@@storage_path
|
144
|
-
end
|
145
|
-
|
146
|
-
def clean_storage
|
147
|
-
MadeleineServer.clean_storage(self)
|
148
|
-
end
|
149
|
-
|
150
|
-
def instance
|
151
|
-
@madeleine ||= MadeleineServer.new(self)
|
152
|
-
@system = @madeleine.system
|
153
|
-
return @system
|
154
|
-
end
|
155
|
-
|
156
|
-
def snapshot
|
157
|
-
@madeleine.snapshot
|
158
|
-
end
|
159
|
-
|
160
|
-
end
|
161
|
-
|
162
|
-
def initialize
|
163
|
-
init_wiki_service
|
164
|
-
end
|
165
|
-
|
166
|
-
end
|
167
|
-
|
168
|
-
class MadeleineServer
|
169
|
-
|
170
|
-
attr_reader :storage_path
|
171
|
-
|
172
|
-
# Clears all the command_log and snapshot files located in the storage directory, so the
|
173
|
-
# database is essentially dropped and recreated as blank
|
174
|
-
def self.clean_storage(service)
|
175
|
-
begin
|
176
|
-
Dir.foreach(service.storage_path) do |file|
|
177
|
-
if file =~ /(command_log|snapshot)$/
|
178
|
-
File.delete(File.join(service.storage_path, file))
|
179
|
-
end
|
180
|
-
end
|
181
|
-
rescue
|
182
|
-
Dir.mkdir(service.storage_path)
|
183
|
-
end
|
184
|
-
end
|
185
|
-
|
186
|
-
def initialize(service)
|
187
|
-
@storage_path = service.storage_path
|
188
|
-
@server = Madeleine::Automatic::AutomaticSnapshotMadeleine.new(service.storage_path,
|
189
|
-
Madeleine::ZMarshal.new) {
|
190
|
-
service.new
|
191
|
-
}
|
192
|
-
start_snapshot_thread
|
193
|
-
end
|
194
|
-
|
195
|
-
def command_log_present?
|
196
|
-
not Dir[storage_path + '/*.command_log'].empty?
|
197
|
-
end
|
198
|
-
|
199
|
-
def snapshot
|
200
|
-
@server.take_snapshot
|
201
|
-
end
|
202
|
-
|
203
|
-
def start_snapshot_thread
|
204
|
-
Thread.new(@server) {
|
205
|
-
hours_since_last_snapshot = 0
|
206
|
-
while true
|
207
|
-
begin
|
208
|
-
hours_since_last_snapshot += 1
|
209
|
-
# Take a snapshot if there is a command log, or 24 hours
|
210
|
-
# have passed since the last snapshot
|
211
|
-
if command_log_present? or hours_since_last_snapshot >= 24
|
212
|
-
ActionController::Base.logger.info "[#{Time.now.strftime('%Y-%m-%d %H:%M:%S')}] " +
|
213
|
-
'Taking a Madeleine snapshot'
|
214
|
-
snapshot
|
215
|
-
hours_since_last_snapshot = 0
|
216
|
-
end
|
217
|
-
sleep(1.hour)
|
218
|
-
rescue => e
|
219
|
-
ActionController::Base.logger.error(e)
|
220
|
-
# wait for a minute (not to spoof the log with the same error)
|
221
|
-
# and go back into the loop, to keep trying
|
222
|
-
sleep(1.minute)
|
223
|
-
ActionController::Base.logger.info("Retrying to save a snapshot")
|
224
|
-
end
|
225
|
-
end
|
226
|
-
}
|
227
|
-
end
|
228
|
-
|
229
|
-
def system
|
230
|
-
@server.system
|
231
|
-
end
|
232
|
-
|
233
|
-
end
|
1
|
+
require 'open-uri'
|
2
|
+
require 'yaml'
|
3
|
+
require 'madeleine'
|
4
|
+
require 'madeleine/automatic'
|
5
|
+
require 'madeleine/zmarshal'
|
6
|
+
|
7
|
+
require 'web'
|
8
|
+
require 'page'
|
9
|
+
require 'author'
|
10
|
+
require 'file_yard'
|
11
|
+
require 'instiki_errors'
|
12
|
+
|
13
|
+
module AbstractWikiService
|
14
|
+
|
15
|
+
attr_reader :webs, :system
|
16
|
+
|
17
|
+
def authenticate(password)
|
18
|
+
# system['password'] variant is for compatibility with storages from older versions
|
19
|
+
password == (@system[:password] || @system['password'] || 'instiki')
|
20
|
+
end
|
21
|
+
|
22
|
+
def create_web(name, address, password = nil)
|
23
|
+
@webs[address] = Web.new(self, name, address, password) unless @webs[address]
|
24
|
+
end
|
25
|
+
|
26
|
+
def delete_web(address)
|
27
|
+
@webs[address] = nil
|
28
|
+
end
|
29
|
+
|
30
|
+
def file_yard(web)
|
31
|
+
raise "Web #{@web.name} does not belong to this wiki service" unless @webs.values.include?(web)
|
32
|
+
# TODO cache FileYards
|
33
|
+
FileYard.new("#{self.storage_path}/#{web.address}", web.max_upload_size)
|
34
|
+
end
|
35
|
+
|
36
|
+
def init_wiki_service
|
37
|
+
@webs = {}
|
38
|
+
@system = {}
|
39
|
+
end
|
40
|
+
|
41
|
+
def read_page(web_address, page_name)
|
42
|
+
ApplicationController.logger.debug "Reading page '#{page_name}' from web '#{web_address}'"
|
43
|
+
web = @webs[web_address]
|
44
|
+
if web.nil?
|
45
|
+
ApplicationController.logger.debug "Web '#{web_address}' not found"
|
46
|
+
return nil
|
47
|
+
else
|
48
|
+
page = web.pages[page_name]
|
49
|
+
ApplicationController.logger.debug "Page '#{page_name}' #{page.nil? ? 'not' : ''} found"
|
50
|
+
return page
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def remove_orphaned_pages(web_address)
|
55
|
+
@webs[web_address].remove_pages(@webs[web_address].select.orphaned_pages)
|
56
|
+
end
|
57
|
+
|
58
|
+
def revise_page(web_address, page_name, content, revised_on, author)
|
59
|
+
page = read_page(web_address, page_name)
|
60
|
+
page.revise(content, revised_on, author)
|
61
|
+
page
|
62
|
+
end
|
63
|
+
|
64
|
+
def rollback_page(web_address, page_name, revision_number, created_at, author_id = nil)
|
65
|
+
page = read_page(web_address, page_name)
|
66
|
+
page.rollback(revision_number, created_at, author_id)
|
67
|
+
page
|
68
|
+
end
|
69
|
+
|
70
|
+
def setup(password, web_name, web_address)
|
71
|
+
@system[:password] = password
|
72
|
+
create_web(web_name, web_address)
|
73
|
+
end
|
74
|
+
|
75
|
+
def setup?
|
76
|
+
not (@webs.empty?)
|
77
|
+
end
|
78
|
+
|
79
|
+
def edit_web(old_address, new_address, name, markup, color, additional_style, safe_mode = false,
|
80
|
+
password = nil, published = false, brackets_only = false, count_pages = false,
|
81
|
+
allow_uploads = true, max_upload_size = nil)
|
82
|
+
|
83
|
+
if not @webs.key? old_address
|
84
|
+
raise Instiki::ValidationError.new("Web with address '#{old_address}' does not exist")
|
85
|
+
end
|
86
|
+
|
87
|
+
if old_address != new_address
|
88
|
+
if @webs.key? new_address
|
89
|
+
raise Instiki::ValidationError.new("There is already a web with address '#{new_address}'")
|
90
|
+
end
|
91
|
+
@webs[new_address] = @webs[old_address]
|
92
|
+
@webs.delete(old_address)
|
93
|
+
@webs[new_address].address = new_address
|
94
|
+
end
|
95
|
+
|
96
|
+
web = @webs[new_address]
|
97
|
+
web.refresh_revisions if settings_changed?(web, markup, safe_mode, brackets_only)
|
98
|
+
|
99
|
+
web.name, web.markup, web.color, web.additional_style, web.safe_mode =
|
100
|
+
name, markup, color, additional_style, safe_mode
|
101
|
+
|
102
|
+
web.password, web.published, web.brackets_only, web.count_pages =
|
103
|
+
password, published, brackets_only, count_pages, allow_uploads
|
104
|
+
web.allow_uploads, web.max_upload_size = allow_uploads, max_upload_size.to_i
|
105
|
+
end
|
106
|
+
|
107
|
+
def write_page(web_address, page_name, content, written_on, author)
|
108
|
+
page = Page.new(@webs[web_address], page_name, content, written_on, author)
|
109
|
+
@webs[web_address].add_page(page)
|
110
|
+
page
|
111
|
+
end
|
112
|
+
|
113
|
+
def storage_path
|
114
|
+
self.class.storage_path
|
115
|
+
end
|
116
|
+
|
117
|
+
private
|
118
|
+
def settings_changed?(web, markup, safe_mode, brackets_only)
|
119
|
+
web.markup != markup ||
|
120
|
+
web.safe_mode != safe_mode ||
|
121
|
+
web.brackets_only != brackets_only
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
class WikiService
|
126
|
+
|
127
|
+
include AbstractWikiService
|
128
|
+
include Madeleine::Automatic::Interceptor
|
129
|
+
|
130
|
+
# These methods do not change the state of persistent objects, and
|
131
|
+
# should not be logged by Madeleine
|
132
|
+
automatic_read_only :authenticate, :read_page, :setup?, :webs, :storage_path, :file_yard
|
133
|
+
|
134
|
+
@@storage_path = './storage/'
|
135
|
+
|
136
|
+
class << self
|
137
|
+
|
138
|
+
def storage_path=(storage_path)
|
139
|
+
@@storage_path = storage_path
|
140
|
+
end
|
141
|
+
|
142
|
+
def storage_path
|
143
|
+
@@storage_path
|
144
|
+
end
|
145
|
+
|
146
|
+
def clean_storage
|
147
|
+
MadeleineServer.clean_storage(self)
|
148
|
+
end
|
149
|
+
|
150
|
+
def instance
|
151
|
+
@madeleine ||= MadeleineServer.new(self)
|
152
|
+
@system = @madeleine.system
|
153
|
+
return @system
|
154
|
+
end
|
155
|
+
|
156
|
+
def snapshot
|
157
|
+
@madeleine.snapshot
|
158
|
+
end
|
159
|
+
|
160
|
+
end
|
161
|
+
|
162
|
+
def initialize
|
163
|
+
init_wiki_service
|
164
|
+
end
|
165
|
+
|
166
|
+
end
|
167
|
+
|
168
|
+
class MadeleineServer
|
169
|
+
|
170
|
+
attr_reader :storage_path
|
171
|
+
|
172
|
+
# Clears all the command_log and snapshot files located in the storage directory, so the
|
173
|
+
# database is essentially dropped and recreated as blank
|
174
|
+
def self.clean_storage(service)
|
175
|
+
begin
|
176
|
+
Dir.foreach(service.storage_path) do |file|
|
177
|
+
if file =~ /(command_log|snapshot)$/
|
178
|
+
File.delete(File.join(service.storage_path, file))
|
179
|
+
end
|
180
|
+
end
|
181
|
+
rescue
|
182
|
+
Dir.mkdir(service.storage_path)
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
def initialize(service)
|
187
|
+
@storage_path = service.storage_path
|
188
|
+
@server = Madeleine::Automatic::AutomaticSnapshotMadeleine.new(service.storage_path,
|
189
|
+
Madeleine::ZMarshal.new) {
|
190
|
+
service.new
|
191
|
+
}
|
192
|
+
start_snapshot_thread
|
193
|
+
end
|
194
|
+
|
195
|
+
def command_log_present?
|
196
|
+
not Dir[storage_path + '/*.command_log'].empty?
|
197
|
+
end
|
198
|
+
|
199
|
+
def snapshot
|
200
|
+
@server.take_snapshot
|
201
|
+
end
|
202
|
+
|
203
|
+
def start_snapshot_thread
|
204
|
+
Thread.new(@server) {
|
205
|
+
hours_since_last_snapshot = 0
|
206
|
+
while true
|
207
|
+
begin
|
208
|
+
hours_since_last_snapshot += 1
|
209
|
+
# Take a snapshot if there is a command log, or 24 hours
|
210
|
+
# have passed since the last snapshot
|
211
|
+
if command_log_present? or hours_since_last_snapshot >= 24
|
212
|
+
ActionController::Base.logger.info "[#{Time.now.strftime('%Y-%m-%d %H:%M:%S')}] " +
|
213
|
+
'Taking a Madeleine snapshot'
|
214
|
+
snapshot
|
215
|
+
hours_since_last_snapshot = 0
|
216
|
+
end
|
217
|
+
sleep(1.hour)
|
218
|
+
rescue => e
|
219
|
+
ActionController::Base.logger.error(e)
|
220
|
+
# wait for a minute (not to spoof the log with the same error)
|
221
|
+
# and go back into the loop, to keep trying
|
222
|
+
sleep(1.minute)
|
223
|
+
ActionController::Base.logger.info("Retrying to save a snapshot")
|
224
|
+
end
|
225
|
+
end
|
226
|
+
}
|
227
|
+
end
|
228
|
+
|
229
|
+
def system
|
230
|
+
@server.system
|
231
|
+
end
|
232
|
+
|
233
|
+
end
|