icfs 0.2.0 → 0.3.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 28031e9c5c67e30c2cf4778fbf56a7840117d256e4384e6e044e7cc888958d09
4
- data.tar.gz: 3abea632e084b00bd09ac570710eab52e99a152d073c6f96779a04c23dbc1c21
3
+ metadata.gz: 6ba64d02837a0591aee2cab20c11795beffa59f3ec3dce2014ac7dd0b55e89e5
4
+ data.tar.gz: 1b452562fa4df0cf870eb0ed5bbbf0f686b4ba78579e47e7ce075e84e18b1954
5
5
  SHA512:
6
- metadata.gz: cd28dc30f59e5bec1b272508d5b64ecb950a3e2b6d26d4318156069d19c338093177302ac09207a4b76b7decfa8272f84a8839f3165eeee28fdb9f15ae5c6ef3
7
- data.tar.gz: '08f19a62a3d3c21cdf46c8120bd0b5846107a57409aba71939587affed4e7daf32c4d0bd805919d884baa4d24b520521527f62e8edf66ed6f886cf2237122b27'
6
+ metadata.gz: '02909a341866abc4eb1eaa844317dd02b72ed55c59b0df518f3e54af06a024f6cb4ac2ea8f14a2b64b99e60bdd9dc2bc87ceee421ed591c498db1f0e5c4305c4'
7
+ data.tar.gz: 562c4de6f8605b2ab7b414101642488a93b98fa94157ba4408ed640b6944f8cee33ecb3f710be87570760fd7d0fc6249c2b353163173f64ea53d52624b7b91fe
data/data/icfs.css CHANGED
@@ -200,6 +200,12 @@ div.list-int {
200
200
  }
201
201
 
202
202
 
