dor-services 5.2.0 → 5.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -13
- data/config/certs/robots-dor-dev.crt +29 -0
- data/config/certs/robots-dor-dev.key +27 -0
- data/config/config_defaults.yml +2 -0
- data/config/dev_console_env.rb +77 -0
- data/lib/dor-services.rb +31 -27
- data/lib/dor/config.rb +25 -19
- data/lib/dor/datastreams/administrative_metadata_ds.rb +19 -20
- data/lib/dor/datastreams/content_metadata_ds.rb +238 -177
- data/lib/dor/datastreams/datastream_spec_solrizer.rb +1 -1
- data/lib/dor/datastreams/default_object_rights_ds.rb +99 -16
- data/lib/dor/datastreams/desc_metadata_ds.rb +37 -34
- data/lib/dor/datastreams/embargo_metadata_ds.rb +16 -16
- data/lib/dor/datastreams/events_ds.rb +2 -2
- data/lib/dor/datastreams/geo_metadata_ds.rb +5 -10
- data/lib/dor/datastreams/identity_metadata_ds.rb +22 -22
- data/lib/dor/datastreams/rights_metadata_ds.rb +43 -32
- data/lib/dor/datastreams/role_metadata_ds.rb +5 -5
- data/lib/dor/datastreams/simple_dublin_core_ds.rb +13 -14
- data/lib/dor/datastreams/version_metadata_ds.rb +22 -23
- data/lib/dor/datastreams/workflow_definition_ds.rb +15 -15
- data/lib/dor/datastreams/workflow_ds.rb +64 -70
- data/lib/dor/exceptions.rb +0 -1
- data/lib/dor/migrations/identifiable/uriify_augmented_contentlocation_refs.rb +4 -4
- data/lib/dor/migrations/processable/unify_workflows.rb +1 -1
- data/lib/dor/models/admin_policy_object.rb +4 -4
- data/lib/dor/models/assembleable.rb +2 -3
- data/lib/dor/models/collection.rb +1 -1
- data/lib/dor/models/contentable.rb +113 -108
- data/lib/dor/models/describable.rb +136 -95
- data/lib/dor/models/editable.rb +205 -119
- data/lib/dor/models/embargoable.rb +16 -16
- data/lib/dor/models/eventable.rb +2 -2
- data/lib/dor/models/geoable.rb +3 -3
- data/lib/dor/models/governable.rb +25 -26
- data/lib/dor/models/identifiable.rb +66 -55
- data/lib/dor/models/item.rb +0 -1
- data/lib/dor/models/itemizable.rb +7 -8
- data/lib/dor/models/preservable.rb +7 -8
- data/lib/dor/models/processable.rb +76 -73
- data/lib/dor/models/publishable.rb +25 -30
- data/lib/dor/models/releaseable.rb +118 -155
- data/lib/dor/models/rightsable.rb +2 -3
- data/lib/dor/models/set.rb +1 -1
- data/lib/dor/models/shelvable.rb +8 -10
- data/lib/dor/models/upgradable.rb +5 -6
- data/lib/dor/models/versionable.rb +3 -4
- data/lib/dor/models/workflow_object.rb +15 -16
- data/lib/dor/services/cleanup_reset_service.rb +15 -16
- data/lib/dor/services/cleanup_service.rb +2 -4
- data/lib/dor/services/digital_stacks_service.rb +10 -13
- data/lib/dor/services/merge_service.rb +8 -9
- data/lib/dor/services/metadata_handlers/catalog_handler.rb +1 -1
- data/lib/dor/services/metadata_handlers/mdtoolkit_handler.rb +3 -3
- data/lib/dor/services/metadata_service.rb +19 -20
- data/lib/dor/services/registration_service.rb +80 -61
- data/lib/dor/services/reset_workspace_service.rb +6 -10
- data/lib/dor/services/sdr_ingest_service.rb +15 -16
- data/lib/dor/services/search_service.rb +18 -23
- data/lib/dor/services/suri_service.rb +6 -6
- data/lib/dor/services/technical_metadata_service.rb +27 -44
- data/lib/dor/utils/ng_tidy.rb +3 -3
- data/lib/dor/utils/sdr_client.rb +2 -3
- data/lib/dor/utils/solr_doc_helper.rb +1 -3
- data/lib/dor/version.rb +1 -1
- data/lib/dor/workflow/document.rb +43 -40
- data/lib/dor/workflow/graph.rb +26 -26
- data/lib/dor/workflow/process.rb +34 -35
- data/lib/tasks/rdoc.rake +5 -5
- metadata +129 -111
- data/lib/dor/models/presentable.rb +0 -146
@@ -5,8 +5,8 @@ module Dor
|
|
5
5
|
extend ActiveSupport::Concern
|
6
6
|
|
7
7
|
included do
|
8
|
-
has_metadata :name =>
|
9
|
-
has_metadata :name =>
|
8
|
+
has_metadata :name => 'provenanceMetadata', :type => ActiveFedora::OmDatastream, :label => 'Provenance Metadata'
|
9
|
+
has_metadata :name => 'technicalMetadata', :type => ActiveFedora::OmDatastream, :label => 'Technical Metadata', :control_group => 'M'
|
10
10
|
end
|
11
11
|
|
12
12
|
def build_provenanceMetadata_datastream(workflow_id, event_text)
|
@@ -15,24 +15,24 @@ module Dor
|
|
15
15
|
ds = datastreams[dsname]
|
16
16
|
ds.label = 'Provenance Metadata' unless datastreams.keys.include?(dsname)
|
17
17
|
ds.ng_xml = workflow_provenance
|
18
|
-
ds.content=ds.ng_xml.to_s
|
18
|
+
ds.content = ds.ng_xml.to_s
|
19
19
|
ds.save
|
20
20
|
end
|
21
21
|
|
22
|
-
def build_technicalMetadata_datastream(ds=nil)
|
22
|
+
def build_technicalMetadata_datastream(ds = nil)
|
23
23
|
TechnicalMetadataService.add_update_technical_metadata(self)
|
24
24
|
end
|
25
25
|
|
26
26
|
def sdr_ingest_transfer(agreement_id)
|
27
|
-
SdrIngestService.transfer(self,agreement_id)
|
27
|
+
SdrIngestService.transfer(self, agreement_id)
|
28
28
|
end
|
29
29
|
|
30
30
|
# @return [Nokogiri::Document]
|
31
31
|
def create_workflow_provenance(workflow_id, event_text)
|
32
32
|
builder = Nokogiri::XML::Builder.new do |xml|
|
33
|
-
xml.provenanceMetadata(:objectId =>
|
33
|
+
xml.provenanceMetadata(:objectId => pid) {
|
34
34
|
xml.agent(:name => 'DOR') {
|
35
|
-
xml.what(:object =>
|
35
|
+
xml.what(:object => pid) {
|
36
36
|
xml.event(:who => "DOR-#{workflow_id}", :when => Time.new.iso8601) {
|
37
37
|
xml.text(event_text)
|
38
38
|
}
|
@@ -42,6 +42,5 @@ module Dor
|
|
42
42
|
end
|
43
43
|
builder.doc
|
44
44
|
end
|
45
|
-
|
46
45
|
end
|
47
46
|
end
|
@@ -11,9 +11,9 @@ module Dor
|
|
11
11
|
after_initialize :set_workflows_datastream_location
|
12
12
|
end
|
13
13
|
|
14
|
-
#verbiage we want to use to describe an item when it has completed a particular step
|
14
|
+
# verbiage we want to use to describe an item when it has completed a particular step
|
15
15
|
STATUS_CODE_DISP_TXT = {
|
16
|
-
0 => 'Unknown Status', #if there are no milestones for the current version, someone likely messed up the versioning process.
|
16
|
+
0 => 'Unknown Status', # if there are no milestones for the current version, someone likely messed up the versioning process.
|
17
17
|
1 => 'Registered',
|
18
18
|
2 => 'In accessioning',
|
19
19
|
3 => 'In accessioning (described)',
|
@@ -25,54 +25,55 @@ module Dor
|
|
25
25
|
9 => 'Opened'
|
26
26
|
}
|
27
27
|
|
28
|
-
#milestones from accessioning and the order they happen in
|
28
|
+
# milestones from accessioning and the order they happen in
|
29
29
|
STEPS = {
|
30
|
-
'registered'
|
31
|
-
'submitted'
|
32
|
-
'described'
|
33
|
-
'published'
|
34
|
-
'deposited'
|
30
|
+
'registered' => 1,
|
31
|
+
'submitted' => 2,
|
32
|
+
'described' => 3,
|
33
|
+
'published' => 4,
|
34
|
+
'deposited' => 5,
|
35
35
|
'accessioned' => 6,
|
36
|
-
'indexed'
|
37
|
-
'shelved'
|
38
|
-
'opened'
|
36
|
+
'indexed' => 7,
|
37
|
+
'shelved' => 8,
|
38
|
+
'opened' => 9
|
39
39
|
}
|
40
40
|
|
41
41
|
# This is a work-around for some strange logic in ActiveFedora that
|
42
42
|
# don't allow self.workflows.new? to work if we load the object using
|
43
43
|
# .load_instance_from_solr.
|
44
44
|
def set_workflows_datastream_location
|
45
|
-
return if self.respond_to?(:inner_object) &&
|
46
|
-
return unless
|
45
|
+
return if self.respond_to?(:inner_object) && inner_object.is_a?(ActiveFedora::SolrDigitalObject)
|
46
|
+
return unless workflows.new?
|
47
47
|
workflows.mimeType = 'application/xml'
|
48
|
-
workflows.dsLocation = File.join(Dor::Config.workflow.url,"dor/objects/#{
|
48
|
+
workflows.dsLocation = File.join(Dor::Config.workflow.url, "dor/objects/#{pid}/workflows")
|
49
49
|
end
|
50
50
|
|
51
51
|
def empty_datastream?(datastream)
|
52
|
-
if datastream.new?
|
53
|
-
|
54
|
-
elsif datastream.class.respond_to?(:xml_template)
|
52
|
+
return true if datastream.new?
|
53
|
+
if datastream.class.respond_to?(:xml_template)
|
55
54
|
datastream.content.to_s.empty? || EquivalentXml.equivalent?(datastream.content, datastream.class.xml_template)
|
56
55
|
else
|
57
56
|
datastream.content.to_s.empty?
|
58
57
|
end
|
59
58
|
end
|
60
59
|
|
61
|
-
# Takes the name of a datastream, as a string.
|
62
60
|
# Tries to find a file for the datastream.
|
63
|
-
#
|
61
|
+
# @param [String] datastream name of a datastream
|
62
|
+
# @return [String, nil] path to datastream or nil
|
64
63
|
def find_metadata_file(datastream)
|
65
64
|
druid = DruidTools::Druid.new(pid, Dor::Config.stacks.local_workspace_root)
|
66
|
-
|
65
|
+
druid.find_metadata("#{datastream}.xml")
|
67
66
|
end
|
68
67
|
|
69
|
-
# Takes the name of a datastream, as a string (fooMetadata).
|
70
68
|
# Builds that datastream using the content of a file if such a file
|
71
69
|
# exists and is newer than the object's current datastream; otherwise,
|
72
70
|
# builds the datastream by calling build_fooMetadata_datastream.
|
71
|
+
# @param [String] datastream name of a datastream (e.g. "fooMetadata")
|
72
|
+
# @param [Boolean] force overwrite existing datastream
|
73
|
+
# @param [Boolean] is_required
|
74
|
+
# @return [SomeDatastream]
|
73
75
|
def build_datastream(datastream, force = false, is_required = false)
|
74
|
-
# See if
|
75
|
-
# timestamp is newer than the datastream's timestamp.
|
76
|
+
# See if datastream exists as a file and if the file's timestamp is newer than datastream's timestamp.
|
76
77
|
ds = datastreams[datastream]
|
77
78
|
filename = find_metadata_file(datastream)
|
78
79
|
use_file = filename && (ds.createDate.nil? || File.mtime(filename) >= ds.createDate)
|
@@ -85,15 +86,13 @@ module Dor
|
|
85
86
|
elsif force || empty_datastream?(ds)
|
86
87
|
meth = "build_#{datastream}_datastream".to_sym
|
87
88
|
if respond_to?(meth)
|
88
|
-
|
89
|
+
send(meth, ds)
|
89
90
|
ds.save unless ds.digital_object.new?
|
90
91
|
end
|
91
92
|
end
|
92
93
|
# Check for success.
|
93
|
-
if is_required && empty_datastream?(ds)
|
94
|
-
|
95
|
-
end
|
96
|
-
return ds
|
94
|
+
raise "Required datastream #{datastream} could not be populated!" if is_required && empty_datastream?(ds)
|
95
|
+
ds
|
97
96
|
end
|
98
97
|
|
99
98
|
def cleanup
|
@@ -101,20 +100,20 @@ module Dor
|
|
101
100
|
end
|
102
101
|
|
103
102
|
def milestones
|
104
|
-
Dor::WorkflowService.get_milestones('dor',
|
103
|
+
Dor::WorkflowService.get_milestones('dor', pid)
|
105
104
|
end
|
106
105
|
|
107
106
|
# @return [Hash] including :current_version, :status_code and :status_time
|
108
107
|
def status_info
|
109
108
|
current_version = '1'
|
110
109
|
begin
|
111
|
-
current_version =
|
110
|
+
current_version = versionMetadata.current_version_id
|
112
111
|
rescue
|
113
112
|
end
|
114
113
|
|
115
114
|
current_milestones = []
|
116
|
-
#only get steps that are part of accessioning and part of the current version. That can mean they were archived with the current version
|
117
|
-
#number, or they might be active (no version number).
|
115
|
+
# only get steps that are part of accessioning and part of the current version. That can mean they were archived with the current version
|
116
|
+
# number, or they might be active (no version number).
|
118
117
|
milestones.each do |m|
|
119
118
|
if STEPS.keys.include?(m[:milestone]) && (m[:version].nil? || m[:version] == current_version)
|
120
119
|
current_milestones << m unless m[:milestone] == 'registered' && current_version.to_i > 1
|
@@ -122,57 +121,57 @@ module Dor
|
|
122
121
|
end
|
123
122
|
|
124
123
|
status_code = 0
|
125
|
-
status_time =
|
126
|
-
#for each milestone in the current version, see if it comes after the current 'last' step, if so, make it the last and record the date/time
|
124
|
+
status_time = nil
|
125
|
+
# for each milestone in the current version, see if it comes after the current 'last' step, if so, make it the last and record the date/time
|
127
126
|
current_milestones.each do |m|
|
128
|
-
|
129
|
-
|
130
|
-
next unless STEPS.keys.include?(
|
131
|
-
status_code = STEPS[
|
132
|
-
status_time =
|
127
|
+
m_name = m[:milestone]
|
128
|
+
m_time = m[:at].utc.xmlschema
|
129
|
+
next unless STEPS.keys.include?(m_name) && (!status_time || m_time > status_time)
|
130
|
+
status_code = STEPS[m_name]
|
131
|
+
status_time = m_time
|
133
132
|
end
|
134
133
|
|
135
|
-
|
134
|
+
{:current_version => current_version, :status_code => status_code, :status_time => status_time}
|
136
135
|
end
|
137
136
|
|
138
137
|
# @param [Boolean] include_time
|
139
138
|
# @return [String] single composed status from status_info
|
140
|
-
def status(include_time=false)
|
141
|
-
status_info_hash = status_info
|
139
|
+
def status(include_time = false)
|
140
|
+
status_info_hash = status_info
|
142
141
|
current_version, status_code, status_time = status_info_hash[:current_version], status_info_hash[:status_code], status_info_hash[:status_time]
|
143
142
|
|
144
|
-
#use the translation table to get the appropriate verbage for the latest step
|
143
|
+
# use the translation table to get the appropriate verbage for the latest step
|
145
144
|
result = "v#{current_version} #{STATUS_CODE_DISP_TXT[status_code]}"
|
146
145
|
result += " #{format_date(status_time)}" if include_time
|
147
|
-
|
146
|
+
result
|
148
147
|
end
|
149
148
|
|
150
149
|
# return the text translation of the status code, minus any trailing parenthetical explanation
|
151
150
|
# e.g. 'In accessioning (described)' and 'In accessioning (described, published)' both come back
|
152
151
|
# as 'In accessioning'
|
153
152
|
def simplified_status_code_disp_txt(status_code)
|
154
|
-
|
153
|
+
STATUS_CODE_DISP_TXT[status_code].gsub(/\(.*\)$/, '').strip
|
155
154
|
end
|
156
155
|
|
157
|
-
def to_solr(solr_doc=
|
156
|
+
def to_solr(solr_doc = {}, *args)
|
158
157
|
super(solr_doc, *args)
|
159
158
|
sortable_milestones = {}
|
160
|
-
current_version='1'
|
159
|
+
current_version = '1'
|
161
160
|
begin
|
162
|
-
current_version =
|
161
|
+
current_version = versionMetadata.current_version_id
|
163
162
|
rescue
|
164
163
|
end
|
165
|
-
current_version_num=current_version.to_i
|
164
|
+
current_version_num = current_version.to_i
|
166
165
|
|
167
166
|
if self.respond_to?('versionMetadata')
|
168
|
-
#add an entry with version id, tag and description for each version
|
167
|
+
# add an entry with version id, tag and description for each version
|
169
168
|
while current_version_num > 0
|
170
|
-
add_solr_value(solr_doc, 'versions', current_version_num.to_s + ';' +
|
169
|
+
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])
|
171
170
|
current_version_num -= 1
|
172
171
|
end
|
173
172
|
end
|
174
173
|
|
175
|
-
|
174
|
+
milestones.each do |milestone|
|
176
175
|
timestamp = milestone[:at].utc.xmlschema
|
177
176
|
sortable_milestones[milestone[:milestone]] ||= []
|
178
177
|
sortable_milestones[milestone[:milestone]] << timestamp
|
@@ -184,26 +183,26 @@ module Dor
|
|
184
183
|
|
185
184
|
sortable_milestones.each do |milestone, unordered_dates|
|
186
185
|
dates = unordered_dates.sort
|
187
|
-
#create the published_dttsi and published_day fields and the like
|
186
|
+
# create the published_dttsi and published_day fields and the like
|
188
187
|
dates.each do |date|
|
189
188
|
solr_doc["#{milestone}_dttsim"] ||= []
|
190
189
|
solr_doc["#{milestone}_dttsim"] << date unless solr_doc["#{milestone}_dttsim"].include?(date)
|
191
190
|
end
|
192
|
-
#fields for OAI havester to sort on: _dttsi is trie date +stored +indexed (single valued, i.e. sortable)
|
191
|
+
# fields for OAI havester to sort on: _dttsi is trie date +stored +indexed (single valued, i.e. sortable)
|
193
192
|
solr_doc["#{milestone}_earliest_dttsi"] = dates.first
|
194
193
|
solr_doc["#{milestone}_latest_dttsi" ] = dates.last
|
195
194
|
end
|
196
|
-
solr_doc[
|
197
|
-
solr_doc[
|
198
|
-
solr_doc[
|
199
|
-
add_solr_value(solr_doc,
|
195
|
+
solr_doc['status_ssi'] = status # status is singular (i.e. the current one)
|
196
|
+
solr_doc['current_version_isi'] = current_version.to_i
|
197
|
+
solr_doc['modified_latest_dttsi'] = modified_date.to_datetime.utc.strftime('%FT%TZ')
|
198
|
+
add_solr_value(solr_doc, 'rights', rights, :string, [:symbol]) if self.respond_to? :rights
|
200
199
|
|
201
|
-
status_info_hash = status_info
|
200
|
+
status_info_hash = status_info
|
202
201
|
status_code = status_info_hash[:status_code]
|
203
202
|
add_solr_value(solr_doc, 'processing_status_text', simplified_status_code_disp_txt(status_code), :string, [:stored_sortable])
|
204
203
|
solr_doc['processing_status_code_isi'] = status_code # no _isi in Solrizer's default descriptors
|
205
204
|
|
206
|
-
|
205
|
+
solr_doc
|
207
206
|
end
|
208
207
|
|
209
208
|
# Initilizes workflow for the object in the workflow service
|
@@ -212,26 +211,30 @@ module Dor
|
|
212
211
|
# @param [String] name of the workflow to be initialized
|
213
212
|
# @param [Boolean] create_ds create a 'workflows' datastream in Fedora for the object
|
214
213
|
# @param [Integer] priority the workflow's priority level
|
215
|
-
def
|
214
|
+
def create_workflow(name, create_ds = true, priority = 0)
|
216
215
|
priority = workflows.current_priority if priority == 0
|
217
216
|
opts = { :create_ds => create_ds, :lane_id => default_workflow_lane }
|
218
217
|
opts[:priority] = priority if priority > 0
|
219
|
-
Dor::WorkflowService.create_workflow(Dor::WorkflowObject.initial_repo(name),
|
218
|
+
Dor::WorkflowService.create_workflow(Dor::WorkflowObject.initial_repo(name), pid, name, Dor::WorkflowObject.initial_workflow(name), opts)
|
219
|
+
workflows.content(true) # refresh the copy of the workflows datastream
|
220
220
|
end
|
221
221
|
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
222
|
+
def initialize_workflow(name, create_ds = true, priority = 0)
|
223
|
+
warn 'WARNING: initialize_workflow is deprecated, use create_workflow instead'
|
224
|
+
create_workflow(name, create_ds, priority)
|
225
|
+
end
|
226
226
|
|
227
|
-
|
228
|
-
DateTime.parse(datetime).in_time_zone(ActiveSupport::TimeZone.new("Pacific Time (US & Canada)"))
|
229
|
-
I18n.l(d).strftime('%Y-%m-%d %I:%M%p')
|
230
|
-
rescue
|
231
|
-
d = datetime.is_a?(Time) ? datetime : Time.parse(datetime.to_s)
|
232
|
-
d.strftime('%Y-%m-%d %I:%M%p')
|
227
|
+
private
|
233
228
|
|
229
|
+
# handles formating utc date/time to human readable
|
230
|
+
# XXX: bad form to hardcode TZ here. Code smell abounds.
|
231
|
+
def format_date(datetime)
|
232
|
+
d = datetime.is_a?(Time) ? datetime :
|
233
|
+
DateTime.parse(datetime).in_time_zone(ActiveSupport::TimeZone.new('Pacific Time (US & Canada)'))
|
234
|
+
I18n.l(d).strftime('%Y-%m-%d %I:%M%p')
|
235
|
+
rescue
|
236
|
+
d = datetime.is_a?(Time) ? datetime : Time.parse(datetime.to_s)
|
237
|
+
d.strftime('%Y-%m-%d %I:%M%p')
|
234
238
|
end
|
235
239
|
end
|
236
|
-
|
237
240
|
end
|
@@ -7,14 +7,13 @@ module Dor
|
|
7
7
|
include Governable
|
8
8
|
include Describable
|
9
9
|
include Itemizable
|
10
|
-
include Presentable
|
11
10
|
include Rightsable
|
12
11
|
|
13
12
|
def public_relationships
|
14
|
-
include_elements = ['fedora:isMemberOf','fedora:isMemberOfCollection']
|
15
|
-
rels_doc = Nokogiri::XML(
|
13
|
+
include_elements = ['fedora:isMemberOf', 'fedora:isMemberOfCollection', 'fedora:isConstituentOf']
|
14
|
+
rels_doc = Nokogiri::XML(datastreams['RELS-EXT'].content)
|
16
15
|
rels_doc.xpath('/rdf:RDF/rdf:Description/*', { 'rdf' => 'http://www.w3.org/1999/02/22-rdf-syntax-ns#' }).each do |rel|
|
17
|
-
unless include_elements.include?([rel.namespace.prefix,rel.name].join(':'))
|
16
|
+
unless include_elements.include?([rel.namespace.prefix, rel.name].join(':'))
|
18
17
|
rel.next_sibling.remove if rel.next_sibling.content.strip.empty?
|
19
18
|
rel.remove
|
20
19
|
end
|
@@ -22,29 +21,30 @@ module Dor
|
|
22
21
|
rels_doc
|
23
22
|
end
|
24
23
|
|
25
|
-
#Generate the public .xml for a PURL page.
|
26
|
-
|
27
|
-
#
|
24
|
+
# Generate the public .xml for a PURL page.
|
25
|
+
# @return [xml] The public xml for the item
|
28
26
|
def public_xml
|
29
|
-
pub = Nokogiri::XML(
|
27
|
+
pub = Nokogiri::XML('<publicObject/>').root
|
30
28
|
pub['id'] = pid
|
31
|
-
pub['published'] = Time.now.xmlschema
|
32
|
-
|
29
|
+
pub['published'] = Time.now.utc.xmlschema
|
30
|
+
pub['publishVersion'] = 'dor-services/' + Dor::VERSION
|
31
|
+
release_xml = Nokogiri(generate_release_xml).xpath('//release')
|
33
32
|
|
34
|
-
im=
|
35
|
-
im.search('//release').each
|
33
|
+
im = datastreams['identityMetadata'].ng_xml.clone
|
34
|
+
im.search('//release').each(&:remove) # remove any <release> tags from public xml which have full history
|
36
35
|
im.root.add_child(release_xml)
|
37
36
|
|
38
37
|
pub.add_child(im.root) # add in modified identityMetadata datastream
|
39
|
-
pub.add_child(
|
40
|
-
pub.add_child(
|
38
|
+
pub.add_child(datastreams['contentMetadata'].public_xml.root.clone)
|
39
|
+
pub.add_child(datastreams['rightsMetadata'].ng_xml.root.clone)
|
41
40
|
|
42
41
|
rels = public_relationships.root
|
43
42
|
pub.add_child(rels.clone) unless rels.nil? # TODO: Should never be nil in practice; working around an ActiveFedora quirk for testing
|
44
|
-
pub.add_child(
|
43
|
+
pub.add_child(generate_dublin_core.root.clone)
|
45
44
|
@public_xml_doc = pub # save this for possible IIIF Presentation manifest
|
46
|
-
pub.add_child(Nokogiri(
|
47
|
-
#Note we cannot base this on if an individual object has release tags or not, because the collection may cause one to be generated for an item,
|
45
|
+
pub.add_child(Nokogiri(generate_release_xml).root.clone) unless release_xml.children.size == 0 # If there are no release_tags, this prevents an empty <releaseData/> from being added
|
46
|
+
# Note we cannot base this on if an individual object has release tags or not, because the collection may cause one to be generated for an item,
|
47
|
+
# so we need to calculate it and then look at the final result.s
|
48
48
|
new_pub = Nokogiri::XML(pub.to_xml) { |x| x.noblanks }
|
49
49
|
new_pub.encoding = 'UTF-8'
|
50
50
|
new_pub.to_xml
|
@@ -55,29 +55,24 @@ module Dor
|
|
55
55
|
def publish_metadata
|
56
56
|
rights = datastreams['rightsMetadata'].ng_xml.clone.remove_namespaces!
|
57
57
|
if rights.at_xpath("//rightsMetadata/access[@type='discover']/machine/world")
|
58
|
-
dc_xml =
|
58
|
+
dc_xml = generate_dublin_core.to_xml {|config| config.no_declaration}
|
59
59
|
DigitalStacksService.transfer_to_document_store(pid, dc_xml, 'dc')
|
60
|
-
|
61
|
-
|
62
|
-
DigitalStacksService.transfer_to_document_store(pid, self.datastreams['rightsMetadata'].to_xml, 'rightsMetadata')
|
63
|
-
DigitalStacksService.transfer_to_document_store(pid, public_xml, 'public')
|
64
|
-
if self.metadata_format == 'mods'
|
65
|
-
DigitalStacksService.transfer_to_document_store(pid, self.generate_public_desc_md, 'mods')
|
66
|
-
end
|
67
|
-
if iiif_presentation_manifest_needed? @public_xml_doc
|
68
|
-
DigitalStacksService.transfer_to_document_store(pid, build_iiif_manifest(@public_xml_doc), 'manifest')
|
60
|
+
%w(identityMetadata contentMetadata rightsMetadata).each do |stream|
|
61
|
+
DigitalStacksService.transfer_to_document_store(pid, datastreams[stream].to_xml, stream)
|
69
62
|
end
|
63
|
+
DigitalStacksService.transfer_to_document_store(pid, public_xml, 'public')
|
64
|
+
DigitalStacksService.transfer_to_document_store(pid, generate_public_desc_md, 'mods') if metadata_format == 'mods'
|
70
65
|
else
|
71
66
|
# Clear out the document cache for this item
|
72
67
|
DigitalStacksService.prune_purl_dir pid
|
73
68
|
end
|
74
69
|
end
|
75
|
-
|
70
|
+
|
71
|
+
# Call dor services app to have it publish the metadata
|
76
72
|
def publish_metadata_remotely
|
77
|
-
dor_services = RestClient::Resource.new(Config.dor_services.url+"/v1/objects/#{pid}/publish")
|
73
|
+
dor_services = RestClient::Resource.new(Config.dor_services.url + "/v1/objects/#{pid}/publish")
|
78
74
|
dor_services.post ''
|
79
75
|
dor_services.url
|
80
76
|
end
|
81
77
|
end
|
82
|
-
|
83
78
|
end
|
@@ -11,18 +11,16 @@ module Dor
|
|
11
11
|
# @param release_tags [Hash, Array<Hash>] hash of a single release tag or an array of many such hashes
|
12
12
|
# @raise [ArgumentError] Raised if the tags are improperly supplied
|
13
13
|
def add_release_nodes_and_start_releaseWF(release_tags)
|
14
|
-
release_tags = [release_tags] unless release_tags.
|
14
|
+
release_tags = [release_tags] unless release_tags.is_a?(Array)
|
15
15
|
|
16
|
-
#Add in each tag
|
16
|
+
# Add in each tag
|
17
17
|
release_tags.each do |r_tag|
|
18
|
-
|
18
|
+
add_release_node(r_tag[:release], r_tag)
|
19
19
|
end
|
20
20
|
|
21
|
-
#Save
|
22
|
-
|
23
|
-
|
24
|
-
#Intialize the release workflow
|
25
|
-
self.initialize_workflow('releaseWF')
|
21
|
+
# Save item to dor so the robots work with the latest data
|
22
|
+
save
|
23
|
+
initialize_workflow('releaseWF')
|
26
24
|
end
|
27
25
|
|
28
26
|
# Generate XML structure for inclusion to Purl
|
@@ -30,70 +28,60 @@ module Dor
|
|
30
28
|
def generate_release_xml
|
31
29
|
builder = Nokogiri::XML::Builder.new do |xml|
|
32
30
|
xml.releaseData {
|
33
|
-
|
34
|
-
xml.release(released_value[
|
31
|
+
released_for.each do |project, released_value|
|
32
|
+
xml.release(released_value['release'], :to => project)
|
35
33
|
end
|
36
34
|
}
|
37
|
-
|
38
|
-
|
35
|
+
end
|
36
|
+
builder.to_xml
|
39
37
|
end
|
40
38
|
|
41
|
-
# Determine which
|
39
|
+
# Determine projects in which an item is released
|
40
|
+
# @param [Boolean] skip_live_purl set true to skip requesting from purl backend
|
42
41
|
# @return [Hash{String => Boolean}] all namespaces, keys are Project name Strings, values are Boolean
|
43
|
-
def released_for
|
42
|
+
def released_for(skip_live_purl = false)
|
44
43
|
released_hash = {}
|
45
44
|
|
46
|
-
#Get
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
self_release_tags = self.get_self_release_tags(release_tags_on_this_item)
|
51
|
-
|
52
|
-
#Get the most recent self tag for all targets and save their result since most recent self always trumps any other non self tags
|
53
|
-
latest_self_tags = self.get_newest_release_tag(self_release_tags)
|
54
|
-
latest_self_tags.keys.each do |target|
|
55
|
-
released_hash[target] = self.clean_release_tag_for_purl(latest_self_tags[target])
|
45
|
+
# Get the most recent self tag for all targets and retain their result since most recent self always trumps any other non self tags
|
46
|
+
latest_self_tags = get_newest_release_tag get_self_release_tags(release_nodes)
|
47
|
+
latest_self_tags.each do |key, payload|
|
48
|
+
released_hash[key] = {'release' => payload['release']}
|
56
49
|
end
|
57
50
|
|
58
|
-
#With Self Tags
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
#Get
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
unless latest_applicable_tag_for_key.nil? #We have a valid tag, record it
|
71
|
-
released_hash[key] = self.clean_release_tag_for_purl(latest_applicable_tag_for_key)
|
72
|
-
end
|
73
|
-
|
51
|
+
# With Self Tags resolved we now need to deal with tags on all sets this object is part of.
|
52
|
+
# 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.
|
53
|
+
# This will be where we store all tags that apply, regardless of their timestamp:
|
54
|
+
potential_applicable_release_tags = get_tags_for_what_value(get_release_tags_for_item_and_all_governing_sets, 'collection')
|
55
|
+
administrative_tags = tags # Get admin tags once here and pass them down
|
56
|
+
|
57
|
+
# 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.
|
58
|
+
# In a nil case, the lack of an explicit false tag we do nothing.
|
59
|
+
(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
|
60
|
+
latest_tag = latest_applicable_release_tag_in_array(potential_applicable_release_tags[key], administrative_tags)
|
61
|
+
next if latest_tag.nil? # Otherwise, we have a valid tag, record it
|
62
|
+
released_hash[key] = {'release' => latest_tag['release']}
|
74
63
|
end
|
75
64
|
|
76
|
-
#See what the application is currently released for on Purl. If
|
77
|
-
|
78
|
-
|
79
|
-
return released_hash
|
65
|
+
# 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
|
66
|
+
add_tags_from_purl(released_hash) unless skip_live_purl
|
67
|
+
released_hash
|
80
68
|
end
|
81
69
|
|
82
70
|
# Take a hash of tags as obtained via Dor::Item.release_tags and returns all self tags
|
83
71
|
# @param tags [Hash] a hash of tags obtained via Dor::Item.release_tags or matching format
|
84
72
|
# @return [Hash] a hash of self tags for each to value
|
85
73
|
def get_self_release_tags(tags)
|
86
|
-
|
74
|
+
get_tags_for_what_value(tags, 'self')
|
87
75
|
end
|
88
76
|
|
89
77
|
# Take an item and get all of its release tags and all tags on collections it is a member of it
|
90
78
|
# @return [Hash] a hash of all tags
|
91
79
|
def get_release_tags_for_item_and_all_governing_sets
|
92
|
-
return_tags =
|
93
|
-
|
94
|
-
return_tags = combine_two_release_tag_hashes(return_tags, Dor::Item.find(collection.id).get_release_tags_for_item_and_all_governing_sets) #
|
80
|
+
return_tags = release_nodes || {}
|
81
|
+
collections.each do |collection|
|
82
|
+
return_tags = combine_two_release_tag_hashes(return_tags, Dor::Item.find(collection.id).get_release_tags_for_item_and_all_governing_sets) # recurvise so parents of parents are found
|
95
83
|
end
|
96
|
-
|
84
|
+
return_tags
|
97
85
|
end
|
98
86
|
|
99
87
|
# Take two hashes of tags and combine them, will not overwrite but will enforce uniqueness of the tags
|
@@ -105,7 +93,7 @@ module Dor
|
|
105
93
|
hash_one[key] = hash_two[key] if hash_one[key].nil?
|
106
94
|
hash_one[key] = (hash_one[key] + hash_two[key]).uniq unless hash_one[key].nil?
|
107
95
|
end
|
108
|
-
|
96
|
+
hash_one
|
109
97
|
end
|
110
98
|
|
111
99
|
# Take a hash of tags and return all tags with the matching what target
|
@@ -115,34 +103,17 @@ module Dor
|
|
115
103
|
def get_tags_for_what_value(tags, what_target)
|
116
104
|
return_hash = {}
|
117
105
|
tags.keys.each do |key|
|
118
|
-
self_tags =
|
106
|
+
self_tags = tags[key].select {|tag| tag['what'] == what_target.downcase}
|
119
107
|
return_hash[key] = self_tags if self_tags.size > 0
|
120
108
|
end
|
121
|
-
|
109
|
+
return_hash
|
122
110
|
end
|
123
111
|
|
124
112
|
# Take a hash of tags as obtained via Dor::Item.release_tags and returns the newest tag for each namespace
|
125
113
|
# @param tags [Hash] a hash of tags obtained via Dor::Item.release_tags or matching format
|
126
114
|
# @return [Hash] a hash of latest tags for each to value
|
127
115
|
def get_newest_release_tag(tags)
|
128
|
-
|
129
|
-
tags.keys.each do |key|
|
130
|
-
latest_for_key = newest_release_tag_in_an_array(tags[key])
|
131
|
-
return_hash[key] = latest_for_key
|
132
|
-
end
|
133
|
-
return return_hash
|
134
|
-
end
|
135
|
-
|
136
|
-
# Take a tag and return only the attributes we want to put into purl
|
137
|
-
# @param tag [Hash] a tag
|
138
|
-
# @return [Hash] a hash of the attributes we want for purl
|
139
|
-
def clean_release_tag_for_purl(tag)
|
140
|
-
for_purl = ['release']
|
141
|
-
return_hash = {}
|
142
|
-
for_purl.each do |attr|
|
143
|
-
return_hash[attr] = tag[attr]
|
144
|
-
end
|
145
|
-
return return_hash
|
116
|
+
Hash[tags.map {|key, val| [key, newest_release_tag_in_an_array(val)]}]
|
146
117
|
end
|
147
118
|
|
148
119
|
# Takes an array of release tags and returns the most recent one
|
@@ -153,51 +124,50 @@ module Dor
|
|
153
124
|
array_of_tags.each do |tag|
|
154
125
|
latest_tag_in_array = tag if tag['when'] > latest_tag_in_array['when']
|
155
126
|
end
|
156
|
-
|
127
|
+
latest_tag_in_array
|
157
128
|
end
|
158
129
|
|
159
130
|
# Takes a tag and returns true or false if it applies to the specific item
|
160
131
|
# @param release_tag [Hash] the tag in a hashed form
|
161
132
|
# @param admin_tags [Array] the administrative tags on an item, if not supplied it will attempt to retrieve them
|
162
133
|
# @return [Boolean] true or false if it applies (not true or false if it is released, that is the release_tag data)
|
163
|
-
def does_release_tag_apply(release_tag, admin_tags=false)
|
164
|
-
#Is the tag global or restricted
|
165
|
-
return true if release_tag['tag'].nil? #
|
166
|
-
|
167
|
-
admin_tags
|
168
|
-
return admin_tags.include?(release_tag['tag'])
|
134
|
+
def does_release_tag_apply(release_tag, admin_tags = false)
|
135
|
+
# Is the tag global or restricted
|
136
|
+
return true if release_tag['tag'].nil? # no specific tag specificied means this tag is global to all members of the collection
|
137
|
+
admin_tags = tags unless admin_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
|
138
|
+
admin_tags.include?(release_tag['tag'])
|
169
139
|
end
|
170
140
|
|
171
141
|
# Takes an array of release tags and returns the most recent one that applies to this item
|
172
142
|
# @param release_tags [Array] an array of release tags in hashed form
|
173
143
|
# @param admin_tags [Array] the administrative tags on an on item
|
174
|
-
# @return [Hash] the tag
|
144
|
+
# @return [Hash] the tag, or nil if none applicable
|
175
145
|
def latest_applicable_release_tag_in_array(release_tags, admin_tags)
|
176
146
|
newest_tag = newest_release_tag_in_an_array(release_tags)
|
177
|
-
return newest_tag if does_release_tag_apply(newest_tag, admin_tags)
|
147
|
+
return newest_tag if does_release_tag_apply(newest_tag, admin_tags)
|
178
148
|
|
179
|
-
#The latest tag wasn't applicable, slice it off and try again
|
180
|
-
#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
|
149
|
+
# The latest tag wasn't applicable, slice it off and try again
|
150
|
+
# 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
|
181
151
|
release_tags.slice!(release_tags.index(newest_tag))
|
182
152
|
|
183
|
-
return latest_applicable_release_tag_in_array(release_tags, admin_tags) if release_tags.size > 0 #Try again after dropping the
|
184
|
-
|
153
|
+
return latest_applicable_release_tag_in_array(release_tags, admin_tags) if release_tags.size > 0 # Try again after dropping the inapplicable
|
154
|
+
nil # We're out of tags, no applicable ones
|
185
155
|
end
|
186
156
|
|
187
157
|
# Helper method to get the release tags as a nodeset
|
188
158
|
# @return [Nokogiri::XML::NodeSet] all release tags and their attributes
|
189
159
|
def release_tags
|
190
|
-
release_tags =
|
160
|
+
release_tags = identityMetadata.ng_xml.xpath('//release')
|
191
161
|
return_hash = {}
|
192
162
|
release_tags.each do |release_tag|
|
193
|
-
hashed_node =
|
163
|
+
hashed_node = release_tag_node_to_hash(release_tag)
|
194
164
|
if !return_hash[hashed_node[:to]].nil?
|
195
165
|
return_hash[hashed_node[:to]] << hashed_node[:attrs]
|
196
166
|
else
|
197
167
|
return_hash[hashed_node[:to]] = [hashed_node[:attrs]]
|
198
168
|
end
|
199
169
|
end
|
200
|
-
|
170
|
+
return_hash
|
201
171
|
end
|
202
172
|
|
203
173
|
# Convert one release element into a Hash
|
@@ -206,21 +176,20 @@ module Dor
|
|
206
176
|
def release_tag_node_to_hash(rtag)
|
207
177
|
to = 'to'
|
208
178
|
release = 'release'
|
209
|
-
when_word = 'when' #TODO: Make to and when_word load from some config file instead of hardcoded here
|
179
|
+
when_word = 'when' # TODO: Make to and when_word load from some config file instead of hardcoded here
|
210
180
|
attrs = rtag.attributes
|
211
181
|
return_hash = { :to => attrs[to].value }
|
212
|
-
attrs.tap { |a| a.delete(to)}
|
213
|
-
attrs[release] = rtag.text.downcase ==
|
182
|
+
attrs.tap { |a| a.delete(to) }
|
183
|
+
attrs[release] = rtag.text.downcase == 'true' # save release as a boolean
|
214
184
|
return_hash[:attrs] = attrs
|
215
185
|
|
216
|
-
#convert all the attrs beside :to to strings, they are currently Nokogiri::XML::Attr
|
217
|
-
(return_hash[:attrs].keys-[to]).each do |a|
|
218
|
-
return_hash[:attrs][a] =
|
186
|
+
# convert all the attrs beside :to to strings, they are currently Nokogiri::XML::Attr
|
187
|
+
(return_hash[:attrs].keys - [to]).each do |a|
|
188
|
+
return_hash[:attrs][a] = return_hash[:attrs][a].to_s if a != release
|
219
189
|
end
|
220
190
|
|
221
|
-
return_hash[:attrs][when_word] = Time.parse(return_hash[:attrs][when_word]) #convert when to a datetime
|
222
|
-
|
223
|
-
return return_hash
|
191
|
+
return_hash[:attrs][when_word] = Time.parse(return_hash[:attrs][when_word]) # convert when to a datetime
|
192
|
+
return_hash
|
224
193
|
end
|
225
194
|
|
226
195
|
# Determine if the supplied tag is a valid release tag that meets all requirements
|
@@ -228,8 +197,8 @@ module Dor
|
|
228
197
|
# @param 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
|
229
198
|
# @raise [RuntimeError] Raises an error of the first fault in the release tag
|
230
199
|
# @return [Boolean] Returns true if no errors found
|
231
|
-
def valid_release_attributes_and_tag(tag, attrs={})
|
232
|
-
raise ArgumentError,
|
200
|
+
def valid_release_attributes_and_tag(tag, attrs = {})
|
201
|
+
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?
|
233
202
|
[:who, :to, :what].each do |check_attr|
|
234
203
|
raise ArgumentError, "#{check_attr} not supplied as a String" if attrs[check_attr].class != String
|
235
204
|
end
|
@@ -238,10 +207,10 @@ module Dor
|
|
238
207
|
%w(self collection).each do |allowed_what_value|
|
239
208
|
what_correct = true if attrs[:what] == allowed_what_value
|
240
209
|
end
|
241
|
-
raise ArgumentError,
|
242
|
-
raise ArgumentError,
|
243
|
-
validate_tag_format(attrs[:tag]) unless attrs[:tag].nil? #Will Raise exception if invalid tag
|
244
|
-
|
210
|
+
raise ArgumentError, ':what must be self or collection' unless what_correct
|
211
|
+
raise ArgumentError, 'the value set for this tag is not a boolean' if !!tag != tag # rubocop:disable Style/DoubleNegation
|
212
|
+
validate_tag_format(attrs[:tag]) unless attrs[:tag].nil? # Will Raise exception if invalid tag
|
213
|
+
true
|
245
214
|
end
|
246
215
|
|
247
216
|
# Add a release node for the item
|
@@ -255,17 +224,16 @@ module Dor
|
|
255
224
|
#
|
256
225
|
# @example
|
257
226
|
# item.add_tag(true,:release,{:tag=>'Fitch : Batch2',:what=>'self',:to=>'Searchworks',:who=>'petucket', :displayType='filmstrip'})
|
258
|
-
def add_release_node(release, attrs={})
|
259
|
-
identity_metadata_ds =
|
260
|
-
attrs[:when] = Time.now.utc.iso8601 if attrs[:when].nil? #add the timestamp
|
261
|
-
attrs[:displayType] = 'file' if attrs[:displayType].nil? #default to file is no display type is passed
|
227
|
+
def add_release_node(release, attrs = {})
|
228
|
+
identity_metadata_ds = identityMetadata
|
229
|
+
attrs[:when] = Time.now.utc.iso8601 if attrs[:when].nil? # add the timestamp
|
230
|
+
attrs[:displayType] = 'file' if attrs[:displayType].nil? # default to file is no display type is passed
|
262
231
|
valid_release_attributes(release, attrs)
|
263
232
|
|
264
|
-
#Remove the old displayType and then add the one for this tag
|
233
|
+
# Remove the old displayType and then add the one for this tag
|
265
234
|
remove_displayTypes
|
266
235
|
identity_metadata_ds.add_value(:displayType, attrs[:displayType], {})
|
267
|
-
|
268
|
-
return identity_metadata_ds.add_value(:release, release.to_s, attrs)
|
236
|
+
identity_metadata_ds.add_value(:release, release.to_s, attrs)
|
269
237
|
end
|
270
238
|
|
271
239
|
# Determine if the supplied tag is a valid release node that meets all requirements
|
@@ -274,8 +242,8 @@ module Dor
|
|
274
242
|
# @param 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,
|
275
243
|
# @raise [ArgumentError] Raises an error of the first fault in the release tag
|
276
244
|
# @return [Boolean] Returns true if no errors found
|
277
|
-
def valid_release_attributes(tag, attrs={})
|
278
|
-
raise ArgumentError,
|
245
|
+
def valid_release_attributes(tag, attrs = {})
|
246
|
+
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?
|
279
247
|
[:who, :to, :what].each do |check_attr|
|
280
248
|
raise ArgumentError, "#{check_attr} not supplied as a String" if attrs[check_attr].class != String
|
281
249
|
end
|
@@ -284,28 +252,28 @@ module Dor
|
|
284
252
|
%w(self collection).each do |allowed_what_value|
|
285
253
|
what_correct = true if attrs[:what] == allowed_what_value
|
286
254
|
end
|
287
|
-
raise ArgumentError,
|
288
|
-
raise ArgumentError,
|
289
|
-
raise ArgumentError,
|
255
|
+
raise ArgumentError, ':what must be self or collection' unless what_correct
|
256
|
+
raise ArgumentError, 'the value set for this tag is not a boolean' if !!tag != tag # rubocop:disable Style/DoubleNegation
|
257
|
+
raise ArgumentError, ':displayType must be passed in as a String' unless attrs[:displayType].class == String
|
290
258
|
|
291
|
-
validate_tag_format(attrs[:tag]) unless attrs[:tag].nil? #Will Raise exception if invalid tag
|
292
|
-
|
259
|
+
validate_tag_format(attrs[:tag]) unless attrs[:tag].nil? # Will Raise exception if invalid tag
|
260
|
+
true
|
293
261
|
end
|
294
262
|
|
295
263
|
# Helper method to get the release nodes as a nodeset
|
296
264
|
# @return [Nokogiri::XML::NodeSet] of all release tags and their attributes
|
297
265
|
def release_nodes
|
298
|
-
release_tags =
|
266
|
+
release_tags = identityMetadata.ng_xml.xpath('//release')
|
299
267
|
return_hash = {}
|
300
268
|
release_tags.each do |release_tag|
|
301
|
-
hashed_node =
|
269
|
+
hashed_node = release_tag_node_to_hash(release_tag)
|
302
270
|
if !return_hash[hashed_node[:to]].nil?
|
303
271
|
return_hash[hashed_node[:to]] << hashed_node[:attrs]
|
304
272
|
else
|
305
|
-
|
273
|
+
return_hash[hashed_node[:to]] = [hashed_node[:attrs]]
|
306
274
|
end
|
307
275
|
end
|
308
|
-
|
276
|
+
return_hash
|
309
277
|
end
|
310
278
|
|
311
279
|
# Get a list of all release nodes found in a purl document
|
@@ -313,81 +281,76 @@ module Dor
|
|
313
281
|
# @raise [OpenURI::HTTPError]
|
314
282
|
# @return [Nokogiri::HTML::Document] parsed XML for the druid or an empty document if no purl is found
|
315
283
|
def get_xml_from_purl
|
316
|
-
|
317
|
-
|
318
|
-
#
|
319
|
-
|
284
|
+
url = form_purl_url
|
285
|
+
handler = proc do |exception, attempt_number, total_delay|
|
286
|
+
# We assume a 404 means the document has never been published before and thus has no purl
|
287
|
+
Dor.logger.warn "[Attempt #{attempt_number}] GET #{url} -- #{exception.class}: #{exception.message}; #{total_delay} seconds elapsed."
|
288
|
+
raise exception unless exception.is_a? OpenURI::HTTPError
|
289
|
+
return Nokogiri::HTML::Document.new if exception.message.strip == '404' # strip is needed if the actual message is "404 "
|
320
290
|
end
|
321
291
|
|
322
|
-
with_retries(:max_retries =>
|
323
|
-
|
324
|
-
|
325
|
-
|
292
|
+
with_retries(:max_retries => 3, :base_sleep_seconds => 3, :max_sleep_seconds => 5, :handler => handler) do |attempt|
|
293
|
+
# If you change the method used for opening the webpage, you can change the :rescue param to handle the new method's errors
|
294
|
+
Dor.logger.info "[Attempt #{attempt}] GET #{url}"
|
295
|
+
return Nokogiri::HTML(OpenURI.open_uri(url))
|
296
|
+
end
|
326
297
|
end
|
327
298
|
|
328
299
|
# Since purl does not use the druid: prefix but much of dor does, use this function to strip the druid: if needed
|
329
300
|
# @return [String] the druid sans the druid: or if there was no druid: prefix, the entire string you passed
|
330
301
|
def remove_druid_prefix
|
331
|
-
druid_prefix =
|
332
|
-
return
|
333
|
-
|
302
|
+
druid_prefix = 'druid:'
|
303
|
+
return id.split(druid_prefix)[1] if id.split(druid_prefix).size > 1
|
304
|
+
druid
|
334
305
|
end
|
335
306
|
|
336
307
|
# Take the and create the entire purl url that will usable for the open method in open-uri, returns http
|
337
|
-
# @return [String]
|
308
|
+
# @return [String] the full url
|
338
309
|
def form_purl_url
|
339
|
-
|
340
|
-
return prefix + Dor::Config.stacks.document_cache_host + "/#{self.remove_druid_prefix}.xml"
|
310
|
+
'https://' + Dor::Config.stacks.document_cache_host + "/#{remove_druid_prefix}.xml"
|
341
311
|
end
|
342
312
|
|
343
313
|
# Pull all release nodes from the public xml obtained via the purl query
|
344
314
|
# @param doc [Nokogiri::HTML::Document] The druid of the object you want
|
345
315
|
# @return [Array] An array containing all the release tags
|
346
316
|
def get_release_tags_from_purl_xml(doc)
|
347
|
-
nodes = doc.xpath(
|
348
|
-
#We only want the nodes with a name that isn't text
|
349
|
-
|
350
|
-
nodes.each do |n|
|
351
|
-
return_array << n.attr('to') if !n.name.nil? && n.name.downcase != "text"
|
352
|
-
end
|
353
|
-
return return_array.uniq
|
317
|
+
nodes = doc.xpath('//html/body/publicobject/releasedata').children
|
318
|
+
# We only want the nodes with a name that isn't text
|
319
|
+
nodes.reject {|n| n.name.nil? || n.name.downcase == 'text'}.map {|n| n.attr('to')}.uniq
|
354
320
|
end
|
355
321
|
|
356
322
|
# Pull all release nodes from the public xml obtained via the purl query
|
357
323
|
# @return [Array] An array containing all the release tags
|
358
324
|
def get_release_tags_from_purl
|
359
|
-
|
360
|
-
return self.get_release_tags_from_purl_xml(xml)
|
325
|
+
get_release_tags_from_purl_xml(get_xml_from_purl)
|
361
326
|
end
|
362
327
|
|
363
328
|
# This function calls purl and gets a list of all release tags currently in purl. It then compares to the list you have generated.
|
364
329
|
# Any tag that is on purl, but not in the newly generated list is added to the new list with a value of false.
|
365
|
-
#
|
366
330
|
# @param new_tags [Hash{String => Boolean}] all new tags in the form of !{"Project" => Boolean}
|
367
|
-
# @return [Hash]
|
331
|
+
# @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
|
368
332
|
def add_tags_from_purl(new_tags)
|
369
|
-
tags_currently_in_purl =
|
333
|
+
tags_currently_in_purl = get_release_tags_from_purl
|
370
334
|
missing_tags = tags_currently_in_purl.map(&:downcase) - new_tags.keys.map(&:downcase)
|
371
335
|
missing_tags.each do |missing_tag|
|
372
|
-
new_tags[missing_tag.capitalize] = {
|
336
|
+
new_tags[missing_tag.capitalize] = {'release' => false}
|
373
337
|
end
|
374
|
-
|
338
|
+
new_tags
|
375
339
|
end
|
376
340
|
|
377
|
-
def to_solr(solr_doc=
|
341
|
+
def to_solr(solr_doc = {}, *args)
|
378
342
|
super(solr_doc, *args)
|
379
343
|
|
380
|
-
#TODO: sort of worried about the performance impact in bulk reindex
|
344
|
+
# TODO: sort of worried about the performance impact in bulk reindex
|
381
345
|
# situations, since released_for recurses all parent collections. jmartin 2015-07-14
|
382
|
-
released_for().each { |key, val|
|
383
|
-
add_solr_value(solr_doc,
|
346
|
+
released_for(true).each { |key, val|
|
347
|
+
add_solr_value(solr_doc, 'released_to', key, :symbol, []) if val
|
384
348
|
}
|
385
349
|
|
386
|
-
#TODO: need to solrize whether item is released to purl? does released_for
|
387
|
-
#
|
388
|
-
# Rights is anything but Dark"
|
350
|
+
# TODO: need to solrize whether item is released to purl? does released_for return that?
|
351
|
+
# logic is: "True when there is a published lifecycle and Access Rights is anything but Dark"
|
389
352
|
|
390
|
-
|
353
|
+
solr_doc
|
391
354
|
end
|
392
355
|
end
|
393
356
|
end
|