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
@@ -6,8 +6,8 @@ module Dor
|
|
6
6
|
include Upgradable
|
7
7
|
|
8
8
|
included do
|
9
|
-
has_metadata :name =>
|
10
|
-
has_metadata :name =>
|
9
|
+
has_metadata :name => "DC", :type => SimpleDublinCoreDs, :label => 'Dublin Core Record for self object'
|
10
|
+
has_metadata :name => "identityMetadata", :type => Dor::IdentityMetadataDS, :label => 'Identity Metadata'
|
11
11
|
end
|
12
12
|
|
13
13
|
module ClassMethods
|
@@ -18,22 +18,24 @@ module Dor
|
|
18
18
|
end
|
19
19
|
end
|
20
20
|
|
21
|
-
def initialize attrs
|
21
|
+
def initialize attrs={}
|
22
22
|
if Dor::Config.suri.mint_ids
|
23
|
-
|
23
|
+
unless attrs[:pid]
|
24
|
+
attrs = attrs.merge!({:pid=>Dor::SuriService.mint_id, :new_object => true})
|
25
|
+
end
|
24
26
|
end
|
25
27
|
super
|
26
28
|
end
|
27
29
|
|
28
30
|
# helper method to get the tags as an array
|
29
31
|
def tags
|
30
|
-
identityMetadata.tag
|
32
|
+
self.identityMetadata.tag
|
31
33
|
end
|
32
34
|
|
33
35
|
# helper method to get just the content type tag
|
34
36
|
def content_type_tag
|
35
|
-
content_tag
|
36
|
-
content_tag.size == 1 ? content_tag[0].split(':').last.strip :
|
37
|
+
content_tag=tags.select {|tag| tag.include?('Process : Content Type')}
|
38
|
+
content_tag.size == 1 ? content_tag[0].split(':').last.strip : ""
|
37
39
|
end
|
38
40
|
|
39
41
|
# Syntactic sugar for identifying applied DOR Concerns
|
@@ -51,131 +53,134 @@ module Dor
|
|
51
53
|
end
|
52
54
|
end
|
53
55
|
|
54
|
-
|
55
|
-
@@
|
56
|
-
@@
|
57
|
-
@@
|
58
|
-
|
59
|
-
|
60
|
-
|
56
|
+
## Module-level variables, shared between ALL mixin includers!
|
57
|
+
@@collection_hash={}
|
58
|
+
@@apo_hash={}
|
59
|
+
@@hydrus_apo_hash={}
|
60
|
+
@@hydrus_collection_hash={}
|
61
|
+
|
62
|
+
def to_solr(solr_doc=Hash.new, *args)
|
63
|
+
self.assert_content_model
|
64
|
+
super(solr_doc, *args)
|
61
65
|
solr_doc[Dor::INDEX_VERSION_FIELD] = Dor::VERSION
|
62
|
-
solr_doc[
|
63
|
-
|
66
|
+
solr_doc['indexed_at_dtsi' ] = Time.now.utc.xmlschema
|
67
|
+
solr_doc['indexed_day_dtsi'] = Time.now.beginning_of_day.utc.xmlschema # technically unnecessary, but convenient
|
64
68
|
datastreams.values.each do |ds|
|
65
|
-
|
66
|
-
add_solr_value(solr_doc, 'ds_specs', ds.datastream_spec_string, :string, [:displayable])
|
69
|
+
add_solr_value(solr_doc,'ds_specs',ds.datastream_spec_string,:string,[:displayable]) unless ds.new?
|
67
70
|
end
|
68
|
-
add_solr_value(solr_doc, 'title_sort', label, :string, [:sortable])
|
69
|
-
|
70
|
-
|
71
|
+
add_solr_value(solr_doc, 'title_sort', self.label, :string, [:sortable])
|
72
|
+
title_attrs = [:searchable, :facetable, :displayable]
|
73
|
+
rels_doc = Nokogiri::XML(self.datastreams['RELS-EXT'].content)
|
74
|
+
apos=rels_doc.search('//rdf:RDF/rdf:Description/hydra:isGovernedBy','hydra' => 'http://projecthydra.org/ns/relations#', 'fedora' => 'info:fedora/fedora-system:def/relations-external#', 'rdf' => 'http://www.w3.org/1999/02/22-rdf-syntax-ns#' )
|
71
75
|
apos.each do |apo_node|
|
72
76
|
druid = apo_node['rdf:resource']
|
73
|
-
next unless druid
|
74
|
-
druid
|
77
|
+
next unless druid # TODO: a warning about the bad APO would be nice
|
78
|
+
druid=druid.gsub('info:fedora/','')
|
79
|
+
# check cache first
|
75
80
|
if @@apo_hash.has_key?(druid) || @@hydrus_apo_hash.has_key?(druid)
|
76
|
-
add_solr_value(solr_doc,
|
77
|
-
add_solr_value(solr_doc,
|
81
|
+
add_solr_value(solr_doc, "hydrus_apo_title", @@hydrus_apo_hash[druid], :string, title_attrs) if @@hydrus_apo_hash.has_key? druid
|
82
|
+
add_solr_value(solr_doc, "apo_title", @@apo_hash[druid] , :string, title_attrs) if @@apo_hash.has_key? druid
|
78
83
|
else
|
79
84
|
begin
|
80
|
-
apo_object
|
85
|
+
apo_object=Dor.find(druid)
|
81
86
|
if apo_object.tags.include? 'Project : Hydrus'
|
82
|
-
add_solr_value(solr_doc,
|
83
|
-
@@hydrus_apo_hash[druid]
|
87
|
+
add_solr_value(solr_doc, "hydrus_apo_title", apo_object.label, :string, title_attrs)
|
88
|
+
@@hydrus_apo_hash[druid]=apo_object.label
|
84
89
|
else
|
85
|
-
add_solr_value(solr_doc,
|
86
|
-
@@apo_hash[druid]
|
90
|
+
add_solr_value(solr_doc, "apo_title", apo_object.label, :string, title_attrs)
|
91
|
+
@@apo_hash[druid]=apo_object.label
|
87
92
|
end
|
88
93
|
rescue
|
89
|
-
add_solr_value(solr_doc,
|
94
|
+
add_solr_value(solr_doc, "apo_title", druid, :string, title_attrs)
|
90
95
|
end
|
91
96
|
end
|
92
97
|
end
|
93
|
-
collections
|
98
|
+
collections=rels_doc.search('//rdf:RDF/rdf:Description/fedora:isMemberOfCollection','fedora' => 'info:fedora/fedora-system:def/relations-external#', 'rdf' => 'http://www.w3.org/1999/02/22-rdf-syntax-ns#' )
|
94
99
|
collections.each do |collection_node|
|
95
|
-
druid
|
96
|
-
next unless druid
|
97
|
-
druid
|
100
|
+
druid=collection_node['rdf:resource']
|
101
|
+
next unless druid ## TODO: warning here would also be useful
|
102
|
+
druid=druid.gsub('info:fedora/','')
|
98
103
|
if @@collection_hash.has_key?(druid) || @@hydrus_collection_hash.has_key?(druid)
|
99
|
-
add_solr_value(solr_doc,
|
100
|
-
add_solr_value(solr_doc,
|
104
|
+
add_solr_value(solr_doc, "hydrus_collection_title", @@hydrus_collection_hash[druid], :string, title_attrs) if @@hydrus_collection_hash.has_key? druid
|
105
|
+
add_solr_value(solr_doc, "collection_title", @@collection_hash[druid], :string, title_attrs) if @@collection_hash.has_key? druid
|
101
106
|
else
|
102
107
|
begin
|
103
|
-
collection_object
|
108
|
+
collection_object=Dor.find(druid)
|
104
109
|
if collection_object.tags.include? 'Project : Hydrus'
|
105
|
-
add_solr_value(solr_doc,
|
106
|
-
@@hydrus_collection_hash[druid]
|
110
|
+
add_solr_value(solr_doc, "hydrus_collection_title", collection_object.label, :string, title_attrs)
|
111
|
+
@@hydrus_collection_hash[druid]=collection_object.label
|
107
112
|
else
|
108
|
-
add_solr_value(solr_doc,
|
109
|
-
@@collection_hash[druid]
|
113
|
+
add_solr_value(solr_doc, "collection_title", collection_object.label, :string, title_attrs)
|
114
|
+
@@collection_hash[druid]=collection_object.label
|
110
115
|
end
|
111
116
|
rescue
|
112
|
-
add_solr_value(solr_doc,
|
117
|
+
add_solr_value(solr_doc, "collection_title", druid, :string, title_attrs)
|
113
118
|
end
|
114
119
|
end
|
115
120
|
end
|
116
|
-
|
117
|
-
solr_doc.each_pair { |k, v|
|
118
|
-
next unless k =~ /(_dt|_date)$/
|
119
|
-
if v.is_a?(Array)
|
120
|
-
solr_doc[k] = v.collect { |t| Time.parse(t.to_s).utc.xmlschema }
|
121
|
-
else
|
122
|
-
solr_doc[k] = Time.parse(v.to_s).utc.xmlschema
|
123
|
-
end
|
124
|
-
}
|
121
|
+
solr_doc["metadata_source_ssi"] = self.identity_metadata_source
|
125
122
|
solr_doc
|
126
123
|
end
|
127
124
|
|
125
|
+
#@return [String] calculated value for Solr index
|
126
|
+
def identity_metadata_source
|
127
|
+
if self.identityMetadata.otherId('catkey').first ||
|
128
|
+
self.identityMetadata.otherId('barcode').first
|
129
|
+
'Symphony'
|
130
|
+
elsif self.identityMetadata.otherId('mdtoolkit').first
|
131
|
+
'Metadata Toolkit'
|
132
|
+
else
|
133
|
+
'DOR'
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
128
137
|
def set_source_id(source_id)
|
129
|
-
identityMetadata.sourceId = source_id
|
138
|
+
self.identityMetadata.sourceId = source_id
|
130
139
|
end
|
131
140
|
|
132
|
-
def add_other_Id(type,
|
133
|
-
if identityMetadata.otherId(type).length
|
134
|
-
raise 'There is an existing entry for '+
|
141
|
+
def add_other_Id(type,val)
|
142
|
+
if self.identityMetadata.otherId(type).length>0
|
143
|
+
raise 'There is an existing entry for '+type+', consider using update_other_Id().'
|
135
144
|
end
|
136
|
-
|
137
|
-
identity_metadata_ds.add_otherId(type+':'+val)
|
145
|
+
self.identityMetadata.add_otherId(type+':'+val)
|
138
146
|
end
|
139
147
|
|
140
|
-
def update_other_Id(type, new_val, val
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
node.content = new_val
|
148
|
-
updated = true
|
148
|
+
def update_other_Id(type, new_val, val=nil)
|
149
|
+
updated=false
|
150
|
+
self.identityMetadata.ng_xml.search('//otherId[@name=\''+type+'\']').each do |node|
|
151
|
+
if node.content==val || val.nil?
|
152
|
+
node.content=new_val
|
153
|
+
updated=true
|
154
|
+
end
|
149
155
|
end
|
150
|
-
updated
|
156
|
+
return updated
|
151
157
|
end
|
152
158
|
|
153
|
-
def remove_other_Id(type, val
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
removed = true
|
159
|
+
def remove_other_Id(type, val=nil)
|
160
|
+
removed=false
|
161
|
+
self.identityMetadata.ng_xml.search('//otherId[@name=\''+type+'\']').each do |node|
|
162
|
+
if node.content===val || val.nil?
|
163
|
+
node.remove
|
164
|
+
removed=true
|
165
|
+
end
|
161
166
|
end
|
162
|
-
removed
|
167
|
+
return removed
|
163
168
|
end
|
164
169
|
|
165
170
|
# turns a tag string into an array with one element per tag part.
|
166
171
|
# split on ":", disregard leading and trailing whitespace on tokens.
|
167
172
|
def split_tag_to_arr(tag_str)
|
168
|
-
tag_str.split(
|
173
|
+
return tag_str.split(":").map {|str| str.strip}
|
169
174
|
end
|
170
175
|
|
171
176
|
# turn a tag array back into a tag string with a standard format
|
172
177
|
def normalize_tag_arr(tag_arr)
|
173
|
-
tag_arr.join(' : ')
|
178
|
+
return tag_arr.join(' : ')
|
174
179
|
end
|
175
180
|
|
176
181
|
# take a tag string and return a normalized tag string
|
177
182
|
def normalize_tag(tag_str)
|
178
|
-
normalize_tag_arr(split_tag_to_arr(tag_str))
|
183
|
+
return normalize_tag_arr(split_tag_to_arr(tag_str))
|
179
184
|
end
|
180
185
|
|
181
186
|
# take a proposed tag string and a list of the existing tags for the object being edited. if
|
@@ -183,66 +188,65 @@ module Dor
|
|
183
188
|
# explanatory message.
|
184
189
|
def validate_and_normalize_tag(tag_str, existing_tag_list)
|
185
190
|
tag_arr = validate_tag_format(tag_str)
|
191
|
+
|
186
192
|
# note that the comparison for duplicate tags is case-insensitive, but we don't change case as part of the normalized version
|
187
193
|
# we return, because we want to preserve the user's intended case.
|
188
194
|
normalized_tag = normalize_tag_arr(tag_arr)
|
189
195
|
dupe_existing_tag = existing_tag_list.detect { |existing_tag| normalize_tag(existing_tag).downcase == normalized_tag.downcase }
|
190
|
-
|
191
|
-
|
196
|
+
if dupe_existing_tag
|
197
|
+
raise "An existing tag (#{dupe_existing_tag}) is the same, consider using update_tag?"
|
198
|
+
end
|
199
|
+
|
200
|
+
return normalized_tag
|
192
201
|
end
|
193
202
|
|
194
|
-
#
|
195
|
-
#
|
196
|
-
|
203
|
+
#Ensure that an administrative tag meets the proper mininum format
|
204
|
+
#
|
205
|
+
#@params tag_str [String] the tag
|
206
|
+
#
|
207
|
+
#@return [Array] the tag split into an array via ':'
|
197
208
|
def validate_tag_format(tag_str)
|
198
209
|
tag_arr = split_tag_to_arr(tag_str)
|
199
|
-
|
200
|
-
|
201
|
-
|
210
|
+
|
211
|
+
if tag_arr.length < 2
|
212
|
+
raise ArgumentError, "Invalid tag structure: tag '#{tag_str}' must have at least 2 elements"
|
213
|
+
end
|
214
|
+
|
215
|
+
if tag_arr.detect {|str| str.empty?}
|
216
|
+
raise ArgumentError, "Invalid tag structure: tag '#{tag_str}' contains empty elements"
|
217
|
+
end
|
218
|
+
return tag_arr
|
202
219
|
end
|
203
220
|
|
204
|
-
#
|
205
|
-
#
|
221
|
+
#Add an administrative tag to an item, you will need to seperately save the item to write it to fedora
|
222
|
+
#
|
223
|
+
#param tag [string] The tag you wish to add
|
206
224
|
def add_tag(tag)
|
207
|
-
|
208
|
-
|
209
|
-
|
225
|
+
identity_metadata_ds = self.identityMetadata
|
226
|
+
normalized_tag = validate_and_normalize_tag(tag, identity_metadata_ds.tags)
|
227
|
+
identity_metadata_ds.add_value(:tag, normalized_tag)
|
210
228
|
end
|
211
229
|
|
212
230
|
def remove_tag(tag)
|
213
|
-
identity_metadata_ds = identityMetadata
|
214
|
-
ds_xml = identity_metadata_ds.ng_xml
|
215
231
|
removed = false
|
216
|
-
|
232
|
+
self.identityMetadata.ng_xml.search('//tag').each do |node|
|
217
233
|
if normalize_tag(node.content) === normalize_tag(tag)
|
218
234
|
node.remove
|
219
235
|
removed = true
|
220
236
|
end
|
221
237
|
end
|
222
|
-
removed
|
223
|
-
end
|
224
|
-
|
225
|
-
# Removes all displayTypes from an item in preparation of adding a new display type
|
226
|
-
# @return Boolean True if displayTypes were removed, False if no displayTypes were removed
|
227
|
-
def remove_displayTypes
|
228
|
-
removed = false
|
229
|
-
identityMetadata.ng_xml.search('//displayType').each do |node|
|
230
|
-
node.remove
|
231
|
-
removed = true
|
232
|
-
end
|
233
|
-
removed
|
238
|
+
return removed
|
234
239
|
end
|
235
240
|
|
236
241
|
def update_tag(old_tag, new_tag)
|
237
|
-
identity_metadata_ds = identityMetadata
|
238
|
-
ds_xml = identity_metadata_ds.ng_xml
|
239
242
|
updated = false
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
243
|
+
self.identityMetadata.ng_xml.search('//tag').each do |node|
|
244
|
+
if normalize_tag(node.content) == normalize_tag(old_tag)
|
245
|
+
node.content = normalize_tag(new_tag)
|
246
|
+
updated = true
|
247
|
+
end
|
244
248
|
end
|
245
|
-
updated
|
249
|
+
return updated
|
246
250
|
end
|
247
251
|
end
|
248
252
|
end
|
data/lib/dor/models/item.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
module Dor
|
2
2
|
module BasicItem
|
3
3
|
extend ActiveSupport::Concern
|
4
|
-
|
4
|
+
|
5
5
|
include Identifiable
|
6
6
|
include Processable
|
7
7
|
include Governable
|
@@ -13,11 +13,11 @@ module Dor
|
|
13
13
|
include Assembleable
|
14
14
|
include Versionable
|
15
15
|
include Contentable
|
16
|
-
include Discoverable
|
17
16
|
include Geoable
|
18
|
-
include
|
17
|
+
include Releasable
|
18
|
+
|
19
19
|
end
|
20
|
-
|
20
|
+
|
21
21
|
class Abstract < ::ActiveFedora::Base
|
22
22
|
include Identifiable
|
23
23
|
end
|
@@ -8,20 +8,20 @@ module Dor
|
|
8
8
|
included do
|
9
9
|
has_metadata :name => "contentMetadata", :type => Dor::ContentMetadataDS, :label => 'Content Metadata', :control_group => 'M'
|
10
10
|
end
|
11
|
-
|
11
|
+
|
12
12
|
DIFF_FILENAME = 'cm_inv_diff'
|
13
13
|
DIFF_QUERY = DIFF_FILENAME.gsub('_', '-')
|
14
14
|
|
15
15
|
# Deletes all cm_inv_diff files in the workspace for the Item
|
16
16
|
def clear_diff_cache
|
17
17
|
if Dor::Config.stacks.local_workspace_root.nil?
|
18
|
-
raise ArgumentError, 'Missing Dor::Config.stacks.local_workspace_root'
|
18
|
+
raise ArgumentError, 'Missing Dor::Config.stacks.local_workspace_root'
|
19
19
|
end
|
20
|
-
druid = DruidTools::Druid.new(pid, Dor::Config.stacks.local_workspace_root)
|
20
|
+
druid = DruidTools::Druid.new(self.pid, Dor::Config.stacks.local_workspace_root)
|
21
21
|
diff_pattern = File.join(druid.temp_dir, DIFF_FILENAME + '.*')
|
22
22
|
FileUtils.rm_f Dir.glob(diff_pattern)
|
23
23
|
end
|
24
|
-
|
24
|
+
|
25
25
|
# Retrieves file difference manifest for contentMetadata from SDR
|
26
26
|
#
|
27
27
|
# @param [String] subset keyword for file attributes :shelve, :preserve, :publish. Default is :all.
|
@@ -29,7 +29,7 @@ module Dor
|
|
29
29
|
# @return [String] XML contents of cm_inv_diff manifest
|
30
30
|
def get_content_diff(subset = :all, version = nil)
|
31
31
|
if Dor::Config.stacks.local_workspace_root.nil?
|
32
|
-
raise Dor::ParameterError, 'Missing Dor::Config.stacks.local_workspace_root'
|
32
|
+
raise Dor::ParameterError, 'Missing Dor::Config.stacks.local_workspace_root'
|
33
33
|
end
|
34
34
|
unless %w{all shelve preserve publish}.include?(subset.to_s)
|
35
35
|
raise Dor::ParameterError, "Invalid subset value: #{subset}"
|
@@ -40,17 +40,17 @@ module Dor
|
|
40
40
|
raise Dor::ParameterError, 'Missing Dor::Config.sdr.rest_client'
|
41
41
|
end
|
42
42
|
sdr_client = Dor::Config.sdr.rest_client
|
43
|
-
current_content = datastreams['contentMetadata'].content
|
43
|
+
current_content = self.datastreams['contentMetadata'].content
|
44
44
|
if current_content.nil?
|
45
45
|
raise Dor::Exception, "Missing contentMetadata datastream"
|
46
46
|
end
|
47
47
|
query_string = { :subset => subset.to_s }
|
48
48
|
query_string[:version] = version.to_s unless version.nil?
|
49
49
|
query_string = URI.encode_www_form(query_string)
|
50
|
-
sdr_query = "objects/#{pid}/#{DIFF_QUERY}?#{query_string}"
|
50
|
+
sdr_query = "objects/#{self.pid}/#{DIFF_QUERY}?#{query_string}"
|
51
51
|
response = sdr_client[sdr_query].post(current_content, :content_type => 'application/xml')
|
52
52
|
response
|
53
53
|
end
|
54
|
-
|
54
|
+
|
55
55
|
end
|
56
|
-
end
|
56
|
+
end
|
@@ -0,0 +1,133 @@
|
|
1
|
+
require 'iiif/presentation'
|
2
|
+
|
3
|
+
module Dor
|
4
|
+
module Presentable
|
5
|
+
|
6
|
+
DC_NS = {"dc"=>"http://purl.org/dc/elements/1.1/", "oai_dc"=>"http://www.openarchives.org/OAI/2.0/oai_dc/"}
|
7
|
+
|
8
|
+
def iiif_presentation_manifest_needed? pub_obj_doc
|
9
|
+
if(pub_obj_doc.at_xpath('/publicObject/contentMetadata[@type="image"]/resource[@type="image"]'))
|
10
|
+
return true
|
11
|
+
elsif(pub_obj_doc.at_xpath('/publicObject/contentMetadata[@type="book"]/resource[@type="page"]'))
|
12
|
+
return true
|
13
|
+
else
|
14
|
+
return false
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
# Bypass this method if there are no image resources in contentMetadata
|
19
|
+
def build_iiif_manifest pub_obj_doc
|
20
|
+
id = self.pid.split(':').last
|
21
|
+
|
22
|
+
lbl_node = pub_obj_doc.at_xpath '//oai_dc:dc/dc:title', DC_NS
|
23
|
+
if lbl_node.nil?
|
24
|
+
lbl_node = pub_obj_doc.at_xpath('/publicObject/identityMetadata/objectLabel')
|
25
|
+
end
|
26
|
+
raise "Unable to build IIIF Presentation manifest: No identityMetadata/objectLabel or dc:title" if lbl_node.nil?
|
27
|
+
lbl = lbl_node.text
|
28
|
+
|
29
|
+
purl_base_uri = "https://#{Dor::Config.stacks.document_cache_host}/#{id}"
|
30
|
+
|
31
|
+
manifest_data = {
|
32
|
+
'@id' => "#{purl_base_uri}/iiif/manifest.json",
|
33
|
+
'label' => lbl,
|
34
|
+
'attribution' => 'Provided by the Stanford University Libraries',
|
35
|
+
'seeAlso' => {
|
36
|
+
'@id' => "#{purl_base_uri}.mods",
|
37
|
+
'format' => 'application/mods+xml'
|
38
|
+
}
|
39
|
+
}
|
40
|
+
# Use the human copyright statement for attribution if present
|
41
|
+
if(cr = pub_obj_doc.at_xpath('/publicObject/rightsMetadata/copyright/human[@type="copyright"]'))
|
42
|
+
manifest_data['attribution'] = cr.text
|
43
|
+
end
|
44
|
+
|
45
|
+
manifest = IIIF::Presentation::Manifest.new manifest_data
|
46
|
+
|
47
|
+
# Set viewingHint to paged if this is a book
|
48
|
+
if(pub_obj_doc.at_xpath('/publicObject/contentMetadata[@type="book"]'))
|
49
|
+
manifest.viewingHint = "paged"
|
50
|
+
end
|
51
|
+
|
52
|
+
metadata = []
|
53
|
+
# make into method, pass in xpath and label
|
54
|
+
add_metadata 'Creator', '//oai_dc:dc/dc:creator', metadata, pub_obj_doc
|
55
|
+
add_metadata 'Contributor', '//oai_dc:dc/dc:contributor', metadata, pub_obj_doc
|
56
|
+
add_metadata 'Publisher', '//oai_dc:dc/dc:publisher', metadata, pub_obj_doc
|
57
|
+
add_metadata 'Date', '//oai_dc:dc/dc:date', metadata, pub_obj_doc
|
58
|
+
|
59
|
+
# Save off the first dc:description without displayLabel
|
60
|
+
if(desc = pub_obj_doc.at_xpath('//oai_dc:dc/dc:description[not(@displayLabel)]', DC_NS))
|
61
|
+
manifest.description = desc.text
|
62
|
+
end
|
63
|
+
|
64
|
+
manifest.metadata = metadata unless metadata.empty?
|
65
|
+
|
66
|
+
seq_data = {
|
67
|
+
'@id' => "#{purl_base_uri}/sequence-1",
|
68
|
+
'label' => 'Current order'
|
69
|
+
}
|
70
|
+
sequence = IIIF::Presentation::Sequence.new seq_data
|
71
|
+
|
72
|
+
# for each resource image, create a canvas
|
73
|
+
count = 0
|
74
|
+
pub_obj_doc.xpath('/publicObject/contentMetadata/resource[@type="image" or @type="page"]').each do |res_node|
|
75
|
+
count += 1
|
76
|
+
img_file_name = res_node.at_xpath('file/@id').text.split('.').first
|
77
|
+
height = res_node.at_xpath('file/imageData/@height').text.to_i
|
78
|
+
width = res_node.at_xpath('file/imageData/@width').text.to_i
|
79
|
+
stacks_uri = "#{Dor::Config.stacks.url}/image/iiif/#{id}%2F#{img_file_name}"
|
80
|
+
|
81
|
+
canv = IIIF::Presentation::Canvas.new
|
82
|
+
canv['@id'] = "#{purl_base_uri}/canvas/canvas-#{count}"
|
83
|
+
canv.label = res_node.at_xpath('label').text
|
84
|
+
canv.height = height
|
85
|
+
canv.width = width
|
86
|
+
|
87
|
+
anno = IIIF::Presentation::Annotation.new
|
88
|
+
anno['@id'] = "#{purl_base_uri}/imageanno/anno-#{count}"
|
89
|
+
anno['on'] = canv['@id']
|
90
|
+
|
91
|
+
img_res = IIIF::Presentation::ImageResource.new
|
92
|
+
img_res['@id'] = "#{stacks_uri}/full/full/0/default.jpg"
|
93
|
+
img_res.format = 'image/jpeg'
|
94
|
+
img_res.height = height
|
95
|
+
img_res.width = width
|
96
|
+
|
97
|
+
svc = IIIF::Service.new ({
|
98
|
+
'@context' => 'http://iiif.io/api/image/2/context.json',
|
99
|
+
'@id' => stacks_uri,
|
100
|
+
'profile' => Dor::Config.stacks.iiif_profile
|
101
|
+
})
|
102
|
+
|
103
|
+
# Use the first image to create a thumbnail on the manifest
|
104
|
+
if count == 1
|
105
|
+
thumb = IIIF::Presentation::Resource.new
|
106
|
+
thumb['@id'] = "#{stacks_uri}/full/400,/0/default.jpg"
|
107
|
+
thumb.service = svc
|
108
|
+
manifest.thumbnail = thumb
|
109
|
+
end
|
110
|
+
|
111
|
+
img_res.service = svc
|
112
|
+
anno.resource = img_res
|
113
|
+
canv.images << anno
|
114
|
+
sequence.canvases << canv
|
115
|
+
end
|
116
|
+
|
117
|
+
manifest.sequences << sequence
|
118
|
+
manifest.to_json(:pretty => true)
|
119
|
+
end
|
120
|
+
|
121
|
+
def add_metadata label, xpath, metadata, pub_obj_doc
|
122
|
+
nodes = pub_obj_doc.xpath xpath, DC_NS
|
123
|
+
nodes.each do |node|
|
124
|
+
h = {
|
125
|
+
'label' => label,
|
126
|
+
'value' => node.text
|
127
|
+
}
|
128
|
+
metadata << h
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
end
|
133
|
+
end
|