sgfa 0.1.0 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: d45c0bbd41d4e9b39a6c9b13f47fff3e0b2de18e
4
- data.tar.gz: 47f07e0899c32bff1374af1e0529d70e5f508221
3
+ metadata.gz: e56f45ef9e949c37978cee9dfc394769eeb6baa2
4
+ data.tar.gz: c4be3d1955e1700dcfa59c02a75cc6259c9ed3b0
5
5
  SHA512:
6
- metadata.gz: 3347b66a768fae632d618c35656b70e813fb1ff8ca5cc207dfb074acfc4c7e85f414115c7d4cc343ac9c185420177f55c9d6624cd4890c7821c52b09056c7ef3
7
- data.tar.gz: 34e8c399c39e436150091609fb0a4ac7c7d53869fe16f7b5b8903e81f5414d69989d6ec96a09efcd3bda0ef70d3fd02d2980d96c5eef25118161fc8b43c1aa7d
6
+ metadata.gz: 5e888580f637df355364aad7e997e1d3c548693bf584f7eaedec7fdc3cefd72b195117d9d8a87073866f545f28ad11dc7cbfe5f4f2b5348a1522feea7e68cc25
7
+ data.tar.gz: 9df09e1204ae56856b24bf30ad6075be1bbad8728ae38bfde47487f21c01c313cbbeead33cf8e07c08d61e6e57d2f1630f1ab708c15d310bfaa289a69e8f9a0d
@@ -119,7 +119,17 @@ div#sgfa_web table.list td.hash {
119
119
  }
120
120
 
121
121
 
122
- /* edit tables */
122
+ /* edit */
123
+ div#sgfa_web form.edit input {
124
+ font-family: inherit;
125
+ font-size: inherit;
126
+ }
127
+
128
+ div#sgfa_web form.edit textarea {
129
+ font-family: inherit;
130
+ font-size: inherit;
131
+ }
132
+
123
133
  div#sgfa_web form.edit fieldset {
124
134
  width: 98%;
125
135
  margin: auto;
@@ -130,40 +140,12 @@ div#sgfa_web form.edit fieldset legend {
130
140
  font-weight: bold;
131
141
  }
132
142
 
133
- div#sgfa_web form.edit p {
134
- clear: both;
135
- padding: 3px;
136
- }
137
-
138
143
  div#sgfa_web form.edit label {
139
144
  float: left;
140
145
  width: 8em;
141
146
  text-align: right;
142
147
  }
143
148
 
144
- /* edit tag table */
145
- div#sgfa_web table.edit_tag th {
146
- text-align: center;
147
- text-decoration: underline;
148
- }
149
-
150
- div#sgfa_web table.edit_tag tr {
151
- border: thick solid red;
152
- }
153
-
154
-
155
- /* edit file table */
156
- div#sgfa_web table.edit_file th {
157
- text-align: center;
158
- text-decoration: underline;
159
- }
160
-
161
- div#sgfa_web table.edit_file td.fname {
162
- min-width: 10em;
163
- }
164
-
165
-
166
- /* edit form */
167
149
  div#sgfa_web form.edit input[type=submit] {
168
150
  border: 1px solid grey;
169
151
  border-radius: 5px;
@@ -172,22 +154,30 @@ div#sgfa_web form.edit input[type=submit] {
172
154
  }
173
155
 
174
156
  div#sgfa_web form.edit textarea {
175
- font-family: inherit;
176
- font-size: inherit;
177
157
  width: 50em;
178
158
  height: 15em;
179
159
  }
180
160
 
181
161
  div#sgfa_web form.edit input.title {
182
- font-family: inherit;
183
- font-size: inherit;
184
162
  width: 50em;
185
163
  }
186
164
 
165
+ div#sgfa_web form.edit input.attname {
166
+ width: 30em;
167
+ }
168
+
187
169
  div#sgfa_web form.edit input.tag {
188
- width: 15em;
170
+ width: 30em;
189
171
  }
190
172
 
173
+ div#sgfa_web form.edit button.add_row {
174
+ border: 1px solid grey;
175
+ border-radius: 5px;
176
+ padding: 2px;
177
+ background: radial-gradient(rgb(245, 245, 245), rgb(200, 200, 200));
178
+ }
179
+
180
+
191
181
 
192
182
  /* entry */
