dor-services 4.25.1 → 5.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/bin/dor-indexer +20 -19
- data/bin/dor-indexerd +3 -2
- data/config/certs/robots-dor-dev.crt +29 -0
- data/config/certs/robots-dor-dev.key +27 -0
- data/config/config_defaults.yml +0 -6
- data/config/dev_console_env.rb +65 -0
- data/config/environments/development.rb +84 -0
- data/config/environments/development.rb.old +84 -0
- data/config/environments/test.rb +84 -0
- data/lib/dor-services.rb +8 -18
- data/lib/dor/config.rb +18 -24
- data/lib/dor/datastreams/administrative_metadata_ds.rb +8 -7
- data/lib/dor/datastreams/content_metadata_ds.rb +200 -278
- data/lib/dor/datastreams/datastream_spec_solrizer.rb +1 -1
- data/lib/dor/datastreams/default_object_rights_ds.rb +10 -8
- data/lib/dor/datastreams/desc_metadata_ds.rb +30 -34
- data/lib/dor/datastreams/embargo_metadata_ds.rb +17 -13
- data/lib/dor/datastreams/events_ds.rb +12 -12
- data/lib/dor/datastreams/geo_metadata_ds.rb +3 -244
- data/lib/dor/datastreams/identity_metadata_ds.rb +34 -30
- data/lib/dor/datastreams/role_metadata_ds.rb +6 -6
- data/lib/dor/datastreams/simple_dublin_core_ds.rb +12 -9
- data/lib/dor/datastreams/version_metadata_ds.rb +14 -33
- data/lib/dor/datastreams/workflow_definition_ds.rb +18 -18
- data/lib/dor/datastreams/workflow_ds.rb +74 -65
- data/lib/dor/migrations/identifiable/assert_adminPolicy.rb +1 -1
- data/lib/dor/migrations/identifiable/fix_model_assertions.rb +1 -1
- data/lib/dor/migrations/identifiable/record_remediation.rb +2 -2
- data/lib/dor/migrations/identifiable/uriify_augmented_contentlocation_refs.rb +1 -1
- data/lib/dor/migrations/identifiable/uriify_contentlocation_refs.rb +1 -1
- data/lib/dor/migrations/processable/unify_workflows.rb +4 -4
- data/lib/dor/migrations/versionable/add_missing_version_md.rb +1 -1
- data/lib/dor/models/admin_policy_object.rb +1 -1
- data/lib/dor/models/assembleable.rb +3 -4
- data/lib/dor/models/collection.rb +0 -2
- data/lib/dor/models/contentable.rb +34 -35
- data/lib/dor/models/describable.rb +80 -122
- data/lib/dor/models/editable.rb +57 -73
- data/lib/dor/models/embargoable.rb +13 -15
- data/lib/dor/models/eventable.rb +3 -3
- data/lib/dor/models/geoable.rb +8 -9
- data/lib/dor/models/governable.rb +36 -54
- data/lib/dor/models/identifiable.rb +119 -115
- data/lib/dor/models/item.rb +4 -4
- data/lib/dor/models/itemizable.rb +9 -9
- data/lib/dor/models/presentable.rb +133 -0
- data/lib/dor/models/preservable.rb +4 -4
- data/lib/dor/models/processable.rb +29 -28
- data/lib/dor/models/publishable.rb +36 -30
- data/lib/dor/models/releasable.rb +310 -0
- data/lib/dor/models/shelvable.rb +14 -14
- data/lib/dor/models/upgradable.rb +13 -13
- data/lib/dor/models/versionable.rb +4 -7
- data/lib/dor/models/workflow_object.rb +16 -36
- data/lib/dor/services/cleanup_reset_service.rb +28 -34
- data/lib/dor/services/cleanup_service.rb +4 -4
- data/lib/dor/services/digital_stacks_service.rb +10 -10
- data/lib/dor/services/merge_service.rb +1 -1
- data/lib/dor/services/metadata_handlers/mdtoolkit_handler.rb +2 -2
- data/lib/dor/services/metadata_service.rb +20 -20
- data/lib/dor/services/registration_service.rb +26 -27
- data/lib/dor/services/reset_workspace_service.rb +15 -15
- data/lib/dor/services/sdr_ingest_service.rb +4 -4
- data/lib/dor/services/search_service.rb +4 -9
- data/lib/dor/services/suri_service.rb +5 -5
- data/lib/dor/services/technical_metadata_service.rb +3 -2
- data/lib/dor/utils/ng_tidy.rb +9 -9
- data/lib/dor/utils/predicate_patch.rb +1 -1
- data/lib/dor/utils/solr_doc_helper.rb +13 -5
- data/lib/dor/version.rb +1 -1
- data/lib/dor/workflow/document.rb +28 -30
- data/lib/dor/workflow/graph.rb +36 -36
- data/lib/dor/workflow/process.rb +12 -12
- data/lib/tasks/dor.rake +1 -1
- data/lib/tasks/rdoc.rake +3 -3
- metadata +67 -76
- data/lib/dor/datastreams/geo2mods.xsl +0 -867
- data/lib/dor/models/discoverable.rb +0 -64
- data/lib/dor/models/releaseable.rb +0 -357
- data/lib/dor/services/indexing_service.rb +0 -64
- data/lib/dor/utils/sdr_client.rb +0 -23
- data/lib/dor/utils/utc_date_field_mapper.rb +0 -7
@@ -17,11 +17,11 @@ module Dor
|
|
17
17
|
ds.label = 'Provenance Metadata'
|
18
18
|
end
|
19
19
|
ds.ng_xml = workflow_provenance
|
20
|
-
|
20
|
+
ds.content=ds.ng_xml.to_s
|
21
21
|
ds.save
|
22
22
|
end
|
23
23
|
|
24
|
-
def build_technicalMetadata_datastream(ds
|
24
|
+
def build_technicalMetadata_datastream(ds=nil)
|
25
25
|
TechnicalMetadataService.add_update_technical_metadata(self)
|
26
26
|
end
|
27
27
|
|
@@ -33,9 +33,9 @@ module Dor
|
|
33
33
|
# @return [Nokogiri::Document]
|
34
34
|
def create_workflow_provenance(workflow_id, event_text)
|
35
35
|
builder = Nokogiri::XML::Builder.new do |xml|
|
36
|
-
xml.provenanceMetadata(:objectId => pid) {
|
36
|
+
xml.provenanceMetadata(:objectId => self.pid) {
|
37
37
|
xml.agent(:name => 'DOR') {
|
38
|
-
xml.what(:object => pid) {
|
38
|
+
xml.what(:object => self.pid) {
|
39
39
|
xml.event(:who => "DOR-#{workflow_id}", :when => Time.new.iso8601) {
|
40
40
|
xml.text(event_text)
|
41
41
|
}
|
@@ -42,11 +42,11 @@ module Dor
|
|
42
42
|
# This is a work-around for some strange logic in ActiveFedora that
|
43
43
|
# don't allow self.workflows.new? to work if we load the object using
|
44
44
|
# .load_instance_from_solr.
|
45
|
-
return if self.respond_to?
|
45
|
+
return if self.respond_to? :inner_object and self.inner_object.is_a? ActiveFedora::SolrDigitalObject
|
46
46
|
|
47
|
-
if workflows.new?
|
47
|
+
if self.workflows.new?
|
48
48
|
workflows.mimeType = 'application/xml'
|
49
|
-
workflows.dsLocation = File.join(Dor::Config.workflow.url,"dor/objects/#{pid}/workflows")
|
49
|
+
workflows.dsLocation = File.join(Dor::Config.workflow.url,"dor/objects/#{self.pid}/workflows")
|
50
50
|
end
|
51
51
|
end
|
52
52
|
|
@@ -54,7 +54,7 @@ module Dor
|
|
54
54
|
if datastream.new?
|
55
55
|
true
|
56
56
|
elsif datastream.class.respond_to?(:xml_template)
|
57
|
-
datastream.content.to_s.empty?
|
57
|
+
datastream.content.to_s.empty? or EquivalentXml.equivalent?(datastream.content, datastream.class.xml_template)
|
58
58
|
else
|
59
59
|
datastream.content.to_s.empty?
|
60
60
|
end
|
@@ -65,7 +65,7 @@ module Dor
|
|
65
65
|
# Returns the path to it or nil.
|
66
66
|
def find_metadata_file(datastream)
|
67
67
|
druid = DruidTools::Druid.new(pid, Dor::Config.stacks.local_workspace_root)
|
68
|
-
druid.find_metadata("#{datastream}.xml")
|
68
|
+
return druid.find_metadata("#{datastream}.xml")
|
69
69
|
end
|
70
70
|
|
71
71
|
# Takes the name of a datastream, as a string (fooMetadata).
|
@@ -84,10 +84,10 @@ module Dor
|
|
84
84
|
ds.content = content
|
85
85
|
ds.ng_xml = Nokogiri::XML(content) if ds.respond_to?(:ng_xml)
|
86
86
|
ds.save unless ds.digital_object.new?
|
87
|
-
elsif force
|
87
|
+
elsif force or empty_datastream?(ds)
|
88
88
|
meth = "build_#{datastream}_datastream".to_sym
|
89
89
|
if respond_to?(meth)
|
90
|
-
content = send(meth, ds)
|
90
|
+
content = self.send(meth, ds)
|
91
91
|
ds.save unless ds.digital_object.new?
|
92
92
|
end
|
93
93
|
end
|
@@ -95,7 +95,7 @@ module Dor
|
|
95
95
|
if is_required && empty_datastream?(ds)
|
96
96
|
raise "Required datastream #{datastream} could not be populated!"
|
97
97
|
end
|
98
|
-
ds
|
98
|
+
return ds
|
99
99
|
end
|
100
100
|
|
101
101
|
def cleanup()
|
@@ -103,13 +103,13 @@ module Dor
|
|
103
103
|
end
|
104
104
|
|
105
105
|
def milestones
|
106
|
-
Dor::WorkflowService.get_milestones('dor',pid)
|
106
|
+
Dor::WorkflowService.get_milestones('dor',self.pid)
|
107
107
|
end
|
108
108
|
|
109
109
|
def status_info()
|
110
110
|
current_version = '1'
|
111
111
|
begin
|
112
|
-
current_version = versionMetadata.current_version_id
|
112
|
+
current_version = self.versionMetadata.current_version_id
|
113
113
|
rescue
|
114
114
|
end
|
115
115
|
|
@@ -117,8 +117,8 @@ module Dor
|
|
117
117
|
#only get steps that are part of accessioning and part of the current version. That can mean they were archived with the current version
|
118
118
|
#number, or they might be active (no version number).
|
119
119
|
milestones.each do |m|
|
120
|
-
if STEPS.keys.include?(m[:milestone])
|
121
|
-
current_milestones << m unless m[:milestone] == 'registered'
|
120
|
+
if STEPS.keys.include?(m[:milestone]) and (m[:version].nil? or m[:version] == current_version)
|
121
|
+
current_milestones << m unless m[:milestone] == 'registered' and current_version.to_i > 1
|
122
122
|
end
|
123
123
|
end
|
124
124
|
|
@@ -136,25 +136,25 @@ module Dor
|
|
136
136
|
end
|
137
137
|
end
|
138
138
|
|
139
|
-
{:current_version => current_version, :status_code => status_code, :status_time => status_time}
|
139
|
+
return {:current_version => current_version, :status_code => status_code, :status_time => status_time}
|
140
140
|
end
|
141
141
|
|
142
|
-
def status(include_time
|
142
|
+
def status(include_time=false)
|
143
143
|
status_info_hash = status_info
|
144
144
|
current_version, status_code, status_time = status_info_hash[:current_version], status_info_hash[:status_code], status_info_hash[:status_time]
|
145
145
|
|
146
146
|
#use the translation table to get the appropriate verbage for the latest step
|
147
147
|
result = "v#{current_version} #{STATUS_CODE_DISP_TXT[status_code]}"
|
148
148
|
result += " #{format_date(status_time)}" if include_time
|
149
|
-
result
|
149
|
+
return result
|
150
150
|
end
|
151
151
|
|
152
|
-
def to_solr(solr_doc
|
152
|
+
def to_solr(solr_doc=Hash.new, *args)
|
153
153
|
super(solr_doc, *args)
|
154
154
|
sortable_milestones = {}
|
155
155
|
current_version='1'
|
156
156
|
begin
|
157
|
-
current_version = versionMetadata.current_version_id
|
157
|
+
current_version = self.versionMetadata.current_version_id
|
158
158
|
rescue
|
159
159
|
end
|
160
160
|
current_version_num=current_version.to_i
|
@@ -162,12 +162,12 @@ module Dor
|
|
162
162
|
if self.respond_to?('versionMetadata')
|
163
163
|
#add an entry with version id, tag and description for each version
|
164
164
|
while current_version_num > 0
|
165
|
-
add_solr_value(solr_doc, 'versions', current_version_num.to_s + ';' + versionMetadata.tag_for_version(current_version_num.to_s) + ';' + versionMetadata.description_for_version(current_version_num.to_s), :string, [:displayable])
|
165
|
+
add_solr_value(solr_doc, 'versions', current_version_num.to_s + ';' + self.versionMetadata.tag_for_version(current_version_num.to_s) + ';' + self.versionMetadata.description_for_version(current_version_num.to_s), :string, [:displayable])
|
166
166
|
current_version_num -= 1
|
167
167
|
end
|
168
168
|
end
|
169
169
|
|
170
|
-
milestones.each do |milestone|
|
170
|
+
self.milestones.each do |milestone|
|
171
171
|
timestamp = milestone[:at].utc.xmlschema
|
172
172
|
sortable_milestones[milestone[:milestone]] ||= []
|
173
173
|
sortable_milestones[milestone[:milestone]] << timestamp
|
@@ -181,8 +181,8 @@ module Dor
|
|
181
181
|
sortable_milestones.each do |milestone, unordered_dates|
|
182
182
|
dates = unordered_dates.sort
|
183
183
|
#create the published_dt and published_day fields and the like
|
184
|
-
add_solr_value(solr_doc, milestone+'_day', DateTime.parse(dates.last).beginning_of_day.utc.xmlschema.split('T').first, :string, [:searchable, :facetable])
|
185
|
-
add_solr_value(solr_doc, milestone, dates.first, :date, [:searchable, :facetable])
|
184
|
+
add_solr_value(solr_doc, milestone+'_day', DateTime.parse(dates.last).beginning_of_day.utc.xmlschema.split('T').first, :string, [:searchable, :stored_searchable, :facetable])
|
185
|
+
add_solr_value(solr_doc, milestone, dates.first, :date, [:searchable, :facetable, :stored_searchable])
|
186
186
|
|
187
187
|
#fields for OAI havester to sort on
|
188
188
|
add_solr_value(solr_doc, "#{milestone}_earliest_dt", dates.first, :date, [:sortable])
|
@@ -201,7 +201,7 @@ module Dor
|
|
201
201
|
add_solr_value(solr_doc, "version_opened", DateTime.parse(opened_date).beginning_of_day.utc.xmlschema.split('T').first, :string, [ :searchable, :facetable])
|
202
202
|
end
|
203
203
|
add_solr_value(solr_doc, "current_version", current_version.to_s, :string, [ :displayable , :facetable])
|
204
|
-
add_solr_value(solr_doc, "last_modified_day", modified_date.to_s.split('T').first, :string, [ :facetable ])
|
204
|
+
add_solr_value(solr_doc, "last_modified_day", self.modified_date.to_s.split('T').first, :string, [ :facetable ])
|
205
205
|
add_solr_value(solr_doc, "rights", rights, :string, [:facetable]) if self.respond_to? :rights
|
206
206
|
solr_doc
|
207
207
|
end
|
@@ -210,24 +210,25 @@ module Dor
|
|
210
210
|
# It will set the priorty of the new workflow to the current_priority if it is > 0
|
211
211
|
# It will set lane_id from the item's APO default workflow lane
|
212
212
|
# @param [String] name of the workflow to be initialized
|
213
|
+
# @param [String] repo name of the repository to create workflow for
|
213
214
|
# @param [Boolean] create_ds create a 'workflows' datastream in Fedora for the object
|
214
|
-
|
215
|
-
def initialize_workflow(name, create_ds = true, priority = 0)
|
215
|
+
def initialize_workflow(name, repo='dor', create_ds=true, priority=0)
|
216
216
|
priority = workflows.current_priority if priority == 0
|
217
217
|
opts = { :create_ds => create_ds }
|
218
218
|
opts[:priority] = priority if(priority > 0)
|
219
219
|
opts[:lane_id] = default_workflow_lane
|
220
|
-
Dor::WorkflowService.create_workflow(
|
220
|
+
Dor::WorkflowService.create_workflow(repo, self.pid, name, Dor::WorkflowObject.initial_workflow(name), opts)
|
221
221
|
end
|
222
222
|
|
223
223
|
|
224
224
|
private
|
225
225
|
#handles formating utc date/time to human readable
|
226
|
+
# XXX: bad form to hardcode TZ here. Code smell abounds.
|
226
227
|
def format_date datetime
|
227
228
|
begin
|
228
|
-
|
229
|
-
|
230
|
-
I18n.l(d)
|
229
|
+
d = datetime.is_a?(Time) ? datetime :
|
230
|
+
DateTime.parse(datetime).in_time_zone(ActiveSupport::TimeZone.new("Pacific Time (US & Canada)"))
|
231
|
+
I18n.l(d).strftime('%Y-%m-%d %I:%M%p')
|
231
232
|
rescue
|
232
233
|
d = datetime.is_a?(Time) ? datetime : Time.parse(datetime.to_s)
|
233
234
|
d.strftime('%Y-%m-%d %I:%M%p')
|
@@ -7,23 +7,24 @@ module Dor
|
|
7
7
|
include Governable
|
8
8
|
include Describable
|
9
9
|
include Itemizable
|
10
|
+
include Presentable
|
10
11
|
|
11
12
|
included do
|
12
|
-
has_metadata name
|
13
|
+
has_metadata :name => "rightsMetadata", :type => ActiveFedora::OmDatastream, :label => 'Rights Metadata'
|
13
14
|
end
|
14
15
|
|
15
16
|
def build_rightsMetadata_datastream(ds)
|
16
|
-
content_ds = admin_policy_object.datastreams['defaultObjectRights']
|
17
|
+
content_ds = self.admin_policy_object.datastreams['defaultObjectRights']
|
17
18
|
ds.dsLabel = 'Rights Metadata'
|
18
19
|
ds.ng_xml = content_ds.ng_xml.clone
|
19
20
|
ds.content = ds.ng_xml.to_xml
|
20
21
|
end
|
21
22
|
|
22
23
|
def public_relationships
|
23
|
-
include_elements = ['fedora:isMemberOf','fedora:isMemberOfCollection'
|
24
|
-
rels_doc = Nokogiri::XML(datastreams['RELS-EXT'].content)
|
25
|
-
rels_doc.xpath('/rdf:RDF/rdf:Description/*', 'rdf' => 'http://www.w3.org/1999/02/22-rdf-syntax-ns#').each do |rel|
|
26
|
-
unless include_elements.include?([rel.namespace.prefix,
|
24
|
+
include_elements = ['fedora:isMemberOf','fedora:isMemberOfCollection']
|
25
|
+
rels_doc = Nokogiri::XML(self.datastreams['RELS-EXT'].content)
|
26
|
+
rels_doc.xpath('/rdf:RDF/rdf:Description/*', { 'rdf' => 'http://www.w3.org/1999/02/22-rdf-syntax-ns#' }).each do |rel|
|
27
|
+
unless include_elements.include?([rel.namespace.prefix,rel.name].join(':'))
|
27
28
|
rel.next_sibling.remove if rel.next_sibling.content.strip.empty?
|
28
29
|
rel.remove
|
29
30
|
end
|
@@ -31,29 +32,29 @@ module Dor
|
|
31
32
|
rels_doc
|
32
33
|
end
|
33
34
|
|
34
|
-
#
|
35
|
-
|
35
|
+
#Generate the public .xml for a PURL page.
|
36
|
+
#@return [xml] The public xml for the item
|
37
|
+
#
|
36
38
|
def public_xml
|
37
|
-
pub = Nokogiri::XML(
|
39
|
+
pub = Nokogiri::XML("<publicObject/>").root
|
38
40
|
pub['id'] = pid
|
39
41
|
pub['published'] = Time.now.xmlschema
|
40
|
-
|
41
|
-
release_xml = Nokogiri(generate_release_xml).xpath('//release')
|
42
|
+
release_xml=Nokogiri(self.generate_release_xml).xpath('//release')
|
42
43
|
|
43
|
-
im
|
44
|
-
im.search('//release').each
|
44
|
+
im=self.datastreams['identityMetadata'].ng_xml.clone
|
45
|
+
im.search('//release').each {|node| node.remove} # remove any <release> tags from public xml which have full history
|
46
|
+
im.root.add_child(release_xml) # now add in final <release> tag #TODO: Adding this breaks tests, rework these
|
45
47
|
|
46
48
|
pub.add_child(im.root) # add in modified identityMetadata datastream
|
47
|
-
pub.add_child(datastreams['contentMetadata'].public_xml.root.clone)
|
48
|
-
pub.add_child(datastreams['rightsMetadata'].ng_xml.root.clone)
|
49
|
+
pub.add_child(self.datastreams['contentMetadata'].public_xml.root.clone)
|
50
|
+
pub.add_child(self.datastreams['rightsMetadata'].ng_xml.root.clone)
|
49
51
|
|
50
52
|
rels = public_relationships.root
|
51
53
|
pub.add_child(rels.clone) unless rels.nil? # TODO: Should never be nil in practice; working around an ActiveFedora quirk for testing
|
52
|
-
pub.add_child(generate_dublin_core.root.clone)
|
53
|
-
|
54
|
-
#
|
55
|
-
|
56
|
-
new_pub = Nokogiri::XML(pub.to_xml, &:noblanks)
|
54
|
+
pub.add_child(self.generate_dublin_core.root.clone)
|
55
|
+
@public_xml_doc = pub # save this for possible IIIF Presentation manifest
|
56
|
+
pub.add_child(Nokogiri(self.generate_release_xml).root.clone) #TODO: Adding this breaks tests, rework these
|
57
|
+
new_pub = Nokogiri::XML(pub.to_xml) { |x| x.noblanks }
|
57
58
|
new_pub.encoding = 'UTF-8'
|
58
59
|
new_pub.to_xml
|
59
60
|
end
|
@@ -62,25 +63,30 @@ module Dor
|
|
62
63
|
# otherwise, it prunes the object's metadata from the document cache
|
63
64
|
def publish_metadata
|
64
65
|
rights = datastreams['rightsMetadata'].ng_xml.clone.remove_namespaces!
|
65
|
-
if
|
66
|
-
dc_xml = generate_dublin_core.to_xml
|
66
|
+
if(rights.at_xpath("//rightsMetadata/access[@type='discover']/machine/world"))
|
67
|
+
dc_xml = self.generate_dublin_core.to_xml {|config| config.no_declaration}
|
67
68
|
DigitalStacksService.transfer_to_document_store(pid, dc_xml, 'dc')
|
68
|
-
DigitalStacksService.transfer_to_document_store(pid, datastreams['identityMetadata'].to_xml, 'identityMetadata')
|
69
|
-
DigitalStacksService.transfer_to_document_store(pid, datastreams['contentMetadata'].to_xml, 'contentMetadata')
|
70
|
-
DigitalStacksService.transfer_to_document_store(pid, datastreams['rightsMetadata'].to_xml, 'rightsMetadata')
|
69
|
+
DigitalStacksService.transfer_to_document_store(pid, self.datastreams['identityMetadata'].to_xml, 'identityMetadata')
|
70
|
+
DigitalStacksService.transfer_to_document_store(pid, self.datastreams['contentMetadata'].to_xml, 'contentMetadata')
|
71
|
+
DigitalStacksService.transfer_to_document_store(pid, self.datastreams['rightsMetadata'].to_xml, 'rightsMetadata')
|
71
72
|
DigitalStacksService.transfer_to_document_store(pid, public_xml, 'public')
|
72
|
-
|
73
|
+
if self.metadata_format == 'mods'
|
74
|
+
DigitalStacksService.transfer_to_document_store(pid, self.generate_public_desc_md, 'mods')
|
75
|
+
end
|
76
|
+
if iiif_presentation_manifest_needed? @public_xml_doc
|
77
|
+
DigitalStacksService.transfer_to_document_store(pid, build_iiif_manifest(@public_xml_doc), 'manifest')
|
78
|
+
end
|
73
79
|
else
|
74
80
|
# Clear out the document cache for this item
|
75
81
|
DigitalStacksService.prune_purl_dir pid
|
76
82
|
end
|
77
83
|
end
|
78
|
-
|
79
|
-
# Call dor services app to have it publish the metadata
|
84
|
+
#call the dor services app to have it publish the metadata
|
80
85
|
def publish_metadata_remotely
|
81
|
-
dor_services = RestClient::Resource.new(Config.dor_services.url
|
86
|
+
dor_services = RestClient::Resource.new(Config.dor_services.url+"/v1/objects/#{pid}/publish")
|
82
87
|
dor_services.post ''
|
83
88
|
dor_services.url
|
84
89
|
end
|
85
90
|
end
|
86
|
-
|
91
|
+
|
92
|
+
end
|
@@ -0,0 +1,310 @@
|
|
1
|
+
module Dor
|
2
|
+
module Releasable
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
include Itemizable
|
5
|
+
|
6
|
+
#Generate XML structure for inclusion to Purl
|
7
|
+
#
|
8
|
+
#@return [String] The XML release node as a string, with ReleaseDigest as the root document
|
9
|
+
def generate_release_xml
|
10
|
+
builder = Nokogiri::XML::Builder.new do |xml|
|
11
|
+
xml.ReleaseDigest {
|
12
|
+
self.released_for.each do |project,released_value|
|
13
|
+
xml.release(released_value["release"],:to=>project)
|
14
|
+
end
|
15
|
+
}
|
16
|
+
end
|
17
|
+
return builder.to_xml
|
18
|
+
end
|
19
|
+
|
20
|
+
#Determine which projects an item is released for
|
21
|
+
#
|
22
|
+
#@return [Hash] all namespaces in the form of {"Project" => Boolean}
|
23
|
+
def released_for
|
24
|
+
released_hash = {}
|
25
|
+
|
26
|
+
#Get release tags on the item itself
|
27
|
+
release_tags_on_this_item = self.release_nodes
|
28
|
+
|
29
|
+
#Get any self tags on this item
|
30
|
+
self_release_tags = self.get_self_release_tags(release_tags_on_this_item)
|
31
|
+
|
32
|
+
#Get the most recent self tag for all targets and save their result since most recent self always trumps any other non self tags
|
33
|
+
latest_self_tags = self.get_newest_release_tag(self_release_tags)
|
34
|
+
latest_self_tags.keys.each do |target|
|
35
|
+
released_hash[target] = self.clean_release_tag_for_purl(latest_self_tags[target])
|
36
|
+
end
|
37
|
+
|
38
|
+
#With Self Tags Resolved We Now need to deal with tags on all sets this object is part of
|
39
|
+
|
40
|
+
potential_applicable_release_tags = {} #This will be where we store all tags that apply, regardless of their timestamp
|
41
|
+
|
42
|
+
#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
|
43
|
+
potential_applicable_release_tags = get_tags_for_what_value(self.get_release_tags_for_item_and_all_governing_sets, 'collection')
|
44
|
+
|
45
|
+
administrative_tags = self.tags #Get them once here and pass them down
|
46
|
+
|
47
|
+
#We now have the keys for all potential releases, we need to check the tags and the most recent time stamp with an explicit true or false wins, in a nil case, the lack of an explicit false tag we do nothing
|
48
|
+
(potential_applicable_release_tags.keys-released_hash.keys).each do |key| #don't bother checking the ones already added to the release hash, they were added due to a self tag and that has won
|
49
|
+
latest_applicable_tag_for_key = latest_applicable_release_tag_in_array(potential_applicable_release_tags[key], administrative_tags)
|
50
|
+
if latest_applicable_tag_for_key != nil #We have a valid tag, record it
|
51
|
+
released_hash[key] = self.clean_release_tag_for_purl(latest_applicable_tag_for_key)
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
55
|
+
|
56
|
+
return released_hash
|
57
|
+
end
|
58
|
+
|
59
|
+
#Take a hash of tags as obtained via Dor::Item.release_tags and returns all self tags
|
60
|
+
#
|
61
|
+
#@param tags [Hash] a hash of tags obtained via Dor::Item.release_tags or matching format
|
62
|
+
#
|
63
|
+
#@return [Hash] a hash of self tags for each to value
|
64
|
+
def get_self_release_tags(tags)
|
65
|
+
return get_tags_for_what_value(tags, 'self')
|
66
|
+
end
|
67
|
+
|
68
|
+
#Take an item and get all of its release tags and all tags on collections it is a member of it
|
69
|
+
#
|
70
|
+
#
|
71
|
+
#@return [Hash] a hash of all tags
|
72
|
+
def get_release_tags_for_item_and_all_governing_sets
|
73
|
+
return_tags = self.release_nodes || {}
|
74
|
+
self.collections.each do |collection|
|
75
|
+
return_tags = combine_two_release_tag_hashes(return_tags, Dor::Item.find(collection.id).get_release_tags_for_item_and_all_governing_sets) #this will function recurvisely so parents of parents are found
|
76
|
+
end
|
77
|
+
return return_tags
|
78
|
+
end
|
79
|
+
|
80
|
+
#Take two hashes of tags and combine them, will not overwrite but will enforce uniqueness of the tags
|
81
|
+
#
|
82
|
+
#@param hash_one [Hash] a hash of tags obtained via Dor::Item.release_tags or matching format
|
83
|
+
#@param hash_two [Hash] a hash of tags obtained via Dor::Item.release_tags or matching format
|
84
|
+
#
|
85
|
+
#@return [Hash] the combined hash with uniquiness enforced
|
86
|
+
def combine_two_release_tag_hashes(hash_one, hash_two)
|
87
|
+
hash_two.keys.each do |key|
|
88
|
+
hash_one[key] = hash_two[key] if hash_one[key].nil?
|
89
|
+
hash_one[key] = (hash_one[key] + hash_two[key]).uniq if hash_one[key] != nil
|
90
|
+
end
|
91
|
+
return hash_one
|
92
|
+
end
|
93
|
+
|
94
|
+
#Take a hash of tags and return all tags with the matching what target
|
95
|
+
#
|
96
|
+
#@param tags [Hash] a hash of tags obtained via Dor::Item.release_tags or matching format
|
97
|
+
#@param what_target [String] the target for the 'what' key, self or collection
|
98
|
+
#
|
99
|
+
#@return [Hash] a hash of self tags for each to value
|
100
|
+
def get_tags_for_what_value(tags, what_target)
|
101
|
+
return_hash = {}
|
102
|
+
tags.keys.each do |key|
|
103
|
+
self_tags = tags[key].select{|tag| tag['what'] == what_target.downcase}
|
104
|
+
return_hash[key] = self_tags if self_tags.size > 0
|
105
|
+
end
|
106
|
+
return return_hash
|
107
|
+
end
|
108
|
+
|
109
|
+
#Take a hash of tags as obtained via Dor::Item.release_tags and returns the newest tag for each namespace
|
110
|
+
#
|
111
|
+
#@params tags [Hash] a hash of tags obtained via Dor::Item.release_tags or matching format
|
112
|
+
#
|
113
|
+
#@return [Hash] a hash of latest tags for each to value
|
114
|
+
def get_newest_release_tag(tags)
|
115
|
+
return_hash = {}
|
116
|
+
tags.keys.each do |key|
|
117
|
+
latest_for_key = newest_release_tag_in_an_array(tags[key])
|
118
|
+
return_hash[key] = latest_for_key
|
119
|
+
end
|
120
|
+
return return_hash
|
121
|
+
end
|
122
|
+
|
123
|
+
#Take a tag and return only the attributes we want to put into purl
|
124
|
+
#
|
125
|
+
#@param tag [Hash] a tag
|
126
|
+
#
|
127
|
+
#@return [Hash] a hash of the attributes we want for purl
|
128
|
+
def clean_release_tag_for_purl(tag)
|
129
|
+
for_purl = ['release']
|
130
|
+
return_hash = {}
|
131
|
+
for_purl.each do |attr|
|
132
|
+
return_hash[attr] = tag[attr]
|
133
|
+
end
|
134
|
+
return return_hash
|
135
|
+
end
|
136
|
+
|
137
|
+
#Takes an array of release tags and returns the most recent one
|
138
|
+
#
|
139
|
+
#@params tags [Array] an array of hashes, with the hashes being release tags
|
140
|
+
#
|
141
|
+
#@return [Hash] the most recent tag
|
142
|
+
def newest_release_tag_in_an_array(array_of_tags)
|
143
|
+
latest_tag_in_array = array_of_tags[0] || {}
|
144
|
+
array_of_tags.each do |tag|
|
145
|
+
latest_tag_in_array = tag if tag['when'] > latest_tag_in_array['when']
|
146
|
+
end
|
147
|
+
return latest_tag_in_array
|
148
|
+
end
|
149
|
+
|
150
|
+
#Takes a tag and returns true or false if it applies to the specific item
|
151
|
+
#
|
152
|
+
#@param release_tag [Hash] the tag in a hashed form
|
153
|
+
#@param Optional admin_tags [Array] the administrative tags on an item, if not supplied it will attempt to retrieve them
|
154
|
+
#
|
155
|
+
#@return [Boolean] true or false if it applies (not true or false if it is released, that is the release_tag data)
|
156
|
+
def does_release_tag_apply(release_tag, admin_tags=false)
|
157
|
+
#Is the tag global or restricted
|
158
|
+
return true if release_tag['tag'].nil? #there is no specific tag specificied, so that means this tag is global to all members of the collection, it applies, return true
|
159
|
+
|
160
|
+
admin_tags = self.tags unless admin_tags #We use false instead of [], since an item can have no admin_tags that which point we'd be passing down this variable as [] and would not an attempt to retrieve it
|
161
|
+
return admin_tags.include?(release_tag['tag'])
|
162
|
+
end
|
163
|
+
|
164
|
+
#Takes an array of release tags and returns the most recent one that applies to this item
|
165
|
+
#
|
166
|
+
#@param release_tags [Array] an array of release tags in hashed form
|
167
|
+
#param admin_tags [Array] the administrative tags on an on item
|
168
|
+
#
|
169
|
+
#@return [Hash] the tag
|
170
|
+
def latest_applicable_release_tag_in_array(release_tags, admin_tags)
|
171
|
+
newest_tag = newest_release_tag_in_an_array(release_tags)
|
172
|
+
return newest_tag if does_release_tag_apply(newest_tag, admin_tags) #Return true if we have it
|
173
|
+
|
174
|
+
#The latest tag wasn't applicable, slice it off and try again
|
175
|
+
#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
|
176
|
+
release_tags.slice!(release_tags.index(newest_tag))
|
177
|
+
|
178
|
+
return latest_applicable_release_tag_in_array(release_tags, admin_tags) if release_tags.size > 0 #Try again after dropping the one that wasn't applicable
|
179
|
+
|
180
|
+
return nil #We're out of tags, no applicable ones
|
181
|
+
end
|
182
|
+
|
183
|
+
#helper method to get the release tags as a nodeset
|
184
|
+
#
|
185
|
+
#@return [Nokogiri::XML::NodeSet] of all release tags and their attributes
|
186
|
+
def release_tags
|
187
|
+
release_tags = self.identityMetadata.ng_xml.xpath('//release')
|
188
|
+
return_hash = {}
|
189
|
+
release_tags.each do |release_tag|
|
190
|
+
hashed_node = self.release_tag_node_to_hash(release_tag)
|
191
|
+
if return_hash[hashed_node[:to]] != nil
|
192
|
+
return_hash[hashed_node[:to]] << hashed_node[:attrs]
|
193
|
+
else
|
194
|
+
return_hash[hashed_node[:to]] = [hashed_node[:attrs]]
|
195
|
+
end
|
196
|
+
end
|
197
|
+
return return_hash
|
198
|
+
end
|
199
|
+
|
200
|
+
#method to convert one release element into an array
|
201
|
+
#
|
202
|
+
#@param rtag [Nokogiri::XML::Element] the release tag element
|
203
|
+
#
|
204
|
+
#return [Hash] in the form of {:to => String :attrs = Hash}
|
205
|
+
def release_tag_node_to_hash(rtag)
|
206
|
+
to = 'to'
|
207
|
+
release = 'release'
|
208
|
+
when_word = 'when' #TODO: Make to and when_word load from some config file instead of hardcoded here
|
209
|
+
attrs = rtag.attributes
|
210
|
+
return_hash = { :to => attrs[to].value }
|
211
|
+
attrs.tap { |a| a.delete(to)}
|
212
|
+
attrs[release] = rtag.text.downcase == "true" #save release as a boolean
|
213
|
+
return_hash[:attrs] = attrs
|
214
|
+
|
215
|
+
#convert all the attrs beside :to to strings, they are currently Nokogiri::XML::Attr
|
216
|
+
(return_hash[:attrs].keys-[to]).each do |a|
|
217
|
+
return_hash[:attrs][a] = return_hash[:attrs][a].to_s if a != release
|
218
|
+
end
|
219
|
+
|
220
|
+
return_hash[:attrs][when_word] = Time.parse(return_hash[:attrs][when_word]) #convert when to a datetime
|
221
|
+
|
222
|
+
return return_hash
|
223
|
+
end
|
224
|
+
|
225
|
+
#Determine if the supplied tag is a valid release tag that meets all requirements
|
226
|
+
#
|
227
|
+
#@raises [RuntimeError] Raises an error of the first fault in the release tag
|
228
|
+
#
|
229
|
+
#@return [Boolean] Returns true if no errors found
|
230
|
+
#
|
231
|
+
#@params attrs [hash] A hash of attributes for the tag, must contain: :when, a ISO 8601 timestamp; :who, to identify who or what added the tag; and :to, a string identifying the release target
|
232
|
+
def valid_release_attributes_and_tag(tag, attrs={})
|
233
|
+
raise ArgumentError, ":when is not iso8601" if attrs[:when].match('\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z') == nil
|
234
|
+
[:who, :to, :what].each do |check_attr|
|
235
|
+
raise ArgumentError, "#{check_attr} not supplied as a String" if attrs[check_attr].class != String
|
236
|
+
end
|
237
|
+
|
238
|
+
what_correct = false
|
239
|
+
['self', 'collection'].each do |allowed_what_value|
|
240
|
+
what_correct = true if attrs[:what] == allowed_what_value
|
241
|
+
end
|
242
|
+
raise ArgumentError, ":what must be self or collection" if ! what_correct
|
243
|
+
raise ArgumentError, "the value set for this tag is not a boolean" if !!tag != tag
|
244
|
+
validate_tag_format(attrs[:tag]) if attrs[:tag] != nil #Will Raise exception if invalid tag
|
245
|
+
return true
|
246
|
+
end
|
247
|
+
|
248
|
+
#Add a release node for the item
|
249
|
+
#Will use the current time to add in the timestamp if you do not supply a timestamp, you can supply a timestap for correcting history, etc if desired
|
250
|
+
#
|
251
|
+
#@return [Nokogiri::XML::Element] the tag added if successful
|
252
|
+
#
|
253
|
+
#@raise [RuntimeError] Raised if attributes are improperly supplied
|
254
|
+
#
|
255
|
+
#@params tag [Boolean] True or false for the release node
|
256
|
+
#@params attrs [hash] A hash of any attributes to be placed onto the tag
|
257
|
+
# release tag example:
|
258
|
+
# item.add_tag(true,:release,{:tag=>'Fitch : Batch2',:what=>'self',:to=>'Searchworks',:who=>'petucket'})
|
259
|
+
def add_release_node(release, attrs={})
|
260
|
+
identity_metadata_ds = self.identityMetadata
|
261
|
+
attrs[:when] = Time.now.utc.iso8601 if attrs[:when] == nil#add the timestamp
|
262
|
+
valid_release_attributes(release, attrs)
|
263
|
+
|
264
|
+
return identity_metadata_ds.add_value(:release, release.to_s, attrs)
|
265
|
+
end
|
266
|
+
|
267
|
+
#Determine if the supplied tag is a valid release node that meets all requirements
|
268
|
+
#
|
269
|
+
#@raises [ArgumentError] Raises an error of the first fault in the release tag
|
270
|
+
#
|
271
|
+
#@return [Boolean] Returns true if no errors found
|
272
|
+
#
|
273
|
+
#@params attrs [hash] A hash of attributes for the tag, must contain :when, a ISO 8601 timestamp and :who to identify who or what added the tag, :to,
|
274
|
+
def valid_release_attributes(tag, attrs={})
|
275
|
+
raise ArgumentError, ":when is not iso8601" if attrs[:when].match('\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z') == nil
|
276
|
+
[:who, :to, :what].each do |check_attr|
|
277
|
+
raise ArgumentError, "#{check_attr} not supplied as a String" if attrs[check_attr].class != String
|
278
|
+
end
|
279
|
+
|
280
|
+
what_correct = false
|
281
|
+
['self', 'collection'].each do |allowed_what_value|
|
282
|
+
what_correct = true if attrs[:what] == allowed_what_value
|
283
|
+
end
|
284
|
+
raise ArgumentError, ":what must be self or collection" if ! what_correct
|
285
|
+
|
286
|
+
raise ArgumentError, "the value set for this tag is not a boolean" if !!tag != tag
|
287
|
+
#identity_metadata_ds = self.identityMetadata
|
288
|
+
validate_tag_format(attrs[:tag]) if attrs[:tag] != nil #Will Raise exception if invalid tag
|
289
|
+
return true
|
290
|
+
end
|
291
|
+
|
292
|
+
#helper method to get the release nodes as a nodeset
|
293
|
+
#
|
294
|
+
#@return [Nokogiri::XML::NodeSet] of all release tags and their attributes
|
295
|
+
def release_nodes
|
296
|
+
release_tags = self.identityMetadata.ng_xml.xpath('//release')
|
297
|
+
return_hash = {}
|
298
|
+
release_tags.each do |release_tag|
|
299
|
+
hashed_node = self.release_tag_node_to_hash(release_tag)
|
300
|
+
if return_hash[hashed_node[:to]] != nil
|
301
|
+
return_hash[hashed_node[:to]] << hashed_node[:attrs]
|
302
|
+
else
|
303
|
+
return_hash[hashed_node[:to]] = [hashed_node[:attrs]]
|
304
|
+
end
|
305
|
+
end
|
306
|
+
return return_hash
|
307
|
+
end
|
308
|
+
|
309
|
+
end
|
310
|
+
end
|