icfs 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/bin/icfs_demo_create.rb +89 -0
- data/bin/icfs_demo_fcgi.rb +51 -0
- data/bin/icfs_demo_ssl_gen.rb +84 -0
- data/bin/icfs_demo_web.rb +50 -0
- data/bin/icfs_dev_todo.rb +20 -0
- data/data/demo_config.yml +94 -0
- data/data/icfs.css +475 -0
- data/data/icfs.js +458 -0
- data/lib/icfs.rb +109 -0
- data/lib/icfs/api.rb +1436 -0
- data/lib/icfs/cache.rb +254 -0
- data/lib/icfs/cache_elastic.rb +1154 -0
- data/lib/icfs/demo/auth.rb +74 -0
- data/lib/icfs/demo/static.rb +59 -0
- data/lib/icfs/demo/timezone.rb +38 -0
- data/lib/icfs/elastic.rb +83 -0
- data/lib/icfs/items.rb +653 -0
- data/lib/icfs/store.rb +278 -0
- data/lib/icfs/store_fs.rb +98 -0
- data/lib/icfs/store_s3.rb +97 -0
- data/lib/icfs/users.rb +80 -0
- data/lib/icfs/users_elastic.rb +166 -0
- data/lib/icfs/users_fs.rb +132 -0
- data/lib/icfs/validate.rb +479 -0
- data/lib/icfs/web/auth_ssl.rb +73 -0
- data/lib/icfs/web/client.rb +4498 -0
- metadata +77 -0
@@ -0,0 +1,89 @@
|
|
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
|
+
require 'yaml'
|
14
|
+
require 'json'
|
15
|
+
require 'faraday'
|
16
|
+
require 'fileutils'
|
17
|
+
|
18
|
+
require_relative '../lib/icfs'
|
19
|
+
require_relative '../lib/icfs/cache_elastic'
|
20
|
+
require_relative '../lib/icfs/store_fs'
|
21
|
+
require_relative '../lib/icfs/users_fs'
|
22
|
+
|
23
|
+
# load the config file
|
24
|
+
cfg = YAML.load_file(ARGV[0])
|
25
|
+
map = {}
|
26
|
+
cfg['cache']['map'].each do |key, val|
|
27
|
+
map[key.to_sym] = val
|
28
|
+
end
|
29
|
+
|
30
|
+
es = Faraday.new(cfg['elastic']['base'])
|
31
|
+
cache = ICFS::CacheElastic.new(map, es)
|
32
|
+
store = ICFS::StoreFs.new(cfg['store']['dir'])
|
33
|
+
users = ICFS::UsersFs.new(cfg['users']['dir'])
|
34
|
+
|
35
|
+
# clear out the store
|
36
|
+
if File.exists?(cfg['store']['dir'])
|
37
|
+
FileUtils.rm_rf(cfg['store']['dir'])
|
38
|
+
puts "Deleted store directory"
|
39
|
+
end
|
40
|
+
FileUtils.mkdir_p(cfg['store']['dir'])
|
41
|
+
puts "Created store directory: %s" % cfg['store']['dir']
|
42
|
+
|
43
|
+
# clear out the users
|
44
|
+
if File.exists?(cfg['users']['dir'])
|
45
|
+
FileUtils.rm_rf(cfg['users']['dir'])
|
46
|
+
puts "Deleted users directory"
|
47
|
+
end
|
48
|
+
FileUtils.mkdir_p(cfg['users']['dir'])
|
49
|
+
puts "Created users directory: %s" % cfg['users']['dir']
|
50
|
+
|
51
|
+
# delete the indexes
|
52
|
+
map.each do |sym, name|
|
53
|
+
resp = es.run_request(:delete, name, '', {})
|
54
|
+
if resp.success?
|
55
|
+
puts 'Deleted index: %s' % name
|
56
|
+
else
|
57
|
+
puts 'Failed to delete index: %s' % name
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
# add the users
|
62
|
+
cfg['init']['urg'].each do |usr|
|
63
|
+
users.write(usr)
|
64
|
+
puts "Added user/role/group: %s" % usr['name']
|
65
|
+
end
|
66
|
+
|
67
|
+
# create the indexes
|
68
|
+
cache.create(ICFS::CacheElastic::Maps)
|
69
|
+
puts "Indexes created"
|
70
|
+
|
71
|
+
api = ICFS::Api.new([], users, cache, store)
|
72
|
+
api.user = cfg['init']['user']
|
73
|
+
|
74
|
+
# add the templates
|
75
|
+
cfg['init']['templates'].each do |tmpl|
|
76
|
+
tp = {
|
77
|
+
'template' => true,
|
78
|
+
'status' => true,
|
79
|
+
'title' => tmpl['template'],
|
80
|
+
'access' => tmpl['access'],
|
81
|
+
}
|
82
|
+
ent = {
|
83
|
+
'caseid' => tmpl['caseid'],
|
84
|
+
'title' => tmpl['entry'],
|
85
|
+
'content' => tmpl['content']
|
86
|
+
}
|
87
|
+
api.case_create(ent, tp)
|
88
|
+
puts "Created template: %s" % tmpl['caseid']
|
89
|
+
end
|
@@ -0,0 +1,51 @@
|
|
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
|
+
require 'yaml'
|
14
|
+
require 'faraday'
|
15
|
+
|
16
|
+
require_relative '../lib/icfs'
|
17
|
+
require_relative '../lib/icfs/cache_elastic'
|
18
|
+
require_relative '../lib/icfs/store_fs'
|
19
|
+
require_relative '../lib/icfs/users_fs'
|
20
|
+
require_relative '../lib/icfs/web/client'
|
21
|
+
require_relative '../lib/icfs/web/auth_ssl'
|
22
|
+
require_relative '../lib/icfs/demo/timezone'
|
23
|
+
|
24
|
+
# load the config file
|
25
|
+
cfg = YAML.load_file(ARGV[0])
|
26
|
+
map = {}
|
27
|
+
cfg['cache']['map'].each do |key, val|
|
28
|
+
map[key.to_sym] = val
|
29
|
+
end
|
30
|
+
|
31
|
+
es = Faraday.new(cfg['elastic']['base'])
|
32
|
+
cache = ICFS::CacheElastic.new(map, es)
|
33
|
+
store = ICFS::StoreFs.new(cfg['store']['dir'])
|
34
|
+
users = ICFS::UsersFs.new(cfg['users']['dir'])
|
35
|
+
api = ICFS::Api.new([], users, cache, store)
|
36
|
+
web = ICFS::Web::Client.new(cfg['web']['css'], cfg['web']['script'])
|
37
|
+
|
38
|
+
user_map = {
|
39
|
+
'CN=client 1,OU=Test Client,OU=example,OU=org' => 'user1',
|
40
|
+
'CN=client 2,OU=Test Client,OU=example,OU=org' => 'user2',
|
41
|
+
'CN=client 3,OU=Test Client,OU=example,OU=org' => 'user3'
|
42
|
+
}
|
43
|
+
|
44
|
+
app = Rack::Builder.new do
|
45
|
+
use(ICFS::Web::AuthSsl, user_map, api)
|
46
|
+
use(ICFS::Demo::Timezone, cfg['web']['tz'])
|
47
|
+
run web
|
48
|
+
end
|
49
|
+
|
50
|
+
puts 'try to run'
|
51
|
+
Rack::Handler::FastCGI.run(app, {Host: '127.0.0.1', Port: 9000} )
|
@@ -0,0 +1,84 @@
|
|
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
|
+
require 'openssl'
|
14
|
+
|
15
|
+
|
16
|
+
# make a CA key
|
17
|
+
ca_key = OpenSSL::PKey::RSA.new 2048
|
18
|
+
ca_cert = OpenSSL::X509::Certificate.new
|
19
|
+
ca_cert.version = 2
|
20
|
+
ca_cert.serial = 1
|
21
|
+
ca_cert.subject = OpenSSL::X509::Name.parse('/OU=org/OU=example/CN=Test Root CA')
|
22
|
+
ca_cert.issuer = ca_cert.subject
|
23
|
+
ca_cert.public_key = ca_key.public_key
|
24
|
+
ca_cert.not_before = Time.now
|
25
|
+
ca_cert.not_after = ca_cert.not_before + (30 * 24 * 60 * 60) # 30 days
|
26
|
+
ef = OpenSSL::X509::ExtensionFactory.new
|
27
|
+
ef.subject_certificate = ca_cert
|
28
|
+
ef.issuer_certificate = ca_cert
|
29
|
+
ca_cert.add_extension(ef.create_extension("basicConstraints","CA:TRUE",true))
|
30
|
+
ca_cert.add_extension(ef.create_extension("keyUsage","keyCertSign, cRLSign", true))
|
31
|
+
ca_cert.add_extension(ef.create_extension("subjectKeyIdentifier","hash",false))
|
32
|
+
ca_cert.add_extension(ef.create_extension("authorityKeyIdentifier","keyid:always",false))
|
33
|
+
ca_cert.sign(ca_key, OpenSSL::Digest::SHA256.new)
|
34
|
+
|
35
|
+
# save CA cert
|
36
|
+
File.open("ca_cert.pem", "wb"){|fi| fi.write ca_cert.to_pem }
|
37
|
+
|
38
|
+
|
39
|
+
# make a server key
|
40
|
+
srv_key = OpenSSL::PKey::RSA.new 2048
|
41
|
+
srv_cert = OpenSSL::X509::Certificate.new
|
42
|
+
srv_cert.version = 2
|
43
|
+
srv_cert.serial = 2
|
44
|
+
srv_cert.subject = OpenSSL::X509::Name.parse('/OU=org/OU=example/OU=Test Server/CN=localhost')
|
45
|
+
srv_cert.issuer = ca_cert.subject
|
46
|
+
srv_cert.public_key = srv_key.public_key
|
47
|
+
srv_cert.not_before = Time.now
|
48
|
+
srv_cert.not_after = srv_cert.not_before + (30 * 24 * 60 * 60) # 30 days
|
49
|
+
ef = OpenSSL::X509::ExtensionFactory.new
|
50
|
+
ef.subject_certificate = srv_cert
|
51
|
+
ef.issuer_certificate = ca_cert
|
52
|
+
srv_cert.add_extension(ef.create_extension("basicConstraints", "CA:FALSE"))
|
53
|
+
srv_cert.add_extension(ef.create_extension("keyUsage", "keyEncipherment,dataEncipherment,digitalSignature"))
|
54
|
+
srv_cert.add_extension(ef.create_extension("subjectKeyIdentifier","hash",false))
|
55
|
+
srv_cert.sign(ca_key, OpenSSL::Digest::SHA256.new)
|
56
|
+
|
57
|
+
# save server key
|
58
|
+
File.open("srv_cert.pem", "wb"){|fi| fi.write srv_cert.to_pem }
|
59
|
+
File.open("srv_key.pem", "wb"){|fi| fi.write srv_key.to_pem }
|
60
|
+
|
61
|
+
# make client certs
|
62
|
+
5.times do |ix|
|
63
|
+
clt_key = OpenSSL::PKey::RSA.new 2048
|
64
|
+
clt_cert = OpenSSL::X509::Certificate.new
|
65
|
+
clt_cert.version = 2
|
66
|
+
clt_cert.serial = ix+3
|
67
|
+
clt_cert.subject = OpenSSL::X509::Name.parse('/OU=org/OU=example/OU=Test Client/CN=client %d' % ix)
|
68
|
+
clt_cert.issuer = ca_cert.subject
|
69
|
+
clt_cert.public_key = clt_key.public_key
|
70
|
+
clt_cert.not_before = Time.now
|
71
|
+
clt_cert.not_after = clt_cert.not_before + (30 * 24 * 60 * 60) # 30 days
|
72
|
+
ef = OpenSSL::X509::ExtensionFactory.new
|
73
|
+
ef.subject_certificate = clt_cert
|
74
|
+
ef.issuer_certificate = ca_cert
|
75
|
+
clt_cert.add_extension(ef.create_extension("basicConstraints", "CA:FALSE"))
|
76
|
+
clt_cert.add_extension(ef.create_extension("keyUsage", "keyEncipherment,dataEncipherment,digitalSignature"))
|
77
|
+
clt_cert.sign(ca_key, OpenSSL::Digest::SHA256.new)
|
78
|
+
|
79
|
+
# pkcs12
|
80
|
+
clt_pkcs12 = OpenSSL::PKCS12.create('demo', 'client-%d' % ix, clt_key, clt_cert)
|
81
|
+
|
82
|
+
# save cert
|
83
|
+
File.open('clt_%d.pfx' % ix, 'wb'){|fi| fi.write clt_pkcs12.to_der }
|
84
|
+
end
|
@@ -0,0 +1,50 @@
|
|
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
|
+
require 'yaml'
|
14
|
+
require 'faraday'
|
15
|
+
|
16
|
+
require_relative '../lib/icfs'
|
17
|
+
require_relative '../lib/icfs/cache_elastic'
|
18
|
+
require_relative '../lib/icfs/store_fs'
|
19
|
+
require_relative '../lib/icfs/users_fs'
|
20
|
+
require_relative '../lib/icfs/web/client'
|
21
|
+
require_relative '../lib/icfs/demo/auth'
|
22
|
+
require_relative '../lib/icfs/demo/static'
|
23
|
+
require_relative '../lib/icfs/demo/timezone'
|
24
|
+
|
25
|
+
# load the config file
|
26
|
+
cfg = YAML.load_file(ARGV[0])
|
27
|
+
map = {}
|
28
|
+
cfg['cache']['map'].each do |key, val|
|
29
|
+
map[key.to_sym] = val
|
30
|
+
end
|
31
|
+
|
32
|
+
es = Faraday.new(cfg['elastic']['base'])
|
33
|
+
cache = ICFS::CacheElastic.new(map, es)
|
34
|
+
store = ICFS::StoreFs.new(cfg['store']['dir'])
|
35
|
+
users = ICFS::UsersFs.new(cfg['users']['dir'])
|
36
|
+
api = ICFS::Api.new([], users, cache, store)
|
37
|
+
web = ICFS::Web::Client.new(cfg['web']['css'], cfg['web']['script'])
|
38
|
+
|
39
|
+
app = Rack::Builder.new do
|
40
|
+
use(ICFS::Demo::Auth, api)
|
41
|
+
use(ICFS::Demo::Static, cfg['web']['static'])
|
42
|
+
use(ICFS::Demo::Timezone, cfg['web']['tz'])
|
43
|
+
run web
|
44
|
+
end
|
45
|
+
|
46
|
+
opts = {}
|
47
|
+
opts[:Host] = cfg['web']['host'] if cfg['web']['host']
|
48
|
+
opts[:Port] = cfg['web']['port'] if cfg['web']['port']
|
49
|
+
|
50
|
+
Rack::Handler::WEBrick.run(app, opts)
|
@@ -0,0 +1,20 @@
|
|
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
|
+
|
14
|
+
require 'yard'
|
15
|
+
|
16
|
+
YARD::Registry.load!.all.each do |o|
|
17
|
+
todo = o.tags(:todo)
|
18
|
+
next if todo.empty?
|
19
|
+
todo.each{|tg| puts "%s\n %s\n\n" % [o.path, tg.text] }
|
20
|
+
end
|
@@ -0,0 +1,94 @@
|
|
1
|
+
#
|
2
|
+
# Investigative Case File System
|
3
|
+
#
|
4
|
+
# Copyright 2019 by Graham A. Field
|
5
|
+
#
|
6
|
+
# This program is free software: you can redistribute it and/or modify
|
7
|
+
# it under the terms of the GNU General Public License version 3.
|
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
|
+
cache:
|
13
|
+
map:
|
14
|
+
entry: entry
|
15
|
+
case: case
|
16
|
+
action: action
|
17
|
+
index: index
|
18
|
+
log: log
|
19
|
+
lock: lock
|
20
|
+
current: current
|
21
|
+
|
22
|
+
elastic:
|
23
|
+
base: "http://localhost:9200"
|
24
|
+
|
25
|
+
store:
|
26
|
+
dir: work/store
|
27
|
+
|
28
|
+
users:
|
29
|
+
dir: work/users
|
30
|
+
|
31
|
+
init:
|
32
|
+
user: user1
|
33
|
+
urg:
|
34
|
+
- name: role1
|
35
|
+
type: role
|
36
|
+
|
37
|
+
- name: role2
|
38
|
+
type: role
|
39
|
+
|
40
|
+
- name: role3
|
41
|
+
type: role
|
42
|
+
|
43
|
+
- name: group1
|
44
|
+
type: group
|
45
|
+
|
46
|
+
- name: group2
|
47
|
+
type: group
|
48
|
+
|
49
|
+
- name: user1
|
50
|
+
type: user
|
51
|
+
roles:
|
52
|
+
- role2
|
53
|
+
- role3
|
54
|
+
groups:
|
55
|
+
- group2
|
56
|
+
perms:
|
57
|
+
- "{perm_a}"
|
58
|
+
- "{perm_b}"
|
59
|
+
|
60
|
+
- name: user2
|
61
|
+
type: user
|
62
|
+
roles:
|
63
|
+
- role1
|
64
|
+
- role2
|
65
|
+
groups:
|
66
|
+
- group1
|
67
|
+
perms:
|
68
|
+
- "{perm_b}"
|
69
|
+
|
70
|
+
templates:
|
71
|
+
- caseid: template1
|
72
|
+
template: "New Template"
|
73
|
+
access:
|
74
|
+
- perm: "[manage]"
|
75
|
+
grant:
|
76
|
+
- user1
|
77
|
+
- perm: "[write]"
|
78
|
+
grant:
|
79
|
+
- group1
|
80
|
+
entry: "Create new template"
|
81
|
+
content: "New template being created"
|
82
|
+
|
83
|
+
|
84
|
+
web:
|
85
|
+
css: "/static/icfs.css"
|
86
|
+
script: "/static/icfs.js"
|
87
|
+
tz: "-04:00"
|
88
|
+
static:
|
89
|
+
"/static/icfs.css":
|
90
|
+
path: "data/icfs.css"
|
91
|
+
mime: "text/css; charset=utf-8"
|
92
|
+
"/static/icfs.js":
|
93
|
+
path: "data/icfs.js"
|
94
|
+
mime: "application/javascript; charset=utf-8"
|
data/data/icfs.css
ADDED
@@ -0,0 +1,475 @@
|
|
1
|
+
/*************************************************************************
|
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
|
+
**************************************************************************/
|
14
|
+
|
15
|
+
/*************************************************************************
|
16
|
+
General
|
17
|
+
*/
|
18
|
+
|
19
|
+
a {
|
20
|
+
color: black;
|
21
|
+
text-decoration: none;
|
22
|
+
}
|
23
|
+
|
24
|
+
a:hover {
|
25
|
+
text-decoration: underline;
|
26
|
+
}
|
27
|
+
|
28
|
+
|
29
|
+
input {
|
30
|
+
border: 1px solid black;
|
31
|
+
border-radius: 3px;
|
32
|
+
font-family: serif;
|
33
|
+
font-size: 12pt;
|
34
|
+
margin: 0.1em;
|
35
|
+
padding: 0.2em;
|
36
|
+
}
|
37
|
+
|
38
|
+
textarea {
|
39
|
+
border: 1px solid black;
|
40
|
+
border-radius: 3px;
|
41
|
+
font-family: serif;
|
42
|
+
font-size: 12pt;
|
43
|
+
margin: 0.1em;
|
44
|
+
padding: 0.2em;
|
45
|
+
}
|
46
|
+
|
47
|
+
|
48
|
+
/*************************************************************************
|
49
|
+
Navbar
|
50
|
+
*/
|
51
|
+
|
52
|
+
div.nav {
|
53
|
+
border-width: 3px;
|
54
|
+
border-style: none none solid none;
|
55
|
+
border-color: green;
|
56
|
+
display: flex;
|
57
|
+
}
|
58
|
+
|
59
|
+
div.nav-icfs {
|
60
|
+
color: green;
|
61
|
+
width: 3em;
|
62
|
+
}
|
63
|
+
|
64
|
+
div.nav-icfs a {
|
65
|
+
color: green;
|
66
|
+
}
|
67
|
+
|
68
|
+
div.nav-case {
|
69
|
+
color: blue;
|
70
|
+
width: 15em;
|
71
|
+
}
|
72
|
+
|
73
|
+
div.nav-case a {
|
74
|
+
color: blue;
|
75
|
+
}
|
76
|
+
|
77
|
+
div.nav-tab {
|
78
|
+
text-align: center;
|
79
|
+
border: 1px;
|
80
|
+
width: 7em;
|
81
|
+
border-style: solid solid none solid;
|
82
|
+
}
|
83
|
+
|
84
|
+
|
85
|
+
/*************************************************************************
|
86
|
+
Sidebar layout
|
87
|
+
*/
|
88
|
+
|
89
|
+
div.sbar {
|
90
|
+
border-bottom: thin solid;
|
91
|
+
display: flex;
|
92
|
+
align-content: stretch;
|
93
|
+
}
|
94
|
+
|
95
|
+
div.sbar-main {
|
96
|
+
flex: 1 0 0;
|
97
|
+
vertical-align: top;
|
98
|
+
}
|
99
|
+
|
100
|
+
div.sbar-side {
|
101
|
+
flex: 0 0 20em;
|
102
|
+
border-right: thin solid;
|
103
|
+
vertical-align: top;
|
104
|
+
}
|
105
|
+
|
106
|
+
div.sbar-side-head {
|
107
|
+
text-align: center;
|
108
|
+
border-bottom: thin solid;
|
109
|
+
color: darkblue;
|
110
|
+
}
|
111
|
+
|
112
|
+
div.sbar-main-head {
|
113
|
+
font-size: 120%;
|
114
|
+
padding: 0.25em;
|
115
|
+
}
|
116
|
+
|
117
|
+
div.sbar-main-sub {
|
118
|
+
font-weight: bold;
|
119
|
+
font-size: 75%;
|
120
|
+
}
|
121
|
+
|
122
|
+
pre.sbar-main-content {
|
123
|
+
font-family: serif;
|
124
|
+
font-size: 12pt;
|
125
|
+
border-top: thin solid;
|
126
|
+
border-bottom: thin solid;
|
127
|
+
margin: 0px;
|
128
|
+
padding: 0.25em;
|
129
|
+
white-space: pre-wrap;
|
130
|
+
}
|
131
|
+
|
132
|
+
|
133
|
+
/*************************************************************************
|
134
|
+
Sections
|
135
|
+
*/
|
136
|
+
|
137
|
+
div.sect {
|
138
|
+
padding: 0.25em;
|
139
|
+
}
|
140
|
+
|
141
|
+
div.sect-head {
|
142
|
+
display: flex;
|
143
|
+
flex-direction: row;
|
144
|
+
justify-content: space-between;
|
145
|
+
padding: 0.2em;
|
146
|
+
background: #d8d8d8;
|
147
|
+
}
|
148
|
+
|
149
|
+
div.sect-main {
|
150
|
+
display: flex;
|
151
|
+
flex-direction: row;
|
152
|
+
justify-content: space-between;
|
153
|
+
padding: 0.2em;
|
154
|
+
background: #99bbff;
|
155
|
+
}
|
156
|
+
|
157
|
+
|
158
|
+
div.sect-label {
|
159
|
+
flex: 0 1 auto;
|
160
|
+
}
|
161
|
+
|
162
|
+
|
163
|
+
div.sect-fill {
|
164
|
+
flex: 1 1 auto;
|
165
|
+
}
|
166
|
+
|
167
|
+
|
168
|
+
div.sect-head-right {
|
169
|
+
flex: 0 1 auto;
|
170
|
+
}
|
171
|
+
|
172
|
+
|
173
|
+
/*************************************************************************
|
174
|
+
List
|
175
|
+
*/
|
176
|
+
|
177
|
+
div.list-head {
|
178
|
+
display: flex;
|
179
|
+
flex-direction: row;
|
180
|
+
border-bottom: solid;
|
181
|
+
border-width: 1px;
|
182
|
+
font-style: italic;
|
183
|
+
}
|
184
|
+
|
185
|
+
|
186
|
+
div.list-row {
|
187
|
+
display: flex;
|
188
|
+
flex-direction: row;
|
189
|
+
flex-wrap: wrap;
|
190
|
+
border-bottom: dotted;
|
191
|
+
border-width: 1px;
|
192
|
+
align-items: flex-start;
|
193
|
+
}
|
194
|
+
|
195
|
+
|
196
|
+
div.list-int {
|
197
|
+
width: 4em;
|
198
|
+
padding: 0 0.15em 0 0.15em;
|
199
|
+
}
|
200
|
+
|
201
|
+
|
202
|
+
div.list-perm {
|
203
|
+
width: 15em;
|
204
|
+
padding: 0 0.15em 0 0.15em;
|
205
|
+
}
|
206
|
+
|
207
|
+
|
208
|
+
div.list-usergrp {
|
209
|
+
width: 15em;
|
210
|
+
padding: 0 0.15em 0 0.15em;
|
211
|
+
}
|
212
|
+
|
213
|
+
|
214
|
+
div.list-label {
|
215
|
+
width: 7em;
|
216
|
+
padding: 0 0.15em 0 0.15em;
|
217
|
+
text-align: right;
|
218
|
+
}
|
219
|
+
|
220
|
+
|
221
|
+
div.list-caseid {
|
222
|
+
width: 10em;
|
223
|
+
padding: 0 0.15em 0 0.15em;
|
224
|
+
}
|
225
|
+
|
226
|
+
|
227
|
+
div.list-text-s {
|
228
|
+
width: 8em;
|
229
|
+
padding: 0 0.15em 0 0.15em;
|
230
|
+
}
|
231
|
+
|
232
|
+
|
233
|
+
div.list-text-m {
|
234
|
+
width: 12em;
|
235
|
+
padding: 0 0.15em 0 0.15em;
|
236
|
+
}
|
237
|
+
|
238
|
+
|
239
|
+
div.list-time {
|
240
|
+
width: 12em;
|
241
|
+
padding: 0 0.15em 0 0.15em;
|
242
|
+
}
|
243
|
+
|
244
|
+
|
245
|
+
div.list-tag {
|
246
|
+
width: 12em;
|
247
|
+
padding: 0 0.15em 0 0.15em;
|
248
|
+
}
|
249
|
+
|
250
|
+
div.list-hash {
|
251
|
+
font-family: monospace;
|
252
|
+
font-size: 12pt;
|
253
|
+
padding: 0 0.15em 0 0.15em;
|
254
|
+
}
|
255
|
+
|
256
|
+
div.list-stat {
|
257
|
+
width: 12em;
|
258
|
+
padding: 0 0.15em 0 0.15em;
|
259
|
+
}
|
260
|
+
|
261
|
+
div.list-snip {
|
262
|
+
flex: 0 1 100%;
|
263
|
+
padding: 0.1em;
|
264
|
+
margin: 0.1em 1em 0.1em 1em;
|
265
|
+
background: #f0f0f0;
|
266
|
+
}
|
267
|
+
|
268
|
+
/*************************************************************************
|
269
|
+
Tasks
|
270
|
+
*/
|
271
|
+
|
272
|
+
div.task {
|
273
|
+
border-bottom: thin solid;
|
274
|
+
}
|
275
|
+
|
276
|
+
|
277
|
+
div.task-ed {
|
278
|
+
background: #99b3ff;
|
279
|
+
}
|
280
|
+
|
281
|
+
|
282
|
+
div.task-ro {
|
283
|
+
background: #ff6666;
|
284
|
+
}
|
285
|
+
|
286
|
+
|
287
|
+
/*************************************************************************
|
288
|
+
Forms
|
289
|
+
*/
|
290
|
+
|
291
|
+
div.form-row {
|
292
|
+
min-height: 2em;
|
293
|
+
display: flex;
|
294
|
+
flex-direction: row;
|
295
|
+
align-items: baseline;
|
296
|
+
}
|
297
|
+
|
298
|
+
|
299
|
+
input.form-index {
|
300
|
+
width: 20em;
|
301
|
+
}
|
302
|
+
|
303
|
+
|
304
|
+
input.form-stat {
|
305
|
+
width: 15em;
|
306
|
+
}
|
307
|
+
|
308
|
+
|
309
|
+
input.form-usergrp {
|
310
|
+
width: 10em;
|
311
|
+
}
|
312
|
+
|
313
|
+
|
314
|
+
input.form-caseid {
|
315
|
+
width: 12em;
|
316
|
+
}
|
317
|
+
|
318
|
+
|
319
|
+
input.form-sort {
|
320
|
+
width: 10em;
|
321
|
+
}
|
322
|
+
|
323
|
+
|
324
|
+
input.form-content {
|
325
|
+
width: 50em;
|
326
|
+
}
|
327
|
+
|
328
|
+
|
329
|
+
input.form-title {
|
330
|
+
width: 40em;
|
331
|
+
}
|
332
|
+
|
333
|
+
|
334
|
+
input.form-file-name {
|
335
|
+
width: 30em;
|
336
|
+
}
|
337
|
+
|
338
|
+
|
339
|
+
input.form-file-upl {
|
340
|
+
}
|
341
|
+
|
342
|
+
|
343
|
+
input.form-float {
|
344
|
+
width: 5em;
|
345
|
+
}
|
346
|
+
|
347
|
+
|
348
|
+
input.form-check {
|
349
|
+
}
|
350
|
+
|
351
|
+
|
352
|
+
input.form-time {
|
353
|
+
width: 15em;
|
354
|
+
}
|
355
|
+
|
356
|
+
|
357
|
+
input.form-perm {
|
358
|
+
width: 15em;
|
359
|
+
}
|
360
|
+
|
361
|
+
|
362
|
+
input.form-int {
|
363
|
+
width: 5em;
|
364
|
+
}
|
365
|
+
|
366
|
+
|
367
|
+
input.form-tag {
|
368
|
+
width: 20em;
|
369
|
+
}
|
370
|
+
|
371
|
+
|
372
|
+
input.form-boolean {
|
373
|
+
width: 5em;
|
374
|
+
}
|
375
|
+
|
376
|
+
|
377
|
+
textarea.form-content {
|
378
|
+
width: 50em;
|
379
|
+
height: 20em;
|
380
|
+
}
|
381
|
+
|
382
|
+
|
383
|
+
/*************************************************************************
|
384
|
+
Other divs
|
385
|
+
*/
|
386
|
+
|
387
|
+
div.desc {
|
388
|
+
padding: 0.25em;
|
389
|
+
background: #fff5e6;
|
390
|
+
margin-bottom: 1em;
|
391
|
+
}
|
392
|
+
|
393
|
+
div.desc-head {
|
394
|
+
font-size: 120%;
|
395
|
+
text-align: center;
|
396
|
+
}
|
397
|
+
|
398
|
+
span.desc-query {
|
399
|
+
text-decoration: underline;
|
400
|
+
|
401
|
+
}
|
402
|
+
|
403
|
+
div.query-form {
|
404
|
+
border-bottom: solid;
|
405
|
+
border-width: 1px;
|
406
|
+
padding: 0.5em;
|
407
|
+
}
|
408
|
+
|
409
|
+
|
410
|
+
|
411
|
+
div.hidden {
|
412
|
+
display: none;
|
413
|
+
}
|
414
|
+
|
415
|
+
div.invisible {
|
416
|
+
visibility: hidden;
|
417
|
+
}
|
418
|
+
|
419
|
+
|
420
|
+
/*************************************************************************
|
421
|
+
Tooltips
|
422
|
+
*/
|
423
|
+
|
424
|
+
div.tip {
|
425
|
+
display: inline-block;
|
426
|
+
}
|
427
|
+
|
428
|
+
div.tip div.tip-disp {
|
429
|
+
border: thin solid;
|
430
|
+
border-radius: 50%;
|
431
|
+
font-size: 60%;
|
432
|
+
text-align: center;
|
433
|
+
width: 1.1em;
|
434
|
+
height: 1.1em;
|
435
|
+
margin: 0 0.1em;
|
436
|
+
}
|
437
|
+
|
438
|
+
|
439
|
+
div.tip div.tip-disp::before {
|
440
|
+
content: "?";
|
441
|
+
}
|
442
|
+
|
443
|
+
|
444
|
+
/* status bar tooltips */
|
445
|
+
div.tip div.tip-info {
|
446
|
+
visibility: hidden;
|
447
|
+
background-color: black;
|
448
|
+
color: #fff;
|
449
|
+
position: fixed;
|
450
|
+
padding: 0.5em;
|
451
|
+
bottom: 0;
|
452
|
+
left: 0;
|
453
|
+
width: 100vw;
|
454
|
+
}
|
455
|
+
|
456
|
+
|
457
|
+
/* floating tooltips
|
458
|
+
div.tip div.tip-info {
|
459
|
+
visibility: hidden;
|
460
|
+
background-color: black;
|
461
|
+
color: #fff;
|
462
|
+
border-radius: 0.5em;
|
463
|
+
padding: 0.5em;
|
464
|
+
margin: 0.25em;
|
465
|
+
width: 25em;
|
466
|
+
margin-left: 0em;
|
467
|
+
position: absolute;
|
468
|
+
z-index: 1;
|
469
|
+
}
|
470
|
+
*/
|
471
|
+
|
472
|
+
|
473
|
+
div.tip:hover div.tip-info {
|
474
|
+
visibility: visible;
|
475
|
+
}
|