193
183
  div#sgfa_web div.entry_left {
@@ -195,6 +185,17 @@ div#sgfa_web div.entry_left {
195
185
  width: 80%;
196
186
  }
197
187
 
188
+ div#sgfa_web div.mainbody div.pages {
189
+ width: 100%;
190
+ border-top: 2px solid;
191
+ }
192
+
193
+ div#sgfa_web div.mainbody div.tagname {
194
+ width: 100%;
195
+ font-size: 150%;
196
+ border-bottom: 2px solid;
197
+ }
198
+
198
199
  div#sgfa_web div.mainbody div.title {
199
200
  width: 100%;
200
201
  font-size: 125%;
@@ -225,6 +226,7 @@ div#sgfa_web div.mainbody div.sidebar div.tags {
225
226
  }
226
227
 
227
228
  div#sgfa_web div.attach {
229
+ border-bottom: thin solid;
228
230
  }
229
231
 
230
232
  div#sgfa_web div.mainbody div.sidebar div.user {
@@ -235,6 +237,7 @@ div#sgfa_web div.hash {
235
237
  width: 100%;
236
238
  clear: both;
237
239
  border-top: thin solid;
240
+ padding-bottom: 3px;
238
241
  font-family: monospace;
239
242
  font-size: 75%;
240
243
  }
@@ -285,12 +285,28 @@ class Binder
285
285
  # @param tag [String] Tag name
286
286
  # @param offs [Integer] Offset to begin reading
287
287
  # @param max [Integer] Maximum number of entries to read
288
- def read_tag(tr, tag, offs, max)
288
+ # @param opts [Hash] Options hash
289
+ # @option opts [Boolean] :raw Get the raw entry when permissions allow
290
+ # @return [Array] \[ enum, rnum, hnum, time, title, num_tags, num_attach,
291
+ # \] Or, if opts[:raw], each Array item will consist of the Entry or,
292
+ # if user does not have read permission, the info array.
293
+ def read_tag(tr, tag, offs, max, opts={})
294
+ raw = opts[:raw]
289
295
  _jacket(tr, 'read') do |jck|
290
296
  size, ents = jck.read_tag(tag, offs, max)
291
297
  lst = ents.map do |ent|
292
- [ent.entry, ent.revision, ent.time, ent.title, ent.tags.size,
293
- ent.attachments.size]
298
+ if raw
299
+ pl = ent.perms
300
+ begin
301
+ _perms(tr, pl)
302
+ item = ent
303
+ rescue Error::Permission
304
+ item = nil
305
+ end
306
+ end
307
+ item ||= [ent.entry, ent.revision, ent.history, ent.time, ent.title,
308
+ ent.tags.size, ent.attachments.size]
309
+ item
294
310
  end
295
311
  [size, lst]
296
312
  end
@@ -398,9 +414,195 @@ class Binder
398
414
  end # def write()
399
415
 
400
416
 