203
+ div.list-int-sm {
204
+ width: 2em;
205
+ padding: 0 0.15em 0 0.15em;
206
+ }
207
+
208
+
203
209
  div.list-perm {
204
210
  width: 15em;
205
211
  padding: 0 0.15em 0 0.15em;
@@ -238,7 +244,7 @@ div.list-text-m {
238
244
 
239
245
 
240
246
  div.list-time {
241
- width: 12em;
247
+ width: 15em;
242
248
  padding: 0 0.15em 0 0.15em;
243
249
  }
244
250
 
data/data/icfs.js CHANGED
@@ -167,6 +167,23 @@
167
167
 
168
168
  // Entry
169
169
 
170
+ ///////////////////////////////////////////////
171
+ // Enable/disable editing the entry
172
+ function entEnable(){
173
+ var ena = document.getElementById("ent-ena");
174
+ var bdy = document.getElementById("ent-body");
175
+
176
+ var enabled = ena.getAttribute("value");
177
+ if( enabled == "true" ){
178
+ ena.setAttribute("value", "false");
179
+ bdy.setAttribute("class", "ent-body hidden");
180
+ } else {
181
+ ena.setAttribute("value", "true");
182
+ bdy.setAttribute("class", "ent-body");
183
+ }
184
+ }
185
+
186
+
170
187
  ///////////////////////////////////////////////
171
188
  // Add an index
172
189
  function entAddIndex(){
@@ -376,25 +393,6 @@
376
393
 
377
394
  // Action
378
395
 
379
- ///////////////////////////////////////////////
380
- // Enable/disable editing the action
381
- function actEnable(){
382
- var but = document.getElementById("act-task-add");
383
- var ena = document.getElementById("act-ena");
384
- var tsk = document.getElementById("act-tasks");
385
-
386
- var enabled = ena.getAttribute("value");
387
- if( enabled == "true" ){
388
- ena.setAttribute("value", "false");
389
- tsk.setAttribute("class", "sect-right hidden");
390
- but.setAttribute("class", "sect-right invisible");
391
- } else {
392
- ena.setAttribute("value", "true");
393
- tsk.setAttribute("class", "sect-right");
394
- but.setAttribute("class", "sect-right");
395
- }
396
- }
397
-
398
396
 
399
397
  ///////////////////////////////////////////////
400
398
  // Add a row to a task
data/devel/run/base.rb CHANGED
@@ -61,7 +61,8 @@ def get_base
61
61
 
62
62
  # default config
63
63
  defaults = {
64
- 'tz' => '-04:00'
64
+ 'tz' => '-04:00',
65
+ 'rel_time' => true,
65
66
  }
66
67
 
67
68
  # base objects
@@ -0,0 +1,24 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # Investigative Case File System
4
+ #
5
+ # Copyright 2019 by Graham A. Field
6
+ #
7
+ # This program is free software: you can redistribute it and/or modify
8
+ # it under the terms of the GNU General Public License version 3.
9
+ #
10
+ # This program is distributed WITHOUT ANY WARRANTY; without even the
11
+ # implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12
+
13
+ # usage: <case-backup> <fs_store_dir> <caseid>
14
+
15
+ # frozen_string_literal: true
16
+
17
+ require_relative 'base'
18
+ require_relative '../../lib/icfs/store_fs'
19
+ require_relative '../../lib/icfs/utils/backup'
20
+
21
+ base = get_base()
22
+ backup = ICFS::Utils::Backup.new(base[:cache], base[:store], base[:log])
23
+ dst = ICFS::StoreFs.new(ARGV[0])
24
+ backup.copy(ARGV[1], dst)
@@ -0,0 +1,24 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # Investigative Case File System
4
+ #
5
+ # Copyright 2019 by Graham A. Field
6
+ #
7
+ # This program is free software: you can redistribute it and/or modify
8
+ # it under the terms of the GNU General Public License version 3.
9
+ #
10
+ # This program is distributed WITHOUT ANY WARRANTY; without even the
11
+ # implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12
+
13
+ # usage: <case-restore> <fs_store_dir> <caseid>
14
+
15
+ # frozen_string_literal: true
16
+
17
+ require_relative 'base'
18
+ require_relative '../../lib/icfs/store_fs'
19
+ require_relative '../../lib/utils/backup'
20
+
21
+ base = get_base()
22
+ backup = ICFS::Utils::Backup.new(base[:cache], base[:store], base[:log])
23
+ src = ICFS::StoreFs.new(ARGV[0])
24
+ backup.restore(ARGV[1], src)
data/devel/run/copy-s3.rb CHANGED
@@ -10,6 +10,8 @@
10
10
  # This program is distributed WITHOUT ANY WARRANTY; without even the
11
11
  # implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12
12
 
13
+ # usage: ./copy-s3.rb <source_dir> <prefix>
14
+
13
15
  # frozen_string_literal: true
14
16
 
15
17
  require 'aws-sdk-s3'
@@ -18,17 +18,6 @@ require 'aws-sdk-s3'
18
18
 
19
19
  require_relative '../../lib/icfs'
20
20
  require_relative '../../lib/icfs/cache_elastic'
21
- require_relative '../../lib/icfs/store_s3'
22
-
23
-
24
- # Minio config
25
- Aws.config.update(
26
- endpoint: 'http://minio:9000',
27
- access_key_id: 'minio_key',
28
- secret_access_key: 'minio_secret',
29
- force_path_style: true,
30
- region: 'us-east-1'
31
- )
32
21
 
33
22
  # default mapping
34
23
  map = {
@@ -42,10 +31,15 @@ map = {
42
31
  }.freeze
43
32
 
44
33
  # base items
45
- s3 = Aws::S3::Client.new
34
+ s3 = Aws::S3::Client.new(
35
+ endpoint: 'http://minio:9000',
36
+ access_key_id: 'minio_key',
37
+ secret_access_key: 'minio_secret',
38
+ force_path_style: true,
39
+ region: 'us-east-1'
40
+ )
46
41
  es = Faraday.new('http://elastic:9200')
47
42
  cache = ICFS::CacheElastic.new(map, es)
48
- store = ICFS::StoreS3.new(s3, 'icfs', 'case/')
49
43
 
50
44
  # create the indexes
51
45
  cache.create(ICFS::CacheElastic::Maps)
@@ -54,27 +48,3 @@ puts "Indexes created"
54
48
  # create a bucket
55
49
  s3.create_bucket(bucket: 'icfs')
56
50
  puts "S3 bucket created"
57
-
58
- api = ICFS::Api.new([], nil, cache, store)
59
-
60
- # add a template
61
- tmpl = {
62
- 'template' => true,
63
- 'status' => true,
64
- 'title' => 'Test template',
65
- 'access' => [
66
- {
67
- 'perm' => '[manage]',
68
- 'grant' => [
69
- 'role2'
70
- ]
71
- }
72
- ]
73
- }
74
- ent = {
75
- 'caseid' => 'test_tmpl',
76
- 'title' => 'Create template',
77
- 'content' => 'To test'
78
- }
79
- api.case_create(ent, tmpl, nil, 'user1')
80
- puts "Create template"
@@ -0,0 +1,33 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # Investigative Case File System
4
+ #
5
+ # Copyright 2019 by Graham A. Field
6
+ #
7
+ # This program is free software: you can redistribute it and/or modify
8
+ # it under the terms of the GNU General Public License version 3.
9
+ #
10
+ # This program is distributed WITHOUT ANY WARRANTY; without even the
11
+ # implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12
+
13
+ # usage: ./init-template.sh <template.json> <user>
14
+
15
+ # frozen_string_literal: true
16
+
17
+ require_relative 'base'
18
+
19
+ json = File.read(ARGV[0])
20
+ tmpl = ICFS::Items.parse(json, 'Template', ICFS::Items::ItemCase)
21
+ tmpl.delete('icfs')
22
+ tmpl.delete('log')
23
+
24
+ ent = {
25
+ 'caseid' => tmpl['caseid'],
26
+ 'title' => 'Create case',
27
+ 'content' => 'Added case using manual script',
28
+ }
29
+
30
+ base = get_base()
31
+ api = base[:api]
32
+
33
+ api.case_create(ent, tmpl, nil, ARGV[1])
data/lib/icfs/api.rb CHANGED
@@ -518,6 +518,7 @@ class Api
518
518
  after: Validate::IsIntPos,
519
519
  before: Validate::IsIntPos,
520
520
  user: Items::FieldUsergrp,
521
+ case_edit: Validate::IsBoolean,
521
522
  entry: Validate::IsIntPos,
522
523
  index: Validate::IsIntPos,
523
524
  action: Validate::IsIntPos,
@@ -908,6 +909,13 @@ class Api
908
909
  Items.validate(ent, 'entry', Items::ItemEntryNew)
909
910
  Items.validate(cse, 'case', Items::ItemCaseEdit)
910
911
 
912
+ # get caseid
913
+ cid = ent['caseid']
914
+ cid ||= cse['caseid']
915
+ unless cid
916
+ raise(Error::Values, 'No caseid provided')
917
+ end
918
+
911
919
  # access users/roles/groups are valid, unless manually specifying user
912
920
  unless unam
913
921
  cse["access"].each do |acc|
@@ -953,11 +961,11 @@ class Api
953
961
  # Prep
954
962
 
955
963
  # case
956
- cid = ent['caseid']
957
964
  cse['icfs'] = 1
958
965
  cse['caseid'] = cid
959
966
  cse['log'] = 1
960
967
  cse['tags'] ||= [ ICFS::TagNone ]
968
+ cse['entry'] = 1
961
969
  citem = Items.generate(cse, 'case', Items::ItemCase)
962
970
 
963
971
  # entry
@@ -979,7 +987,10 @@ class Api
979
987
  'entry' => {
980
988
  'num' => 1,
981
989
  },
982
- 'case_hash' => ICFS.hash(citem),
990
+ 'case' => {
991
+ 'set' => true,
992
+ 'hash' => ICFS.hash(citem),
993
+ },
983
994
  }
984
995
  log['files_hash'] = fhash if fhash
985
996
 
@@ -1040,7 +1051,7 @@ class Api
1040
1051
  ###############################################
1041
1052
  # Write items to a case
1042
1053
  #
1043
- # @param ent [Hash] Entry to record, required
1054
+ # @param ent [Hash] Entry to record, optional
1044
1055
  # @param act [Hash, Nilclass] Action to record, optional
1045
1056
  # @param idx [Hash, Nilclass] Index to record, optional
1046
1057
  # @param cse [Hash, Nilclass] Case to record, optional
@@ -1051,22 +1062,36 @@ class Api
1051
1062
  # Sanity checks
1052
1063
 
1053
1064
  # form & content
1054
- if idx || cse
1055
- Items.validate(ent, 'New Entry', Items::ItemEntryNew)
1056
- else
1057
- Items.validate(ent, 'Editable Entry', Items::ItemEntryEdit)
1065
+ if ent
1066
+ if (act || idx || cse)
1067
+ Items.validate(ent, 'New Entry', Items::ItemEntryNew)
1068
+ else
1069
+ Items.validate(ent, 'Editable Entry', Items::ItemEntryEdit)
1070
+ end
1058
1071
  end
1059
1072
  Items.validate(act, 'action', Items::ItemActionEdit) if act
1060
1073
  Items.validate(idx, 'index', Items::ItemIndexEdit) if idx
1061
1074
  Items.validate(cse, 'case', Items::ItemCaseEdit) if cse
1062
1075
 
1063
- # edit index OR case, not both
1064
- if idx && cse
1065
- raise(Error::Value, 'May not edit both case and index at once')
1076
+ # get caseid
1077
+ cid ||= ent['caseid'] if ent
1078
+ cid ||= cse['caseid'] if cse
1079
+ cid ||= act['caseid'] if act
1080
+ cid ||= idx['caseid'] if idx
1081
+ unless cid
1082
+ raise(Error::Values, 'No caseid provided')
1083
+ end
1084
+
1085
+ # no conflicting caseids
1086
+ if( (cse && cse['caseid'] && cse['caseid'] != cid) ||
1087
+ (act && act['caseid'] && act['caseid'] != cid) ||
1088
+ (idx && idx['caseid'] && idx['caseid'] != cid) )
1089
+ raise(Error::Values, 'Conflicting caseids provided')
1066
1090
  end
1067
1091
 
1068
1092
  # no changing the action
1069
- if act && ent['action'] && act['action'] && act['action'] != ent['action']
1093
+ if( act && ent && ent['action'] && act['action'] &&
1094
+ act['action'] != ent['action'] )
1070
1095
  raise(Error::Conflict, 'May not change entry\'s action')
1071
1096
  end
1072
1097
 
@@ -1103,17 +1128,21 @@ class Api
1103
1128
 
1104
1129
  ####################
1105
1130
  # Prep
1106
- cid = ent['caseid']
1107
1131
 
1108
1132
  # entry
1109
- ent['icfs'] = 1
1110
- ent['tags'] ||= [ ]
1111
- ent['user'] = @user
1112
- files, fhash = _pre_files(ent)
1133
+ if ent
1134
+ ent['icfs'] = 1
1135
+ ent['tags'] ||= [ ]
1136
+ ent['user'] = @user
1137
+ ent['caseid'] = cid
1138
+ files, fhash = _pre_files(ent)
1139
+ else
1140
+ files = []
1141
+ end
1113
1142
 
1114
1143
  # action
1115
1144
  if act
1116
- ent['tags'] << ICFS::TagAction
1145
+ ent['tags'] << ICFS::TagAction if ent
1117
1146
  act['icfs'] = 1
1118
1147
  act['caseid'] = cid
1119
1148
  act['tasks'].each do |tk|
@@ -1123,7 +1152,7 @@ class Api
1123
1152
 
1124
1153
  # index
1125
1154
  if idx
1126
- ent['tags'] << ICFS::TagIndex
1155
+ ent['tags'] << ICFS::TagIndex if ent
1127
1156
  idx['icfs'] = 1
1128
1157
  idx['caseid'] = cid
1129
1158
  idx['tags'] ||= [ ICFS::TagNone ]
@@ -1131,7 +1160,7 @@ class Api
1131
1160
 
1132
1161
  # case
1133
1162
  if cse
1134
- ent['tags'] << ICFS::TagCase
1163
+ ent['tags'] << ICFS::TagCase if ent
1135
1164
  cse['icfs'] = 1
1136
1165
  cse['caseid'] = cid
1137
1166
  cse['tags'] ||= [ ICFS::TagNone ]
@@ -1146,7 +1175,7 @@ class Api
1146
1175
  log['files_hash'] = fhash if fhash
1147
1176
 
1148
1177
  # no tags
1149
- ent['tags'] = [ ICFS::TagNone ] if ent['tags'].empty?
1178
+ ent['tags'] = [ ICFS::TagNone ] if ent && ent['tags'].empty?
1150
1179
 
1151
1180
  # current
1152
1181
  nxt = {
@@ -1171,14 +1200,18 @@ class Api
1171
1200
  cur = Items.parse(json, 'current', Items::ItemCurrent)
1172
1201
 
1173
1202
  # entry
1174
- if ent['entry']
1175
- enum = ent['entry']
1176
- json = @cache.entry_read(cid, enum)
1177
- ent_pri = Items.parse(json, 'entry', Items::ItemEntry)
1178
- nxt['entry'] = cur['entry']
1203
+ if ent
1204
+ if ent['entry']
1205
+ enum = ent['entry']
1206
+ json = @cache.entry_read(cid, enum)
1207
+ ent_pri = Items.parse(json, 'entry', Items::ItemEntry)
1208
+ nxt['entry'] = cur['entry']
1209
+ else
1210
+ enum = cur['entry'] + 1
1211
+ nxt['entry'] = enum
1212
+ end
1179
1213
  else
1180
- enum = cur['entry'] + 1
1181
- nxt['entry'] = enum
1214
+ nxt['entry'] = cur['entry']
1182
1215
  end
1183
1216
 
1184
1217
  # action
@@ -1186,7 +1219,7 @@ class Api
1186
1219
  anum = ent_pri['action']
1187
1220
  elsif act && act['action']
1188
1221
  anum = act['action']
1189
- elsif ent['action']
1222
+ elsif ent && ent['action']
1190
1223
  anum = ent['action']
1191
1224
  end
1192
1225
  if anum
@@ -1228,7 +1261,7 @@ class Api
1228
1261
  perms = Set.new
1229
1262
 
1230
1263
  # entry
1231
- perms.merge(ent['perms']) if ent['perms']
1264
+ perms.merge(ent['perms']) if ent && ent['perms']
1232
1265
  if ent_pri
1233
1266
 
1234
1267
  # must have those perms
@@ -1326,7 +1359,7 @@ class Api
1326
1359
 
1327
1360
  # write unless a case or pre-existing action
1328
1361
  unless cse || act_pri
1329
- perms.add( ICFS::PermWrite)
1362
+ perms.add( ICFS::PermWrite )
1330
1363
  end
1331
1364
 
1332
1365
  # permissions
@@ -1341,28 +1374,32 @@ class Api
1341
1374
  # Items
1342
1375
 
1343
1376
  # entry
1344
- ent['entry'] = enum
1345
- ent['log'] = lnum
1346
- ent['time'] ||= now
1347
- ent['action'] = anum if act
1348
- if idx
1349
- if ent['index']
1350
- ent['index'] = ent['index'].push(xnum).uniq.sort
1351
- else
1352
- ent['index'] = [ xnum ]
1377
+ if ent
1378
+ ent['entry'] = enum
1379
+ ent['log'] = lnum
1380
+ ent['time'] ||= now
1381
+ ent['action'] = anum if act
1382
+ if idx
1383
+ if ent['index']
1384
+ ent['index'] << xnum
1385
+ else
1386
+ ent['index'] = [ xnum ]
1387
+ end
1353
1388
  end
1389
+ ent['index'].sort!.uniq! if ent['index']
1390
+ ent['files'].each{|fi| fi['log'] ||= lnum } if ent['files']
1391
+ eitem = Items.generate(ent, 'entry', Items::ItemEntry)
1392
+ log['entry'] = {
1393
+ 'num' => enum,
1394
+ 'hash' => ICFS.hash(eitem)
1395
+ }
1354
1396
  end
1355
- ent['files'].each{|fi| fi['log'] ||= lnum } if ent['files']
1356
- eitem = Items.generate(ent, 'entry', Items::ItemEntry)
1357
- log['entry'] = {
1358
- 'num' => enum,
1359
- 'hash' => ICFS.hash(eitem)
1360
- }
1361
1397
 
1362
1398
  # action
1363
1399
  if act
1364
1400
  act['action'] = anum
1365
1401
  act['log'] = lnum
1402
+ act['entry'] = enum if ent
1366
1403
  aitem = Items.generate(act, 'action', Items::ItemAction)
1367
1404
  log['action'] = {
1368
1405
  'num' => anum,
@@ -1374,6 +1411,7 @@ class Api
1374
1411
  if idx
1375
1412
  idx['index'] = xnum
1376
1413
  idx['log'] = lnum
1414
+ idx['entry'] = enum if ent
1377
1415
  xitem = Items.generate(idx, 'index', Items::ItemIndex)
1378
1416
  log['index'] = {
1379
1417
  'num' => xnum,
@@ -1384,8 +1422,12 @@ class Api
1384
1422
  # case
1385
1423
  if cse
1386
1424
  cse['log'] = lnum
1425
+ cse['entry'] = enum if ent
1387
1426
  citem = Items.generate(cse, 'case', Items::ItemCase)
1388
- log['case_hash'] = ICFS.hash(citem)
1427
+ log['case'] = {
1428
+ 'set' => true,
1429
+ 'hash' => ICFS.hash(citem),
1430
+ }
1389
1431
  end
1390
1432
 
1391
1433
  # log
@@ -1403,8 +1445,10 @@ class Api
1403
1445
  # Write
1404
1446
 
1405
1447
  # entry
1406
- @cache.entry_write(cid, enum, eitem)
1407
- @store.entry_write(cid, enum, lnum, eitem)
1448
+ if ent
1449
+ @cache.entry_write(cid, enum, eitem)
1450
+ @store.entry_write(cid, enum, lnum, eitem)
1451
+ end
1408
1452
 
1409
1453
  # action
1410
1454
  if act
@@ -35,6 +35,7 @@ class CacheElastic < Cache
35
35
  "icfs": { "enabled": false },
36
36
  "caseid": { "type": "keyword" },
37
37
  "log": { "enabled": false },
38
+ "entry": { "enabled": false },
38
39
  "template": { "type": "boolean" },
39
40
  "status": { "type": "boolean" },
40
41
  "title": { "type": "text" },
@@ -66,7 +67,10 @@ class CacheElastic < Cache
66
67
  "num": { "type": "integer" },
67
68
  "hash": { "enabled": false }
68
69
  }},
69
- "case_hash": { "enabled": false },
70
+ "case": { "properties": {
71
+ "set": { "type": "boolean" },
72
+ "hash": { "enabled": false }
73
+ }},
70
74
  "files_hash": { "enabled": false }
71
75
  }}}
72
76
  }',
