dor-services 5.28.0 → 5.29.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: b405326996caca8509c78729f71564556e96474b
4
- data.tar.gz: 490ca85fcfff48888eb5081bcd556afbb113dda2
3
+ metadata.gz: a6c61cecf3d248803eae00ed5d38ff6777274637
4
+ data.tar.gz: a54d41fcd06b9e1ddfa27901d1b60a255b687eb3
5
5
  SHA512:
6
- metadata.gz: 4a9613d14e8b682020227b821ac75867b861afd1e5c99de45b066a47f004f64c5cb40c63fcdfd24e29390df3687f7aa08ddff7595b6cd4cbd75413ac13f798a3
7
- data.tar.gz: 305aa2640181a30f12218bc556b5551a7ccc202cfeab2996444da2650160d94e0915379ff92f10c81e0142b1f37a033e07d408db01f5403eb0bdbe70cda6f4e1
6
+ metadata.gz: 73c5d62258abc20a79bc4bf1fe4eedbaa1ea6e089171ef9d62a80fd5de792d8f2e188c8fefcb3dafacc550cd40617d1fd4fefe091a5bfa857b06dca83756501c
7
+ data.tar.gz: bafc0afb99e3ef8186bcd6613421fd9bf48aa22c8ef1e5a57203215f0d2950cfa69d356b070331e1f2a21788d2e36bf1bf0d7c8475af726b002dab1bfe6094c8
@@ -1,11 +1,13 @@
1
1
  require 'moab/stanford'
2
2
 
3
3
  module Dor
4
+ # Note: This should probably live in common-accessioning robot sdr-ingest-transfer
5
+ # as that is the only robot that uses it. See also preservable concern.
4
6
  class SdrIngestService
5
7
 
6
8
  # @param [Dor::Item] dor_item The representation of the digital object
7
9
  # @param [String] agreement_id depreciated, included for backward compatability with common-accessoning
8
- # @return [void] Create the moab manifests, export data to a BagIt bag, kick off the SDR ingest workflow
10
+ # @return [void] Create the Moab/bag manifests for new version, export data to BagIt bag, kick off the SDR preservation workflow
9
11
  def self.transfer(dor_item, agreement_id = nil)
10
12
  druid = dor_item.pid
11
13
  workspace = DruidTools::Druid.new(druid, Dor::Config.sdr.local_workspace_root)
@@ -35,12 +37,14 @@ module Dor
35
37
  bagger.deposit_group('metadata', metadata_dir)
36
38
  bagger.create_tagfiles
37
39
  verify_bag_structure(bag_dir)
38
- # Now bootstrap SDR workflow. but do not create the workflows datastream
39
- dor_item.create_workflow('sdrIngestWF', false)
40
+ # start SDR preservation workflow (but do not create the workflows datastream)
41
+ dor_item.create_workflow('preservationIngestWF', false)
40
42
  rescue Exception => e
41
- raise Dor::Exception, 'Export failure'
43
+ raise Dor::Exception, "Error exporting new object version to bag: #{e.message}"
42
44
  end
43
45
 
46
+ # Note: the following methods should probably all be private
47
+
44
48
  # @param [String] druid The object identifier
45
49
  # @return [Moab::SignatureCatalog] the catalog of all files previously ingested
46
50
  def self.get_signature_catalog(druid)
@@ -1,3 +1,3 @@
1
1
  module Dor
2
- VERSION = '5.28.0'.freeze
2
+ VERSION = '5.29.0'.freeze
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dor-services
3
3
  version: !ruby/object:Gem::Version
4
- version: 5.28.0
4
+ version: 5.29.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Michael Klein
@@ -14,7 +14,7 @@ authors:
14
14
  autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
- date: 2018-03-22 00:00:00.000000000 Z
17
+ date: 2018-04-18 00:00:00.000000000 Z
18
18
  dependencies:
19
19
  - !ruby/object:Gem::Dependency
20
20
  name: active-fedora
@@ -562,10 +562,7 @@ extensions: []
562
562
  extra_rdoc_files: []
563
563
  files:
564
564
  - config/certs/README
565
- - config/certs/robots-dor-dev.crt
566
- - config/certs/robots-dor-dev.key
567
565
  - config/config_defaults.yml
568
- - config/dev_console_env.rb
569
566
  - config/dev_console_env.rb.example
570
567
  - config/predicate_mappings.yml
571
568
  - lib/dor-services.rb
@@ -612,7 +609,6 @@ files:
612
609
  - lib/dor/models/concerns/shelvable.rb
613
610
  - lib/dor/models/concerns/versionable.rb
