dor-services 6.0.5 → 6.1.0

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