sakai-info 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.
- data/CHANGELOG.md +12 -0
- data/LICENSE +8 -0
- data/README.md +148 -0
- data/ROADMAP.md +37 -0
- data/bin/sakai-info +79 -0
- data/lib/sakai-info.rb +62 -0
- data/lib/sakai-info/announcement.rb +170 -0
- data/lib/sakai-info/assignment.rb +281 -0
- data/lib/sakai-info/authz.rb +378 -0
- data/lib/sakai-info/cli.rb +35 -0
- data/lib/sakai-info/cli/help.rb +103 -0
- data/lib/sakai-info/configuration.rb +288 -0
- data/lib/sakai-info/content.rb +300 -0
- data/lib/sakai-info/db.rb +19 -0
- data/lib/sakai-info/gradebook.rb +176 -0
- data/lib/sakai-info/group.rb +119 -0
- data/lib/sakai-info/instance.rb +122 -0
- data/lib/sakai-info/message.rb +122 -0
- data/lib/sakai-info/sakai_object.rb +58 -0
- data/lib/sakai-info/sakai_xml_entity.rb +126 -0
- data/lib/sakai-info/samigo.rb +219 -0
- data/lib/sakai-info/site.rb +752 -0
- data/lib/sakai-info/user.rb +220 -0
- data/lib/sakai-info/version.rb +3 -0
- metadata +90 -0
@@ -0,0 +1,35 @@
|
|
1
|
+
# sakai-info/cli.rb
|
2
|
+
# - sakai-info command line tool support
|
3
|
+
#
|
4
|
+
# Created 2012-02-19 daveadams@gmail.com
|
5
|
+
# Last updated 2012-02-19 daveadams@gmail.com
|
6
|
+
#
|
7
|
+
# https://github.com/daveadams/sakai-info
|
8
|
+
#
|
9
|
+
# This software is public domain.
|
10
|
+
#
|
11
|
+
|
12
|
+
require 'sakai-info/cli/help'
|
13
|
+
|
14
|
+
module SakaiInfo
|
15
|
+
class CLI
|
16
|
+
def self.validate_config
|
17
|
+
return true if Configuration.configured?
|
18
|
+
|
19
|
+
begin
|
20
|
+
Configuration.load_config
|
21
|
+
return true
|
22
|
+
rescue NoConfigFoundException
|
23
|
+
STDERR.puts "ERROR: No configuration file was found at #{Configuration::DEFAULT_CONFIG_FILE}"
|
24
|
+
return false
|
25
|
+
rescue InvalidConfigException => e
|
26
|
+
STDERR.puts "ERROR: Configuration was invalid:"
|
27
|
+
e.message.each_line do |line|
|
28
|
+
STDERR.puts " #{line.chomp}"
|
29
|
+
end
|
30
|
+
return false
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
@@ -0,0 +1,103 @@
|
|
1
|
+
# sakai-info/cli/help.rb
|
2
|
+
# - sakai-info command line help
|
3
|
+
#
|
4
|
+
# Created 2012-02-19 daveadams@gmail.com
|
5
|
+
# Last updated 2012-02-19 daveadams@gmail.com
|
6
|
+
#
|
7
|
+
# https://github.com/daveadams/sakai-info
|
8
|
+
#
|
9
|
+
# This software is public domain.
|
10
|
+
#
|
11
|
+
|
12
|
+
module SakaiInfo
|
13
|
+
class CLI
|
14
|
+
class Help
|
15
|
+
STRINGS = {
|
16
|
+
:default => <<EOF,
|
17
|
+
sakai-info #{VERSION}
|
18
|
+
|
19
|
+
Object commands:
|
20
|
+
user Print information about a user or users
|
21
|
+
site Print information about a site or sites
|
22
|
+
|
23
|
+
Misc commands:
|
24
|
+
validate Validates configuration
|
25
|
+
help Prints general help
|
26
|
+
version Prints version
|
27
|
+
|
28
|
+
Type 'sakai-info help <command>' for help on a specific command.
|
29
|
+
EOF
|
30
|
+
|
31
|
+
"help" => <<EOF,
|
32
|
+
sakai-info help
|
33
|
+
|
34
|
+
Prints usage information for other sakai-info commands, or without an
|
35
|
+
argument it prints a list of possible commands.
|
36
|
+
|
37
|
+
Usage: sakai-info help [<command>]
|
38
|
+
EOF
|
39
|
+
|
40
|
+
"version" => <<EOF,
|
41
|
+
sakai-info version
|
42
|
+
|
43
|
+
Prints the current version of sakai-info.
|
44
|
+
|
45
|
+
Usage: sakai-info version
|
46
|
+
EOF
|
47
|
+
|
48
|
+
"validate" => <<EOF,
|
49
|
+
sakai-info validate
|
50
|
+
|
51
|
+
Reads and validates the current configuration format. To test the actual
|
52
|
+
database connections, use 'sakai-info test'.
|
53
|
+
|
54
|
+
Usage: sakai-info validate
|
55
|
+
EOF
|
56
|
+
|
57
|
+
"test" => <<EOF,
|
58
|
+
sakai-info test
|
59
|
+
|
60
|
+
[NOT YET IMPLEMENTED]
|
61
|
+
|
62
|
+
Reads configuration and tests connecting to each database specified, or with
|
63
|
+
an argument, it will test only the named instance.
|
64
|
+
|
65
|
+
Usage: sakai-info test [<instance>]
|
66
|
+
EOF
|
67
|
+
|
68
|
+
"user" => <<EOF,
|
69
|
+
sakai-info user
|
70
|
+
|
71
|
+
In this release, this command prints the total number of records in the
|
72
|
+
SAKAI_USER table. In future releases, subcommands will be available to
|
73
|
+
perform more tasks.
|
74
|
+
|
75
|
+
Usage: sakai-info user [<subcommand>]
|
76
|
+
EOF
|
77
|
+
|
78
|
+
"site" => <<EOF,
|
79
|
+
sakai-info site
|
80
|
+
|
81
|
+
In this release, this command prints the total number of records in the
|
82
|
+
SAKAI_SITE table. In future releases, subcommands will be available to
|
83
|
+
perform more tasks.
|
84
|
+
|
85
|
+
Usage: sakai-info site [<subcommand>]
|
86
|
+
EOF
|
87
|
+
}
|
88
|
+
|
89
|
+
def self.help(topic = :default, io = STDOUT)
|
90
|
+
topic ||= :default
|
91
|
+
if STRINGS.has_key? topic
|
92
|
+
io.puts STRINGS[topic]
|
93
|
+
else
|
94
|
+
STDERR.puts "ERROR: help topic '#{topic}' was unrecognized"
|
95
|
+
STDERR.puts
|
96
|
+
CLI.help(:default, STDERR)
|
97
|
+
exit 1
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
@@ -0,0 +1,288 @@
|
|
1
|
+
# sakai-info/configuration.rb
|
2
|
+
# SakaiInfo::Configuration library
|
3
|
+
#
|
4
|
+
# Created 2012-02-15 daveadams@gmail.com
|
5
|
+
# Last updated 2012-02-19 daveadams@gmail.com
|
6
|
+
#
|
7
|
+
# https://github.com/daveadams/sakai-info
|
8
|
+
#
|
9
|
+
# This software is public domain.
|
10
|
+
#
|
11
|
+
|
12
|
+
module SakaiInfo
|
13
|
+
class NoConfigFoundException < SakaiException; end
|
14
|
+
class AlreadyConfiguredException < SakaiException; end
|
15
|
+
class InvalidInstanceNameException < SakaiException; end
|
16
|
+
class InvalidConfigException < SakaiException; end
|
17
|
+
class UnsupportedConfigException < InvalidConfigException; end
|
18
|
+
class MultipleConfigException < InvalidConfigException
|
19
|
+
def initialize
|
20
|
+
@exceptions = []
|
21
|
+
end
|
22
|
+
|
23
|
+
def add(instance_name, exception)
|
24
|
+
@exceptions << [instance_name, exception]
|
25
|
+
end
|
26
|
+
|
27
|
+
def count
|
28
|
+
@exceptions.length
|
29
|
+
end
|
30
|
+
|
31
|
+
def message
|
32
|
+
"Multiple config exceptions were found:\n " +
|
33
|
+
@exceptions.collect{ |e| "#{e[0]}: #{e[1].message}" }.join("\n ")
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
# config format assumptions:
|
38
|
+
# a YAML string containing one of the following options:
|
39
|
+
# - a single database connection spec
|
40
|
+
# - eg:
|
41
|
+
# ---
|
42
|
+
# dbtype: oracle
|
43
|
+
# dbsid: PROD
|
44
|
+
# username: sakai
|
45
|
+
# password: Sekrit1
|
46
|
+
# OR:
|
47
|
+
# - an "instances" key containing a hash of named connection specs
|
48
|
+
# - with a "default" key naming the spec to use if none is specified
|
49
|
+
# - eg:
|
50
|
+
# ---
|
51
|
+
# default: production
|
52
|
+
# instances:
|
53
|
+
# production:
|
54
|
+
# dbtype: oracle
|
55
|
+
# service: PROD
|
56
|
+
# username: sakai
|
57
|
+
# password: prodpass
|
58
|
+
# test:
|
59
|
+
# dbtype: oracle
|
60
|
+
# service: TEST
|
61
|
+
# host: testdb.host
|
62
|
+
# port: 1521
|
63
|
+
# username: sakai
|
64
|
+
# password: testpass
|
65
|
+
# dev:
|
66
|
+
# dbtype: mysql
|
67
|
+
# host: dbserver.hostname.int
|
68
|
+
# port: 3306
|
69
|
+
# username: sakai
|
70
|
+
# password: ironchef
|
71
|
+
# dbname: sakaidev
|
72
|
+
# localdev:
|
73
|
+
# dbtype: mysql
|
74
|
+
# host: localhost
|
75
|
+
# port: 3306
|
76
|
+
# username: sakai
|
77
|
+
# password: ironchef
|
78
|
+
# dbname: sakailocal
|
79
|
+
#
|
80
|
+
#
|
81
|
+
# NOTES:
|
82
|
+
# - Oracle connections should use either an alias defined in the
|
83
|
+
# driver's tnsnames.ora file, or specify the host and port as the
|
84
|
+
# test instance example above does
|
85
|
+
#
|
86
|
+
class Configuration
|
87
|
+
@@config = nil
|
88
|
+
|
89
|
+
# validate just a single database connection configuration hash
|
90
|
+
def self.validate_single_connection_config(config)
|
91
|
+
if config.nil?
|
92
|
+
raise InvalidConfigException.new("The config provided was nil")
|
93
|
+
end
|
94
|
+
|
95
|
+
if not config.is_a? Hash
|
96
|
+
raise InvalidConfigException.new("The config provided must be a Hash")
|
97
|
+
end
|
98
|
+
|
99
|
+
# we have to have a dbtype value or we can't validate
|
100
|
+
if config["dbtype"].nil?
|
101
|
+
raise InvalidConfigException.new("The config does not specify 'dbtype'")
|
102
|
+
end
|
103
|
+
|
104
|
+
# force lowercase to simplify comparisons
|
105
|
+
dbtype = config["dbtype"].downcase
|
106
|
+
|
107
|
+
if not %w(oracle mysql).include? dbtype
|
108
|
+
raise UnsupportedConfigException.new("Database type '#{dbtype}' is not supported.")
|
109
|
+
end
|
110
|
+
|
111
|
+
# now check per-dbtype requirements
|
112
|
+
if dbtype == "oracle"
|
113
|
+
%w(service username password).each do |required_key|
|
114
|
+
if config[required_key].nil? or config[required_key] == ""
|
115
|
+
raise InvalidConfigException.new("Oracle config requires values for 'service', 'username', and 'password'.")
|
116
|
+
end
|
117
|
+
end
|
118
|
+
elsif dbtype == "mysql"
|
119
|
+
%w(host username password dbname).each do |required_key|
|
120
|
+
if config[required_key].nil? or config[required_key] == ""
|
121
|
+
raise InvalidConfigException.new("MySQL config requires values for 'host', 'username', 'password', and 'dbname'.")
|
122
|
+
end
|
123
|
+
end
|
124
|
+
else
|
125
|
+
# we should never have made it here
|
126
|
+
raise UnsupportedConfigException.new("Database type '#{dbtype}' is not supported.")
|
127
|
+
end
|
128
|
+
|
129
|
+
# for both types, 'port' is optional but if it exists it must be a valid TCP port
|
130
|
+
if not config["port"].nil?
|
131
|
+
begin
|
132
|
+
port = config["port"].to_i
|
133
|
+
if port < 1 or port > 65535
|
134
|
+
raise
|
135
|
+
end
|
136
|
+
rescue
|
137
|
+
raise InvalidConfigException.new("Config value 'port' must be a valid TCP port number.")
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
# if we made it here the config is complete and well-formed
|
142
|
+
return true
|
143
|
+
end
|
144
|
+
|
145
|
+
# validate that configuration is complete and well-formed
|
146
|
+
def self.validate_config(config)
|
147
|
+
if config.nil?
|
148
|
+
raise InvalidConfigException.new("The config provided was nil")
|
149
|
+
end
|
150
|
+
|
151
|
+
if not config.is_a? Hash
|
152
|
+
raise InvalidConfigException.new("The config provided must be a Hash")
|
153
|
+
end
|
154
|
+
|
155
|
+
# if 'dbtype' exists, it will be a single database configuration
|
156
|
+
if config["dbtype"]
|
157
|
+
self.validate_single_connection_config(config)
|
158
|
+
else
|
159
|
+
# otherwise both 'default' and 'instances' keys are required
|
160
|
+
if config.keys.sort != ["default","instances"]
|
161
|
+
raise InvalidConfigException.new("The config must specify either 'dbtype' or both 'default' and 'instances'.")
|
162
|
+
end
|
163
|
+
|
164
|
+
# enforce types on the values of 'default' and 'instances'
|
165
|
+
if not config["default"].is_a? String
|
166
|
+
raise InvalidConfigException.new("The value of 'default' must be a String.")
|
167
|
+
end
|
168
|
+
if not config["instances"].is_a? Hash
|
169
|
+
raise InvalidConfigException.new("The value of 'instances' must be a Hash.")
|
170
|
+
end
|
171
|
+
|
172
|
+
# 'default' must be a string pointing to one of the 'instances' keys
|
173
|
+
if not config["instances"].keys.include? config["default"]
|
174
|
+
raise InvalidConfigException.new("The default instance '#{config["default"]}' was not among the instance names given: #{config["instances"].keys.inspect}")
|
175
|
+
end
|
176
|
+
|
177
|
+
# check the validity of each instance, collecting exceptions as we go
|
178
|
+
multi_exceptions = nil
|
179
|
+
config["instances"].keys.each do |instance_name|
|
180
|
+
begin
|
181
|
+
self.validate_single_connection_config(config["instances"][instance_name])
|
182
|
+
rescue InvalidConfigException => e
|
183
|
+
# create the object if it doesn't already exist
|
184
|
+
multi_exceptions ||= MultipleConfigException.new
|
185
|
+
|
186
|
+
# add this exception to the list
|
187
|
+
multi_exceptions.add(instance_name, e)
|
188
|
+
end
|
189
|
+
# continue the loop no matter what
|
190
|
+
end
|
191
|
+
|
192
|
+
# if this object was created, the exception needs to be raised
|
193
|
+
if not multi_exceptions.nil?
|
194
|
+
raise multi_exceptions
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
198
|
+
# if we've made it this far, the configuration must be fine
|
199
|
+
return true
|
200
|
+
end
|
201
|
+
|
202
|
+
def initialize(config)
|
203
|
+
begin
|
204
|
+
if config.is_a? Hash
|
205
|
+
@config = config
|
206
|
+
elsif config.is_a? String and File.exist?(config)
|
207
|
+
# try to parse as a filename first
|
208
|
+
if File.exist?(config)
|
209
|
+
@config = YAML::load_file(config)
|
210
|
+
end
|
211
|
+
else
|
212
|
+
# otherwise try to parse it generically
|
213
|
+
@config = YAML::load(config)
|
214
|
+
end
|
215
|
+
rescue Exception => e
|
216
|
+
raise InvalidConfigException.new("Unable to parse configuration: #{e}")
|
217
|
+
end
|
218
|
+
|
219
|
+
# check that the configuration specified is well formed
|
220
|
+
if not Configuration.validate_config(@config)
|
221
|
+
raise InvalidConfigException.new("Config provided is either incomplete or poorly formed.")
|
222
|
+
end
|
223
|
+
|
224
|
+
# create instance objects
|
225
|
+
@instances = {}
|
226
|
+
if not @config["instances"].nil?
|
227
|
+
@config["instances"].keys.each do |instance_name|
|
228
|
+
@instances[instance_name] = Instance.create(@config["instances"][instance_name])
|
229
|
+
if instance_name == @config["default"]
|
230
|
+
@instances[:default] = @instances[instance_name]
|
231
|
+
end
|
232
|
+
end
|
233
|
+
else
|
234
|
+
@instances[:default] = Instance.create(@config)
|
235
|
+
end
|
236
|
+
end
|
237
|
+
|
238
|
+
# instance accessibility
|
239
|
+
def get_instance(instance_name)
|
240
|
+
@instances[instance_name] or raise InvalidInstanceNameException
|
241
|
+
end
|
242
|
+
|
243
|
+
def default_instance
|
244
|
+
@instances[:default]
|
245
|
+
end
|
246
|
+
|
247
|
+
DEFAULT_CONFIG_FILE = File.expand_path("~/.sakai-info")
|
248
|
+
# check to see if configuration file exists
|
249
|
+
# by default ~/.sakai-info
|
250
|
+
def self.config_file_path
|
251
|
+
if File.readable? DEFAULT_CONFIG_FILE
|
252
|
+
DEFAULT_CONFIG_FILE
|
253
|
+
else
|
254
|
+
nil
|
255
|
+
end
|
256
|
+
end
|
257
|
+
|
258
|
+
# are we already configured?
|
259
|
+
def self.configured?
|
260
|
+
not @@config.nil?
|
261
|
+
end
|
262
|
+
|
263
|
+
# load configuration as a class variable (and return it as well)
|
264
|
+
def self.load_config(alternate_config_file = nil)
|
265
|
+
if Configuration.configured?
|
266
|
+
raise AlreadyConfiguredException
|
267
|
+
end
|
268
|
+
|
269
|
+
unless(config_file = alternate_config_file || Configuration.config_file_path)
|
270
|
+
raise NoConfigFoundException
|
271
|
+
end
|
272
|
+
|
273
|
+
@@config = Configuration.new(config_file)
|
274
|
+
end
|
275
|
+
|
276
|
+
# return specified database connection configuration
|
277
|
+
def self.get_instance(instance_name = nil)
|
278
|
+
Configuration.load_config unless Configuration.configured?
|
279
|
+
|
280
|
+
if instance_name.nil?
|
281
|
+
@@config.default_instance
|
282
|
+
else
|
283
|
+
@@config.get_instance(instance_name)
|
284
|
+
end
|
285
|
+
end
|
286
|
+
end
|
287
|
+
end
|
288
|
+
|
@@ -0,0 +1,300 @@
|
|
1
|
+
# sakai-info/content.rb
|
2
|
+
# SakaiInfo::Content library
|
3
|
+
#
|
4
|
+
# Created 2012-02-17 daveadams@gmail.com
|
5
|
+
# Last updated 2012-02-18 daveadams@gmail.com
|
6
|
+
#
|
7
|
+
# https://github.com/daveadams/sakai-info
|
8
|
+
#
|
9
|
+
# This software is public domain.
|
10
|
+
#
|
11
|
+
|
12
|
+
module SakaiInfo
|
13
|
+
class Content < SakaiObject
|
14
|
+
attr_reader :parent_id
|
15
|
+
|
16
|
+
def self.find(id)
|
17
|
+
begin
|
18
|
+
ContentResource.find(id)
|
19
|
+
rescue ObjectNotFoundException
|
20
|
+
begin
|
21
|
+
ContentCollection.find(id)
|
22
|
+
rescue ObjectNotFoundException
|
23
|
+
if not id.match(/\/$/)
|
24
|
+
ContentCollection.find(id + "/")
|
25
|
+
else
|
26
|
+
raise ObjectNotFoundException.new(Content, id)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def parent
|
33
|
+
if @parent_id.nil?
|
34
|
+
nil
|
35
|
+
else
|
36
|
+
@parent ||= ContentCollection.find(@parent_id)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def binary
|
41
|
+
if @binary.nil?
|
42
|
+
DB.connect.exec("select binary_entity from #{@table_name} " +
|
43
|
+
"where #{@id_column} = :id", id) do |row|
|
44
|
+
@binary = row[0].read
|
45
|
+
end
|
46
|
+
end
|
47
|
+
@binary
|
48
|
+
end
|
49
|
+
|
50
|
+
def size_on_disk
|
51
|
+
0
|
52
|
+
end
|
53
|
+
|
54
|
+
def default_serialization
|
55
|
+
{
|
56
|
+
"id" => self.id,
|
57
|
+
"parent" => self.parent_id,
|
58
|
+
"size_on_disk" => self.size_on_disk
|
59
|
+
}
|
60
|
+
end
|
61
|
+
|
62
|
+
def summary_serialization
|
63
|
+
{
|
64
|
+
"id" => self.id,
|
65
|
+
"parent" => self.parent_id,
|
66
|
+
"size_on_disk" => self.size_on_disk
|
67
|
+
}
|
68
|
+
end
|
69
|
+
|
70
|
+
def realm
|
71
|
+
if @realm_is_nil
|
72
|
+
nil
|
73
|
+
else
|
74
|
+
begin
|
75
|
+
@realm ||= AuthzRealm.find_by_name("/content#{@id}")
|
76
|
+
rescue AuthzRealmNotFoundException
|
77
|
+
@realm_is_nil = true
|
78
|
+
nil
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def effective_realm
|
84
|
+
self.realm || (parent.nil? ? nil : parent.effective_realm)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
class ContentResource < Content
|
89
|
+
attr_reader :file_path, :uuid, :context, :resource_type_id
|
90
|
+
|
91
|
+
def initialize(id, parent_id, file_path, uuid, file_size, context, resource_type_id)
|
92
|
+
@id = id
|
93
|
+
@parent_id = parent_id
|
94
|
+
@file_path = File.join(Instance.content_base_directory, file_path)
|
95
|
+
@uuid = uuid
|
96
|
+
@file_size = file_size
|
97
|
+
@context = context
|
98
|
+
@resource_type_id = resource_type_id
|
99
|
+
|
100
|
+
@table_name = "content_resource"
|
101
|
+
@id_column = "resource_id"
|
102
|
+
end
|
103
|
+
|
104
|
+
@@cache = {}
|
105
|
+
def self.find(id)
|
106
|
+
if @@cache[id].nil?
|
107
|
+
DB.connect.exec("select resource_id, in_collection, file_path, " +
|
108
|
+
"resource_uuid, file_size, context, resource_type_id " +
|
109
|
+
"from content_resource " +
|
110
|
+
"where resource_id=:id", id) do |row|
|
111
|
+
@@cache[id] = ContentResource.new(row[0], row[1], row[2], row[3], row[4].to_i, row[5], row[6])
|
112
|
+
end
|
113
|
+
if @@cache[id].nil?
|
114
|
+
raise ObjectNotFoundException.new(ContentResource, id)
|
115
|
+
end
|
116
|
+
end
|
117
|
+
@@cache[id]
|
118
|
+
end
|
119
|
+
|
120
|
+
def size_on_disk
|
121
|
+
@file_size
|
122
|
+
end
|
123
|
+
|
124
|
+
def default_serialization
|
125
|
+
{
|
126
|
+
"id" => self.id,
|
127
|
+
"parent" => self.parent_id,
|
128
|
+
"uuid" => self.uuid,
|
129
|
+
"file_path" => self.file_path,
|
130
|
+
"size_on_disk" => self.size_on_disk,
|
131
|
+
"context" => self.context,
|
132
|
+
"resource_type_id" => self.resource_type_id,
|
133
|
+
"effective_realm" => (self.effective_realm.nil? ? nil : self.effective_realm.name)
|
134
|
+
}
|
135
|
+
end
|
136
|
+
|
137
|
+
def self.find_by_parent(parent_id)
|
138
|
+
resources = []
|
139
|
+
DB.connect.exec("select resource_id, in_collection, file_path, " +
|
140
|
+
"resource_uuid, file_size, context, resource_type_id " +
|
141
|
+
"from content_resource " +
|
142
|
+
"where in_collection=:parent_id", parent_id) do |row|
|
143
|
+
@@cache[row[0]] = ContentResource.new(row[0], row[1], row[2], row[3], row[4].to_i, row[5], row[6])
|
144
|
+
resources << @@cache[row[0]]
|
145
|
+
end
|
146
|
+
resources
|
147
|
+
end
|
148
|
+
|
149
|
+
def self.count_by_parent(parent_id)
|
150
|
+
count = 0
|
151
|
+
DB.connect.exec("select count(*) from content_resource " +
|
152
|
+
"where in_collection=:parent_id", parent_id) do |row|
|
153
|
+
count = row[0].to_i
|
154
|
+
end
|
155
|
+
count
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
class ContentCollection < Content
|
160
|
+
def initialize(id, parent_id)
|
161
|
+
@id = id
|
162
|
+
@parent_id = parent_id
|
163
|
+
|
164
|
+
@table_name = "content_collection"
|
165
|
+
@id_column = "collection_id"
|
166
|
+
end
|
167
|
+
|
168
|
+
@@cache = {}
|
169
|
+
def self.find(id)
|
170
|
+
if id !~ /\/$/
|
171
|
+
id += "/"
|
172
|
+
end
|
173
|
+
|
174
|
+
if @@cache[id].nil?
|
175
|
+
DB.connect.exec("select collection_id, in_collection from content_collection " +
|
176
|
+
"where collection_id=:id", id) do |row|
|
177
|
+
@@cache[id] = ContentCollection.new(row[0], row[1])
|
178
|
+
end
|
179
|
+
if @@cache[id].nil?
|
180
|
+
raise ObjectNotFoundException.new(ContentCollection, id)
|
181
|
+
end
|
182
|
+
end
|
183
|
+
@@cache[id]
|
184
|
+
end
|
185
|
+
|
186
|
+
# return a content collection, even if it's a missing one
|
187
|
+
def self.find!(id)
|
188
|
+
begin
|
189
|
+
ContentCollection.find(id)
|
190
|
+
rescue ObjectNotFoundException
|
191
|
+
MissingContentCollection.find(id)
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
def self.find_portfolio_interaction_collections
|
196
|
+
collections = []
|
197
|
+
DB.connect.exec("select collection_id, in_collection from content_collection " +
|
198
|
+
"where collection_id like '%/portfolio-interaction/'") do |row|
|
199
|
+
@@cache[row[0]] = ContentCollection.new(row[0], row[1])
|
200
|
+
collections << @@cache[row[0]]
|
201
|
+
end
|
202
|
+
collections
|
203
|
+
end
|
204
|
+
|
205
|
+
def self.count_by_parent(parent_id)
|
206
|
+
count = 0
|
207
|
+
DB.connect.exec("select count(*) from content_collection " +
|
208
|
+
"where in_collection=:parent_id", parent_id) do |row|
|
209
|
+
count = row[0].to_i
|
210
|
+
end
|
211
|
+
count
|
212
|
+
end
|
213
|
+
|
214
|
+
def size_on_disk
|
215
|
+
if @size_on_disk.nil?
|
216
|
+
DB.connect.exec("select sum(file_size) from content_resource " +
|
217
|
+
"where resource_id like :collmatch", (@id + "%")) do |row|
|
218
|
+
@size_on_disk = row[0].to_i
|
219
|
+
end
|
220
|
+
end
|
221
|
+
@size_on_disk
|
222
|
+
end
|
223
|
+
|
224
|
+
def children
|
225
|
+
@child_collections ||= ContentCollection.find_by_parent(@id)
|
226
|
+
@child_resources ||= ContentResource.find_by_parent(@id)
|
227
|
+
{
|
228
|
+
"collections" => @child_collections,
|
229
|
+
"resources" => @child_resources
|
230
|
+
}
|
231
|
+
end
|
232
|
+
|
233
|
+
def child_counts
|
234
|
+
@child_collection_count ||= if @child_collections.nil?
|
235
|
+
ContentCollection.count_by_parent(@id)
|
236
|
+
else
|
237
|
+
@child_collections.length
|
238
|
+
end
|
239
|
+
@child_resource_count ||= if @child_resources.nil?
|
240
|
+
ContentResource.count_by_parent(@id)
|
241
|
+
else
|
242
|
+
@child_resources.length
|
243
|
+
end
|
244
|
+
{
|
245
|
+
"collections" => @child_collection_count,
|
246
|
+
"resources" => @child_resource_count,
|
247
|
+
"total" => @child_collection_count + @child_resource_count
|
248
|
+
}
|
249
|
+
end
|
250
|
+
|
251
|
+
def default_serialization
|
252
|
+
{
|
253
|
+
"id" => self.id,
|
254
|
+
"parent" => self.parent_id,
|
255
|
+
"size_on_disk" => self.size_on_disk,
|
256
|
+
"children" => self.child_counts,
|
257
|
+
"effective_realm" => (self.effective_realm.nil? ? nil : effective_realm.name)
|
258
|
+
}
|
259
|
+
end
|
260
|
+
|
261
|
+
def summary_serialization
|
262
|
+
{
|
263
|
+
"id" => self.id,
|
264
|
+
"parent" => self.parent_id
|
265
|
+
}
|
266
|
+
end
|
267
|
+
|
268
|
+
def children_serialization
|
269
|
+
{
|
270
|
+
"collections" => self.children["collections"].collect { |cc|
|
271
|
+
cc.serialize(:summary, :children)
|
272
|
+
},
|
273
|
+
"resources" => self.children["resources"].collect { |cr|
|
274
|
+
cr.serialize(:summary)
|
275
|
+
}
|
276
|
+
}
|
277
|
+
end
|
278
|
+
|
279
|
+
def self.find_by_parent(parent_id)
|
280
|
+
collections = []
|
281
|
+
DB.connect.exec("select collection_id, in_collection from content_collection " +
|
282
|
+
"where in_collection = :parent_id", parent_id) do |row|
|
283
|
+
@@cache[row[0]] = ContentCollection.new(row[0], row[1])
|
284
|
+
collections << @@cache[row[0]]
|
285
|
+
end
|
286
|
+
collections
|
287
|
+
end
|
288
|
+
end
|
289
|
+
|
290
|
+
class MissingContentCollection < ContentCollection
|
291
|
+
@@cache = {}
|
292
|
+
def self.find(id)
|
293
|
+
@@cache[id] ||= MissingContentCollection.new(id, nil)
|
294
|
+
end
|
295
|
+
|
296
|
+
def size_on_disk
|
297
|
+
0
|
298
|
+
end
|
299
|
+
end
|
300
|
+
end
|