dor-services 2.2.4 → 4.4.10

Sign up to get free protection for your applications and to get access to all the features.
Files changed (106) hide show
  1. checksums.yaml +15 -0
  2. data/bin/dor-indexer +108 -0
  3. data/bin/dor-indexerd +73 -0
  4. data/bin/nokogiri +19 -0
  5. data/bin/rake +19 -0
  6. data/bin/ruby_noexec_wrapper +14 -0
  7. data/bin/solrizer +19 -0
  8. data/bin/solrizerd +19 -0
  9. data/config/certs/README +1 -0
  10. data/config/config_defaults.yml +62 -0
  11. data/config/dev_console_env.rb.example +67 -0
  12. data/config/predicate_mappings.yml +55 -0
  13. data/lib/dor-services.rb +152 -19
  14. data/lib/dor/config.rb +133 -35
  15. data/lib/dor/datastreams/administrative_metadata_ds.rb +84 -0
  16. data/lib/dor/datastreams/content_metadata_ds.rb +337 -0
  17. data/lib/dor/datastreams/datastream_spec_solrizer.rb +18 -0
  18. data/lib/dor/datastreams/default_object_rights_ds.rb +52 -0
  19. data/lib/dor/datastreams/desc_metadata_ds.rb +39 -0
  20. data/lib/{datastreams → dor/datastreams}/embargo_metadata_ds.rb +25 -20
  21. data/lib/{datastreams → dor/datastreams}/events_ds.rb +14 -9
  22. data/lib/dor/datastreams/identity.xsl +8 -0
  23. data/lib/dor/datastreams/identity_metadata_ds.rb +112 -0
  24. data/lib/dor/datastreams/role_metadata_ds.rb +51 -0
  25. data/lib/dor/datastreams/simple_dublin_core_ds.rb +45 -0
  26. data/lib/dor/datastreams/version_metadata_ds.rb +214 -0
  27. data/lib/dor/datastreams/workflow_definition_ds.rb +113 -0
  28. data/lib/dor/datastreams/workflow_ds.rb +103 -0
  29. data/lib/dor/exceptions.rb +0 -1
  30. data/lib/dor/migrations/content_metadata_ds/change_content_type.rb +7 -0
  31. data/lib/dor/migrations/identifiable/assert_adminPolicy.rb +9 -0
  32. data/lib/dor/migrations/identifiable/fix_model_assertions.rb +13 -0
  33. data/lib/dor/migrations/identifiable/record_remediation.rb +18 -0
  34. data/lib/dor/migrations/identifiable/uriify_augmented_contentlocation_refs.rb +18 -0
  35. data/lib/dor/migrations/identifiable/uriify_contentlocation_refs.rb +18 -0
  36. data/lib/dor/migrations/processable/unify_workflows.rb +17 -0
  37. data/lib/dor/migrations/versionable/add_missing_version_md.rb +9 -0
  38. data/lib/dor/models/admin_policy_object.rb +16 -0
  39. data/lib/dor/models/assembleable.rb +14 -0
  40. data/lib/dor/models/collection.rb +14 -0
  41. data/lib/dor/models/contentable.rb +227 -0
  42. data/lib/dor/models/describable.rb +194 -0
  43. data/lib/dor/models/discoverable.rb +66 -0
  44. data/lib/dor/models/editable.rb +267 -0
  45. data/lib/dor/models/embargoable.rb +97 -0
  46. data/lib/dor/models/eventable.rb +12 -0
  47. data/lib/dor/models/governable.rb +162 -0
  48. data/lib/dor/models/identifiable.rb +211 -0
  49. data/lib/dor/models/item.rb +44 -0
  50. data/lib/dor/models/itemizable.rb +66 -0
  51. data/lib/dor/{mods2dc.xslt → models/mods2dc.xslt} +39 -12
  52. data/lib/dor/models/preservable.rb +50 -0
  53. data/lib/dor/models/processable.rb +229 -0
  54. data/lib/dor/models/publishable.rb +74 -0
  55. data/lib/dor/models/set.rb +12 -0
  56. data/lib/dor/models/shelvable.rb +27 -0
  57. data/lib/dor/models/upgradable.rb +74 -0
  58. data/lib/dor/models/versionable.rb +94 -0
  59. data/lib/dor/models/workflow_object.rb +54 -0
  60. data/lib/dor/services/cleanup_service.rb +47 -0
  61. data/lib/dor/services/digital_stacks_service.rb +55 -0
  62. data/lib/dor/services/merge_service.rb +96 -0
  63. data/lib/dor/{metadata_handlers → services/metadata_handlers}/catalog_handler.rb +0 -2
  64. data/lib/dor/{metadata_handlers → services/metadata_handlers}/mdtoolkit_handler.rb +0 -2
  65. data/lib/dor/{metadata_service.rb → services/metadata_service.rb} +1 -3
  66. data/lib/dor/services/registration_service.rb +181 -0
  67. data/lib/dor/services/sdr_ingest_service.rb +181 -0
  68. data/lib/dor/services/search_service.rb +131 -0
  69. data/lib/dor/services/suri_service.rb +32 -0
  70. data/lib/dor/services/technical_metadata_service.rb +226 -0
  71. data/lib/dor/{tei2dc.xslt → services/tei2dc.xslt} +0 -0
  72. data/lib/dor/utils/ng_tidy.rb +37 -0
  73. data/lib/dor/utils/predicate_patch.rb +23 -0
  74. data/lib/dor/utils/solr_doc_helper.rb +9 -0
  75. data/lib/dor/utils/utc_date_field_mapper.rb +7 -0
  76. data/lib/dor/version.rb +3 -0
  77. data/lib/dor/workflow/document.rb +131 -0
  78. data/lib/dor/workflow/graph.rb +166 -0
  79. data/lib/dor/workflow/process.rb +99 -0
  80. data/lib/gsearch/demoFoxmlToSolr.xslt +340 -122
  81. data/lib/tasks/dor.rake +39 -0
  82. metadata +494 -384
  83. data/lib/datastreams/content_metadata_ds.rb +0 -12
  84. data/lib/datastreams/identity_metadata_ds.rb +0 -28
  85. data/lib/datastreams/ng_tidy.rb +0 -19
  86. data/lib/datastreams/simple_dublin_core_ds.rb +0 -23
  87. data/lib/datastreams/workflow_definition_ds.rb +0 -105
  88. data/lib/datastreams/workflow_ds.rb +0 -16
  89. data/lib/dor/admin_policy_object.rb +0 -11
  90. data/lib/dor/base.rb +0 -81
  91. data/lib/dor/cleanup_service.rb +0 -32
  92. data/lib/dor/digital_stacks_service.rb +0 -82
  93. data/lib/dor/druid_utils.rb +0 -41
  94. data/lib/dor/embargo.rb +0 -41
  95. data/lib/dor/item.rb +0 -141
  96. data/lib/dor/provenance_metadata_service.rb +0 -65
  97. data/lib/dor/registration_service.rb +0 -87
  98. data/lib/dor/rsolr.rb +0 -27
  99. data/lib/dor/sdr_ingest_service.rb +0 -117
  100. data/lib/dor/search_service.rb +0 -86
  101. data/lib/dor/suri_service.rb +0 -37
  102. data/lib/dor/workflow_object.rb +0 -13
  103. data/lib/dor/workflow_service.rb +0 -111
  104. data/lib/xml_models/foxml.rb +0 -261
  105. data/lib/xml_models/identity_metadata/dublin_core.rb +0 -119
  106. data/lib/xml_models/identity_metadata/identity_metadata.rb +0 -288
