fedora-migrate 0.0.1
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/.gitignore +16 -0
- data/.rspec +2 -0
- data/.travis.yml +19 -0
- data/Gemfile +7 -0
- data/LICENSE.txt +22 -0
- data/README.md +31 -0
- data/Rakefile +5 -0
- data/config/fedora.yml +14 -0
- data/config/fedora3.yml +12 -0
- data/config/jetty.yml +6 -0
- data/config/solr.yml +15 -0
- data/fedora-migrate.gemspec +30 -0
- data/lib/fedora-migrate.rb +82 -0
- data/lib/fedora_migrate/datastream_mover.rb +78 -0
- data/lib/fedora_migrate/errors.rb +7 -0
- data/lib/fedora_migrate/file_configurator.rb +34 -0
- data/lib/fedora_migrate/hooks.rb +11 -0
- data/lib/fedora_migrate/logger.rb +36 -0
- data/lib/fedora_migrate/migration_options.rb +11 -0
- data/lib/fedora_migrate/mover.rb +44 -0
- data/lib/fedora_migrate/object_mover.rb +62 -0
- data/lib/fedora_migrate/permissions.rb +32 -0
- data/lib/fedora_migrate/permissions_mover.rb +31 -0
- data/lib/fedora_migrate/rdf_datastream_mover.rb +28 -0
- data/lib/fedora_migrate/rdf_datastream_parser.rb +29 -0
- data/lib/fedora_migrate/rels_ext_datastream_mover.rb +90 -0
- data/lib/fedora_migrate/repository_migrator.rb +60 -0
- data/lib/fedora_migrate/rights_metadata.rb +281 -0
- data/lib/fedora_migrate/rubydora_connection.rb +21 -0
- data/lib/fedora_migrate/triple_converter.rb +39 -0
- data/lib/fedora_migrate/version.rb +3 -0
- data/lib/tasks/fedora-migrate.rake +45 -0
- data/spec/fixtures/datastreams/rdf_ntriples_datastream.txt +2 -0
- data/spec/fixtures/datastreams/sufia-rb68xc089-characterization.xml +27 -0
- data/spec/fixtures/objects/f3-migration-a.xml +110 -0
- data/spec/fixtures/objects/gf-versioned-content.xml +2776 -0
- data/spec/fixtures/objects/sufia-batch-gf-1.xml +94 -0
- data/spec/fixtures/objects/sufia-batch-gf-2.xml +93 -0
- data/spec/fixtures/objects/sufia-batch.xml +51 -0
- data/spec/integration/content_versions_spec.rb +42 -0
- data/spec/integration/fedora3_interface_spec.rb +23 -0
- data/spec/integration/object_migration_spec.rb +112 -0
- data/spec/integration/permission_migration_spec.rb +13 -0
- data/spec/integration/rdf_migration_spec.rb +22 -0
- data/spec/integration/relationship_migration_spec.rb +51 -0
- data/spec/integration/repository_migration_spec.rb +59 -0
- data/spec/spec_helper.rb +39 -0
- data/spec/support/example_model.rb +36 -0
- data/spec/unit/datastream_mover_spec.rb +39 -0
- data/spec/unit/fedora_migrate_spec.rb +19 -0
- data/spec/unit/file_configurator_spec.rb +17 -0
- data/spec/unit/mover_spec.rb +39 -0
- data/spec/unit/object_mover_spec.rb +38 -0
- data/spec/unit/permissions_mover_spec.rb +53 -0
- data/spec/unit/rdf_datastream_mover_spec.rb +8 -0
- data/spec/unit/rdf_datastream_parser_spec.rb +38 -0
- data/spec/unit/rels_ext_datastream_mover_spec.rb +36 -0
- data/spec/unit/repository_migrator_spec.rb +43 -0
- data/spec/unit/rubydora_connection_spec.rb +25 -0
- data/spec/unit/triple_converter_spec.rb +35 -0
- data/tasks/dev.rake +37 -0
- metadata +246 -0
@@ -0,0 +1,44 @@
|
|
1
|
+
module FedoraMigrate
|
2
|
+
class Mover
|
3
|
+
|
4
|
+
include MigrationOptions
|
5
|
+
include Hooks
|
6
|
+
|
7
|
+
attr_accessor :target, :source
|
8
|
+
|
9
|
+
def initialize *args
|
10
|
+
@source = args[0]
|
11
|
+
@target = args[1]
|
12
|
+
@options = args[2]
|
13
|
+
post_initialize
|
14
|
+
end
|
15
|
+
|
16
|
+
def post_initialize
|
17
|
+
end
|
18
|
+
|
19
|
+
def save
|
20
|
+
if target.save
|
21
|
+
Logger.info "success for target UID #{target_description}"
|
22
|
+
else
|
23
|
+
raise FedoraMigrate::Errors::MigrationError, "Failed to save target: #{target_errors}"
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def target_errors
|
28
|
+
if target.respond_to?(:errors)
|
29
|
+
target.errors.full_messages.join(" -- ")
|
30
|
+
else
|
31
|
+
target.inspect
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def target_description
|
36
|
+
if target.respond_to?(:id)
|
37
|
+
target.id
|
38
|
+
else
|
39
|
+
target.inspect
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
module FedoraMigrate
|
2
|
+
class ObjectMover < Mover
|
3
|
+
|
4
|
+
RIGHTS_DATASTREAM = "rightsMetadata".freeze
|
5
|
+
|
6
|
+
def migrate
|
7
|
+
prepare_target
|
8
|
+
migrate_content_datastreams
|
9
|
+
conversions.collect { |ds| convert_rdf_datastream(ds) }
|
10
|
+
migrate_permissions
|
11
|
+
complete_target
|
12
|
+
end
|
13
|
+
|
14
|
+
def post_initialize
|
15
|
+
conversion_options
|
16
|
+
create_target_model if target.nil?
|
17
|
+
end
|
18
|
+
|
19
|
+
def prepare_target
|
20
|
+
Logger.info "running before_object_migration hooks"
|
21
|
+
before_object_migration
|
22
|
+
save
|
23
|
+
end
|
24
|
+
|
25
|
+
def complete_target
|
26
|
+
Logger.info "running after_object_migration hooks"
|
27
|
+
after_object_migration
|
28
|
+
save
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def migrate_content_datastreams
|
34
|
+
target.attached_files.keys.each do |ds|
|
35
|
+
mover = FedoraMigrate::DatastreamMover.new(source.datastreams[ds.to_s], target.attached_files[ds.to_s])
|
36
|
+
mover.migrate
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def convert_rdf_datastream ds
|
41
|
+
if source.datastreams.keys.include?(ds)
|
42
|
+
mover = FedoraMigrate::RDFDatastreamMover.new(source.datastreams[ds.to_s], target)
|
43
|
+
mover.migrate
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def migrate_permissions
|
48
|
+
if source.datastreams.keys.include?(RIGHTS_DATASTREAM) && target.respond_to?(:permissions)
|
49
|
+
mover = FedoraMigrate::PermissionsMover.new(source.datastreams[RIGHTS_DATASTREAM], target)
|
50
|
+
mover.migrate
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def create_target_model
|
55
|
+
afmodel = source.models.map { |m| m if m.match(/afmodel/) }.compact.first.split(/:/).last
|
56
|
+
Logger.info "found #{afmodel} in source object #{source.pid}"
|
57
|
+
@target = afmodel.constantize.new(id: source.pid.split(/:/).last)
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module FedoraMigrate::Permissions
|
2
|
+
|
3
|
+
# Taken from Hydra::AccessControls::Permissions under version 7.2.2
|
4
|
+
#
|
5
|
+
# We need the reader methods to get permissions from the Fedora3
|
6
|
+
# rightsMetadata datastreams
|
7
|
+
|
8
|
+
def read_groups
|
9
|
+
rightsMetadata.groups.map {|k, v| k if v == 'read'}.compact
|
10
|
+
end
|
11
|
+
|
12
|
+
def edit_groups
|
13
|
+
rightsMetadata.groups.map {|k, v| k if v == 'edit'}.compact
|
14
|
+
end
|
15
|
+
|
16
|
+
def discover_groups
|
17
|
+
rightsMetadata.groups.map {|k, v| k if v == 'discover'}.compact
|
18
|
+
end
|
19
|
+
|
20
|
+
def read_users
|
21
|
+
rightsMetadata.users.map {|k, v| k if v == 'read'}.compact
|
22
|
+
end
|
23
|
+
|
24
|
+
def edit_users
|
25
|
+
rightsMetadata.users.map {|k, v| k if v == 'edit'}.compact
|
26
|
+
end
|
27
|
+
|
28
|
+
def discover_users
|
29
|
+
rightsMetadata.users.map {|k, v| k if v == 'discover'}.compact
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module FedoraMigrate
|
2
|
+
class PermissionsMover < Mover
|
3
|
+
|
4
|
+
include FedoraMigrate::Permissions
|
5
|
+
|
6
|
+
attr_accessor :rightsMetadata
|
7
|
+
|
8
|
+
def post_initialize
|
9
|
+
if source.respond_to?(:content)
|
10
|
+
@rightsMetadata = datastream_from_content
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def migrate
|
15
|
+
FedoraMigrate::Permissions.instance_methods.each do |permission|
|
16
|
+
Logger.info "setting #{permission} to #{self.send(permission)}"
|
17
|
+
target.send(permission.to_s+"=", self.send(permission))
|
18
|
+
end
|
19
|
+
save
|
20
|
+
end
|
21
|
+
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def datastream_from_content ds = FedoraMigrate::RightsMetadata.new
|
26
|
+
ds.ng_xml = source.content
|
27
|
+
ds
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module FedoraMigrate
|
2
|
+
class RDFDatastreamMover < Mover
|
3
|
+
|
4
|
+
def migrate
|
5
|
+
Logger.info "converting datastream '#{source.dsid}' to RDF"
|
6
|
+
parse_rdf_triples
|
7
|
+
force_attribute_change
|
8
|
+
save
|
9
|
+
end
|
10
|
+
|
11
|
+
def parse_rdf_triples
|
12
|
+
parser = FedoraMigrate::RDFDatastreamParser.new(target.uri, source.content)
|
13
|
+
parser.parse
|
14
|
+
parser.statements.each do |statement|
|
15
|
+
target.resource << statement
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
# See projecthydra/active_fedora#540
|
20
|
+
# Forcibly setting each attribute's changed status to true
|
21
|
+
def force_attribute_change
|
22
|
+
target.class.delegated_attributes.keys.each do |term|
|
23
|
+
target.send(term+"_will_change!")
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module FedoraMigrate
|
2
|
+
class RDFDatastreamParser
|
3
|
+
|
4
|
+
attr_accessor :subject, :datastream, :statements
|
5
|
+
|
6
|
+
def initialize subject, content
|
7
|
+
@subject = subject
|
8
|
+
@datastream = content
|
9
|
+
@statements = []
|
10
|
+
end
|
11
|
+
|
12
|
+
def parse
|
13
|
+
datastream.split(/\n/).each do |s|
|
14
|
+
triple = FedoraMigrate::TripleConverter.new(s)
|
15
|
+
build_statement(triple) unless triple.predicate.nil?
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def build_statement triple
|
22
|
+
statement = RDF::Statement(RDF::URI.new(subject), triple.predicate, triple.object)
|
23
|
+
Logger.info "using converted rdf triple #{statement}"
|
24
|
+
statements << statement
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
require 'rubydora'
|
2
|
+
module FedoraMigrate
|
3
|
+
class RelsExtDatastreamMover < Mover
|
4
|
+
|
5
|
+
attr_accessor :relationships, :ng_xml, :subject
|
6
|
+
|
7
|
+
RELS_EXT = Rubydora::RelationshipsMixin::RELS_EXT
|
8
|
+
RELS_EXT_DATASTREAM = "RELS-EXT".freeze
|
9
|
+
|
10
|
+
def post_initialize
|
11
|
+
retrieve_subject
|
12
|
+
@relationships ||= {}
|
13
|
+
@ng_xml = Nokogiri::XML(source.datastreams[RELS_EXT_DATASTREAM].content)
|
14
|
+
parse_relationships if has_relationships?
|
15
|
+
end
|
16
|
+
|
17
|
+
def has_relationships?
|
18
|
+
source.datastreams.keys.include?(RELS_EXT_DATASTREAM)
|
19
|
+
end
|
20
|
+
|
21
|
+
def migrate
|
22
|
+
relationships.each do |predicate, objects|
|
23
|
+
unless objects.empty?
|
24
|
+
if is_singular?(predicate.to_s)
|
25
|
+
objects.collect { |object| migrate_incomming_relationship(predicate, object) }
|
26
|
+
else
|
27
|
+
migrate_outgoing_relationship(predicate, objects)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
# because of projecthydra/rubydora#90
|
36
|
+
def parse_relationships
|
37
|
+
RELS_EXT.keys.each do |key|
|
38
|
+
query = "//ns0:"+RELS_EXT[key].split(/#/).last
|
39
|
+
relationships[key.to_sym] = query_results(query)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def query_results query, results = Array.new
|
44
|
+
ng_xml.xpath(query).each do |predicate|
|
45
|
+
results << retrieve_object(predicate.attribute("resource").text.split(/:/).last)
|
46
|
+
end
|
47
|
+
return results
|
48
|
+
end
|
49
|
+
|
50
|
+
def retrieve_subject
|
51
|
+
@subject = ActiveFedora::Base.find(source.pid.split(/:/).last)
|
52
|
+
rescue ActiveFedora::ObjectNotFoundError
|
53
|
+
raise FedoraMigrate::Errors::MigrationError, "Source was not found in Fedora4. Did you migrated it?"
|
54
|
+
end
|
55
|
+
|
56
|
+
def retrieve_object id
|
57
|
+
object = ActiveFedora::Base.find(id)
|
58
|
+
rescue ActiveFedora::ObjectNotFoundError
|
59
|
+
raise FedoraMigrate::Errors::MigrationError, "Could not find object with id #{id}"
|
60
|
+
end
|
61
|
+
|
62
|
+
# TODO: This is problematic and may not work in all situations (issue #7)
|
63
|
+
def migrate_incomming_relationship predicate, object
|
64
|
+
Logger.info "adding #{subject.id} to #{object.id} with predicate #{predicate.to_s}"
|
65
|
+
object.reflections.each do |key, association|
|
66
|
+
unless association.predicate.to_s.split(/#/).empty?
|
67
|
+
if association.predicate.to_s.split(/#/).last.gsub(/is/,"").underscore == predicate.to_s
|
68
|
+
object.send(key.to_s) << subject
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
# TODO: Very stinky... needs a different approach (issue #7)
|
75
|
+
def migrate_outgoing_relationship predicate, objects
|
76
|
+
Logger.info "adding #{objects.count.to_s} members to #{subject.id} with predicate #{predicate.to_s}"
|
77
|
+
subject.reflections.each do |key, association|
|
78
|
+
if key.to_s.match(/_ids$/)
|
79
|
+
subject.send(key.to_s+"=", objects.collect { |o| o.id })
|
80
|
+
subject.save
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
def is_singular?(str)
|
86
|
+
str.singularize == str
|
87
|
+
end
|
88
|
+
|
89
|
+
end
|
90
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
module FedoraMigrate
|
2
|
+
class RepositoryMigrator
|
3
|
+
|
4
|
+
include MigrationOptions
|
5
|
+
|
6
|
+
attr_accessor :source_objects, :results, :namespace
|
7
|
+
|
8
|
+
def initialize namespace = nil, options = {}
|
9
|
+
@namespace = namespace || repository_namespace
|
10
|
+
@options = options
|
11
|
+
@source_objects = get_source_objects
|
12
|
+
@results = []
|
13
|
+
conversion_options
|
14
|
+
end
|
15
|
+
|
16
|
+
def migrate_objects
|
17
|
+
source_objects.each do |source|
|
18
|
+
Logger.info "Migrating source object #{source.pid}"
|
19
|
+
begin
|
20
|
+
results << { source.pid => [FedoraMigrate::ObjectMover.new(source, nil, options).migrate] }
|
21
|
+
rescue NameError => e
|
22
|
+
results << { source.pid => e.to_s }
|
23
|
+
rescue FedoraMigrate::Errors::MigrationError => e
|
24
|
+
results << { source.pid => e.to_s }
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
# TODO: need a reporting mechanism for results (issue #4)
|
30
|
+
def migrate_relationships
|
31
|
+
source_objects.each do |source|
|
32
|
+
Logger.info "Migrating relationships for source object #{source.pid}"
|
33
|
+
begin
|
34
|
+
FedoraMigrate::RelsExtDatastreamMover.new(source).migrate
|
35
|
+
rescue FedoraMigrate::Errors::MigrationError => e
|
36
|
+
results << { source.pid => e.to_s }
|
37
|
+
rescue ActiveFedora::AssociationTypeMismatch => e
|
38
|
+
results << { source.pid => e.to_s }
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
# TODO: page through all the objects (issue #6)
|
44
|
+
def get_source_objects
|
45
|
+
FedoraMigrate.source.connection.search(nil).collect { |o| qualifying_object(o) }.compact
|
46
|
+
end
|
47
|
+
|
48
|
+
private
|
49
|
+
|
50
|
+
def repository_namespace
|
51
|
+
FedoraMigrate.source.connection.repository_profile["repositoryPID"]["repositoryPID"].split(/:/).first.strip
|
52
|
+
end
|
53
|
+
|
54
|
+
def qualifying_object object
|
55
|
+
name = object.pid.split(/:/).first
|
56
|
+
return object if name.match(namespace)
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,281 @@
|
|
1
|
+
require 'active_support/core_ext/string'
|
2
|
+
|
3
|
+
module FedoraMigrate
|
4
|
+
# Implements Hydra RightsMetadata XML terminology for asserting access permissions
|
5
|
+
class RightsMetadata < ActiveFedora::OmDatastream
|
6
|
+
extend Deprecation
|
7
|
+
|
8
|
+
set_terminology do |t|
|
9
|
+
t.root(:path=>"rightsMetadata", :xmlns=>"http://hydra-collab.stanford.edu/schemas/rightsMetadata/v1", :schema=>"http://github.com/projecthydra/schemas/tree/v1/rightsMetadata.xsd")
|
10
|
+
t.copyright {
|
11
|
+
## BEGIN possible delete, justin 2012-06-22
|
12
|
+
t.machine {
|
13
|
+
t.cclicense
|
14
|
+
t.license
|
15
|
+
}
|
16
|
+
t.human_readable(:path=>"human")
|
17
|
+
t.license(:proxy=>[:machine, :license ])
|
18
|
+
t.cclicense(:proxy=>[:machine, :cclicense ])
|
19
|
+
## END possible delete
|
20
|
+
|
21
|
+
t.title(:path=>'human', :attributes=>{:type=>'title'})
|
22
|
+
t.description(:path=>'human', :attributes=>{:type=>'description'})
|
23
|
+
t.url(:path=>'machine', :attributes=>{:type=>'uri'})
|
24
|
+
}
|
25
|
+
t.access do
|
26
|
+
t.human_readable(:path=>"human")
|
27
|
+
t.machine {
|
28
|
+
t.group
|
29
|
+
t.person
|
30
|
+
}
|
31
|
+
t.person(:proxy=>[:machine, :person])
|
32
|
+
t.group(:proxy=>[:machine, :group])
|
33
|
+
# accessor :access_person, :term=>[:access, :machine, :person]
|
34
|
+
end
|
35
|
+
t.discover_access(:ref=>[:access], :attributes=>{:type=>"discover"})
|
36
|
+
t.read_access(:ref=>[:access], :attributes=>{:type=>"read"})
|
37
|
+
t.edit_access(:ref=>[:access], :attributes=>{:type=>"edit"})
|
38
|
+
# A bug in OM prevnts us from declaring proxy terms at the root of a Terminology
|
39
|
+
# t.access_person(:proxy=>[:access,:machine,:person])
|
40
|
+
# t.access_group(:proxy=>[:access,:machine,:group])
|
41
|
+
|
42
|
+
t.embargo {
|
43
|
+
t.human_readable(path: "human")
|
44
|
+
t.machine{
|
45
|
+
t.date(type: :time, attributes: {type: "release"})
|
46
|
+
t.date_deactivated(type: "deactivated")
|
47
|
+
t.visibility_during(path: "visibility", attributes: {scope: 'during'})
|
48
|
+
t.visibility_after(path: "visibility", attributes: {scope: 'after'})
|
49
|
+
}
|
50
|
+
}
|
51
|
+
|
52
|
+
t.lease {
|
53
|
+
t.human_readable(path: "human")
|
54
|
+
t.machine{
|
55
|
+
t.date(type: :time, attributes: {type: "expire"})
|
56
|
+
t.date_deactivated(type: :time, attributes: {type: "deactivated"})
|
57
|
+
t.visibility_during(path: "visibility", attributes: {scope: 'during'})
|
58
|
+
t.visibility_after(path: "visibility", attributes: {scope: 'after'})
|
59
|
+
}
|
60
|
+
}
|
61
|
+
|
62
|
+
t.license(:ref=>[:copyright])
|
63
|
+
|
64
|
+
t.visibility_during_embargo proxy: [:embargo, :machine, :visibility_during]
|
65
|
+
t.visibility_after_embargo proxy: [:embargo, :machine, :visibility_after]
|
66
|
+
t.visibility_during_lease proxy: [:lease, :machine, :visibility_during]
|
67
|
+
t.visibility_after_lease proxy: [:lease, :machine, :visibility_after]
|
68
|
+
t.embargo_history proxy: [:embargo, :human_readable]
|
69
|
+
t.lease_history proxy: [:lease, :human_readable]
|
70
|
+
t.embargo_release_date proxy: [:embargo, :machine, :date], type: :time
|
71
|
+
t.embargo_deactivation_date proxy: [:embargo, :machine, :date_deactivated]
|
72
|
+
t.lease_expiration_date proxy: [:lease, :machine, :date], type: :time
|
73
|
+
t.lease_deactivation_date proxy: [:lease, :machine, :date_deactivated]
|
74
|
+
|
75
|
+
end
|
76
|
+
|
77
|
+
# Generates an empty Mods Article (used when you call ModsArticle.new without passing in existing xml)
|
78
|
+
def self.xml_template
|
79
|
+
builder = Nokogiri::XML::Builder.new do |xml|
|
80
|
+
xml.rightsMetadata(:version=>"0.1", "xmlns"=>"http://hydra-collab.stanford.edu/schemas/rightsMetadata/v1") {
|
81
|
+
xml.copyright {
|
82
|
+
xml.human(:type=>'title')
|
83
|
+
xml.human(:type=>'description')
|
84
|
+
xml.machine(:type=>'uri')
|
85
|
+
|
86
|
+
}
|
87
|
+
xml.access(:type=>"discover") {
|
88
|
+
xml.human
|
89
|
+
xml.machine
|
90
|
+
}
|
91
|
+
xml.access(:type=>"read") {
|
92
|
+
xml.human
|
93
|
+
xml.machine
|
94
|
+
}
|
95
|
+
xml.access(:type=>"edit") {
|
96
|
+
xml.human
|
97
|
+
xml.machine
|
98
|
+
}
|
99
|
+
xml.embargo{
|
100
|
+
xml.machine
|
101
|
+
}
|
102
|
+
xml.lease{
|
103
|
+
xml.machine
|
104
|
+
}
|
105
|
+
}
|
106
|
+
end
|
107
|
+
return builder.doc
|
108
|
+
end
|
109
|
+
|
110
|
+
# Returns the permissions for the selected person/group
|
111
|
+
# If new_access_level is provided, updates the selected person/group access_level to the one specified
|
112
|
+
# A new_access_level of "none" will remove all access_levels for the selected person/group
|
113
|
+
# @param [Hash] selector hash in format {type => identifier}
|
114
|
+
# @param new_access_level (default nil)
|
115
|
+
# @return Hash in format {type => access_level}.
|
116
|
+
#
|
117
|
+
# ie.
|
118
|
+
# permissions({:person=>"person123"})
|
119
|
+
# => {"person123"=>"edit"}
|
120
|
+
# permissions({:person=>"person123"}, "read")
|
121
|
+
# => {"person123"=>"read"}
|
122
|
+
# permissions({:person=>"person123"})
|
123
|
+
# => {"person123"=>"read"}
|
124
|
+
def permissions(selector, new_access_level=nil)
|
125
|
+
type = selector.keys.first.to_sym
|
126
|
+
actor = selector.values.first
|
127
|
+
if new_access_level.nil?
|
128
|
+
xpath = xpath(type, actor)
|
129
|
+
nodeset = self.find_by_terms(xpath)
|
130
|
+
if nodeset.empty?
|
131
|
+
return "none"
|
132
|
+
else
|
133
|
+
return nodeset.first.ancestors("access").first.attributes["type"].text
|
134
|
+
end
|
135
|
+
else
|
136
|
+
remove_all_permissions(selector)
|
137
|
+
if new_access_level == "none"
|
138
|
+
self.content = self.to_xml
|
139
|
+
else
|
140
|
+
access_type_symbol = "#{new_access_level}_access".to_sym
|
141
|
+
current_values = term_values(access_type_symbol, type)
|
142
|
+
self.update_values([access_type_symbol, type] => current_values + [actor] )
|
143
|
+
end
|
144
|
+
return new_access_level
|
145
|
+
end
|
146
|
+
|
147
|
+
end
|
148
|
+
|
149
|
+
# Reports on which groups have which permissions
|
150
|
+
# @return Hash in format {group_name => group_permissions, group_name => group_permissions}
|
151
|
+
def groups
|
152
|
+
return quick_search_by_type(:group)
|
153
|
+
end
|
154
|
+
|
155
|
+
def individuals
|
156
|
+
Deprecation.warn(RightsMetadata, "The method `individuals' is deprecated and will be removed from Hydra::Datastream::RightsMetadata in hydra-head 8.0. Use `users' instead.", caller)
|
157
|
+
users
|
158
|
+
end
|
159
|
+
|
160
|
+
# Reports on which users have which permissions
|
161
|
+
# @return Hash in format {user_name => user_permissions, user_name => user_permissions}
|
162
|
+
def users
|
163
|
+
return quick_search_by_type(:person)
|
164
|
+
end
|
165
|
+
|
166
|
+
# Updates permissions for all of the persons and groups in a hash
|
167
|
+
# @param params ex. {"group"=>{"group1"=>"discover","group2"=>"edit"}, "person"=>{"person1"=>"read","person2"=>"discover"}}
|
168
|
+
# Currently restricts actor type to group or person. Any others will be ignored
|
169
|
+
def update_permissions(params)
|
170
|
+
params.fetch("group", {}).each_pair {|group_id, access_level| self.permissions({"group"=>group_id}, access_level)}
|
171
|
+
params.fetch("person", {}).each_pair {|person_id, access_level| self.permissions({"person"=>person_id}, access_level)}
|
172
|
+
end
|
173
|
+
|
174
|
+
# Updates all permissions
|
175
|
+
# @param params ex. {"group"=>{"group1"=>"discover","group2"=>"edit"}, "person"=>{"person1"=>"read","person2"=>"discover"}}
|
176
|
+
# Restricts actor type to group or person. Any others will be ignored
|
177
|
+
def permissions= (params)
|
178
|
+
groups_for_update = params['group'] ? params['group'].keys : []
|
179
|
+
group_ids = groups.keys | groups_for_update
|
180
|
+
group_ids.each {|group_id| self.permissions({"group"=>group_id}, params['group'].fetch(group_id, 'none'))}
|
181
|
+
users_for_update = params['person'] ? params['person'].keys : []
|
182
|
+
user_ids = users.keys | users_for_update
|
183
|
+
user_ids.each {|person_id| self.permissions({"person"=>person_id}, params['person'].fetch(person_id, 'none'))}
|
184
|
+
end
|
185
|
+
|
186
|
+
# @param [Symbol] type (either :group or :person)
|
187
|
+
# @return
|
188
|
+
# This method limits the response to known access levels. Probably runs a bit faster than .permissions().
|
189
|
+
def quick_search_by_type(type)
|
190
|
+
result = {}
|
191
|
+
[{:discover_access=>"discover"},{:read_access=>"read"},{:edit_access=>"edit"}].each do |access_levels_hash|
|
192
|
+
access_level = access_levels_hash.keys.first
|
193
|
+
access_level_name = access_levels_hash.values.first
|
194
|
+
self.find_by_terms(*[access_level, type]).each do |entry|
|
195
|
+
result[entry.text] = access_level_name
|
196
|
+
end
|
197
|
+
end
|
198
|
+
return result
|
199
|
+
end
|
200
|
+
|
201
|
+
def under_embargo?
|
202
|
+
(embargo_release_date.present? && Date.today < embargo_release_date.first) ? true : false
|
203
|
+
end
|
204
|
+
|
205
|
+
def active_lease?
|
206
|
+
lease_expiration_date.present? && Date.today < lease_expiration_date.first
|
207
|
+
end
|
208
|
+
|
209
|
+
def to_solr(solr_doc=Hash.new)
|
210
|
+
[:discover, :read, :edit].each do |access|
|
211
|
+
vals = send("#{access}_access").machine.group
|
212
|
+
solr_doc[Hydra.config.permissions[access].group] = vals unless vals.empty?
|
213
|
+
vals = send("#{access}_access").machine.person
|
214
|
+
solr_doc[Hydra.config.permissions[access].individual] = vals unless vals.empty?
|
215
|
+
end
|
216
|
+
if embargo_release_date.present?
|
217
|
+
key = Hydra.config.permissions.embargo.release_date.sub(/_[^_]+$/, '') #Strip off the suffix
|
218
|
+
::Solrizer.insert_field(solr_doc, key, embargo_release_date, :stored_sortable)
|
219
|
+
end
|
220
|
+
if lease_expiration_date.present?
|
221
|
+
key = Hydra.config.permissions.lease.expiration_date.sub(/_[^_]+$/, '') #Strip off the suffix
|
222
|
+
::Solrizer.insert_field(solr_doc, key, lease_expiration_date, :stored_sortable)
|
223
|
+
end
|
224
|
+
solr_doc[::Solrizer.solr_name("visibility_during_embargo", :symbol)] = visibility_during_embargo unless visibility_during_embargo.nil?
|
225
|
+
solr_doc[::Solrizer.solr_name("visibility_after_embargo", :symbol)] = visibility_after_embargo unless visibility_after_embargo.nil?
|
226
|
+
solr_doc[::Solrizer.solr_name("visibility_during_lease", :symbol)] = visibility_during_lease unless visibility_during_lease.nil?
|
227
|
+
solr_doc[::Solrizer.solr_name("visibility_after_lease", :symbol)] = visibility_after_lease unless visibility_after_lease.nil?
|
228
|
+
solr_doc[::Solrizer.solr_name("embargo_history", :symbol)] = embargo_history unless embargo_history.nil?
|
229
|
+
solr_doc[::Solrizer.solr_name("lease_history", :symbol)] = lease_history unless lease_history.nil?
|
230
|
+
solr_doc
|
231
|
+
end
|
232
|
+
|
233
|
+
def indexer
|
234
|
+
self.class.indexer
|
235
|
+
end
|
236
|
+
|
237
|
+
def self.indexer
|
238
|
+
@indexer ||= Solrizer::Descriptor.new(:string, :stored, :indexed, :multivalued)
|
239
|
+
end
|
240
|
+
|
241
|
+
def date_indexer
|
242
|
+
self.class.date_indexer
|
243
|
+
end
|
244
|
+
|
245
|
+
def self.date_indexer
|
246
|
+
@date_indexer ||= Solrizer::Descriptor.new(:date, :stored, :indexed)
|
247
|
+
end
|
248
|
+
|
249
|
+
# Completely clear the permissions
|
250
|
+
def clear_permissions!
|
251
|
+
remove_all_permissions({:person=>true})
|
252
|
+
remove_all_permissions({:group=>true})
|
253
|
+
end
|
254
|
+
|
255
|
+
|
256
|
+
|
257
|
+
private
|
258
|
+
# Purge all access given group/person
|
259
|
+
def remove_all_permissions(selector)
|
260
|
+
return unless ng_xml
|
261
|
+
type = selector.keys.first.to_sym
|
262
|
+
actor = selector.values.first
|
263
|
+
xpath = xpath(type, actor)
|
264
|
+
nodes_to_purge = self.find_by_terms(xpath)
|
265
|
+
nodes_to_purge.each {|node| node.remove}
|
266
|
+
end
|
267
|
+
|
268
|
+
# @param [Symbol] type (:group, :person)
|
269
|
+
# @param [String,TrueClass] actor the user we want to find. If actor is true, then don't query.
|
270
|
+
def xpath(type, actor)
|
271
|
+
raise ArgumentError, "Type must either be ':group' or ':person'. You provided: '#{type.inspect}'" unless [:group, :person].include?(type)
|
272
|
+
path = "//oxns:access/oxns:machine/oxns:#{type}"
|
273
|
+
if actor.is_a? String
|
274
|
+
clean_actor = actor.gsub("'", '')
|
275
|
+
path += "[text() = '#{clean_actor}']"
|
276
|
+
end
|
277
|
+
path
|
278
|
+
end
|
279
|
+
|
280
|
+
end
|
281
|
+
end
|