@@ -104,6 +108,7 @@ class CacheElastic < Cache
104
108
  "caseid": { "type": "keyword" },
105
109
  "action": { "type": "integer" },
106
110
  "log": { "enabled": false },
111
+ "entry": { "enabled": false },
107
112
  "tasks": { "type": "nested","properties": {
108
113
  "assigned": { "type": "keyword" },
109
114
  "title": { "type": "text" },
@@ -121,6 +126,7 @@ class CacheElastic < Cache
121
126
  "caseid": { "type": "keyword" },
122
127
  "index": { "type": "integer" },
123
128
  "log": { "enabled": false },
129
+ "entry": { "enabled": false },
124
130
  "title": {
125
131
  "type": "text",
126
132
  "fields": { "raw": { "type": "keyword" }}
@@ -532,7 +538,7 @@ class CacheElastic < Cache
532
538
  tags: 'tags',
533
539
  perms: ['perms', :empty],
534
540
  action: ['action', :zero],
535
- index: ['index', :size],
541
+ indexes: ['index', :size],
536
542
  files: ['files', :size],
537
543
  stats: ['stats', :size],
538
544
  }.freeze
@@ -792,6 +798,7 @@ class CacheElastic < Cache
792
798
  entry: ['entry', :sub, 'num'].freeze,
793
799
  index: ['index', :sub, 'num'].freeze,
794
800
  action: ['action', :sub, 'num'].freeze,
801
+ case: ['case', :sub, 'set'].freeze,
795
802
  files: ['files_hash', :size].freeze,
796
803
  }.freeze
797
804
 
@@ -806,6 +813,7 @@ class CacheElastic < Cache
806
813
  _query_term('caseid', query[:caseid]),
807
814
  _query_times('times', query[:after], query[:before]),
808
815
  _query_term('user', query[:user]),
816
+ _query_exists('case.set', query[:case_edit]),
809
817
  _query_term('entry.num', query[:entry]),
810
818
  _query_term('index.num', query[:index]),
811
819
  _query_term('action.num', query[:action]),
data/lib/icfs/config.rb CHANGED
@@ -30,9 +30,10 @@ class Config
30
30
  method: :string,
31
31
  valid: /[+\-](0[0-9]|1[0-2]):[0-5][0-9]/.freeze,
32
32
  whitelist: true,
33
- }
34
- }
35
- }
33
+ }.freeze,
34
+ 'rel_time' => Validate::IsBoolean,
35
+ }.freeze
36
+ }.freeze
36
37
 
37
38
  ###############################################
38
39
  # New instance