@@ -0,0 +1,229 @@
1
+ require 'equivalent-xml'
2
+
3
+ module Dor
4
+ module Processable
5
+ extend ActiveSupport::Concern
6
+ include SolrDocHelper
7
+ include Upgradable
8
+
9
+ included do
10
+ has_metadata :name => 'workflows', :type => Dor::WorkflowDs, :label => 'Workflows', :control_group => 'E'
11
+ after_initialize :set_workflows_datastream_location
12
+ end
13
+
14
+ def set_workflows_datastream_location
15
+ # This is a work-around for some strange logic in ActiveFedora that
16
+ # don't allow self.workflows.new? to work if we load the object using
17
+ # .load_instance_from_solr.
18
+ return if self.respond_to? :inner_object and self.inner_object.is_a? ActiveFedora::SolrDigitalObject
19
+
20
+ if self.workflows.new?
21
+ workflows.mimeType = 'application/xml'
22
+ workflows.dsLocation = File.join(Dor::Config.workflow.url,"dor/objects/#{self.pid}/workflows")
23
+ end
24
+ end
25
+
26
+ def empty_datastream?(datastream)
27
+ if datastream.new?
28
+ true
29
+ elsif datastream.class.respond_to?(:xml_template)
30
+ datastream.content.to_s.empty? or EquivalentXml.equivalent?(datastream.content, datastream.class.xml_template)
31
+ else
32
+ datastream.content.to_s.empty?
33
+ end
34
+ end
35
+
36
+ # Takes the name of a datastream, as a string.
37
+ # Tries to find a file for the datastream.
38
+ # Returns the path to it or nil.
39
+ def find_metadata_file(datastream)
40
+ druid = DruidTools::Druid.new(pid, Dor::Config.stacks.local_workspace_root)
41
+ return druid.find_metadata("#{datastream}.xml")
42
+ end
43
+
44
+ # Takes the name of a datastream, as a string (fooMetadata).
45
+ # Builds that datastream using the content of a file if such a file
46
+ # exists and is newer than the object's current datastream; otherwise,
47
+ # builds the datastream by calling build_fooMetadata_datastream.
48
+ def build_datastream(datastream, force = false, is_required = false)
49
+ # See if the datastream exists as a file and if the file's
50
+ # timestamp is newer than the datastream's timestamp.
51
+ ds = datastreams[datastream]
52
+ filename = find_metadata_file(datastream)
53
+ use_file = filename && (ds.createDate.nil? || File.mtime(filename) >= ds.createDate)
54
+ # Build datastream.
55
+ if use_file
56
+ content = File.read(filename)
57
+ ds.content = content
58
+ ds.ng_xml = Nokogiri::XML(content) if ds.respond_to?(:ng_xml)
59
+ ds.save unless ds.digital_object.new?
60
+ elsif force or empty_datastream?(ds)
61
+ meth = "build_#{datastream}_datastream".to_sym
62
+ if respond_to?(meth)
63
+ content = self.send(meth, ds)
64
+ ds.save unless ds.digital_object.new?
65
+ end
66
+ end
67
+ # Check for success.
68
+ if is_required && empty_datastream?(ds)
69
+ raise "Required datastream #{datastream} could not be populated!"
70
+ end
71
+ return ds
72
+ end
73
+
74
+ def cleanup()
75
+ CleanupService.cleanup(self)
76
+ end
77
+
78
+ def milestones
79
+ Dor::WorkflowService.get_milestones('dor',self.pid)
80
+ end
81
+ def status(include_time=false)
82
+ current_version='1'
83
+ begin
84
+ current_version = self.versionMetadata.current_version_id
85
+ rescue
86
+ end
87
+ status = 0
88
+ version = ''
89
+ #verbage we want to use to describe an item when it has completed a particular step
90
+ status_hash={
91
+ 0 => 'Unknown Status', #if there are no milestones for the current version, someone likely messed up the versioning process.
92
+ 1 => 'Registered',
93
+ 2 => 'In accessioning',
94
+ 3 => 'In accessioning (described)',
95
+ 4 => 'In accessioning (described, published)',
96
+ 5 => 'In accessioning (described, published, deposited)',
97
+ 6 => 'Accessioned',
98
+ 7 => 'Accessioned (indexed)',
99
+ 8 => 'Accessioned (indexed, ingested)',
100
+ 9 => 'Opened'
101
+ }
102
+ #milestones from accesioning and the order they happen in
103
+ steps={
104
+ 'registered' => 1,
105
+ 'submitted' => 2,
106
+ 'described' => 3,
107
+ 'published' => 4,
108
+ 'deposited' => 5,
109
+ 'accessioned' => 6,
110
+ 'indexed' => 7,
111
+ 'shelved' => 8,
112
+ 'opened' => 1
113
+ }
114
+ status_time=nil
115
+
116
+ current=false
117
+ versions=[]
118
+ result=""
119
+ current_milestones = []
120
+ #only get steps that are part of accessioning and part of the current version. That can mean they were archived with the current version number, or they might be active (no version number)
121
+ milestones.each do |m|
122
+ if steps.keys.include?(m[:milestone]) and (m[:version].nil? or m[:version] == current_version)
123
+ current_milestones << m unless m[:milestone] == 'registered' and current_version.to_i > 1
124
+ end
125
+ end
126
+ status = 0
127
+ status_time = ''
128
+ #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
129
+ current_milestones.each do |m|
130
+ name=m[:milestone]
131
+ time=m[:at].utc.xmlschema
132
+ if steps.keys.include? name
133
+ if steps[name] > status
134
+ status = steps[name]
135
+ status_time=time
136
+ end
137
+ end
138
+ end
139
+ #use the translation table to get the appropriate verbage for the latest step
140
+ result='v'+current_version.to_s+' '+status_hash[status].to_s
141
+ result +=" #{format_date(status_time)}" if include_time
142
+ result
143
+ end
144
+
145
+ def to_solr(solr_doc=Hash.new, *args)
146
+ super(solr_doc, *args)
147
+ sortable_milestones = {}
148
+ current_version='1'
149
+ begin
150
+ current_version = self.versionMetadata.current_version_id
151
+ rescue
152
+ end
153
+ current_version_num=current_version.to_i
154
+
155
+ if self.respond_to?('versionMetadata')
156
+ #add an entry with version id, tag and description for each version
157
+ while current_version_num > 0
158
+ add_solr_value(solr_doc, 'versions', current_version_num.to_s + ';' + self.versionMetadata.tag_for_version(current_version_num.to_s) + ';' + self.versionMetadata.description_for_version(current_version_num.to_s), :string, [:displayable])
159
+ current_version_num -= 1
160
+ end
161
+ end
162
+
163
+ self.milestones.each do |milestone|
164
+ timestamp = milestone[:at].utc.xmlschema
165
+ sortable_milestones[milestone[:milestone]] ||= []
166
+ sortable_milestones[milestone[:milestone]] << timestamp
167
+ add_solr_value(solr_doc, 'lifecycle', milestone[:milestone], :string, [:searchable, :facetable])
168
+ unless milestone[:version]
169
+ milestone[:version]=current_version
170
+ end
171
+ add_solr_value(solr_doc, 'lifecycle', "#{milestone[:milestone]}:#{timestamp};#{milestone[:version]}", :string, [:displayable])
172
+ end
173
+
174
+ sortable_milestones.each do |milestone, unordered_dates|
175
+ dates = unordered_dates.sort
176
+ #create the published_dt and published_day fields and the like
177
+ add_solr_value(solr_doc, milestone+'_day', DateTime.parse(dates.last).beginning_of_day.utc.xmlschema.split('T').first, :string, [:searchable, :facetable])
178
+ add_solr_value(solr_doc, milestone, dates.first, :date, [:searchable, :facetable])
179
+
180
+ #fields for OAI havester to sort on
181
+ add_solr_value(solr_doc, "#{milestone}_earliest_dt", dates.first, :date, [:sortable])
182
+ add_solr_value(solr_doc, "#{milestone}_latest_dt", dates.last, :date, [:sortable])
183
+
184
+ #for future faceting
185
+ add_solr_value(solr_doc, "#{milestone}_earliest", dates.first, :date, [:searchable, :facetable])
186
+ add_solr_value(solr_doc, "#{milestone}_latest", dates.last, :date, [ :searchable, :facetable])
187
+
188
+ end
189
+ add_solr_value(solr_doc,"status",status,:string, [:displayable])
190
+
191
+ if sortable_milestones['opened']
192
+ #add a facetable field for the date when the open version was opened
193
+ opened_date=sortable_milestones['opened'].sort.last
194
+ add_solr_value(solr_doc, "version_opened", DateTime.parse(opened_date).beginning_of_day.utc.xmlschema.split('T').first, :string, [ :searchable, :facetable])
195
+ end
196
+ add_solr_value(solr_doc, "current_version", current_version.to_s, :string, [ :displayable , :facetable])
197
+ add_solr_value(solr_doc, "last_modified_day", self.modified_date.to_s.split('T').first, :string, [ :facetable ])
198
+ add_solr_value(solr_doc, "rights", rights, :string, [:facetable]) if self.respond_to? :rights
199
+ solr_doc
200
+ end
201
+
202
+ # Initilizes workflow for the object in the workflow service
203
+ # It will set the priorty of the new workflow to the current_priority if it is > 0
204
+ # @param [String] name of the workflow to be initialized
205
+ # @param [String] repo name of the repository to create workflow for
206
+ # @param [Boolean] create_ds create a 'workflows' datastream in Fedora for the object
207
+ def initialize_workflow(name, repo='dor', create_ds=true, priority=0)
208
+ priority = workflows.current_priority if priority == 0
209
+ opts = { :create_ds => create_ds }
210
+ opts[:priority] = priority if(priority > 0)
211
+ Dor::WorkflowService.create_workflow(repo, self.pid, name, Dor::WorkflowObject.initial_workflow(name), opts)
212
+ end
213
+ private
214
+ #handles formating utc date/time to human readable
215
+ def format_date datetime
216
+ begin
217
+ zone = ActiveSupport::TimeZone.new("Pacific Time (US & Canada)")
218
+ d = datetime.is_a?(Time) ? datetime : DateTime.parse(datetime).in_time_zone(zone)
219
+ I18n.l(d)
220
+ rescue
221
+ d = datetime.is_a?(Time) ? datetime : Time.parse(datetime.to_s)
222
+ d.strftime('%Y-%m-%d %I:%M%p')
223
+ end
224
+ end
225
+ end
226
+
227
+
228
+ end
229
+
@@ -0,0 +1,74 @@
1
+ require 'dor/datastreams/content_metadata_ds'
2
+
3
+ module Dor
4
+ module Publishable
5
+ extend ActiveSupport::Concern
6
+ include Identifiable
7
+ include Governable
8
+ include Describable
9
+ include Itemizable
10
+
11
+ included do
12
+ has_metadata :name => "rightsMetadata", :type => ActiveFedora::OmDatastream, :label => 'Rights Metadata'
13
+ end
14
+
15
+ def build_rightsMetadata_datastream(ds)
16
+ content_ds = self.admin_policy_object.first.datastreams['defaultObjectRights']
17
+ ds.dsLabel = 'Rights Metadata'
18
+ ds.ng_xml = content_ds.ng_xml.clone
19
+ ds.content = ds.ng_xml.to_xml
20
+ end
21
+
22
+ def public_relationships
23
+ include_elements = ['fedora:isMemberOf','fedora:isMemberOfCollection']
24
+ rels_doc = Nokogiri::XML(self.datastreams['RELS-EXT'].content)
25
+ rels_doc.xpath('/rdf:RDF/rdf:Description/*', { 'rdf' => 'http://www.w3.org/1999/02/22-rdf-syntax-ns#' }).each do |rel|
26
+ unless include_elements.include?([rel.namespace.prefix,rel.name].join(':'))
27
+ rel.next_sibling.remove if rel.next_sibling.content.strip.empty?
28
+ rel.remove
29
+ end
30
+ end
31
+ rels_doc
32
+ end
33
+
34
+ def public_xml
35
+ pub = Nokogiri::XML("<publicObject/>").root
36
+ pub['id'] = pid
37
+ pub['published'] = Time.now.xmlschema
38
+ pub.add_child(self.datastreams['identityMetadata'].ng_xml.root.clone)
39
+ pub.add_child(self.datastreams['contentMetadata'].public_xml.root.clone)
40
+ pub.add_child(self.datastreams['rightsMetadata'].ng_xml.root.clone)
41
+ rels = public_relationships.root
42
+ pub.add_child(rels.clone) unless rels.nil? # TODO: Should never be nil in practice; working around an ActiveFedora quirk for testing
43
+ pub.add_child(self.generate_dublin_core.root.clone)
44
+ Nokogiri::XML(pub.to_xml) { |x| x.noblanks }.to_xml { |config| config.no_declaration }
45
+ end
46
+
47
+ # Copies this object's public_xml to the Purl document cache if it is world discoverable
48
+ # otherwise, it prunes the object's metadata from the document cache
49
+ def publish_metadata
50
+ rights = datastreams['rightsMetadata'].ng_xml.clone.remove_namespaces!
51
+ if(rights.at_xpath("//rightsMetadata/access[@type='discover']/machine/world"))
52
+ dc_xml = self.generate_dublin_core.to_xml {|config| config.no_declaration}
53
+ DigitalStacksService.transfer_to_document_store(pid, dc_xml, 'dc')
54
+ DigitalStacksService.transfer_to_document_store(pid, self.datastreams['identityMetadata'].to_xml, 'identityMetadata')
55
+ DigitalStacksService.transfer_to_document_store(pid, self.datastreams['contentMetadata'].to_xml, 'contentMetadata')
56
+ DigitalStacksService.transfer_to_document_store(pid, self.datastreams['rightsMetadata'].to_xml, 'rightsMetadata')
57
+ DigitalStacksService.transfer_to_document_store(pid, public_xml, 'public')
58
+ if self.metadata_format == 'mods'
59
+ DigitalStacksService.transfer_to_document_store(pid, self.add_collection_reference, 'mods')
60
+ end
61
+ else
62
+ # Clear out the document cache for this item
63
+ DigitalStacksService.prune_purl_dir pid
64
+ end
65
+ end
66
+ #call the dor services app to have it publish the metadata
67
+ def publish_metadata_remotely
68
+ dor_services = RestClient::Resource.new(Config.dor_services.url+"/objects/#{pid}/publish")
69
+ dor_services.post ''
70
+ dor_services.url
71
+ end
72
+ end
73
+
74
+ end
@@ -0,0 +1,12 @@
1
+ module Dor
2
+ class Set < ::ActiveFedora::Base
3
+ include Identifiable
4
+ include Processable
5
+ include Governable
6
+ include Describable
7
+ include Publishable
8
+
9
+ has_many :members, :property => :is_member_of_collection, :inbound => true, :class_name => "ActiveFedora::Base"
10
+ has_object_type 'set'
11
+ end
12
+ end
@@ -0,0 +1,27 @@
1
+ require 'moab_stanford'
2
+
3
+ module Dor
4
+ module Shelvable
5
+ extend ActiveSupport::Concern
6
+ include Itemizable
7
+
8
+ # Push file changes for shelve-able files into the stacks
9
+ def shelve
10
+ inventory_diff_xml = self.get_content_diff(:shelve)
11
+ inventory_diff = Moab::FileInventoryDifference.parse(inventory_diff_xml)
12
+ content_group_diff = inventory_diff.group_difference("content")
13
+ deltas = content_group_diff.file_deltas
14
+
15
+ if content_group_diff.rename_require_temp_files(deltas[:renamed])
16
+ triplets = content_group_diff.rename_tempfile_triplets(deltas[:renamed])
17
+ DigitalStacksService.rename_in_stacks self.pid, triplets.collect{|old,new,temp| [old,temp]}
18
+ DigitalStacksService.rename_in_stacks self.pid, triplets.collect{|old,new,temp| [temp,new]}
19
+ else
20
+ DigitalStacksService.rename_in_stacks self.pid, deltas[:renamed]
21
+ end
22
+ DigitalStacksService.shelve_to_stacks self.pid, deltas[:modified] + deltas[:added] + deltas[:copyadded].collect{|old,new| new}
23
+ DigitalStacksService.remove_from_stacks self.pid, deltas[:deleted] + deltas[:copydeleted]
24
+ end
25
+
26
+ end
27
+ end
@@ -0,0 +1,74 @@
1
+ module Dor
2
+ module Upgradable
3
+
4
+ # The Upgradable mixin is responsible for making sure all DOR objects,
5
+ # concerns, and datastreams know how to upgrade themselves to the latest
6
+ # Chimera/DOR content standards.
7
+ #
8
+ # To add a new upgrade:
9
+ # 1) include Dor::Upgradable within whatever model, datastream, or mixin
10
+ # you want to make upgradable.
11
+ # 2) Add a block to the model, datastream, or mixin as follows:
12
+ #
13
+ # on_upgrade(v) do |obj|
14
+ # # Do whatever needs to be done to obj
15
+ # end
16
+ #
17
+ # where v is the first released version of dor-services that will
18
+ # include the upgrade.
19
+ #
20
+ # The block can either be defined on the model itself, or in a file
21
+ # in the dor/migrations/[model] directory. See Dor::Identifiable and
22
+ # dor/migrations/identifiable/* for an example.
23
+
24
+ Callback = Struct.new :module, :version, :description, :block
25
+
26
+ mattr_accessor :__upgrade_callbacks
27
+ @@__upgrade_callbacks = []
28
+ def self.add_upgrade_callback c, v, d, &b
29
+ @@__upgrade_callbacks << Callback.new(c, Gem::Version.new(v), d, b)
30
+ end
31
+
32
+ def self.run_upgrade_callbacks(obj, event_handler)
33
+ relevant = @@__upgrade_callbacks.select { |c| obj.is_a?(c.module) }.sort_by(&:version)
34
+ results = relevant.collect do |c|
35
+ result = c.block.call(obj)
36
+ if result and event_handler.respond_to?(:add_event)
37
+ event_handler.add_event 'remediation', "#{c.module.name} #{c.version}", c.description
38
+ end
39
+ if result
40
+ Dor.logger.info "Applied remediation '#{c.description}' to '#{obj.pid}'"
41
+ end
42
+ result
43
+ end
44
+ results.any?
45
+ end
46
+
47
+ def self.included(base)
48
+ base.instance_eval do
49
+ def self.on_upgrade version, desc, &block
50
+ Dor::Upgradable.add_upgrade_callback self, version, desc, &block
51
+ end
52
+
53
+ Dir[File.join(Dor.root,'dor','migrations',base.name.split(/::/).last.underscore,'*.rb')].each do |migration|
54
+ require migration
55
+ end
56
+ end
57
+ end
58
+
59
+ def upgrade!
60
+ results = [Dor::Upgradable.run_upgrade_callbacks(self, self)]
61
+ if self.respond_to?(:datastreams)
62
+ self.datastreams.each_pair do |dsid, ds|
63
+ results << Dor::Upgradable.run_upgrade_callbacks(ds, self) unless ds.new?
64
+ end
65
+ end
66
+
67
+ if results.any?
68
+ self.save
69
+ else
70
+ false
71
+ end
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,94 @@
1
+ module Dor
2
+ module Versionable
3
+ extend ActiveSupport::Concern
4
+ include Processable
5
+ include Upgradable
6
+
7
+ included do
8
+ has_metadata :name => 'versionMetadata', :type => Dor::VersionMetadataDS, :label => 'Version Metadata', :autocreate => true
9
+ end
10
+
11
+ # Increments the version number and initializes versioningWF for the object
12
+ # @param [Hash] opts optional params
13
+ # @option opts [Boolean] :assume_accessioned If true, does not check whether object has been accessioned.
14
+ # @option opts [Boolean] :create_workflows_ds If false, initialize_workflow() will not initialize the workflows datastream.
15
+ # @raise [Dor::Exception] if the object hasn't been accessioned, or if a version is already opened
16
+ def open_new_version(opts = {})
17
+ # During local development, we need a way to open a new version
18
+ # even if the object has not been accessioned.
19
+ raise(Dor::Exception, 'Object net yet accessioned') unless
20
+ opts[:assume_accessioned] ||
21
+ Dor::WorkflowService.get_lifecycle('dor', pid, 'accessioned')
22
+
23
+ raise Dor::Exception, 'Object already opened for versioning' if(new_version_open?)
24
+ raise Dor::Exception, 'Object currently being accessioned' if(Dor::WorkflowService.get_active_lifecycle('dor', pid, 'submitted'))
25
+
26
+
27
+ ds = datastreams['versionMetadata']
28
+ ds.increment_version
29
+ ds.content = ds.ng_xml.to_s
30
+ ds.save unless self.new_object?
31
+
32
+ k = :create_workflows_ds
33
+ if opts.has_key?(k)
34
+ # During local development, Hydrus (or some other app running Fedora locally)
35
+ # does not want this call to initialize the workflows datastream.
36
+ initialize_workflow('versioningWF', 'dor', opts[k])
37
+ else
38
+ initialize_workflow('versioningWF')
39
+ end
40
+ end
41
+
42
+ def current_version
43
+ datastreams['versionMetadata'].current_version_id
44
+ end
45
+
46
+ # Sets versioningWF:submit-version to completed and initiates accessionWF for the object
47
+ # @param [Hash] opts optional params
48
+ # @option opts [String] :description describes the version change
49
+ # @option opts [Symbol] :significance which part of the version tag to increment
50
+ # :major, :minor, :admin (see Dor::VersionTag#increment)
51
+ # @option opts [String] :version_num version number to archive rows with. Otherwise, current version is used
52
+ # @option opts [Boolean] :start_accesion set to true if you want accessioning to start (default), false otherwise
53
+ # @raise [Dor::Exception] if the object hasn't been opened for versioning, or if accessionWF has
54
+ # already been instantiated or the current version is missing a tag or description
55
+ def close_version(opts={})
56
+ unless(opts.empty?)
57
+ datastreams['versionMetadata'].update_current_version opts
58
+ datastreams['versionMetadata'].save
59
+ end
60
+
61
+ raise Dor::Exception, 'latest version in versionMetadata requires tag and description before it can be closed' unless(datastreams['versionMetadata'].current_version_closeable?)
62
+ raise Dor::Exception, 'Trying to close version on an object not opened for versioning' unless(new_version_open?)
63
+ raise Dor::Exception, 'accessionWF already created for versioned object' if(Dor::WorkflowService.get_active_lifecycle('dor', pid, 'submitted'))
64
+
65
+ Dor::WorkflowService.update_workflow_status 'dor', pid, 'versioningWF', 'submit-version', 'completed'
66
+ # TODO setting start-accession to completed could happen later if we have a universal robot to kick of accessioning across workflows,
67
+ # or if there's a review step after versioning is closed
68
+ Dor::WorkflowService.update_workflow_status 'dor', pid, 'versioningWF', 'start-accession', 'completed'
69
+ Dor::WorkflowService.archive_workflow 'dor', pid, 'versioningWF', opts[:version_num]
70
+
71
+ initialize_workflow 'accessionWF' if(opts[:start_accession].nil? || opts[:start_accession])
72
+ end
73
+
74
+ # @return [Boolean] true if 'opened' lifecycle is active, false otherwise
75
+ def new_version_open?
76
+ return true if(Dor::WorkflowService.get_active_lifecycle('dor', pid, 'opened'))
77
+ false
78
+ end
79
+
80
+ # @return [Boolean] true if the object is in a state that allows it to be modified. States that will allow modification are: has not been submitted for accessioning, has an open version or has sdr-ingest set to hold
81
+ def allows_modification?
82
+ if Dor::WorkflowService.get_lifecycle('dor', pid, 'submitted' ) and not new_version_open? and not Dor::WorkflowService.get_workflow_status('dor', pid, 'accessionWF', 'sdr-ingest-transfer')=='hold'
83
+ false
84
+ else
85
+ true
86
+ end
87
+ end
88
+
89
+ # Following chart of processes on this consul page: https://consul.stanford.edu/display/chimera/Versioning+workflows
90
+ alias_method :start_version, :open_new_version
91
+ alias_method :submit_version, :close_version
92
+
93
+ end
94
+ end