417
+ #########################################################
418
+ # @!group Backup
419
+
420
+
421
+ #####################################
422
+ # Push to a backup store
423
+ #
424
+ # @param bsto [Store] Backup store
425
+ # @param opts [Hash] Options
426
+ # @option opts [Hash] :log The log. Defaults to STDERR at warn level.
427
+ # @option opts [Hash] :prev Jacket id_hash to previously pushed max history
428
+ # @return [Hash] Jacket id_hash to max history backed up
429
+ def backup_push(bsto, opts={})
430
+
431
+ stat = {}
432
+ prev = opts[:prev] || {}
433
+ log = opts[:log]
434
+ if !log
435
+ log = Logger.new(STDERR)
436
+ log.level = Logger::WARN
437
+ end
438
+
439
+ # control jacket push
440
+ jcks = nil
441
+ _shared do
442
+ ctl = _jacket_open(0)
443
+ begin
444
+ min = (prev[@id_hash] || 0) + 1
445
+ log.info('Backup push control %s at %d' % [@id_hash, min])
446
+ stat[@id_hash] = ctl.backup(bsto, min_history: min, log: log)
447
+ ensure
448
+ ctl.close
449
+ end
450
+ jcks = @jackets.values
451
+ end
452
+
453
+ # all other jackets
454
+ jcks.each do |info|
455
+ jck = _jacket_open(info[:num])
456
+ begin
457
+ id = info[:id_hash]
458
+ min = (prev[id] || 0) + 1
459
+ log.info('Backup push jacket %s at %d' % [id, min])
460
+ stat[id] = jck.backup(bsto, min_history: min, log: log)
461
+ ensure
462
+ jck.close
463
+ end
464
+ end
465
+
466
+ return stat
467
+ end # def backup_push()
468
+
469
+
470
+ #####################################
471
+ # Pull from backup store
472
+ #
473
+ # @param bsto [Store] Backup store
474
+ # @param opts [Hash] Options
475
+ # @option opts [Hash] :log The log. Defaults to SDTERR at warn level
476
+ def backup_pull(bsto, opts={})
477
+
478
+ log = opts[:log]
479
+ if !log
480
+ log = Logger.new(STDERR)
481
+ log.level = Logger::WARN
482
+ end
483
+
484
+ @lock.do_ex do
485
+
486
+ # control jacket
487
+ ctl = _jacket_open(0)
488
+ begin
489
+ log.info('Backup pull control %s' % @id_hash)
490
+ ctl.restore(bsto, log: log)
491
+ log.info('Backup pull update cache state')
492
+ _update(ctl)
493
+ ensure
494
+ ctl.close()
495
+ end
496
+ _cache_write()
497
+
498
+ # all other jackets
499
+ @jackets.values.each do |info|
500
+ begin
501
+ jck = _jacket_open(info[:num])
502
+ rescue Error::NonExistent
503
+ log.info('Backup pull create jacket %s' % info[:id_hash])
504
+ _jacket_create_raw(info)
505
+ jck = _jacket_open(info[:num])
506
+ end
507
+ begin
508
+ log.info('Backup pull jacket %s' % info[:id_hash])
509
+ jck.restore(bsto, log: log)
510
+ ensure
511
+ jck.close
512
+ end
513
+ end
514
+
515
+ end
516
+
517
+ end # def backup_pull()
518
+
519
+
401
520
  private
402
521
 
403
522
 
523
+ #####################################
524
+ # Update cache
525
+ def _update(ctl)
526
+
527
+ values = {}
528
+ users = {}
529
+ jackets = {}
530
+
531
+ ctl.read_list.each do |tag|
532
+
533
+ # values
534
+ if tag == 'values'
535
+
536
+ # process all values entries
537
+ offs = 0
538
+ while true
539
+ size, ary = ctl.read_tag(tag, offs, 2)
540
+ break if ary.empty?
541
+ ary.each do |ent|
542
+ info = _get_json(ent)
543
+ info.each do |val, sta|
544
+ next if values.has_key?(val)
545
+ values[val] = sta
546
+ end
547
+ end
548
+ offs += 2
549
+ end
550
+
551
+ # clear unset values
552
+ values.delete_if{ |val, sta| !sta.is_a?(String) }
553
+
554
+ # jacket
555
+ elsif /^jacket:/.match(tag)
556
+ size, ary = ctl.read_tag(tag, 0, 1)
557
+ info = _get_json(ary.first)
558
+ jackets[info['name']] = {
559
+ num: info['num'],
560
+ name: info['name'],
561
+ id_hash: info['id_hash'],
562
+ id_text: info['id_text'],
563
+ title: info['title'],
564
+ perms: info['perms'],
565
+ }
566
+
567
+ # user
568
+ elsif /^user:/.match(tag)
569
+ size, ary = ctl.read_tag(tag, 0, 1)
570
+ info = _get_json(ary.first)
571
+ name = info['name']
572
+ perms = info['perms']
573
+ users[name] = perms
574
+
575
+ end
576
+ end
577
+
578
+ @users = users
579
+ @values = values
580
+ @jackets = jackets
581
+
582
+ end # def _update()
583
+
584
+
585
+ #####################################
586
+ # Get json from a body
587
+ def _get_json(ent)
588
+ lines = ent.body.lines
589
+ st = lines.index{|li| li[0] == '{' || li[0] == '[' }
590
+ if !st || (lines[-1][0] != '}' && lines[-1][0] != ']')
591
+ puts ent.body.inspect
592
+ raise Error::Corrupt, 'Control jacket entry does not contain JSON'
593
+ end
594
+ json = lines[st..-1].join
595
+
596
+ info = nil
597
+ begin
598
+ info = JSON.parse(json)
599
+ rescue
600
+ raise Error::Corrupt, 'Control jacket entry JSON parse error'
601
+ end
602
+ return info
603
+ end # def _get_json()
604
+
605
+
404
606
  #####################################
