sgfa 0.1.0
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.
- checksums.yaml +7 -0
- data/LICENSE.txt +674 -0
- data/README.txt +14 -0
- data/bin/sgfa +15 -0
- data/data/sgfa_web.css +240 -0
- data/lib/sgfa/binder.rb +627 -0
- data/lib/sgfa/binder_fs.rb +203 -0
- data/lib/sgfa/cli/binder.rb +160 -0
- data/lib/sgfa/cli/jacket.rb +299 -0
- data/lib/sgfa/cli.rb +36 -0
- data/lib/sgfa/demo/web_binders.rb +111 -0
- data/lib/sgfa/demo/web_css.rb +60 -0
- data/lib/sgfa/entry.rb +697 -0
- data/lib/sgfa/error.rb +95 -0
- data/lib/sgfa/history.rb +445 -0
- data/lib/sgfa/jacket.rb +556 -0
- data/lib/sgfa/jacket_fs.rb +136 -0
- data/lib/sgfa/lock_fs.rb +141 -0
- data/lib/sgfa/state_fs.rb +342 -0
- data/lib/sgfa/store_fs.rb +214 -0
- data/lib/sgfa/web/base.rb +225 -0
- data/lib/sgfa/web/binder.rb +1190 -0
- data/lib/sgfa.rb +37 -0
- metadata +68 -0
@@ -0,0 +1,1190 @@
|
|
1
|
+
#
|
2
|
+
# Simple Group of Filing Applications
|
3
|
+
# Web interface to Binder
|
4
|
+
#
|
5
|
+
# Copyright (C) 2015 by Graham A. Field.
|
6
|
+
#
|
7
|
+
# See LICENSE.txt for licensing information.
|
8
|
+
#
|
9
|
+
# This program is distributed WITHOUT ANY WARRANTY; without even the
|
10
|
+
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
11
|
+
|
12
|
+
require 'rack'
|
13
|
+
require 'time'
|
14
|
+
|
15
|
+
require_relative 'base'
|
16
|
+
require_relative '../error'
|
17
|
+
|
18
|
+
module Sgfa
|
19
|
+
module Web
|
20
|
+
|
21
|
+
|
22
|
+
#####################################################################
|
23
|
+
# Binder web interface
|
24
|
+
#
|
25
|
+
# @todo Add a docket view
|
26
|
+
class Binder < Base
|
27
|
+
|
28
|
+
#####################################
|
29
|
+
# Request
|
30
|
+
#
|
31
|
+
# @param env [Hash] The Rack environment for this request, with app
|
32
|
+
# specific options
|
33
|
+
# @option env [String] 'sgfa.binder.url' URL encoded binder name
|
34
|
+
# @option env [String] 'sgfa.binder.name' The name of the binder
|
35
|
+
# @option env [Binder] 'sgfa.binder' The binder
|
36
|
+
# @option env [String] 'sgfa.user' The user name
|
37
|
+
# @option env [Array] 'sgfa.groups' Array of groups the user belongs to
|
38
|
+
#
|
39
|
+
def call(env)
|
40
|
+
_call(env)
|
41
|
+
return response(env)
|
42
|
+
|
43
|
+
rescue Error::Permission => exp
|
44
|
+
env['sgfa.status'] = :deny
|
45
|
+
env['sgfa.html'] = _escape_html(exp.message)
|
46
|
+
return response(env)
|
47
|
+
|
48
|
+
rescue Error::Conflict => exp
|
49
|
+
env['sgfa.status'] = :conflict
|
50
|
+
env['sgfa.html'] = _escape_html(exp.message)
|
51
|
+
return response(env)
|
52
|
+
|
53
|
+
rescue Error::NonExistent => exp
|
54
|
+
env['sgfa.status'] = :notfound
|
55
|
+
env['sgfa.html'] = _escape_html(exp.message)
|
56
|
+
return response(env)
|
57
|
+
|
58
|
+
rescue Error::Limits => exp
|
59
|
+
env['sgfa.status'] = :badreq
|
60
|
+
env['sgfa.html'] = _escape_html(exp.message)
|
61
|
+
return response(env)
|
62
|
+
|
63
|
+
rescue Error::Corrupt => exp
|
64
|
+
env['sgfa.status'] = :servererror
|
65
|
+
env['sgfa.html'] = _escape_html(exp.message)
|
66
|
+
return response(env)
|
67
|
+
|
68
|
+
end # def request()
|
69
|
+
|
70
|
+
|
71
|
+
#####################################
|
72
|
+
# Process the request
|
73
|
+
#
|
74
|
+
# This is a seperate method to simplify the flow control by using return.
|
75
|
+
def _call(env)
|
76
|
+
|
77
|
+
# defaults
|
78
|
+
env['sgfa.status'] = :badreq
|
79
|
+
env['sgfa.title'] = 'SFGA Error'
|
80
|
+
env['sfga.navbar'] = ''
|
81
|
+
env['sgfa.html'] = 'Invalid request'
|
82
|
+
|
83
|
+
path = env['PATH_INFO'].split('/')
|
84
|
+
if path.empty?
|
85
|
+
jacket = nil
|
86
|
+
else
|
87
|
+
path.shift if path[0].empty?
|
88
|
+
jacket = path.shift
|
89
|
+
end
|
90
|
+
|
91
|
+
# just the binder
|
92
|
+
if !jacket
|
93
|
+
case env['REQUEST_METHOD']
|
94
|
+
when 'GET'; return _get_jackets(env, path)
|
95
|
+
when 'POST'; return _post_binder(env)
|
96
|
+
else; return
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
# special binder pages
|
101
|
+
if jacket[0] == '_'
|
102
|
+
return if env['REQUEST_METHOD'] != 'GET'
|
103
|
+
case jacket
|
104
|
+
when '_jackets'
|
105
|
+
return _get_jackets(env, path)
|
106
|
+
when '_users'
|
107
|
+
return _get_users(env, path)
|
108
|
+
when '_values'
|
109
|
+
return _get_values(env, path)
|
110
|
+
when '_info'
|
111
|
+
return _get_binder(env, path)
|
112
|
+
else
|
113
|
+
return
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
# jacket info stored
|
118
|
+
env['sgfa.jacket.url'] = jacket
|
119
|
+
env['sgfa.jacket.name'] = _escape_un(jacket)
|
120
|
+
cmd = path.shift
|
121
|
+
|
122
|
+
# just the jacket
|
123
|
+
if !cmd
|
124
|
+
case env['REQUEST_METHOD']
|
125
|
+
when 'GET'; return _get_tag(env, path)
|
126
|
+
when 'POST'; return _post_jacket(env)
|
127
|
+
else; return
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
# jacket stuff
|
132
|
+
return if env['REQUEST_METHOD'] != 'GET'
|
133
|
+
case cmd
|
134
|
+
when '_edit'; return _get_edit(env, path)
|
135
|
+
when '_entry'; return _get_entry(env, path)
|
136
|
+
when '_history'; return _get_history(env, path)
|
137
|
+
when '_attach'; return _get_attach(env, path)
|
138
|
+
when '_tag'; return _get_tag(env, path)
|
139
|
+
when '_log'; return _get_log(env, path)
|
140
|
+
when '_list'; return _get_list(env, path)
|
141
|
+
when '_info'; return _get_info(env, path)
|
142
|
+
else; return
|
143
|
+
end
|
144
|
+
|
145
|
+
end # def _call()
|
146
|
+
|
147
|
+
|
148
|
+
NavBarBinder = [
|
149
|
+
['Jackets', '_jackets'],
|
150
|
+
['Users', '_users'],
|
151
|
+
['Values', '_values'],
|
152
|
+
['Binder', '_info'],
|
153
|
+
]
|
154
|
+
|
155
|
+
|
156
|
+
#####################################
|
157
|
+
# Generate navigation bar for a binder
|
158
|
+
def _navbar_binder(env, act)
|
159
|
+
env['sgfa.title'] = 'SGFA Binder %s — %s' %
|
160
|
+
[act, _escape_html(env['sgfa.binder.name'])]
|
161
|
+
base = env['SCRIPT_NAME']
|
162
|
+
txt = _navbar(env, act, NavBarBinder, base)
|
163
|
+
if env['sgfa.cabinet.url']
|
164
|
+
txt << "<div class='link'><a href='%s'>Cabinet</a></div>\n" %
|
165
|
+
env['sgfa.cabinet.url']
|
166
|
+
end
|
167
|
+
env['sgfa.navbar'] = txt
|
168
|
+
end # def _navbar_binder()
|
169
|
+
|
170
|
+
|
171
|
+
NavBarJacket = [
|
172
|
+
['Tag', '_tag'],
|
173
|
+
['List', '_list'],
|
174
|
+
['Entry', nil],
|
175
|
+
['Edit', '_edit'],
|
176
|
+
['History', nil],
|
177
|
+
['Attachment', nil],
|
178
|
+
['Jacket', '_info'],
|
179
|
+
['Log', '_log'],
|
180
|
+
]
|
181
|
+
|
182
|
+
#####################################
|
183
|
+
# Generate navbar for a jacket
|
184
|
+
def _navbar_jacket(env, act)
|
185
|
+
env['sgfa.title'] = 'SGFA Jacket %s — %s : %s' % [
|
186
|
+
act,
|
187
|
+
_escape_html(env['sgfa.binder.name']),
|
188
|
+
_escape_html(env['sgfa.jacket.name'])
|
189
|
+
]
|
190
|
+
base = env['SCRIPT_NAME'] + '/' + env['sgfa.jacket.url']
|
191
|
+
txt = _navbar(env, act, NavBarJacket, base)
|
192
|
+
txt << "<div class='link'><a href='%s'>Binder</a></div>\n" %
|
193
|
+
env['SCRIPT_NAME']
|
194
|
+
env['sgfa.navbar'] = txt
|
195
|
+
end # def _navbar_jacket()
|
196
|
+
|
197
|
+
|
198
|
+
#####################################
|
199
|
+
# Link to a jacket
|
200
|
+
def _link_jacket(env, jnam, disp)
|
201
|
+
"<a href='%s/%s'>%s</a>" % [env['SCRIPT_NAME'], _escape(jnam), disp]
|
202
|
+
end # def _link_jacket()
|
203
|
+
|
204
|
+
|
205
|
+
#####################################
|
206
|
+
# Link to edit entry
|
207
|
+
def _link_edit(env, enum, disp)
|
208
|
+
"<a href='%s/%s/_edit/%d'>%s</a>" % [
|
209
|
+
env['SCRIPT_NAME'],
|
210
|
+
env['sgfa.jacket.url'],
|
211
|
+
enum,
|
212
|
+
disp
|
213
|
+
]
|
214
|
+
end # def _link_edit()
|
215
|
+
|
216
|
+
|
217
|
+
#####################################
|
218
|
+
# Link to display an entry
|
219
|
+
def _link_entry(env, enum, disp)
|
220
|
+
"<a href='%s/%s/_entry/%d'>%s</a>" % [
|
221
|
+
env['SCRIPT_NAME'],
|
222
|
+
env['sgfa.jacket.url'],
|
223
|
+
enum,
|
224
|
+
disp
|
225
|
+
]
|
226
|
+
end # def _link_entry()
|
227
|
+
|
228
|
+
|
229
|
+
#####################################
|
230
|
+
# Link to a specific revision
|
231
|
+
def _link_revision(env, enum, rnum, disp)
|
232
|
+
"<a href='%s/%s/_entry/%d/%d'>%s</a>" % [
|
233
|
+
env['SCRIPT_NAME'],
|
234
|
+
env['sgfa.jacket.url'],
|
235
|
+
enum,
|
236
|
+
rnum,
|
237
|
+
disp
|
238
|
+
]
|
239
|
+
end # def _link_revision()
|
240
|
+
|
241
|
+
|
242
|
+
#####################################
|
243
|
+
# Link to a history item
|
244
|
+
def _link_history(env, hnum, disp)
|
245
|
+
"<a href='%s/%s/_history/%d'>%s</a>" % [
|
246
|
+
env['SCRIPT_NAME'],
|
247
|
+
env['sgfa.jacket.url'],
|
248
|
+
hnum, disp
|
249
|
+
]
|
250
|
+
end # def _link_history()
|
251
|
+
|
252
|
+
|
253
|
+
#####################################
|
254
|
+
# Link to a tag
|
255
|
+
def _link_tag(env, tag, disp)
|
256
|
+
"<a href='%s/%s/_tag/%s'>%s</a>" % [
|
257
|
+
env['SCRIPT_NAME'],
|
258
|
+
env['sgfa.jacket.url'],
|
259
|
+
_escape(tag),
|
260
|
+
disp
|
261
|
+
]
|
262
|
+
end # def _link_tag()
|
263
|
+
|
264
|
+
|
265
|
+
#####################################
|
266
|
+
# Link to a tag prefix
|
267
|
+
def _link_prefix(env, pre, disp)
|
268
|
+
"<a href='%s/%s/_list/%s'>%s</a>" % [
|
269
|
+
env['SCRIPT_NAME'],
|
270
|
+
env['sgfa.jacket.url'],
|
271
|
+
_escape(pre),
|
272
|
+
disp
|
273
|
+
]
|
274
|
+
end # def _link_prefix()
|
275
|
+
|
276
|
+
|
277
|
+
#####################################
|
278
|
+
# Link to an attachments
|
279
|
+
def _link_attach(env, enum, anum, hnum, name, disp)
|
280
|
+
"<a href='%s/%s/_attach/%d-%d-%d/%s'>%s</a>" % [
|
281
|
+
env['SCRIPT_NAME'],
|
282
|
+
env['sgfa.jacket.url'],
|
283
|
+
enum,
|
284
|
+
anum,
|
285
|
+
hnum,
|
286
|
+
_escape(name),
|
287
|
+
disp
|
288
|
+
]
|
289
|
+
end # def _link_attach()
|
290
|
+
|
291
|
+
|
292
|
+
JacketsTable =
|
293
|
+
"<table class='list'>" +
|
294
|
+
"<tr><th>Jacket</th><th>Title</th><th>Perms</th></tr>\n" +
|
295
|
+
"%s</table>"
|
296
|
+
|
297
|
+
JacketsRow =
|
298
|
+
"<tr><td class='name'>%s</td>" +
|
299
|
+
"<td class='title'>%s</td><td class='perms'>%s</td></tr>\n"
|
300
|
+
|
301
|
+
JacketsForm =
|
302
|
+
"\n<hr>\n<form class='edit' method='post' action='%s' " +
|
303
|
+
"enctype='multipart/form-data'>\n" +
|
304
|
+
"<fieldset><legend>Create or Edit Jacket</legend>\n" +
|
305
|
+
"<label for='jacket'>Name:</label>" +
|
306
|
+
"<input class='jacket' name='jacket' type='text'><br>\n" +
|
307
|
+
"<label for='newname'>Rename:</label>" +
|
308
|
+
"<input class='jacket' name='newname' type='text'><br>\n" +
|
309
|
+
"<label for='title'>Title:</label>" +
|
310
|
+
"<input class='title' name='title' type='text'><br>\n" +
|
311
|
+
"<label for='perms'>Perms:</label>" +
|
312
|
+
"<input class='perms' name='perms' type='text'><br>\n" +
|
313
|
+
"</fieldset>\n" +
|
314
|
+
"<input type='submit' name='create' value='Create/Edit'>\n" +
|
315
|
+
"</form>\n"
|
316
|
+
|
317
|
+
#####################################
|
318
|
+
# Get jacket list
|
319
|
+
def _get_jackets(env, path)
|
320
|
+
_navbar_binder(env, 'Jackets')
|
321
|
+
|
322
|
+
if !path.empty?
|
323
|
+
env['sgfa.status'] = :badreq
|
324
|
+
env['sgfa.html'] = 'Invalid URL requested'
|
325
|
+
return
|
326
|
+
end
|
327
|
+
|
328
|
+
tr = _trans(env)
|
329
|
+
info = env['sgfa.binder'].binder_info(tr)
|
330
|
+
|
331
|
+
env['sgfa.status'] = :ok
|
332
|
+
env['sgfa.html'] = _disp_jackets(env, info[:jackets], tr)
|
333
|
+
end # def _get_jackets()
|
334
|
+
|
335
|
+
|
336
|
+
#####################################
|
337
|
+
# Display jacket list
|
338
|
+
def _disp_jackets(env, jackets, tr)
|
339
|
+
|
340
|
+
rows = ''
|
341
|
+
jackets.each do |jnam, jinfo|
|
342
|
+
perms = jinfo[:perms]
|
343
|
+
ps = perms.empty? ? '-' : _escape_html(perms.join(', '))
|
344
|
+
rows << JacketsRow % [_link_jacket(env, jnam, _escape_html(jnam)),
|
345
|
+
_escape_html(jinfo[:title]), ps]
|
346
|
+
end
|
347
|
+
html = JacketsTable % rows
|
348
|
+
html << JacketsForm % env['SCRIPT_NAME'] if tr[:perms].include?('manage')
|
349
|
+
|
350
|
+
return html
|
351
|
+
end # def _disp_jackets()
|
352
|
+
|
353
|
+
|
354
|
+
BinderTable =
|
355
|
+
"<table>\n" +
|
356
|
+
"<tr><td>Hash ID:</td><td>%s</td></tr>\n" +
|
357
|
+
"<tr><td>Text ID:</td><td>%s</td></tr>\n" +
|
358
|
+
"<tr><td>Jackets:</td><td>%d</td></tr>\n" +
|
359
|
+
"<tr><td>Users:</td><td>%d</td></tr>\n" +
|
360
|
+
"<tr><td>Values:</td><td>%d</td></tr>\n" +
|
361
|
+
"<tr><td>User Permissions:</td><td>%s</td></tr>\n" +
|
362
|
+
"</table>\n"
|
363
|
+
|
364
|
+
#####################################
|
365
|
+
# Get binder info
|
366
|
+
def _get_binder(env, path)
|
367
|
+
_navbar_binder(env, 'Binder')
|
368
|
+
return if !path.empty?
|
369
|
+
|
370
|
+
tr = _trans(env)
|
371
|
+
info = env['sgfa.binder'].binder_info(tr)
|
372
|
+
|
373
|
+
env['sgfa.status'] = :ok
|
374
|
+
env['sgfa.html'] = BinderTable % [
|
375
|
+
info[:id_hash], _escape_html(info[:id_text]),
|
376
|
+
info[:jackets].size, info[:users].size, info[:values].size,
|
377
|
+
_escape_html(tr[:perms].join(', '))
|
378
|
+
]
|
379
|
+
end # def _get_binder()
|
380
|
+
|
381
|
+
|
382
|
+
UsersTable =
|
383
|
+
"<table class='list'>\n" +
|
384
|
+
"<tr><th>User</th><th>Permissions</th></tr>\n" +
|
385
|
+
"%s</table>\n"
|
386
|
+
|
387
|
+
UsersRow =
|
388
|
+
"<tr><td>%s</td><td>%s</td></tr>\n"
|
389
|
+
|
390
|
+
UsersForm =
|
391
|
+
"\n<hr>\n<form class='edit' method='post' action='%s' " +
|
392
|
+
"enctype='multipart/form-data'>\n" +
|
393
|
+
"<fieldset><legend>Set User or Group Permissions</legend>\n" +
|
394
|
+
"<label for='user'>Name:</label>" +
|
395
|
+
"<input class='user' name='user' type='text'><br>\n" +
|
396
|
+
"<label for='perms'>Perms:</label>" +
|
397
|
+
"<input class='perms' name='perms' type='text'><br>\n" +
|
398
|
+
"</fieldset>\n" +
|
399
|
+
"<input type='submit' name='set' value='Set'>\n" +
|
400
|
+
"</form>\n"
|
401
|
+
|
402
|
+
#####################################
|
403
|
+
# Get users
|
404
|
+
def _get_users(env, path)
|
405
|
+
_navbar_binder(env, 'Users')
|
406
|
+
|
407
|
+
if !path.empty?
|
408
|
+
env['sgfa.status'] = :badreq
|
409
|
+
env['sgfa.html'] = 'Invalid URL requested'
|
410
|
+
return
|
411
|
+
end
|
412
|
+
|
413
|
+
tr = _trans(env)
|
414
|
+
info = env['sgfa.binder'].binder_info(tr)
|
415
|
+
|
416
|
+
env['sgfa.status'] = :ok
|
417
|
+
env['sgfa.html'] = _disp_users(env, info[:users], tr)
|
418
|
+
end # def _get_users()
|
419
|
+
|
420
|
+
|
421
|
+
#####################################
|
422
|
+
# Display users
|
423
|
+
def _disp_users(env, users, tr)
|
424
|
+
rows = ''
|
425
|
+
users.each do |unam, pl|
|
426
|
+
perms = pl.empty? ? '-' : pl.join(', ')
|
427
|
+
rows << UsersRow % [
|
428
|
+
_escape_html(unam), _escape_html(perms)
|
429
|
+
]
|
430
|
+
end
|
431
|
+
html = UsersTable % rows
|
432
|
+
html << (UsersForm % env['SCRIPT_NAME']) if tr[:perms].include?('manage')
|
433
|
+
return html
|
434
|
+
end # def _disp_users()
|
435
|
+
|
436
|
+
|
437
|
+
ValuesTable =
|
438
|
+
"<table class='list'>\n" +
|
439
|
+
"<tr><th>Value</th><th>State</th></tr>\n" +
|
440
|
+
"%s\n</table>\n"
|
441
|
+
|
442
|
+
ValuesRow =
|
443
|
+
"<tr><td>%s</td><td>%s</td></tr>\n"
|
444
|
+
|
445
|
+
ValuesForm =
|
446
|
+
"\n<hr>\n<form class='edit' method='post' action='%s' " +
|
447
|
+
"enctype='multipart/form-data'>\n" +
|
448
|
+
"<fieldset><legend>Assign Binder Values</legend>\n" +
|
449
|
+
"<label for='value'>Value:</label>" +
|
450
|
+
"<input class='value' name='value' type='text'><br>\n" +
|
451
|
+
"<label for='state'>State:</label>" +
|
452
|
+
"<input class='state' name='state' type='text'><br>\n" +
|
453
|
+
"</fieldset>\n" +
|
454
|
+
"<input type='submit' name='assign' value='Assign'>\n" +
|
455
|
+
"</form>\n"
|
456
|
+
|
457
|
+
#####################################
|
458
|
+
# Get values
|
459
|
+
def _get_values(env, path)
|
460
|
+
_navbar_binder(env, 'Values')
|
461
|
+
|
462
|
+
if !path.empty?
|
463
|
+
env['sgfa.status'] = :badreq
|
464
|
+
env['sgfa.html'] = 'Invalid URL requested'
|
465
|
+
return
|
466
|
+
end
|
467
|
+
|
468
|
+
tr = _trans(env)
|
469
|
+
info = env['sgfa.binder'].binder_info(tr)
|
470
|
+
|
471
|
+
env['sgfa.status'] = :ok
|
472
|
+
env['sgfa.html'] = _disp_values(env, info[:values], tr)
|
473
|
+
end # def _get_values()
|
474
|
+
|
475
|
+
|
476
|
+
#####################################
|
477
|
+
# Display values
|
478
|
+
def _disp_values(env, values, tr)
|
479
|
+
rows = ''
|
480
|
+
values.each do |vnam, vset|
|
481
|
+
rows << ValuesRow % [
|
482
|
+
_escape_html(vnam), _escape_html(vset)
|
483
|
+
]
|
484
|
+
end
|
485
|
+
html = ValuesTable % rows
|
486
|
+
html << (ValuesForm % env['SCRIPT_NAME']) if tr[:perms].include?('manage')
|
487
|
+
|
488
|
+
return html
|
489
|
+
end # def _disp_values()
|
490
|
+
|
491
|
+
|
492
|
+
TagTable =
|
493
|
+
"<div class='title'>Tag: %s</div>\n" +
|
494
|
+
"<table class='list'>\n<tr>" +
|
495
|
+
"<th>Time</th><th>Title</th><th>Files</th><th>Tags</th><th>Edit</th>" +
|
496
|
+
"</tr>\n%s</table>\n"
|
497
|
+
|
498
|
+
TagRow =
|
499
|
+
"<tr><td class='time'>%s</td><td class='title'>%s</td>" +
|
500
|
+
"<td class='num'>%d</td><td class='num'>%d</td>" +
|
501
|
+
"<td class='act'>%s</td></tr>\n"
|
502
|
+
|
503
|
+
PageSize = 25
|
504
|
+
PageSizeMax = 100
|
505
|
+
|
506
|
+
#####################################
|
507
|
+
# Get a tag
|
508
|
+
def _get_tag(env, path)
|
509
|
+
_navbar_jacket(env, 'Tag')
|
510
|
+
|
511
|
+
if path.empty?
|
512
|
+
tag = '_all'
|
513
|
+
else
|
514
|
+
tag = _escape_un(path.shift)
|
515
|
+
end
|
516
|
+
page = path.empty? ? 1 : path.shift.to_i
|
517
|
+
page = 1 if page == 0
|
518
|
+
rck = Rack::Request.new(env)
|
519
|
+
params = rck.GET
|
520
|
+
per = params['perpage'] ? params['perpage'].to_i : 0
|
521
|
+
if per == 0 || per > PageSizeMax
|
522
|
+
per = PageSize
|
523
|
+
end
|
524
|
+
|
525
|
+
tr = _trans(env)
|
526
|
+
size, ents = env['sgfa.binder'].read_tag(tr, tag, (page-1)*per, per)
|
527
|
+
if ents.size == 0
|
528
|
+
html = 'No entries'
|
529
|
+
else
|
530
|
+
rows = ''
|
531
|
+
ents.reverse_each do |enum, rnum, time, title, tcnt, acnt|
|
532
|
+
rows << TagRow % [
|
533
|
+
time.localtime.strftime("%F %T %z"),
|
534
|
+
_link_entry(env, enum, _escape_html(title)),
|
535
|
+
acnt, tcnt,
|
536
|
+
_link_edit(env, enum, 'edit')
|
537
|
+
]
|
538
|
+
end
|
539
|
+
html = TagTable % [_escape_html(tag), rows]
|
540
|
+
end
|
541
|
+
|
542
|
+
link = '%s/%s/_tag/%s' % [
|
543
|
+
env['SCRIPT_NAME'],
|
544
|
+
env['sgfa.jacket.url'],
|
545
|
+
_escape(tag)
|
546
|
+
]
|
547
|
+
query = (per != PageSize) ? { 'perpage' => per.to_s } : nil
|
548
|
+
pages = _link_pages(page, per, size, link, query)
|
549
|
+
|
550
|
+
env['sgfa.status'] = :ok
|
551
|
+
env['sgfa.html'] = html + pages
|
552
|
+
end # def _get_tag()
|
553
|
+
|
554
|
+
|
555
|
+
LogTable =
|
556
|
+
"<table class='list'>\n<tr>" +
|
557
|
+
"<th>History</th><th>Date/Time</th><th>User</th><th>Entries</th>" +
|
558
|
+
"<th>Attachs</th></tr>\n%s</table>\n"
|
559
|
+
|
560
|
+
LogRow =
|
561
|
+
"<tr><td class='hnum'>%d</td><td class='time'>%s</td>" +
|
562
|
+
"<td class='user'>%s</td><td>%d</td><td>%d</td></tr>\n"
|
563
|
+
|
564
|
+
#####################################
|
565
|
+
# Get the log
|
566
|
+
def _get_log(env, path)
|
567
|
+
_navbar_jacket(env, 'Log')
|
568
|
+
|
569
|
+
page = path.empty? ? 1 : path.shift.to_i
|
570
|
+
page = 1 if page == 0
|
571
|
+
return if !path.empty?
|
572
|
+
rck = Rack::Request.new(env)
|
573
|
+
params = rck.GET
|
574
|
+
per = params['perpage'] ? params['perpage'].to_i : 0
|
575
|
+
if per == 0 || per > PageSizeMax
|
576
|
+
per = PageSize
|
577
|
+
end
|
578
|
+
|
579
|
+
tr = _trans(env)
|
580
|
+
size, hsts = env['sgfa.binder'].read_log(tr, (page-1)*per, per)
|
581
|
+
if hsts.size == 0
|
582
|
+
env['sgfa.html'] = 'No history'
|
583
|
+
env['sgfa.status'] = :notfound
|
584
|
+
else
|
585
|
+
rows = ''
|
586
|
+
hsts.each do |hnum, time, user, ecnt, acnt|
|
587
|
+
rows << LogRow % [
|
588
|
+
hnum,
|
589
|
+
_link_history(env, hnum, time.localtime.strftime('%F %T %z')),
|
590
|
+
_escape_html(user),
|
591
|
+
ecnt, acnt
|
592
|
+
]
|
593
|
+
end
|
594
|
+
link = '%s/%s/_log' % [env['SCRIPT_NAME'], env['sgfa.jacket.url']]
|
595
|
+
query = (per != PageSize) ? { 'perpage' => per.to_s } : nil
|
596
|
+
env['sgfa.status'] = :ok
|
597
|
+
env['sgfa.html'] = (LogTable % rows) +
|
598
|
+
_link_pages(page, per, size, link, query)
|
599
|
+
end
|
600
|
+
|
601
|
+
end # def _get_log()
|
602
|
+
|
603
|
+
|
604
|
+
#####################################
|
605
|
+
# Get an entry
|
606
|
+
def _get_entry(env, path)
|
607
|
+
_navbar_jacket(env, 'Entry')
|
608
|
+
|
609
|
+
return if path.empty?
|
610
|
+
enum = path.shift.to_i
|
611
|
+
rnum = path.empty? ? 0 : path.shift.to_i
|
612
|
+
return if enum == 0
|
613
|
+
|
614
|
+
tr = _trans(env)
|
615
|
+
ent = env['sgfa.binder'].read_entry(tr, enum, rnum)
|
616
|
+
|
617
|
+
env['sgfa.status'] = :ok
|
618
|
+
env['sgfa.html'] = _disp_entry(env, ent)
|
619
|
+
end # def _get_entry()
|
620
|
+
|
621
|
+
|
622
|
+
EntryDisp =
|
623
|
+
"<div class='title'>%s</div>\n" +
|
624
|
+
"<div class='body'><pre>%s</pre></div>\n" +
|
625
|
+
"<div class='sidebar'>\n" +
|
626
|
+
"<div class='time'>%s</div>\n" +
|
627
|
+
"<div class='history'>Revision: %d %s %s<br>History: %s %s</div>\n" +
|
628
|
+
"<div class='tags'>%s</div>\n" +
|
629
|
+
"<div class='attach'>%s</div>\n" +
|
630
|
+
"</div>\n" +
|
631
|
+
"<div class='hash'>Hash: %s<br>Jacket: %s</div>\n"
|
632
|
+
|
633
|
+
#####################################
|
634
|
+
# Display an entry
|
635
|
+
def _disp_entry(env, ent)
|
636
|
+
|
637
|
+
enum = ent.entry
|
638
|
+
rnum = ent.revision
|
639
|
+
hnum = ent.history
|
640
|
+
|
641
|
+
tl = ent.tags
|
642
|
+
tags = "Tags:<br>\n"
|
643
|
+
if tl.empty?
|
644
|
+
tags << "none\n"
|
645
|
+
else
|
646
|
+
tl.each do |tag|
|
647
|
+
tags << _link_tag(env, tag, _escape_html(tag)) + "<br>\n"
|
648
|
+
end
|
649
|
+
end
|
650
|
+
|
651
|
+
al = ent.attachments
|
652
|
+
att = "Attachments:<br>\n"
|
653
|
+
if al.empty?
|
654
|
+
att << "none\n"
|
655
|
+
else
|
656
|
+
al.each do |anum, hnum, name|
|
657
|
+
att << _link_attach(env, enum, anum, hnum, name, _escape_html(name)) +
|
658
|
+
"<br>\n"
|
659
|
+
end
|
660
|
+
end
|
661
|
+
if rnum == 1
|
662
|
+
prev = 'previous'
|
663
|
+
else
|
664
|
+
prev = _link_revision(env, enum, rnum-1, 'previous')
|
665
|
+
end
|
666
|
+
curr = _link_entry(env, enum, 'current')
|
667
|
+
edit = _link_edit(env, enum, 'edit')
|
668
|
+
|
669
|
+
body = EntryDisp % [
|
670
|
+
_escape_html(ent.title),
|
671
|
+
_escape_html(ent.body),
|
672
|
+
ent.time.localtime.strftime('%F %T %z'),
|
673
|
+
rnum, prev, curr, _link_history(env, hnum, hnum.to_s), edit,
|
674
|
+
tags,
|
675
|
+
att,
|
676
|
+
ent.hash, ent.jacket
|
677
|
+
]
|
678
|
+
|
679
|
+
return body
|
680
|
+
end # def _disp_entry()
|
681
|
+
|
682
|
+
|
683
|
+
HistoryDisp =
|
684
|
+
"<div class='title'>History %d</div>\n" +
|
685
|
+
"<div class='body'>%s</div>" +
|
686
|
+
"<div class='sidebar'>\n" +
|
687
|
+
"<div class='time'>%s</div>\n" +
|
688
|
+
"<div class='user'>%s</div>\n" +
|
689
|
+
"<div class='nav'>%s %s</div>\n" +
|
690
|
+
"</div>\n<div class='hash'>Hash: %s<br>Jacket: %s</div>\n"
|
691
|
+
|
692
|
+
HistoryTable =
|
693
|
+
"<table class='list'>\n<tr><th>Item</th><th>Hash</th></tr>\n" +
|
694
|
+
"%s</table>\n"
|
695
|
+
|
696
|
+
HistoryItem =
|
697
|
+
"<tr><td>%s</td><td class='hash'>%s</td></tr>\n"
|
698
|
+
|
699
|
+
#####################################
|
700
|
+
# Display a history item
|
701
|
+
def _get_history(env, path)
|
702
|
+
_navbar_jacket(env, 'History')
|
703
|
+
|
704
|
+
return if path.empty?
|
705
|
+
hnum = path.shift.to_i
|
706
|
+
return if hnum == 0
|
707
|
+
|
708
|
+
tr = _trans(env)
|
709
|
+
hst = env['sgfa.binder'].read_history(tr, hnum)
|
710
|
+
hnum = hst.history
|
711
|
+
plnk = (hnum == 1) ? 'Previous' : _link_history(env, hnum-1, 'Previous')
|
712
|
+
nlnk = _link_history(env, hnum+1, 'Next')
|
713
|
+
|
714
|
+
rows = ""
|
715
|
+
hst.entries.each do |enum, rnum, hash|
|
716
|
+
disp = "Entry %d-%d" % [enum, rnum]
|
717
|
+
rows << (HistoryItem % [_link_revision(env, enum, rnum, disp), hash])
|
718
|
+
end
|
719
|
+
hst.attachments.each do |enum, anum, hash|
|
720
|
+
disp = "Attach %d-%d-%d" % [enum, anum, hnum]
|
721
|
+
rows << HistoryItem %
|
722
|
+
[_link_attach(env, enum, anum, hnum, hash + '.bin', disp), hash]
|
723
|
+
end
|
724
|
+
tab = HistoryTable % rows
|
725
|
+
|
726
|
+
body = HistoryDisp % [
|
727
|
+
hnum, tab, hst.time.localtime.strftime('%F %T %z'),
|
728
|
+
_escape_html(hst.user), plnk, nlnk, hst.hash, hst.jacket
|
729
|
+
]
|
730
|
+
|
731
|
+
env['sgfa.status'] = :ok
|
732
|
+
env['sgfa.html'] = body
|
733
|
+
end # def _get_history()
|
734
|
+
|
735
|
+
|
736
|
+
ListTable =
|
737
|
+
"<table class='list'>\n<tr>" +
|
738
|
+
"<th>Tag</th><th>Number</th></tr>\n" +
|
739
|
+
"%s\n</table>\n"
|
740
|
+
|
741
|
+
ListPrefix =
|
742
|
+
"<tr><td class='prefix'>%s: prefix</td><td>%d tags</td></tr>\n"
|
743
|
+
|
744
|
+
ListTag =
|
745
|
+
"<tr><td class='tag'>%s</td><td>%d entries</td></tr>\n"
|
746
|
+
|
747
|
+
#####################################
|
748
|
+
# Get list of tags
|
749
|
+
def _get_list(env, path)
|
750
|
+
_navbar_jacket(env, 'List')
|
751
|
+
|
752
|
+
bnd = env['sgfa.binder']
|
753
|
+
tr = _trans(env)
|
754
|
+
lst = bnd.read_list(tr)
|
755
|
+
|
756
|
+
# sort into prefixed & regular
|
757
|
+
prefix = {}
|
758
|
+
regular = []
|
759
|
+
lst.each do |tag|
|
760
|
+
idx = tag.index(':')
|
761
|
+
if !idx
|
762
|
+
regular.push tag
|
763
|
+
next
|
764
|
+
end
|
765
|
+
pre = tag[0,idx].strip
|
766
|
+
if prefix[pre]
|
767
|
+
prefix[pre].push tag
|
768
|
+
else
|
769
|
+
prefix[pre] = [tag]
|
770
|
+
end
|
771
|
+
end
|
772
|
+
|
773
|
+
# regular & prefix list
|
774
|
+
rows = ''
|
775
|
+
if path.empty?
|
776
|
+
prefix.keys.sort.each do |pre|
|
777
|
+
size = prefix[pre].size
|
778
|
+
rows << ListPrefix %
|
779
|
+
[_link_prefix(env, pre, _escape_html(pre)), size]
|
780
|
+
end
|
781
|
+
regular.sort.each do |tag|
|
782
|
+
size, ents = bnd.read_tag(tr, tag, 0, 0)
|
783
|
+
rows << ListTag %
|
784
|
+
[_link_tag(env, tag, _escape_html(tag)), size]
|
785
|
+
end
|
786
|
+
|
787
|
+
# list entire prefix
|
788
|
+
else
|
789
|
+
pre = _escape_un(path.shift)
|
790
|
+
return if !path.empty?
|
791
|
+
if !prefix[pre]
|
792
|
+
env['sgfa.status'] = :notfound
|
793
|
+
env['sgfa.html'] = 'Tag prefix not found'
|
794
|
+
return
|
795
|
+
end
|
796
|
+
|
797
|
+
prefix[pre].sort.each do |tag|
|
798
|
+
size, ents = bnd.read_tag(tr, tag, 0, 0)
|
799
|
+
rows << ListTag %
|
800
|
+
[_link_tag(env, tag, _escape_html(tag)), size]
|
801
|
+
end
|
802
|
+
end
|
803
|
+
|
804
|
+
env['sgfa.status'] = :ok
|
805
|
+
env['sgfa.html'] = ListTable % rows
|
806
|
+
end # def _get_list
|
807
|
+
|
808
|
+
|
809
|
+
InfoTable =
|
810
|
+
"<table>\n" +
|
811
|
+
"<tr><td>Text ID:</td><td>%s</td></tr>\n" +
|
812
|
+
"<tr><td>Hash ID:</td><td>%s</td></tr>\n" +
|
813
|
+
"<tr><td>Last Edit:</td><td>%s</td></tr>\n" +
|
814
|
+
"<tr><td>History:</td><td>%d</td></tr>\n" +
|
815
|
+
"<tr><td>Entries:</td><td>%d</td></tr>\n" +
|
816
|
+
"</table>\n"
|
817
|
+
|
818
|
+
#####################################
|
819
|
+
# Get jacket info
|
820
|
+
def _get_info(env, path)
|
821
|
+
_navbar_jacket(env, 'Jacket')
|
822
|
+
return if !path.empty?
|
823
|
+
tr = _trans(env)
|
824
|
+
|
825
|
+
info = env['sgfa.binder'].binder_info(tr)
|
826
|
+
hst = env['sgfa.binder'].read_history(tr, 0)
|
827
|
+
if hst
|
828
|
+
hmax = hst.history
|
829
|
+
emax = hst.entry_max
|
830
|
+
time = hst.time.localtime.strftime('%F %T %z')
|
831
|
+
else
|
832
|
+
hmax = 0
|
833
|
+
emax = 0
|
834
|
+
time = 'none'
|
835
|
+
end
|
836
|
+
|
837
|
+
jinf = info[:jackets][env['sgfa.jacket.name']]
|
838
|
+
env['sgfa.status'] = :ok
|
839
|
+
env['sgfa.html'] = InfoTable % [
|
840
|
+
jinf[:id_text], jinf[:id_hash],
|
841
|
+
time, hmax, emax
|
842
|
+
]
|
843
|
+
end # def _get_info()
|
844
|
+
|
845
|
+
|
846
|
+
#####################################
|
847
|
+
# Get an attachment
|
848
|
+
def _get_attach(env, path)
|
849
|
+
_navbar_jacket(env, 'Attachment')
|
850
|
+
|
851
|
+
spec = path.shift
|
852
|
+
return if !spec
|
853
|
+
ma = /^(\d+)-(\d+)-(\d+)$/.match(spec)
|
854
|
+
return if !ma
|
855
|
+
name = path.shift
|
856
|
+
return if !name
|
857
|
+
return if !path.empty?
|
858
|
+
enum, anum, hnum = ma[1,3].map{|st| st.to_i}
|
859
|
+
name = _escape_un(name)
|
860
|
+
|
861
|
+
ext = name.rpartition('.')[2]
|
862
|
+
if ext.empty?
|
863
|
+
mime = 'application/octet-stream'
|
864
|
+
else
|
865
|
+
mime = Rack::Mime.mime_type('.' + ext)
|
866
|
+
end
|
867
|
+
|
868
|
+
tr = _trans(env)
|
869
|
+
file = env['sgfa.binder'].read_attach(tr, enum, anum, hnum)
|
870
|
+
|
871
|
+
env['sgfa.status'] = :ok
|
872
|
+
env['sgfa.headers'] = {
|
873
|
+
'Content-Length' => file.size.to_s,
|
874
|
+
'Content-Type' => mime,
|
875
|
+
'Content-Disposition' => 'attachment',
|
876
|
+
}
|
877
|
+
env['sgfa.file'] = FileBody.new(file)
|
878
|
+
|
879
|
+
end # def _get_attach()
|
880
|
+
|
881
|
+
|
882
|
+
EditForm =
|
883
|
+
"<form class='edit' method='post' action='%s/%s' " +
|
884
|
+
"enctype='multipart/form-data'>\n" +
|
885
|
+
"<input name='entry' type='hidden' value='%d'>\n" +
|
886
|
+
"<input name='revision' type='hidden' value='%d'>\n" +
|
887
|
+
|
888
|
+
"<div class='edit'>\n" +
|
889
|
+
|
890
|
+
"<fieldset><legend>Basic Info</legend>\n" +
|
891
|
+
|
892
|
+
"<label for='title'>Title:</label>" +
|
893
|
+
"<input class='title' name='title' type='text' value='%s'><br>\n" +
|
894
|
+
|
895
|
+
"<label for='time'>Time:</label>" +
|
896
|
+
"<input name='time' type='text' value='%s'><br>\n" +
|
897
|
+
|
898
|
+
"<label for='body'>Body:</label>" +
|
899
|
+
"<textarea class='body' name='body'>%s</textarea>\n" +
|
900
|
+
|
901
|
+
"</fieldset>\n" +
|
902
|
+
"<fieldset><legend>Attachments</legend>\n%s</fieldset>\n" +
|
903
|
+
"<fieldset><legend>Tags</legend>\n%s</fieldset>\n" +
|
904
|
+
|
905
|
+
"<input type='submit' name='save' value='Save Changes'>\n" +
|
906
|
+
"</div></form>\n"
|
907
|
+
|
908
|
+
EditFilePre =
|
909
|
+
"<table class='edit_file'>\n" +
|
910
|
+
"<tr><th>Name</th><th>Upload/Replace</th></tr>\n"
|
911
|
+
|
912
|
+
EditFileEach =
|
913
|
+
"<tr><td><input name='attname%d' type='text' value='%s'>" +
|
914
|
+
"<input name='attnumb%d' type='hidden' value='%d'></td>" +
|
915
|
+
"<td><input name='attfile%d' type='file'></td></tr>\n"
|
916
|
+
|
917
|
+
EditFileCnt =
|
918
|
+
"</table>\n<input name='attcnt' type='hidden' value='%d'>\n"
|
919
|
+
|
920
|
+
EditTagOld =
|
921
|
+
"<input name='tag%d' type='text' value='%s'><br>\n"
|
922
|
+
|
923
|
+
EditTagNew =
|
924
|
+
"<input name='tag%d' type='text'><br>\n"
|
925
|
+
|
926
|
+
EditTagSel =
|
927
|
+
"%s: <select name='tag%d'>" +
|
928
|
+
"<option value='' selected></option>%s</select><br>\n"
|
929
|
+
|
930
|
+
EditTagOpt =
|
931
|
+
"<option value='%s: %s'>%s</option>"
|
932
|
+
|
933
|
+
EditTagCnt =
|
934
|
+
"<input name='tagcnt' type='hidden' value='%d'>\n"
|
935
|
+
|
936
|
+
#####################################
|
937
|
+
# Get edit form
|
938
|
+
def _get_edit(env, path)
|
939
|
+
_navbar_jacket(env, 'Edit')
|
940
|
+
|
941
|
+
tr = _trans(env)
|
942
|
+
|
943
|
+
if path.empty?
|
944
|
+
enum = 0
|
945
|
+
rnum = 0
|
946
|
+
ent = Entry.new
|
947
|
+
ent.title = 'Title'
|
948
|
+
ent.body = 'Body'
|
949
|
+
ent.time = Time.now
|
950
|
+
else
|
951
|
+
enum = path.shift.to_i
|
952
|
+
return if enum == 0 || !path.empty?
|
953
|
+
ent = env['sgfa.binder'].read_entry(tr, enum)
|
954
|
+
rnum = ent.revision
|
955
|
+
end
|
956
|
+
|
957
|
+
lst = env['sgfa.binder'].read_list(tr)
|
958
|
+
prefix = {}
|
959
|
+
lst.each do |tag|
|
960
|
+
idx = tag.index(':')
|
961
|
+
next unless idx
|
962
|
+
pre = tag[0,idx].strip
|
963
|
+
post = tag[idx+1..-1].strip
|
964
|
+
if prefix[pre]
|
965
|
+
prefix[pre].push post
|
966
|
+
else
|
967
|
+
prefix[pre] = [post]
|
968
|
+
end
|
969
|
+
end
|
970
|
+
|
971
|
+
tags = ''
|
972
|
+
cnt = 0
|
973
|
+
ent.tags.each do |tag|
|
974
|
+
tags << EditTagOld % [cnt, _escape_html(tag)]
|
975
|
+
cnt += 1
|
976
|
+
end
|
977
|
+
prefix.each do |pre, lst|
|
978
|
+
px = _escape_html(pre)
|
979
|
+
opts = ''
|
980
|
+
lst.each do |post|
|
981
|
+
ex = _escape_html(post)
|
982
|
+
opts << EditTagOpt % [px, ex, ex]
|
983
|
+
end
|
984
|
+
tags << EditTagSel % [px, cnt, opts]
|
985
|
+
cnt += 1
|
986
|
+
end
|
987
|
+
5.times do |tg|
|
988
|
+
tags << EditTagNew % cnt
|
989
|
+
cnt += 1
|
990
|
+
end
|
991
|
+
tags << EditTagCnt % cnt
|
992
|
+
|
993
|
+
atts = "Attachments go here\n"
|
994
|
+
atts = EditFilePre.dup
|
995
|
+
cnt = 0
|
996
|
+
ent.attachments.each do |anum, hnum, name|
|
997
|
+
atts << EditFileEach % [cnt, _escape_html(name), cnt, anum, cnt]
|
998
|
+
cnt += 1
|
999
|
+
end
|
1000
|
+
5.times do |ix|
|
1001
|
+
atts << EditFileEach % [cnt, '', cnt, 0, cnt]
|
1002
|
+
cnt += 1
|
1003
|
+
end
|
1004
|
+
atts << EditFileCnt % cnt
|
1005
|
+
|
1006
|
+
html = EditForm % [
|
1007
|
+
env['SCRIPT_NAME'], env['sgfa.jacket.url'], enum, rnum,
|
1008
|
+
_escape_html(ent.title), ent.time.localtime.strftime('%F %T %z'),
|
1009
|
+
_escape_html(ent.body), atts, tags,
|
1010
|
+
]
|
1011
|
+
|
1012
|
+
env['sgfa.status'] = :ok
|
1013
|
+
env['sgfa.html'] = html
|
1014
|
+
end # def _get_edit
|
1015
|
+
|
1016
|
+
JacketPost = [
|
1017
|
+
'entry',
|
1018
|
+
'revision',
|
1019
|
+
'time',
|
1020
|
+
'tagcnt',
|
1021
|
+
'attcnt',
|
1022
|
+
]
|
1023
|
+
|
1024
|
+
#####################################
|
1025
|
+
# Handle jacket post
|
1026
|
+
def _post_jacket(env)
|
1027
|
+
_navbar_jacket(env, 'Edit')
|
1028
|
+
|
1029
|
+
rck = Rack::Request.new(env)
|
1030
|
+
params = rck.POST
|
1031
|
+
|
1032
|
+
# validate fields present
|
1033
|
+
JacketPost.each do |fn|
|
1034
|
+
next if params[fn]
|
1035
|
+
raise Error::Limits, 'Bad form submission'
|
1036
|
+
end
|
1037
|
+
tagcnt = params['tagcnt'].to_i
|
1038
|
+
attcnt = params['attcnt'].to_i
|
1039
|
+
tagcnt.times do |ix|
|
1040
|
+
next if params['tag%d' % ix]
|
1041
|
+
raise Error::Limits, 'Bad form submission'
|
1042
|
+
end
|
1043
|
+
attcnt.times do |ix|
|
1044
|
+
next if params['attnumb%d' % ix]
|
1045
|
+
raise Error::Limits, 'Bad form submission'
|
1046
|
+
end
|
1047
|
+
|
1048
|
+
# get the entry being edited
|
1049
|
+
enum = params['entry'].to_i
|
1050
|
+
rnum = params['revision'].to_i
|
1051
|
+
tr = _trans(env)
|
1052
|
+
if enum != 0
|
1053
|
+
ent = env['sgfa.binder'].read_entry(tr, enum, rnum)
|
1054
|
+
else
|
1055
|
+
ent = Entry.new
|
1056
|
+
end
|
1057
|
+
|
1058
|
+
# tags
|
1059
|
+
oldt = ent.tags
|
1060
|
+
newt = []
|
1061
|
+
tagcnt.times do |ix|
|
1062
|
+
tx = 'tag%d' % ix
|
1063
|
+
if !params[tx].empty?
|
1064
|
+
newt.push params[tx]
|
1065
|
+
end
|
1066
|
+
end
|
1067
|
+
|
1068
|
+
# attachments
|
1069
|
+
attcnt.times do |ix|
|
1070
|
+
anum = params['attnumb%d' % ix].to_i
|
1071
|
+
name = params['attname%d' % ix]
|
1072
|
+
file = params['attfile%d' % ix]
|
1073
|
+
|
1074
|
+
# copy uploaded file
|
1075
|
+
if file && file != ''
|
1076
|
+
ftmp = env['sgfa.binder'].temp
|
1077
|
+
IO::copy_stream(file[:tempfile], ftmp)
|
1078
|
+
file[:tempfile].close!
|
1079
|
+
else
|
1080
|
+
ftmp = nil
|
1081
|
+
end
|
1082
|
+
|
1083
|
+
# new file
|
1084
|
+
if anum == 0
|
1085
|
+
next if !ftmp
|
1086
|
+
name = file[:filename] if name == ''
|
1087
|
+
ent.attach(name, ftmp)
|
1088
|
+
|
1089
|
+
# old file
|
1090
|
+
else
|
1091
|
+
ent.rename(anum, name) if name != ''
|
1092
|
+
ent.replace(anum, ftmp) if ftmp
|
1093
|
+
end
|
1094
|
+
|
1095
|
+
end
|
1096
|
+
|
1097
|
+
# general
|
1098
|
+
ent.title = params['title']
|
1099
|
+
ent.body = params['body']
|
1100
|
+
begin
|
1101
|
+
time = Time.parse(params['time'])
|
1102
|
+
ent.time = time
|
1103
|
+
rescue ArgumentError
|
1104
|
+
end
|
1105
|
+
oldt.each{|tag| ent.untag(tag) if !newt.include?(tag) }
|
1106
|
+
newt.each{|tag| ent.tag(tag) if !oldt.include?(tag) }
|
1107
|
+
env['sgfa.binder'].write(tr, [ent])
|
1108
|
+
|
1109
|
+
env['sgfa.status'] = :ok
|
1110
|
+
env['sgfa.message'] = 'Entry edited.'
|
1111
|
+
env['sgfa.html'] = _disp_entry(env, ent)
|
1112
|
+
|
1113
|
+
end # def _post_jacket()
|
1114
|
+
|
1115
|
+
|
1116
|
+
#####################################
|
1117
|
+
# Handle binder post
|
1118
|
+
def _post_binder(env)
|
1119
|
+
_navbar_binder(env, 'Edit')
|
1120
|
+
|
1121
|
+
rck = Rack::Request.new(env)
|
1122
|
+
params = rck.POST
|
1123
|
+
|
1124
|
+
tr = _trans(env)
|
1125
|
+
tr[:title] = 'Test title'
|
1126
|
+
tr[:body] = 'Test description of action'
|
1127
|
+
|
1128
|
+
bnd = env['sgfa.binder']
|
1129
|
+
|
1130
|
+
# jacket
|
1131
|
+
if params['create']
|
1132
|
+
_navbar_binder(env, 'Jackets')
|
1133
|
+
['jacket', 'newname', 'title', 'perms'].each do |fn|
|
1134
|
+
next if params[fn]
|
1135
|
+
raise Error::Limits, 'Bad form submission'
|
1136
|
+
end
|
1137
|
+
perms = params['perms'].split(',').map{|it| it.strip }
|
1138
|
+
title = params['title']
|
1139
|
+
newname = params['newname']
|
1140
|
+
jacket = params['jacket']
|
1141
|
+
tr[:jacket] = jacket
|
1142
|
+
|
1143
|
+
info = bnd.binder_info(tr)
|
1144
|
+
oj = info[:jackets][jacket]
|
1145
|
+
if oj
|
1146
|
+
newname ||= jacket
|
1147
|
+
title = oj['title'] if title.empty?
|
1148
|
+
jck = bnd.jacket_edit(tr, newname, title, perms)
|
1149
|
+
env['sgfa.message'] = 'Jacket edited.'
|
1150
|
+
else
|
1151
|
+
jck = bnd.jacket_create(tr, title, perms)
|
1152
|
+
env['sgfa.message'] = 'Jacket created.'
|
1153
|
+
end
|
1154
|
+
env['sgfa.status'] = :ok
|
1155
|
+
env['sgfa.html'] = _disp_jackets(env, jck, tr)
|
1156
|
+
|
1157
|
+
# user
|
1158
|
+
elsif params['set']
|
1159
|
+
_navbar_binder(env, 'Users')
|
1160
|
+
['user', 'perms'].each do |fn|
|
1161
|
+
next if params[fn]
|
1162
|
+
raise Error::Limits, 'Bad form submission'
|
1163
|
+
end
|
1164
|
+
perms = params['perms'].split(',').map{|it| it.strip }
|
1165
|
+
users = bnd.binder_user(tr, params['user'], perms)
|
1166
|
+
env['sgfa.status'] = :ok
|
1167
|
+
env['sgfa.message'] = 'User edited.'
|
1168
|
+
env['sgfa.html'] = _disp_users(env, users, tr)
|
1169
|
+
|
1170
|
+
# binder
|
1171
|
+
elsif params['assign']
|
1172
|
+
_navbar_binder(env, 'Values')
|
1173
|
+
['value', 'state'].each do |fn|
|
1174
|
+
next if params[fn]
|
1175
|
+
raise Error::Limits, 'Bad form submission'
|
1176
|
+
end
|
1177
|
+
vals = { params['value'] => params['state'] }
|
1178
|
+
values = bnd.binder_values(tr, vals)
|
1179
|
+
env['sgfa.status'] = :ok
|
1180
|
+
env['sgfa.message'] = 'Values assigned.'
|
1181
|
+
env['sgfa.html'] = _disp_values(env, values, tr)
|
1182
|
+
|
1183
|
+
end
|
1184
|
+
|
1185
|
+
end # def _post_binder()
|
1186
|
+
|
1187
|
+
end # class Binder
|
1188
|
+
|
1189
|
+
end # module Web
|
1190
|
+
end # module Sgfa
|