614
611
  - lib/dor/models/item.rb
615
- - lib/dor/models/releaseable.rb.bak
616
612
  - lib/dor/models/set.rb
617
613
  - lib/dor/models/workflow_object.rb
618
614
  - lib/dor/rest_resource_factory.rb
@@ -662,7 +658,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
662
658
  version: 1.3.6
663
659
  requirements: []
664
660
  rubyforge_project:
665
- rubygems_version: 2.6.13
661
+ rubygems_version: 2.6.11
666
662
  signing_key:
667
663
  specification_version: 4
668
664
  summary: Ruby implmentation of DOR services used by the SULAIR Digital Library
@@ -1,29 +0,0 @@
1
- -----BEGIN CERTIFICATE-----
2
- MIIE9zCCAt8CCQCcUJVDTu7rTjANBgkqhkiG9w0BAQUFADCBvzELMAkGA1UEBhMC
3
- VVMxEzARBgNVBAgTCkNhbGlmb3JuaWExETAPBgNVBAcTCFN0YW5mb3JkMRwwGgYD
4
- VQQKExNTdGFuZm9yZCBVbml2ZXJzaXR5MRIwEAYDVQQLEwlMaWJyYXJpZXMxJTAj
5
- BgNVBAMTHFNVTCBET1IgKGRldi90ZXN0KSBhY2Nlc3MgQ0ExLzAtBgkqhkiG9w0B
6
- CQEWIHN1bC13ZWJtYXN0ZXJAbGlzdHMuc3RhbmZvcmQuZWR1MB4XDTEyMDkyNjE1
7
- NTQzNVoXDTIyMDkyNDE1NTQzNVowgboxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpD
8
- YWxpZm9ybmlhMREwDwYDVQQHEwhTdGFuZm9yZDEcMBoGA1UEChMTU3RhbmZvcmQg
9
- VW5pdmVyc2l0eTEyMDAGA1UEAxQpcm9ib3RzQHN1bC1seWJlcnNlcnZpY2VzLWRl
10
- di5zdGFuZm9yZC5lZHUxMTAvBgkqhkiG9w0BCQEWImRsc3MtZGV2ZWxvcGVyc0Bs
11
- aXN0cy5zdGFuZm9yZC5lZHUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB
12
- AQC1t+PPhLTDFQBuAf4f1GOj7jHdXMN0tvSHL1OCaibe0d8iKbXCMMIE0z8lbBpZ
13
- 1pA+8terIvxoTlly92EEvGkywZb+DIxnjyG0b9ftDJ64pARJMcnvwJMW/w3WdHCd
14
- 5uJ5HVM8ZCbEItUfskFukS6tfNBpG8ri6cIQ2ulJzOWYxPV5R2eRneM6vtFD0xtu
15
- RJb/uMwiOwB79qk4QazbEQtkeZsrvomDFkIuHO/nf9wmgYG99H7q1kXplIrzDDX+
16
- fvD+JHvuMr5YXfmBQ9wmaZf6YIL7CXj79HUF4uG/a0SjrLZyLh262zWJQFVpjSW8
17
- Brl/fAyjeZvWJMyCDXRqsedjAgMBAAEwDQYJKoZIhvcNAQEFBQADggIBAG0jIfTj
18
- 1FJsxTzcD938vtKuNo5XEIX8phqW1MA2eJ5Ojgu1hpW5Jf7fmUHKVnamm7eya5QR
19
- 356KCJzZzwdCS4xVFq/l43gCA2j8TYrwO6/0EfM/5psUZS3ecwahzcJXfHPHvd9u
20
- qaXRqbjJYi+Ls70uStTC/JBIIu48atDF0DeXJ21hAbstZ1OKKNzAcYgZk14GKxCi
21
- ArgAzdg8UDtC4KsdqgxP6vF8Vc/gcHSu9AkVHgPVXbITjV0/kaKNgFi0bVqP7bJD
22
- hrUFYrS+S+hflQJCmVrDwW0JU2HnJ3EJOFX5F4FYHL4vt6ZjVsMzucraMqiLyUaO
23
- s6DVaWO8WMlphmFUfb3ChQjrKxFZZ6vKJPD/ExnQHa0kWwOELyMJuXw2PLk7k+qD
24
- rmjX+K3Ed+JiJuiDG5k8f1BDZE24lvxDwye5fQyDWPfib1yTClYYy4PRh4AI688f
25
- qgMyv51Qx82o0IJ1On1rNg5/iB7OEVcYSol3U5xiY9gRUd3vq637agZzMuRQnKXs
26
- Uj0+Vl4OrZ54F8MVsUKfi7Je1oVUEhuHqFt/tSOGlHg1XyR/ExwulCNBKIVdhhrF
27
- rxZCYrGDSUCF6tteJXpOBTtFT9b7hbMmBohKyS4XjfPSPRh9QHx7wepqSp3aHpmR
28
- 4gY8Go40mS83xHCKBooqsy2/Z9vQlSb9Cr8u
29
- -----END CERTIFICATE-----
@@ -1,27 +0,0 @@
1
- -----BEGIN RSA PRIVATE KEY-----
2
- MIIEpQIBAAKCAQEAtbfjz4S0wxUAbgH+H9Rjo+4x3VzDdLb0hy9Tgmom3tHfIim1
3
- wjDCBNM/JWwaWdaQPvLXqyL8aE5ZcvdhBLxpMsGW/gyMZ48htG/X7QyeuKQESTHJ
4
- 78CTFv8N1nRwnebieR1TPGQmxCLVH7JBbpEurXzQaRvK4unCENrpSczlmMT1eUdn
5
- kZ3jOr7RQ9MbbkSW/7jMIjsAe/apOEGs2xELZHmbK76JgxZCLhzv53/cJoGBvfR+
6
- 6tZF6ZSK8ww1/n7w/iR77jK+WF35gUPcJmmX+mCC+wl4+/R1BeLhv2tEo6y2ci4d
7
- uts1iUBVaY0lvAa5f3wMo3mb1iTMgg10arHnYwIDAQABAoIBAQCmlDzsZZ2C24S6
8
- U29OZFc21kq1vqyaGIEOT48BFSguyDsAyTWKH0IsRC41suRxCGTQn7KeegLh5zjO
9
- UAGK4uCYm6g7mOs4n5fpHUHWVuRFJG2dX1vbQTBVO1fHCJSX2ygumHhN+w0ibT/r
10
- v2+F2ObuOGWghM62tkylQNfsRD0QjalE8ST2xp9p4xwPrAwEuURlA04qnLYNi4+6
11
- qexDtrLG8E6JV/dd3dY95WjKPscadR1fAKm9sebD0XO31Lu6BGCvpmBU0OmYVfrY
12
- BN/ofhEmJh+Xgwfxx6PicRDvu9bPnffqbGhDO4ckGDlqXp1fDBgf1I7CclXdrVMH
13
- Qsxunh5pAoGBAN7929V1aw0S7Rqvuf4N+o1XwBExa2DL7EYI1Oea/nuBMPqvfeNh
14
- SrbM0gYejEzLmeRUUKRhMMoAgXQnxcg3Kz/5pUXT3b4YtzeskX8nkJ6/2gEU3M7E
15
- lJj7c5wW1otqMH19+iDNR4N8vjPh98kDJW1HZUHSAcdyE+dZiFP2ofMfAoGBANCe
16
- AJgA9EkdwZsom+I7Ty9LSrTWx6cKmNEbLVn1iSlyJx1kHoWyTMtuZszCWBjiXx70
17
- 84WpX2B4MuJBq6sSoEiUahNsphks82glOd6i+eoE/I+NtZehOFnkAMyGux+FbMyG
18
- q/9ZlQgsbWpFHxS59vX/kvlNun2RpJkaF6QPBuc9AoGBAIGdscCb5cPNsoCGkGf6
19
- HB84WG4l+bJkFkyHKaoOT8neSGgOxe/7R4CGAbaI3yhsWGF+GPnTfJaOQLERxUch
20
- ukQPil/STNwIZcc4ycHNb6S3A7MJO2f8oBtkXcjnBYI4EUOAjHmwmNb3FV/3Ax6W
21
- c5TXoSXnOh2CU8twIWwcufolAoGATqnFf4uckZVu05rUbNIAYcz6NoZmck5EoVSm
22
- HS3Asqggp6yA9djtrCfNC5icr4VsaLfku5nKJQ4t4bLxkGhNmBCejwSA/S09+x9O
23
- Wu0JX3zR3y+IMczQ+tRGmNiU5qXhCJ4fbQHSeGqIN3Io7h/RR6E/QlJU59RrRG/a
24
- SjqAV+ECgYEA2mA5VFK3jECnLde+biEaXidIOuBMcqaP0VG6sBwz2wTGSTBE1WkG
25
- 6prCD0McWVAgDTm3MMKxeuMaoW5eEi83+G+CywLaTBpc8Hz7qgRhBpKhRirCfHMW
26
- EfQV5k32vEGoSI+eG8zcGb9mwDeb/EWXAMGXrx2YuYFu7txtwC2mZXc=
27
- -----END RSA PRIVATE KEY-----
@@ -1,69 +0,0 @@
1
- cert_dir = File.join(File.dirname(__FILE__), 'certs')
2
-
3
- Dor::Config.configure do
4
-
5
- ssl do
6
- cert_file File.join(cert_dir, 'robots-dor-dev.crt')
7
- key_file File.join(cert_dir, 'robots-dor-dev.key')
8
- key_pass ''
9
- end
10
-
11
- suri do
12
- mint_ids true
13
- id_namespace 'druid'
14
- url 'https://lyberservices-dev.stanford.edu'
15
- user 'labware'
16
- pass 'lyberteam'
17
- end
18
-
19
- metadata do
20
- exist.url 'http://viewer:l3l%40nd@lyberapps-dev.stanford.edu/exist/rest/'
21
- catalog.url 'http://lyberservices-prod.stanford.edu/catalog/mods'
22
- end
23
-
24
- stacks do
25
- document_cache_host 'purl-dev.stanford.edu'
26
- local_stacks_root '/stacks'
27
- local_document_cache_root '/purl/document_cache'
28
- local_workspace_root '/dor/workspace'
29
- end
30
-
31
- solrizer.url 'http://sul-solr.stanford.edu/solr/argo_test'
32
- fedora.url 'https://sul-dor-test.stanford.edu/fedora'
33
- workflow.url 'https://lyberservices-dev.stanford.edu/workflow/'
34
- dor_services.url 'https://dorAdmin:dorAdmin@sul-lyberservices-dev.stanford.edu/dor'
35
-
36
- cleanup do
37
- local_workspace_root '/dor/workspace'
38
- local_export_home '/dor/export'
39
- end
40
-
41
- sdr do
42
- url 'https://sdrAdmin:sdrAdmin@sdr-services-test.stanford.edu/sdr/'
43
- local_workspace_root '/dor/workspace'
44
- local_export_home '/dor/export'
45
- datastreams do
46
- administrativeMetadata 'optional'
47
- contentMetadata 'optional'
48
- descMetadata 'required'
49
- defaultObjectRights 'optional'
50
- events 'optional'
51
- embargoMetadata 'optional'
52
- identityMetadata 'required'
53
- provenanceMetadata 'required'
54
- relationshipMetadata 'required'
55
- rightsMetadata 'optional'
56
- roleMetadata 'optional'
57
- sourceMetadata 'optional'
58
- technicalMetadata 'optional'
59
- versionMetadata 'required'
60
- workflows 'optional'
61
- end
62
- end
63
-
64
- accessioning_robot_sleep_time 30
65
-
66
- end
67
-
68
- # External application locations
69
- JHOVE_HOME = File.join(ENV['HOME'], 'jhoveToolkit')
@@ -1,402 +0,0 @@
1
- require 'open-uri'
2
- require 'retries'
3
-
4
- module Dor
5
- module Releaseable
6
- extend ActiveSupport::Concern
7
- # include Itemizable
8
- included do
9
- has_metadata :name => 'contentMetadata', :type => Dor::ContentMetadataDS, :label => 'Content Metadata', :control_group => 'M'
10
- end
11
-
12
- DIFF_FILENAME = 'cm_inv_diff'
13
- DIFF_QUERY = DIFF_FILENAME.tr('_', '-')
14
-
15
- # Deletes all cm_inv_diff files in the workspace for the Item
16
- def clear_diff_cache
17
- if Dor::Config.stacks.local_workspace_root.nil?
18
- raise ArgumentError, 'Missing Dor::Config.stacks.local_workspace_root'
19
- end
20
- druid = DruidTools::Druid.new(pid, Dor::Config.stacks.local_workspace_root)
21
- diff_pattern = File.join(druid.temp_dir, DIFF_FILENAME + '.*')
22
- FileUtils.rm_f Dir.glob(diff_pattern)
23
- end
24
-
25
- # Retrieves file difference manifest for contentMetadata from SDR
26
- #
27
- # @param [String] subset keyword for file attributes :shelve, :preserve, :publish. Default is :all.
28
- # @param [String] version
29
- # @return [String] XML contents of cm_inv_diff manifest
30
- def get_content_diff(subset = :all, version = nil)
31
- if Dor::Config.stacks.local_workspace_root.nil?
32
- raise Dor::ParameterError, 'Missing Dor::Config.stacks.local_workspace_root'
33
- end
34
- unless %w(all shelve preserve publish).include?(subset.to_s)
35
- raise Dor::ParameterError, "Invalid subset value: #{subset}"
36
- end
37
-
38
- # fetch content metadata inventory difference from SDR
39
- if Dor::Config.dor_services.rest_client.nil?
40
- raise Dor::ParameterError, 'Missing Dor::Config.dor_services.rest_client'
41
- end
42
- sdr_client = Dor::Config.dor_services.rest_client
43
- current_content = datastreams['contentMetadata'].content
44
- if current_content.nil?
45
- raise Dor::Exception, 'Missing contentMetadata datastream'
46
- end
47
- query_string = { :subset => subset.to_s }
48
- query_string[:version] = version.to_s unless version.nil?
49
- query_string = URI.encode_www_form(query_string)
50
- sdr_query = "sdr/objects/#{pid}/#{DIFF_QUERY}?#{query_string}"
51
- response = sdr_client[sdr_query].post(current_content, :content_type => 'application/xml')
52
- response
53
- end
54
-
55
- # Add release tags to an item and initialize the item release workflow
56
- # Each tag should be of the form !{:tag => 'Fitch : Batch2', :what => 'self', :to => 'Searchworks', :who => 'petucket', :release => true}
57
- # @param release_tags [Hash, Array<Hash>] hash of a single release tag or an array of many such hashes
58
- # @raise [ArgumentError] Raised if the tags are improperly supplied
59
- def add_release_nodes_and_start_releaseWF(release_tags)
60
- release_tags = [release_tags] unless release_tags.is_a?(Array)
61
-
62
- # Add in each tag
63
- release_tags.each do |r_tag|
64
- add_release_node(r_tag[:release], r_tag)
65
- end
66
-
67
- # Save item to dor so the robots work with the latest data
68
- save
69
- initialize_workflow('releaseWF')
70
- end
71
-
72
- # Generate XML structure for inclusion to Purl
73
- # @return [String] The XML release node as a string, with ReleaseDigest as the root document
74
- def generate_release_xml
75
- builder = Nokogiri::XML::Builder.new do |xml|
76
- xml.releaseData {
77
- released_for.each do |project, released_value|
78
- xml.release(released_value['release'], :to => project)
79
- end
80
- }
81
- end
82
- builder.to_xml
83
- end
84
-
85
- # Determine projects in which an item is released
86
- # @param [Boolean] skip_live_purl set true to skip requesting from purl backend
87
- # @return [Hash{String => Boolean}] all namespaces, keys are Project name Strings, values are Boolean
88
- def released_for(skip_live_purl = false)
89
- released_hash = {}
90
-
91
- # Get the most recent self tag for all targets and retain their result since most recent self always trumps any other non self tags
92
- latest_self_tags = get_newest_release_tag get_self_release_tags(release_nodes)
93
- latest_self_tags.each do |key, payload|
94
- released_hash[key] = {'release' => payload['release']}
95
- end
96
-
97
- # With Self Tags resolved we now need to deal with tags on all sets this object is part of.
98
- # 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.
99
- # This will be where we store all tags that apply, regardless of their timestamp:
100
- potential_applicable_release_tags = get_tags_for_what_value(get_release_tags_for_item_and_all_governing_sets, 'collection')
101
- administrative_tags = tags # Get admin tags once here and pass them down
102
-
103
- # 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.
104
- # In a nil case, the lack of an explicit false tag we do nothing.
105
- (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
106
- latest_tag = latest_applicable_release_tag_in_array(potential_applicable_release_tags[key], administrative_tags)
107
- next if latest_tag.nil? # Otherwise, we have a valid tag, record it
108
- released_hash[key] = {'release' => latest_tag['release']}
109
- end
110
-
111
- # 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
112
- add_tags_from_purl(released_hash) unless skip_live_purl
113
- released_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 get_self_release_tags(tags)
120
- get_tags_for_what_value(tags, 'self')
121
- end
122
-
123
- # Take an item and get all of its release tags and all tags on collections it is a member of it
124
- # @return [Hash] a hash of all tags
125
- def get_release_tags_for_item_and_all_governing_sets
126
- return_tags = release_nodes || {}
127
- collections.each do |collection|
128
- 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
129
- end
130
- return_tags
131
- end
132
-
133
- # Take two hashes of tags and combine them, will not overwrite but will enforce uniqueness of the tags
134
- # @param hash_one [Hash] a hash of tags obtained via Dor::Item.release_tags or matching format
135
- # @param hash_two [Hash] a hash of tags obtained via Dor::Item.release_tags or matching format
136
- # @return [Hash] the combined hash with uniquiness enforced
137
- def combine_two_release_tag_hashes(hash_one, hash_two)
138
- hash_two.keys.each do |key|
139
- hash_one[key] = hash_two[key] if hash_one[key].nil?
140
- hash_one[key] = (hash_one[key] + hash_two[key]).uniq unless hash_one[key].nil?
141
- end
142
- hash_one
143
- end
144
-
145
- # Take a hash of tags and return all tags with the matching what target
146
- # @param tags [Hash] a hash of tags obtained via Dor::Item.release_tags or matching format
147
- # @param what_target [String] the target for the 'what' key, self or collection
148
- # @return [Hash] a hash of self tags for each to value
149
- def get_tags_for_what_value(tags, what_target)
150
- return_hash = {}
151
- tags.keys.each do |key|
152
- self_tags = tags[key].select {|tag| tag['what'] == what_target.downcase}
153
- return_hash[key] = self_tags if self_tags.size > 0
154
- end
155
- return_hash
156
- end
157
-
158
- # Take a hash of tags as obtained via Dor::Item.release_tags and returns the newest tag for each namespace
159
- # @param tags [Hash] a hash of tags obtained via Dor::Item.release_tags or matching format
160
- # @return [Hash] a hash of latest tags for each to value
161
- def get_newest_release_tag(tags)
162
- Hash[tags.map {|key, val| [key, newest_release_tag_in_an_array(val)]}]
163
- end
164
-
165
- # Takes an array of release tags and returns the most recent one
166
- # @param array_of_tags [Array] an array of hashes, each hash a release tag
167
- # @return [Hash] the most recent tag
168
- def newest_release_tag_in_an_array(array_of_tags)
169
- latest_tag_in_array = array_of_tags[0] || {}
170
- array_of_tags.each do |tag|
171
- latest_tag_in_array = tag if tag['when'] > latest_tag_in_array['when']
172
- end
173
- latest_tag_in_array
174
- end
175
-
176
- # Takes a tag and returns true or false if it applies to the specific item
177
- # @param release_tag [Hash] the tag in a hashed form
178
- # @param admin_tags [Array] the administrative tags on an item, if not supplied it will attempt to retrieve them
179
- # @return [Boolean] true or false if it applies (not true or false if it is released, that is the release_tag data)
180
- def does_release_tag_apply(release_tag, admin_tags = false)
181
- # Is the tag global or restricted
182
- return true if release_tag['tag'].nil? # no specific tag specificied means this tag is global to all members of the collection
183
- 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
184
- admin_tags.include?(release_tag['tag'])
185
- end
186
-
187
- # Takes an array of release tags and returns the most recent one that applies to this item
188
- # @param release_tags [Array] an array of release tags in hashed form
189
- # @param admin_tags [Array] the administrative tags on an on item
190
- # @return [Hash] the tag, or nil if none applicable
191
- def latest_applicable_release_tag_in_array(release_tags, admin_tags)
192
- newest_tag = newest_release_tag_in_an_array(release_tags)
193
- return newest_tag if does_release_tag_apply(newest_tag, admin_tags)
194
-
195
- # The latest tag wasn't applicable, slice it off and try again
196
- # 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
197
- release_tags.slice!(release_tags.index(newest_tag))
198
-
199
- return latest_applicable_release_tag_in_array(release_tags, admin_tags) if release_tags.size > 0 # Try again after dropping the inapplicable
200
- nil # We're out of tags, no applicable ones
201
- end
202
-
203
- # Helper method to get the release tags as a nodeset
204
- # @return [Nokogiri::XML::NodeSet] all release tags and their attributes
205
- def release_tags
206
- release_tags = identityMetadata.ng_xml.xpath('//release')
207
- return_hash = {}
208
- release_tags.each do |release_tag|
209
- hashed_node = release_tag_node_to_hash(release_tag)
210
- if !return_hash[hashed_node[:to]].nil?
211
- return_hash[hashed_node[:to]] << hashed_node[:attrs]
212
- else
213
- return_hash[hashed_node[:to]] = [hashed_node[:attrs]]
214
- end
215
- end
216
- return_hash
217
- end
218
-
219
- # Convert one release element into a Hash
220
- # @param rtag [Nokogiri::XML::Element] the release tag element
221
- # @return [Hash{:to, :attrs => String, Hash}] in the form of !{:to => String :attrs = Hash}
222
- def release_tag_node_to_hash(rtag)
223
- to = 'to'
224
- release = 'release'
225
- when_word = 'when' # TODO: Make to and when_word load from some config file instead of hardcoded here
226
- attrs = rtag.attributes
227
- return_hash = { :to => attrs[to].value }
228
- attrs.tap { |a| a.delete(to) }
229
- attrs[release] = rtag.text.downcase == 'true' # save release as a boolean
230
- return_hash[:attrs] = attrs
231
-
232
- # convert all the attrs beside :to to strings, they are currently Nokogiri::XML::Attr
233
- (return_hash[:attrs].keys - [to]).each do |a|
234
- return_hash[:attrs][a] = return_hash[:attrs][a].to_s if a != release
235
- end
236
-
237
- return_hash[:attrs][when_word] = Time.parse(return_hash[:attrs][when_word]) # convert when to a datetime
238
- return_hash
239
- end
240
-
241
- # Determine if the supplied tag is a valid release tag that meets all requirements
242
- #
243
- # @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
244
- # @raise [RuntimeError] Raises an error of the first fault in the release tag
245
- # @return [Boolean] Returns true if no errors found
246
- def valid_release_attributes_and_tag(tag, attrs = {})
247
- 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?
248
- [:who, :to, :what].each do |check_attr|
249
- raise ArgumentError, "#{check_attr} not supplied as a String" if attrs[check_attr].class != String
250
- end
251
-
252
- what_correct = false
253
- %w(self collection).each do |allowed_what_value|
254
- what_correct = true if attrs[:what] == allowed_what_value
255
- end
256
- raise ArgumentError, ':what must be self or collection' unless what_correct
257
- raise ArgumentError, 'the value set for this tag is not a boolean' if !!tag != tag # rubocop:disable Style/DoubleNegation
258
- validate_tag_format(attrs[:tag]) unless attrs[:tag].nil? # Will Raise exception if invalid tag
259
- true
260
- end
261
-
262
- # Add a release node for the item
263
- # Will use the current time if timestamp not supplied. You can supply a timestap for correcting history, etc if desired
264
- # Timestamp will be calculated by the function, if no displayType is passed in, it will default to file
265
- #
266
- # @param release [Boolean] True or false for the release node
267
- # @param attrs [hash] A hash of any attributes to be placed onto the tag
268
- # @return [Nokogiri::XML::Element] the tag added if successful
269
- # @raise [ArgumentError] Raised if attributes are improperly supplied
270
- #
271
- # @example
272
- # item.add_tag(true,:release,{:tag=>'Fitch : Batch2',:what=>'self',:to=>'Searchworks',:who=>'petucket', :displayType='filmstrip'})
273
- def add_release_node(release, attrs = {})
274
- identity_metadata_ds = identityMetadata
275
- attrs[:when] = Time.now.utc.iso8601 if attrs[:when].nil? # add the timestamp
276
- attrs[:displayType] = 'file' if attrs[:displayType].nil? # default to file is no display type is passed
277
- valid_release_attributes(release, attrs)
278
-
279
- # Remove the old displayType and then add the one for this tag
280
- remove_displayTypes
281
- identity_metadata_ds.add_value(:displayType, attrs[:displayType], {})
282
- identity_metadata_ds.add_value(:release, release.to_s, attrs)
283
- end
284
-
285
- # Determine if the supplied tag is a valid release node that meets all requirements
286
- #
287
- # @param tag [Boolean] True or false for the release node
288
- # @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,
289
- # @raise [ArgumentError] Raises an error of the first fault in the release tag
290
- # @return [Boolean] Returns true if no errors found
291
- def valid_release_attributes(tag, attrs = {})
292
- 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?
293
- [:who, :to, :what].each do |check_attr|
294
- raise ArgumentError, "#{check_attr} not supplied as a String" if attrs[check_attr].class != String
295
- end
296
-
297
- what_correct = false
298
- %w(self collection).each do |allowed_what_value|
299
- what_correct = true if attrs[:what] == allowed_what_value
300
- end
301
- raise ArgumentError, ':what must be self or collection' unless what_correct
302
- raise ArgumentError, 'the value set for this tag is not a boolean' if !!tag != tag # rubocop:disable Style/DoubleNegation
303
- raise ArgumentError, ':displayType must be passed in as a String' unless attrs[:displayType].class == String
304
-
305
- validate_tag_format(attrs[:tag]) unless attrs[:tag].nil? # Will Raise exception if invalid tag
306
- true
307
- end
308
-
309
- # Helper method to get the release nodes as a nodeset
310
- # @return [Nokogiri::XML::NodeSet] of all release tags and their attributes
311
- def release_nodes
312
- release_tags = identityMetadata.ng_xml.xpath('//release')
313
- return_hash = {}
314
- release_tags.each do |release_tag|
315
- hashed_node = release_tag_node_to_hash(release_tag)
316
- if !return_hash[hashed_node[:to]].nil?
317
- return_hash[hashed_node[:to]] << hashed_node[:attrs]
318
- else
319
- return_hash[hashed_node[:to]] = [hashed_node[:attrs]]
320
- end
321
- end
322
- return_hash
323
- end
324
-
325
- # Get a list of all release nodes found in a purl document
326
- # Fetches purl xml for a druid
327
- # @raise [OpenURI::HTTPError]
328
- # @return [Nokogiri::HTML::Document] parsed XML for the druid or an empty document if no purl is found
329
- def get_xml_from_purl
330
- url = form_purl_url
331
- handler = proc do |exception, attempt_number, total_delay|
332
- # We assume a 404 means the document has never been published before and thus has no purl
333
- Dor.logger.warn "[Attempt #{attempt_number}] GET #{url} -- #{exception.class}: #{exception.message}; #{total_delay} seconds elapsed."
334
- raise exception unless exception.is_a? OpenURI::HTTPError
335
- return Nokogiri::HTML::Document.new if exception.message.strip == '404' # strip is needed if the actual message is "404 "
336
- end
337
-
338
- with_retries(:max_retries => 3, :base_sleep_seconds => 3, :max_sleep_seconds => 5, :handler => handler) do |attempt|
339
- # If you change the method used for opening the webpage, you can change the :rescue param to handle the new method's errors
340
- Dor.logger.info "[Attempt #{attempt}] GET #{url}"
341
- return Nokogiri::HTML(OpenURI.open_uri(url))
342
- end
343
- end
344
-
345
- # Since purl does not use the druid: prefix but much of dor does, use this function to strip the druid: if needed
346
- # @return [String] the druid sans the druid: or if there was no druid: prefix, the entire string you passed
347
- def remove_druid_prefix
348
- druid_prefix = 'druid:'
349
- return id.split(druid_prefix)[1] if id.split(druid_prefix).size > 1
350
- druid
351
- end
352
-
353
- # Take the and create the entire purl url that will usable for the open method in open-uri, returns http
354
- # @return [String] the full url
355
- def form_purl_url
356
- 'https://' + Dor::Config.stacks.document_cache_host + "/#{remove_druid_prefix}.xml"
357
- end
358
-
359
- # Pull all release nodes from the public xml obtained via the purl query
360
- # @param doc [Nokogiri::HTML::Document] The druid of the object you want
361
- # @return [Array] An array containing all the release tags
362
- def get_release_tags_from_purl_xml(doc)
363
- nodes = doc.xpath('//html/body/publicobject/releasedata').children
364
- # We only want the nodes with a name that isn't text
365
- nodes.reject {|n| n.name.nil? || n.name.downcase == 'text'}.map {|n| n.attr('to')}.uniq
366
- end
367
-
368
- # Pull all release nodes from the public xml obtained via the purl query
369
- # @return [Array] An array containing all the release tags
370
- def get_release_tags_from_purl
371
- get_release_tags_from_purl_xml(get_xml_from_purl)
372
- end
373
-
374
- # This function calls purl and gets a list of all release tags currently in purl. It then compares to the list you have generated.
375
- # Any tag that is on purl, but not in the newly generated list is added to the new list with a value of false.
376
- # @param new_tags [Hash{String => Boolean}] all new tags in the form of !{"Project" => Boolean}
377
- # @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
378
- def add_tags_from_purl(new_tags)
379
- tags_currently_in_purl = get_release_tags_from_purl
380
- missing_tags = tags_currently_in_purl.map(&:downcase) - new_tags.keys.map(&:downcase)
381
- missing_tags.each do |missing_tag|
382
- new_tags[missing_tag.capitalize] = {'release' => false}
383
- end
384
- new_tags
385
- end
386
-
387
- def to_solr(solr_doc = {}, *args)
388
- super(solr_doc, *args)
389
-
390
- # TODO: sort of worried about the performance impact in bulk reindex
391
- # situations, since released_for recurses all parent collections. jmartin 2015-07-14
392
- released_for(true).each { |key, val|
393
- add_solr_value(solr_doc, 'released_to', key, :symbol, []) if val
394
- }
395
-
396
- # TODO: need to solrize whether item is released to purl? does released_for return that?
397
- # logic is: "True when there is a published lifecycle and Access Rights is anything but Dark"
398
-
399
- solr_doc
400
- end
401
- end
402
- end