405
607
  # Shared creation stuff
406
608
  #
@@ -26,16 +26,15 @@ module Sgfa
26
26
  class BinderFs < Binder
27
27
 
28
28
  #####################################
29
- # Create a new binder
29
+ # Create a raw binder
30
+ #
31
+ # @note You almost certainly want to use {#create} not this method
30
32
  #
31
33
  # @param path [String] Path to the binder
32
- # @param tr (see Binder#jacket_create)
33
- # @param init [Hash] The binder initialization options
34
- # @return [String] The hash ID of the new binder
35
- def create(path, tr, init)
36
- raise Error::Sanity, 'Binder already open' if @path
37
- Binder.limits_create(init)
38
-
34
+ # @param id_text [String] The text ID
35
+ # @return [String] The hash ID
36
+ def create_raw(path, id_text)
37
+
39
38
  # create directory
40
39
  begin
41
40
  Dir.mkdir(path)
@@ -45,8 +44,45 @@ class BinderFs < Binder
45
44
 
46
45
  # create control binder
47
46
  dn_ctrl = File.join(path, '0')
48
- id_hash = JacketFs.create(dn_ctrl, init[:id_text])
49
- jck = JacketFs.new(dn_ctrl)
47
+ id_hash = JacketFs.create(dn_ctrl, id_text)
48
+
49
+ # write info
50
+ info = {
51
+ 'sgfa_binder_ver' => 1,
52
+ 'id_hash' => id_hash,
53
+ 'id_text' => id_text,
54
+ }
55
+ json = JSON.pretty_generate(info) + "\n"
56
+ fn_info = File.join(path, 'sgfa_binder.json')
57
+ File.open(fn_info, 'w', :encoding => 'utf-8'){|fi| fi.write json }
58
+
59
+ # create empty cache
60
+ @path = path
61
+ @id_hash = id_hash
62
+ @jackets = {}
63
+ @users = {}
64
+ @values = {}
65
+ _cache_write
66
+ @path = nil
67
+ @id_hash = nil
68
+
69
+ return id_hash
70
+ end # def create_raw()
71
+
72
+
73
+ #####################################
74
+ # Create a new binder
75
+ #
76
+ # @param path [String] Path to the binder
77
+ # @param tr (see Binder#jacket_create)
78
+ # @param init [Hash] The binder initialization options
79
+ # @return [String] The hash ID of the new binder
80
+ def create(path, tr, init)
81
+ raise Error::Sanity, 'Binder already open' if @path
82
+ Binder.limits_create(init)
83
+
84
+ # create completely empty binder
85
+ id_hash = create_raw(path, init[:id_text])
50
86
 
51
87
  # do the common creation
52
88
  @path = path
@@ -54,22 +90,13 @@ class BinderFs < Binder
54
90
  @jackets = {}
55
91
  @users = {}
56
92
  @values = {}
93
+ jck = _jacket_open(0)
57
94
  _create(jck, tr, init)
58
95
  jck.close
59
96
  _cache_write
60
97
  @path = nil
61
98
  @id_hash = nil
62
99
 
63
- # write info
64
- info = {
65
- 'sgfa_binder_ver' => 1,
66
- 'id_hash' => id_hash,
67
- 'id_text' => init[:id_text],
68
- }
69
- json = JSON.pretty_generate(info) + "\n"
70
- fn_info = File.join(path, 'sgfa_binder.json')
71
- File.open(fn_info, 'w', :encoding => 'utf-8'){|fi| fi.write json }
72
-
73
100
  return id_hash
74
101
  end # def create()
75
102
 
@@ -143,6 +170,14 @@ class BinderFs < Binder
143
170
  end # def _jacket_create()
144
171
 
145
172
 
173
+ #####################################
174
+ # Create a jacket from backup
175
+ def _jacket_create_raw(info)
176
+ jp = File.join(@path, info[:num].to_s)
177
+ JacketFs.create_raw(jp, info[:id_text], info[:id_hash])
178
+ end # def _jacket_create_raw()
179
+
180
+
146
181
  #####################################
147
182
  # Open a jacket
148
183
  def _jacket_open(num)