dor-services 6.0.5 → 6.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/dor-services.rb +27 -12
- data/lib/dor/config.rb +45 -42
- data/lib/dor/datastreams/administrative_metadata_ds.rb +137 -44
- data/lib/dor/datastreams/content_metadata_ds.rb +42 -42
- data/lib/dor/datastreams/datastream_spec_solrizer.rb +1 -1
- data/lib/dor/datastreams/default_object_rights_ds.rb +185 -44
- data/lib/dor/datastreams/desc_metadata_ds.rb +36 -28
- data/lib/dor/datastreams/embargo_metadata_ds.rb +12 -14
- data/lib/dor/datastreams/events_ds.rb +10 -10
- data/lib/dor/datastreams/geo_metadata_ds.rb +4 -5
- data/lib/dor/datastreams/identity_metadata_ds.rb +14 -14
- data/lib/dor/datastreams/rights_metadata_ds.rb +23 -23
- data/lib/dor/datastreams/role_metadata_ds.rb +61 -15
- data/lib/dor/datastreams/simple_dublin_core_ds.rb +8 -8
- data/lib/dor/datastreams/version_metadata_ds.rb +10 -12
- data/lib/dor/datastreams/workflow_definition_ds.rb +6 -6
- data/lib/dor/datastreams/workflow_ds.rb +13 -13
- data/lib/dor/exceptions.rb +2 -2
- data/lib/dor/indexers/data_indexer.rb +1 -7
- data/lib/dor/indexers/describable_indexer.rb +1 -1
- data/lib/dor/indexers/identifiable_indexer.rb +0 -2
- data/lib/dor/indexers/processable_indexer.rb +55 -28
- data/lib/dor/indexers/releasable_indexer.rb +2 -2
- data/lib/dor/models/admin_policy_object.rb +4 -4
- data/lib/dor/models/concerns/assembleable.rb +4 -0
- data/lib/dor/models/concerns/contentable.rb +27 -69
- data/lib/dor/models/concerns/describable.rb +14 -29
- data/lib/dor/models/concerns/editable.rb +20 -334
- data/lib/dor/models/concerns/embargoable.rb +7 -11
- data/lib/dor/models/concerns/eventable.rb +5 -1
- data/lib/dor/models/concerns/geoable.rb +4 -4
- data/lib/dor/models/concerns/governable.rb +18 -87
- data/lib/dor/models/concerns/identifiable.rb +15 -75
- data/lib/dor/models/concerns/itemizable.rb +9 -11
- data/lib/dor/models/concerns/preservable.rb +4 -0
- data/lib/dor/models/concerns/processable.rb +30 -129
- data/lib/dor/models/concerns/publishable.rb +6 -55
- data/lib/dor/models/concerns/releaseable.rb +14 -227
- data/lib/dor/models/concerns/rightsable.rb +3 -3
- data/lib/dor/models/concerns/shelvable.rb +4 -49
- data/lib/dor/models/concerns/versionable.rb +21 -44
- data/lib/dor/models/set.rb +1 -1
- data/lib/dor/models/workflow_object.rb +2 -2
- data/lib/dor/services/ability.rb +77 -0
- data/lib/dor/services/cleanup_reset_service.rb +1 -3
- data/lib/dor/services/create_workflow_service.rb +51 -0
- data/lib/dor/services/creative_commons_license_service.rb +31 -0
- data/lib/dor/services/datastream_builder.rb +90 -0
- data/lib/dor/services/digital_stacks_service.rb +3 -21
- data/lib/dor/services/dublin_core_service.rb +40 -0
- data/lib/dor/services/file_metadata_merge_service.rb +67 -0
- data/lib/dor/services/indexing_service.rb +8 -4
- data/lib/dor/services/merge_service.rb +5 -5
- data/lib/dor/services/metadata_handlers/catalog_handler.rb +1 -1
- data/lib/dor/services/metadata_service.rb +6 -8
- data/lib/dor/{models/concerns → services}/mods2dc.xslt +0 -0
- data/lib/dor/services/ontology.rb +35 -0
- data/lib/dor/services/open_data_license_service.rb +20 -0
- data/lib/dor/services/public_desc_metadata_service.rb +21 -14
- data/lib/dor/services/public_xml_service.rb +6 -6
- data/lib/dor/services/publish_metadata_service.rb +100 -0
- data/lib/dor/services/registration_service.rb +43 -46
- data/lib/dor/services/release_tag_service.rb +251 -0
- data/lib/dor/services/reset_workspace_service.rb +1 -3
- data/lib/dor/services/sdr_ingest_service.rb +5 -7
- data/lib/dor/services/search_service.rb +10 -10
- data/lib/dor/services/secondary_file_name_service.rb +10 -0
- data/lib/dor/services/shelving_service.rb +67 -0
- data/lib/dor/services/status_service.rb +121 -0
- data/lib/dor/services/suri_service.rb +3 -5
- data/lib/dor/services/tag_service.rb +100 -0
- data/lib/dor/services/technical_metadata_service.rb +5 -4
- data/lib/dor/services/version_service.rb +84 -0
- data/lib/dor/utils/ng_tidy.rb +1 -1
- data/lib/dor/utils/sdr_client.rb +25 -9
- data/lib/dor/version.rb +1 -1
- data/lib/dor/workflow/document.rb +13 -13
- data/lib/dor/workflow/process.rb +71 -26
- data/lib/tasks/rdoc.rake +1 -1
- metadata +77 -51
- data/config/certs/robots-dor-dev.crt +0 -29
- data/config/certs/robots-dor-dev.key +0 -27
- data/config/dev_console_env.rb +0 -80
@@ -15,9 +15,7 @@ module Dor
|
|
15
15
|
return Dor::SuriService.mint_id unless pid
|
16
16
|
|
17
17
|
existing_pid = SearchService.query_by_id(pid).first
|
18
|
-
unless existing_pid.nil?
|
19
|
-
raise Dor::DuplicateIdError.new(existing_pid), "An object with the PID #{pid} has already been registered."
|
20
|
-
end
|
18
|
+
raise Dor::DuplicateIdError.new(existing_pid), "An object with the PID #{pid} has already been registered." unless existing_pid.nil?
|
21
19
|
|
22
20
|
pid
|
23
21
|
end
|
@@ -27,7 +25,7 @@ module Dor
|
|
27
25
|
# @raise [Dor::DuplicateIdError]
|
28
26
|
def check_source_id(source_id_string)
|
29
27
|
return '' if source_id_string == ''
|
30
|
-
unless SearchService.query_by_id(
|
28
|
+
unless SearchService.query_by_id(source_id_string.to_s).first.nil?
|
31
29
|
raise Dor::DuplicateIdError.new(source_id_string), "An object with the source ID '#{source_id_string}' has already been registered."
|
32
30
|
end
|
33
31
|
|
@@ -49,13 +47,11 @@ module Dor
|
|
49
47
|
# @option params [Array<String>] :initiate_workflow workflow_ids
|
50
48
|
# @option params [Array] :tags
|
51
49
|
def register_object(params = {})
|
52
|
-
[
|
50
|
+
%i[object_type label].each do |required_param|
|
53
51
|
raise Dor::ParameterError, "#{required_param.inspect} must be specified in call to #{name}.register_object" unless params[required_param]
|
54
52
|
end
|
55
53
|
metadata_source = params[:metadata_source]
|
56
|
-
if params[:label].length < 1 && %w[label none].include?(metadata_source)
|
57
|
-
raise Dor::ParameterError, "label cannot be empty to call #{name}.register_object"
|
58
|
-
end
|
54
|
+
raise Dor::ParameterError, "label cannot be empty to call #{name}.register_object" if params[:label].length < 1 && %w[label none].include?(metadata_source)
|
59
55
|
|
60
56
|
object_type = params[:object_type]
|
61
57
|
item_class = Dor.registered_classes[object_type]
|
@@ -78,16 +74,12 @@ module Dor
|
|
78
74
|
rights = nil
|
79
75
|
if params[:rights]
|
80
76
|
rights = params[:rights]
|
81
|
-
unless rights == 'default' || RightsMetadataDS.valid_rights_type?(rights)
|
82
|
-
raise Dor::ParameterError, "Unknown rights setting '#{rights}' when calling #{name}.register_object"
|
83
|
-
end
|
77
|
+
raise Dor::ParameterError, "Unknown rights setting '#{rights}' when calling #{name}.register_object" unless rights == 'default' || RightsMetadataDS.valid_rights_type?(rights)
|
84
78
|
end
|
85
79
|
|
86
|
-
if (other_ids.key?(:uuid) || other_ids.key?('uuid')) == false
|
87
|
-
other_ids[:uuid] = UUIDTools::UUID.timestamp_create.to_s
|
88
|
-
end
|
80
|
+
other_ids[:uuid] = UUIDTools::UUID.timestamp_create.to_s if (other_ids.key?(:uuid) || other_ids.key?('uuid')) == false
|
89
81
|
apo_object = Dor.find(params[:admin_policy])
|
90
|
-
new_item = item_class.new(:
|
82
|
+
new_item = item_class.new(pid: pid)
|
91
83
|
new_item.label = label.length > 254 ? label[0, 254] : label
|
92
84
|
idmd = new_item.identityMetadata
|
93
85
|
idmd.sourceId = source_id_string
|
@@ -115,25 +107,13 @@ module Dor
|
|
115
107
|
new_item.set_read_rights(rights) unless rights == 'default' # already defaulted to default!
|
116
108
|
end
|
117
109
|
# create basic mods from the label
|
118
|
-
if metadata_source == 'label'
|
119
|
-
ds = new_item.build_datastream('descMetadata')
|
120
|
-
builder = Nokogiri::XML::Builder.new { |xml|
|
121
|
-
xml.mods(Dor::DescMetadataDS::MODS_HEADER_CONFIG) {
|
122
|
-
xml.titleInfo {
|
123
|
-
xml.title label
|
124
|
-
}
|
125
|
-
}
|
126
|
-
}
|
127
|
-
ds.content = builder.to_xml
|
128
|
-
end
|
110
|
+
build_desc_metadata_from_label(new_item, label) if metadata_source == 'label'
|
129
111
|
|
130
112
|
workflow_priority = params[:workflow_priority] ? params[:workflow_priority].to_i : 0
|
131
113
|
|
132
|
-
Array(params[:seed_datastream])
|
114
|
+
seed_datastreams(Array(params[:seed_datastream]), new_item)
|
133
115
|
Array(params[:initiate_workflow]).each { |workflow_id| new_item.create_workflow(workflow_id, !new_item.new_record?, workflow_priority) }
|
134
116
|
|
135
|
-
new_item.assert_content_model
|
136
|
-
|
137
117
|
new_item.class.ancestors.select { |x| x.respond_to?(:to_class_uri) && x != ActiveFedora::Base }.each do |parent_class|
|
138
118
|
new_item.add_relationship(:has_model, parent_class.to_class_uri)
|
139
119
|
end
|
@@ -161,28 +141,28 @@ module Dor
|
|
161
141
|
end
|
162
142
|
|
163
143
|
dor_params = {
|
164
|
-
:
|
165
|
-
:
|
166
|
-
:
|
167
|
-
:
|
168
|
-
:
|
169
|
-
:
|
170
|
-
:
|
171
|
-
:
|
172
|
-
:
|
173
|
-
:
|
174
|
-
:
|
175
|
-
:
|
176
|
-
:
|
177
|
-
:
|
178
|
-
:
|
144
|
+
pid: params[:pid],
|
145
|
+
admin_policy: params[:admin_policy],
|
146
|
+
content_model: params[:model],
|
147
|
+
label: params[:label],
|
148
|
+
object_type: params[:object_type],
|
149
|
+
other_ids: ids_to_hash(other_ids),
|
150
|
+
parent: params[:parent],
|
151
|
+
source_id: ids_to_hash(params[:source_id]),
|
152
|
+
tags: params[:tag] || [],
|
153
|
+
seed_datastream: params[:seed_datastream],
|
154
|
+
initiate_workflow: Array(params[:initiate_workflow]) + Array(params[:workflow_id]),
|
155
|
+
rights: params[:rights],
|
156
|
+
metadata_source: params[:metadata_source],
|
157
|
+
collection: params[:collection],
|
158
|
+
workflow_priority: params[:workflow_priority]
|
179
159
|
}
|
180
|
-
dor_params.delete_if { |
|
160
|
+
dor_params.delete_if { |_k, v| v.nil? }
|
181
161
|
|
182
162
|
dor_obj = register_object(dor_params)
|
183
163
|
pid = dor_obj.pid
|
184
164
|
location = URI.parse(Dor::Config.fedora.safeurl.sub(/\/*$/, '/')).merge("objects/#{pid}").to_s
|
185
|
-
dor_params.dup.merge(
|
165
|
+
dor_params.dup.merge(location: location, pid: pid)
|
186
166
|
end
|
187
167
|
|
188
168
|
private
|
@@ -192,6 +172,23 @@ module Dor
|
|
192
172
|
|
193
173
|
Hash[Array(ids).map { |id| id.split(':', 2) }]
|
194
174
|
end
|
175
|
+
|
176
|
+
def seed_datastreams(names, item)
|
177
|
+
names.each do |datastream_name|
|
178
|
+
item.build_datastream(datastream_name)
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
def build_desc_metadata_from_label(new_item, label)
|
183
|
+
builder = Nokogiri::XML::Builder.new do |xml|
|
184
|
+
xml.mods(Dor::DescMetadataDS::MODS_HEADER_CONFIG) do
|
185
|
+
xml.titleInfo do
|
186
|
+
xml.title label
|
187
|
+
end
|
188
|
+
end
|
189
|
+
end
|
190
|
+
new_item.descMetadata.content = builder.to_xml
|
191
|
+
end
|
195
192
|
end
|
196
193
|
end
|
197
194
|
end
|
@@ -0,0 +1,251 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Dor
|
4
|
+
class ReleaseTagService
|
5
|
+
# Determine projects in which an item is released
|
6
|
+
# @param [Dor::Item] item to get the release tags for
|
7
|
+
# @return [Hash{String => Boolean}] all namespaces, keys are Project name Strings, values are Boolean
|
8
|
+
def self.for(item)
|
9
|
+
new(item)
|
10
|
+
end
|
11
|
+
|
12
|
+
def initialize(item)
|
13
|
+
@item = item
|
14
|
+
end
|
15
|
+
|
16
|
+
# Called in Dor::UpdateMarcRecordService (in dor-services-app too)
|
17
|
+
# Determine projects in which an item is released
|
18
|
+
# @return [Hash{String => Boolean}] all namespaces, keys are Project name Strings, values are Boolean
|
19
|
+
def released_for(skip_live_purl:)
|
20
|
+
released_hash = {}
|
21
|
+
|
22
|
+
# Get the most recent self tag for all targets and retain their result since most recent self always trumps any other non self tags
|
23
|
+
latest_self_tags = newest_release_tag self_release_tags(release_nodes)
|
24
|
+
latest_self_tags.each do |key, payload|
|
25
|
+
released_hash[key] = { 'release' => payload['release'] }
|
26
|
+
end
|
27
|
+
|
28
|
+
# With Self Tags resolved we now need to deal with tags on all sets this object is part of.
|
29
|
+
# Get all release tags on the item and strip out the what = self ones, we've already processed all the self tags on this item.
|
30
|
+
# This will be where we store all tags that apply, regardless of their timestamp:
|
31
|
+
potential_applicable_release_tags = tags_for_what_value(release_tags_for_item_and_all_governing_sets, 'collection')
|
32
|
+
administrative_tags = item.tags # Get admin tags once here and pass them down
|
33
|
+
|
34
|
+
# We now have the keys for all potential releases, we need to check the tags: the most recent timestamp with an explicit true or false wins.
|
35
|
+
# In a nil case, the lack of an explicit false tag we do nothing.
|
36
|
+
(potential_applicable_release_tags.keys - released_hash.keys).each do |key| # don't bother checking if already added to the release hash, they were added due to a self tag so that has won
|
37
|
+
latest_tag = latest_applicable_release_tag_in_array(potential_applicable_release_tags[key], administrative_tags)
|
38
|
+
next if latest_tag.nil? # Otherwise, we have a valid tag, record it
|
39
|
+
|
40
|
+
released_hash[key] = { 'release' => latest_tag['release'] }
|
41
|
+
end
|
42
|
+
|
43
|
+
# See what the application is currently released for on Purl. If released in purl but not listed here, it needs to be added as a false
|
44
|
+
add_tags_from_purl(released_hash) unless skip_live_purl
|
45
|
+
released_hash
|
46
|
+
end
|
47
|
+
|
48
|
+
# Take an item and get all of its release tags and all tags on collections it is a member of it
|
49
|
+
# @return [Hash] a hash of all tags
|
50
|
+
def release_tags_for_item_and_all_governing_sets
|
51
|
+
return_tags = release_nodes || {}
|
52
|
+
item.collections.each do |collection|
|
53
|
+
next if collection.id == item.id # recursive, so parents of parents are found, but we need to avoid an infinite loop if the collection references itself (i.e. bad data)
|
54
|
+
|
55
|
+
return_tags = Releases.combine_two_release_tag_hashes(return_tags, collection.releases.release_tags_for_item_and_all_governing_sets)
|
56
|
+
end
|
57
|
+
return_tags
|
58
|
+
end
|
59
|
+
|
60
|
+
private
|
61
|
+
|
62
|
+
# Helper method to get the release nodes as a nodeset
|
63
|
+
# @return [Nokogiri::XML::NodeSet] of all release tags and their attributes
|
64
|
+
def release_nodes
|
65
|
+
release_tags = item.identityMetadata.ng_xml.xpath('//release')
|
66
|
+
return_hash = {}
|
67
|
+
release_tags.each do |release_tag|
|
68
|
+
hashed_node = release_tag_node_to_hash(release_tag)
|
69
|
+
if !return_hash[hashed_node[:to]].nil?
|
70
|
+
return_hash[hashed_node[:to]] << hashed_node[:attrs]
|
71
|
+
else
|
72
|
+
return_hash[hashed_node[:to]] = [hashed_node[:attrs]]
|
73
|
+
end
|
74
|
+
end
|
75
|
+
return_hash
|
76
|
+
end
|
77
|
+
|
78
|
+
# Helper method to get the release tags as a nodeset
|
79
|
+
# @return [Nokogiri::XML::NodeSet] all release tags and their attributes
|
80
|
+
def release_tags
|
81
|
+
release_tags = identityMetadata.ng_xml.xpath('//release')
|
82
|
+
return_hash = {}
|
83
|
+
release_tags.each do |release_tag|
|
84
|
+
hashed_node = release_tag_node_to_hash(release_tag)
|
85
|
+
if !return_hash[hashed_node[:to]].nil?
|
86
|
+
return_hash[hashed_node[:to]] << hashed_node[:attrs]
|
87
|
+
else
|
88
|
+
return_hash[hashed_node[:to]] = [hashed_node[:attrs]]
|
89
|
+
end
|
90
|
+
end
|
91
|
+
return_hash
|
92
|
+
end
|
93
|
+
|
94
|
+
# Convert one release element into a Hash
|
95
|
+
# @param rtag [Nokogiri::XML::Element] the release tag element
|
96
|
+
# @return [Hash{:to, :attrs => String, Hash}] in the form of !{:to => String :attrs = Hash}
|
97
|
+
def release_tag_node_to_hash(rtag)
|
98
|
+
to = 'to'
|
99
|
+
release = 'release'
|
100
|
+
when_word = 'when' # TODO: Make to and when_word load from some config file instead of hardcoded here
|
101
|
+
attrs = rtag.attributes
|
102
|
+
return_hash = { to: attrs[to].value }
|
103
|
+
attrs.tap { |a| a.delete(to) }
|
104
|
+
attrs[release] = rtag.text.casecmp('true') == 0 # save release as a boolean
|
105
|
+
return_hash[:attrs] = attrs
|
106
|
+
|
107
|
+
# convert all the attrs beside :to to strings, they are currently Nokogiri::XML::Attr
|
108
|
+
(return_hash[:attrs].keys - [to]).each do |a|
|
109
|
+
return_hash[:attrs][a] = return_hash[:attrs][a].to_s if a != release
|
110
|
+
end
|
111
|
+
|
112
|
+
return_hash[:attrs][when_word] = Time.parse(return_hash[:attrs][when_word]) # convert when to a datetime
|
113
|
+
return_hash
|
114
|
+
end
|
115
|
+
|
116
|
+
# Take a hash of tags as obtained via Dor::Item.release_tags and returns all self tags
|
117
|
+
# @param tags [Hash] a hash of tags obtained via Dor::Item.release_tags or matching format
|
118
|
+
# @return [Hash] a hash of self tags for each to value
|
119
|
+
def self_release_tags(tags)
|
120
|
+
tags_for_what_value(tags, 'self')
|
121
|
+
end
|
122
|
+
|
123
|
+
# Take a hash of tags and return all tags with the matching what target
|
124
|
+
# @param tags [Hash] a hash of tags obtained via Dor::Item.release_tags or matching format
|
125
|
+
# @param what_target [String] the target for the 'what' key, self or collection
|
126
|
+
# @return [Hash] a hash of self tags for each to value
|
127
|
+
def tags_for_what_value(tags, what_target)
|
128
|
+
return_hash = {}
|
129
|
+
tags.keys.each do |key|
|
130
|
+
self_tags = tags[key].select { |tag| tag['what'].casecmp(what_target) == 0 }
|
131
|
+
return_hash[key] = self_tags if self_tags.size > 0
|
132
|
+
end
|
133
|
+
return_hash
|
134
|
+
end
|
135
|
+
|
136
|
+
# Take two hashes of tags and combine them, will not overwrite but will enforce uniqueness of the tags
|
137
|
+
# @param hash_one [Hash] a hash of tags obtained via Dor::Item.release_tags or matching format
|
138
|
+
# @param hash_two [Hash] a hash of tags obtained via Dor::Item.release_tags or matching format
|
139
|
+
# @return [Hash] the combined hash with uniquiness enforced
|
140
|
+
def self.combine_two_release_tag_hashes(hash_one, hash_two)
|
141
|
+
hash_two.keys.each do |key|
|
142
|
+
hash_one[key] = hash_two[key] if hash_one[key].nil?
|
143
|
+
hash_one[key] = (hash_one[key] + hash_two[key]).uniq unless hash_one[key].nil?
|
144
|
+
end
|
145
|
+
hash_one
|
146
|
+
end
|
147
|
+
private_class_method :combine_two_release_tag_hashes
|
148
|
+
|
149
|
+
# Take a hash of tags as obtained via Dor::Item.release_tags and returns the newest tag for each namespace
|
150
|
+
# @param tags [Hash] a hash of tags obtained via Dor::Item.release_tags or matching format
|
151
|
+
# @return [Hash] a hash of latest tags for each to value
|
152
|
+
def newest_release_tag(tags)
|
153
|
+
Hash[tags.map { |key, val| [key, newest_release_tag_in_an_array(val)] }]
|
154
|
+
end
|
155
|
+
|
156
|
+
# Takes an array of release tags and returns the most recent one
|
157
|
+
# @param array_of_tags [Array] an array of hashes, each hash a release tag
|
158
|
+
# @return [Hash] the most recent tag
|
159
|
+
def newest_release_tag_in_an_array(array_of_tags)
|
160
|
+
latest_tag_in_array = array_of_tags[0] || {}
|
161
|
+
array_of_tags.each do |tag|
|
162
|
+
latest_tag_in_array = tag if tag['when'] > latest_tag_in_array['when']
|
163
|
+
end
|
164
|
+
latest_tag_in_array
|
165
|
+
end
|
166
|
+
|
167
|
+
# Takes a tag and returns true or false if it applies to the specific item
|
168
|
+
# @param release_tag [Hash] the tag in a hashed form
|
169
|
+
# @param admin_tags [Array] the administrative tags on an item, if not supplied it will attempt to retrieve them
|
170
|
+
# @return [Boolean] true or false if it applies (not true or false if it is released, that is the release_tag data)
|
171
|
+
def does_release_tag_apply(release_tag, admin_tags = false)
|
172
|
+
# Is the tag global or restricted
|
173
|
+
return true if release_tag['tag'].nil? # no specific tag specificied means this tag is global to all members of the collection
|
174
|
+
|
175
|
+
admin_tags ||= item.tags # We use false instead of [], since an item can have no admin_tags at which point we'd be passing this var as [] and would not attempt to retrieve it
|
176
|
+
admin_tags.include?(release_tag['tag'])
|
177
|
+
end
|
178
|
+
|
179
|
+
# Takes an array of release tags and returns the most recent one that applies to this item
|
180
|
+
# @param release_tags [Array] an array of release tags in hashed form
|
181
|
+
# @param admin_tags [Array] the administrative tags on an on item
|
182
|
+
# @return [Hash] the tag, or nil if none applicable
|
183
|
+
def latest_applicable_release_tag_in_array(release_tags, admin_tags)
|
184
|
+
newest_tag = newest_release_tag_in_an_array(release_tags)
|
185
|
+
return newest_tag if does_release_tag_apply(newest_tag, admin_tags)
|
186
|
+
|
187
|
+
# The latest tag wasn't applicable, slice it off and try again
|
188
|
+
# This could be optimized by reordering on the timestamp and just running down it instead of constantly resorting, at least if we end up getting numerous release tags on an item
|
189
|
+
release_tags.slice!(release_tags.index(newest_tag))
|
190
|
+
|
191
|
+
return latest_applicable_release_tag_in_array(release_tags, admin_tags) if release_tags.size > 0 # Try again after dropping the inapplicable
|
192
|
+
|
193
|
+
nil # We're out of tags, no applicable ones
|
194
|
+
end
|
195
|
+
|
196
|
+
# This function calls purl and gets a list of all release tags currently in purl. It then compares to the list you have generated.
|
197
|
+
# Any tag that is on purl, but not in the newly generated list is added to the new list with a value of false.
|
198
|
+
# @param new_tags [Hash{String => Boolean}] all new tags in the form of !{"Project" => Boolean}
|
199
|
+
# @return [Hash] same form as new_tags, with all missing tags not in new_tags, but in current_tag_names, added in with a Boolean value of false
|
200
|
+
def add_tags_from_purl(new_tags)
|
201
|
+
missing_tags = release_tags_from_purl.map(&:downcase) - new_tags.keys.map(&:downcase)
|
202
|
+
missing_tags.each do |missing_tag|
|
203
|
+
new_tags[missing_tag.capitalize] = { 'release' => false }
|
204
|
+
end
|
205
|
+
new_tags
|
206
|
+
end
|
207
|
+
|
208
|
+
# Get a list of all release nodes found in a purl document
|
209
|
+
# Fetches purl xml for a druid
|
210
|
+
# @raise [OpenURI::HTTPError]
|
211
|
+
# @return [Nokogiri::HTML::Document] parsed XML for the druid or an empty document if no purl is found
|
212
|
+
def xml_from_purl
|
213
|
+
url = form_purl_url
|
214
|
+
handler = proc do |exception, attempt_number, total_delay|
|
215
|
+
# We assume a 404 means the document has never been published before and thus has no purl
|
216
|
+
Dor.logger.warn "[Attempt #{attempt_number}] GET #{url} -- #{exception.class}: #{exception.message}; #{total_delay} seconds elapsed."
|
217
|
+
raise exception unless exception.is_a? OpenURI::HTTPError
|
218
|
+
return Nokogiri::HTML::Document.new if exception.io.status.first == '404' # ["404", "Not Found"] from OpenURI::Meta.status
|
219
|
+
end
|
220
|
+
|
221
|
+
with_retries(max_retries: 3, base_sleep_seconds: 3, max_sleep_seconds: 5, handler: handler) do |attempt|
|
222
|
+
# If you change the method used for opening the webpage, you can change the :rescue param to handle the new method's errors
|
223
|
+
Dor.logger.info "[Attempt #{attempt}] GET #{url}"
|
224
|
+
return Nokogiri::HTML(OpenURI.open_uri(url))
|
225
|
+
end
|
226
|
+
end
|
227
|
+
|
228
|
+
# Take the and create the entire purl url that will usable for the open method in open-uri, returns http
|
229
|
+
# @return [String] the full url
|
230
|
+
def form_purl_url
|
231
|
+
'https://' + Dor::Config.stacks.document_cache_host + "/#{item.remove_druid_prefix}.xml"
|
232
|
+
end
|
233
|
+
|
234
|
+
# Pull all release nodes from the public xml obtained via the purl query
|
235
|
+
# @param doc [Nokogiri::HTML::Document] The druid of the object you want
|
236
|
+
# @return [Array] An array containing all the release tags
|
237
|
+
def release_tags_from_purl_xml(doc)
|
238
|
+
nodes = doc.xpath('//html/body/publicobject/releasedata').children
|
239
|
+
# We only want the nodes with a name that isn't text
|
240
|
+
nodes.reject { |n| n.name.nil? || n.name.casecmp('text') == 0 }.map { |n| n.attr('to') }.uniq
|
241
|
+
end
|
242
|
+
|
243
|
+
# Pull all release nodes from the public xml obtained via the purl query
|
244
|
+
# @return [Array] An array containing all the release tags
|
245
|
+
def release_tags_from_purl
|
246
|
+
release_tags_from_purl_xml(xml_from_purl)
|
247
|
+
end
|
248
|
+
|
249
|
+
attr_reader :item
|
250
|
+
end
|
251
|
+
end
|
@@ -21,9 +21,7 @@ module Dor
|
|
21
21
|
|
22
22
|
FileUtils.mv(bag_dir, "#{bag_dir}_v#{version}") if File.exist?(bag_dir)
|
23
23
|
|
24
|
-
if File.exist?("#{bag_dir}.tar")
|
25
|
-
FileUtils.mv("#{bag_dir}.tar", "#{bag_dir}_v#{version}.tar")
|
26
|
-
end
|
24
|
+
FileUtils.mv("#{bag_dir}.tar", "#{bag_dir}_v#{version}.tar") if File.exist?("#{bag_dir}.tar")
|
27
25
|
end
|
28
26
|
end
|
29
27
|
end
|
@@ -9,7 +9,7 @@ module Dor
|
|
9
9
|
# @param [Dor::Item] dor_item The representation of the digital object
|
10
10
|
# @param [String] agreement_id depreciated, included for backward compatability with common-accessoning
|
11
11
|
# @return [void] Create the Moab/bag manifests for new version, export data to BagIt bag, kick off the SDR preservation workflow
|
12
|
-
def self.transfer(dor_item,
|
12
|
+
def self.transfer(dor_item, _agreement_id = nil)
|
13
13
|
druid = dor_item.pid
|
14
14
|
workspace = DruidTools::Druid.new(druid, Dor::Config.sdr.local_workspace_root)
|
15
15
|
signature_catalog = get_signature_catalog(druid)
|
@@ -26,9 +26,7 @@ module Dor
|
|
26
26
|
content_dir = workspace.find_filelist_parent('content', new_file_list)
|
27
27
|
end
|
28
28
|
content_group = version_inventory.group('content')
|
29
|
-
unless content_group.nil? || content_group.files.empty?
|
30
|
-
signature_catalog.normalize_group_signatures(content_group, content_dir)
|
31
|
-
end
|
29
|
+
signature_catalog.normalize_group_signatures(content_group, content_dir) unless content_group.nil? || content_group.files.empty?
|
32
30
|
# export the bag (in tar format)
|
33
31
|
bag_dir = Pathname(Dor::Config.sdr.local_export_home).join(druid.sub('druid:', ''))
|
34
32
|
bagger = Moab::Bagger.new(version_inventory, signature_catalog, bag_dir)
|
@@ -74,7 +72,7 @@ module Dor
|
|
74
72
|
# If not found, return nil unless it is a required datastream in which case raise exception
|
75
73
|
def self.get_datastream_content(dor_item, ds_name, required)
|
76
74
|
ds = (ds_name == 'relationshipMetadata' ? 'RELS-EXT' : ds_name)
|
77
|
-
if dor_item.datastreams.
|
75
|
+
if dor_item.datastreams.key?(ds) && !dor_item.datastreams[ds].new?
|
78
76
|
return dor_item.datastreams[ds].content
|
79
77
|
elsif required == 'optional'
|
80
78
|
return nil
|
@@ -130,7 +128,7 @@ module Dor
|
|
130
128
|
if content_metadata
|
131
129
|
Stanford::ContentInventory.new.inventory_from_cm(content_metadata, druid, 'preserve', version_id)
|
132
130
|
else
|
133
|
-
Moab::FileInventory.new(:
|
131
|
+
Moab::FileInventory.new(type: 'version', digital_object_id: druid, version_id: version_id)
|
134
132
|
end
|
135
133
|
end
|
136
134
|
|
@@ -144,7 +142,7 @@ module Dor
|
|
144
142
|
# @param [Pathname] metadata_dir The location of the the object's metadata files
|
145
143
|
# @return [Moab::FileGroup] Traverse the metadata directory and generate a metadata group
|
146
144
|
def self.get_metadata_file_group(metadata_dir)
|
147
|
-
file_group = Moab::FileGroup.new(:
|
145
|
+
file_group = Moab::FileGroup.new(group_id: 'metadata').group_from_directory(metadata_dir)
|
148
146
|
file_group
|
149
147
|
end
|
150
148
|
|