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.
- checksums.yaml +15 -0
- data/bin/dor-indexer +108 -0
- data/bin/dor-indexerd +73 -0
- data/bin/nokogiri +19 -0
- data/bin/rake +19 -0
- data/bin/ruby_noexec_wrapper +14 -0
- data/bin/solrizer +19 -0
- data/bin/solrizerd +19 -0
- data/config/certs/README +1 -0
- data/config/config_defaults.yml +62 -0
- data/config/dev_console_env.rb.example +67 -0
- data/config/predicate_mappings.yml +55 -0
- data/lib/dor-services.rb +152 -19
- data/lib/dor/config.rb +133 -35
- data/lib/dor/datastreams/administrative_metadata_ds.rb +84 -0
- data/lib/dor/datastreams/content_metadata_ds.rb +337 -0
- data/lib/dor/datastreams/datastream_spec_solrizer.rb +18 -0
- data/lib/dor/datastreams/default_object_rights_ds.rb +52 -0
- data/lib/dor/datastreams/desc_metadata_ds.rb +39 -0
- data/lib/{datastreams → dor/datastreams}/embargo_metadata_ds.rb +25 -20
- data/lib/{datastreams → dor/datastreams}/events_ds.rb +14 -9
- data/lib/dor/datastreams/identity.xsl +8 -0
- data/lib/dor/datastreams/identity_metadata_ds.rb +112 -0
- data/lib/dor/datastreams/role_metadata_ds.rb +51 -0
- data/lib/dor/datastreams/simple_dublin_core_ds.rb +45 -0
- data/lib/dor/datastreams/version_metadata_ds.rb +214 -0
- data/lib/dor/datastreams/workflow_definition_ds.rb +113 -0
- data/lib/dor/datastreams/workflow_ds.rb +103 -0
- data/lib/dor/exceptions.rb +0 -1
- data/lib/dor/migrations/content_metadata_ds/change_content_type.rb +7 -0
- data/lib/dor/migrations/identifiable/assert_adminPolicy.rb +9 -0
- data/lib/dor/migrations/identifiable/fix_model_assertions.rb +13 -0
- data/lib/dor/migrations/identifiable/record_remediation.rb +18 -0
- data/lib/dor/migrations/identifiable/uriify_augmented_contentlocation_refs.rb +18 -0
- data/lib/dor/migrations/identifiable/uriify_contentlocation_refs.rb +18 -0
- data/lib/dor/migrations/processable/unify_workflows.rb +17 -0
- data/lib/dor/migrations/versionable/add_missing_version_md.rb +9 -0
- data/lib/dor/models/admin_policy_object.rb +16 -0
- data/lib/dor/models/assembleable.rb +14 -0
- data/lib/dor/models/collection.rb +14 -0
- data/lib/dor/models/contentable.rb +227 -0
- data/lib/dor/models/describable.rb +194 -0
- data/lib/dor/models/discoverable.rb +66 -0
- data/lib/dor/models/editable.rb +267 -0
- data/lib/dor/models/embargoable.rb +97 -0
- data/lib/dor/models/eventable.rb +12 -0
- data/lib/dor/models/governable.rb +162 -0
- data/lib/dor/models/identifiable.rb +211 -0
- data/lib/dor/models/item.rb +44 -0
- data/lib/dor/models/itemizable.rb +66 -0
- data/lib/dor/{mods2dc.xslt → models/mods2dc.xslt} +39 -12
- data/lib/dor/models/preservable.rb +50 -0
- data/lib/dor/models/processable.rb +229 -0
- data/lib/dor/models/publishable.rb +74 -0
- data/lib/dor/models/set.rb +12 -0
- data/lib/dor/models/shelvable.rb +27 -0
- data/lib/dor/models/upgradable.rb +74 -0
- data/lib/dor/models/versionable.rb +94 -0
- data/lib/dor/models/workflow_object.rb +54 -0
- data/lib/dor/services/cleanup_service.rb +47 -0
- data/lib/dor/services/digital_stacks_service.rb +55 -0
- data/lib/dor/services/merge_service.rb +96 -0
- data/lib/dor/{metadata_handlers → services/metadata_handlers}/catalog_handler.rb +0 -2
- data/lib/dor/{metadata_handlers → services/metadata_handlers}/mdtoolkit_handler.rb +0 -2
- data/lib/dor/{metadata_service.rb → services/metadata_service.rb} +1 -3
- data/lib/dor/services/registration_service.rb +181 -0
- data/lib/dor/services/sdr_ingest_service.rb +181 -0
- data/lib/dor/services/search_service.rb +131 -0
- data/lib/dor/services/suri_service.rb +32 -0
- data/lib/dor/services/technical_metadata_service.rb +226 -0
- data/lib/dor/{tei2dc.xslt → services/tei2dc.xslt} +0 -0
- data/lib/dor/utils/ng_tidy.rb +37 -0
- data/lib/dor/utils/predicate_patch.rb +23 -0
- data/lib/dor/utils/solr_doc_helper.rb +9 -0
- data/lib/dor/utils/utc_date_field_mapper.rb +7 -0
- data/lib/dor/version.rb +3 -0
- data/lib/dor/workflow/document.rb +131 -0
- data/lib/dor/workflow/graph.rb +166 -0
- data/lib/dor/workflow/process.rb +99 -0
- data/lib/gsearch/demoFoxmlToSolr.xslt +340 -122
- data/lib/tasks/dor.rake +39 -0
- metadata +494 -384
- data/lib/datastreams/content_metadata_ds.rb +0 -12
- data/lib/datastreams/identity_metadata_ds.rb +0 -28
- data/lib/datastreams/ng_tidy.rb +0 -19
- data/lib/datastreams/simple_dublin_core_ds.rb +0 -23
- data/lib/datastreams/workflow_definition_ds.rb +0 -105
- data/lib/datastreams/workflow_ds.rb +0 -16
- data/lib/dor/admin_policy_object.rb +0 -11
- data/lib/dor/base.rb +0 -81
- data/lib/dor/cleanup_service.rb +0 -32
- data/lib/dor/digital_stacks_service.rb +0 -82
- data/lib/dor/druid_utils.rb +0 -41
- data/lib/dor/embargo.rb +0 -41
- data/lib/dor/item.rb +0 -141
- data/lib/dor/provenance_metadata_service.rb +0 -65
- data/lib/dor/registration_service.rb +0 -87
- data/lib/dor/rsolr.rb +0 -27
- data/lib/dor/sdr_ingest_service.rb +0 -117
- data/lib/dor/search_service.rb +0 -86
- data/lib/dor/suri_service.rb +0 -37
- data/lib/dor/workflow_object.rb +0 -13
- data/lib/dor/workflow_service.rb +0 -111
- data/lib/xml_models/foxml.rb +0 -261
- data/lib/xml_models/identity_metadata/dublin_core.rb +0 -119
- 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
|