dor-services 6.0.0 → 6.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/dor-services.rb +7 -6
- data/lib/dor/certificate_authenticated_rest_resource_factory.rb +2 -1
- data/lib/dor/config.rb +38 -37
- data/lib/dor/datastreams/administrative_metadata_ds.rb +98 -98
- data/lib/dor/datastreams/content_metadata_ds.rb +26 -17
- data/lib/dor/datastreams/datastream_spec_solrizer.rb +4 -2
- data/lib/dor/datastreams/default_object_rights_ds.rb +10 -7
- data/lib/dor/datastreams/desc_metadata_ds.rb +6 -6
- data/lib/dor/datastreams/embargo_metadata_ds.rb +94 -94
- data/lib/dor/datastreams/events_ds.rb +55 -54
- data/lib/dor/datastreams/geo_metadata_ds.rb +7 -6
- data/lib/dor/datastreams/identity_metadata_ds.rb +128 -125
- data/lib/dor/datastreams/provenance_metadata_ds.rb +3 -1
- data/lib/dor/datastreams/rights_metadata_ds.rb +4 -4
- data/lib/dor/datastreams/role_metadata_ds.rb +42 -42
- data/lib/dor/datastreams/simple_dublin_core_ds.rb +45 -43
- data/lib/dor/datastreams/technical_metadata_ds.rb +3 -1
- data/lib/dor/datastreams/version_metadata_ds.rb +12 -5
- data/lib/dor/datastreams/workflow_definition_ds.rb +74 -73
- data/lib/dor/datastreams/workflow_ds.rb +12 -7
- data/lib/dor/exceptions.rb +2 -0
- data/lib/dor/indexers/data_indexer.rb +16 -0
- data/lib/dor/indexers/describable_indexer.rb +2 -0
- data/lib/dor/indexers/editable_indexer.rb +2 -0
- data/lib/dor/indexers/identifiable_indexer.rb +23 -8
- data/lib/dor/indexers/processable_indexer.rb +2 -0
- data/lib/dor/indexers/releasable_indexer.rb +2 -0
- data/lib/dor/models/abstract.rb +2 -0
- data/lib/dor/models/admin_policy_object.rb +2 -0
- data/lib/dor/models/agreement.rb +2 -0
- data/lib/dor/models/collection.rb +3 -0
- data/lib/dor/models/concerns/assembleable.rb +2 -0
- data/lib/dor/models/concerns/contentable.rb +5 -2
- data/lib/dor/models/concerns/describable.rb +7 -2
- data/lib/dor/models/concerns/editable.rb +28 -21
- data/lib/dor/models/concerns/embargoable.rb +4 -0
- data/lib/dor/models/concerns/eventable.rb +2 -0
- data/lib/dor/models/concerns/geoable.rb +2 -0
- data/lib/dor/models/concerns/governable.rb +7 -2
- data/lib/dor/models/concerns/identifiable.rb +33 -34
- data/lib/dor/models/concerns/itemizable.rb +4 -1
- data/lib/dor/models/concerns/preservable.rb +2 -0
- data/lib/dor/models/concerns/processable.rb +15 -9
- data/lib/dor/models/concerns/publishable.rb +9 -4
- data/lib/dor/models/concerns/releaseable.rb +21 -11
- data/lib/dor/models/concerns/rightsable.rb +2 -0
- data/lib/dor/models/concerns/shelvable.rb +6 -2
- data/lib/dor/models/concerns/versionable.rb +8 -4
- data/lib/dor/models/item.rb +2 -0
- data/lib/dor/models/set.rb +2 -0
- data/lib/dor/models/workflow_object.rb +5 -1
- data/lib/dor/rest_resource_factory.rb +2 -0
- data/lib/dor/services/cleanup_reset_service.rb +2 -1
- data/lib/dor/services/cleanup_service.rb +2 -1
- data/lib/dor/services/digital_stacks_service.rb +3 -1
- data/lib/dor/services/indexing_service.rb +3 -1
- data/lib/dor/services/merge_service.rb +6 -4
- data/lib/dor/services/metadata_handlers/catalog_handler.rb +2 -0
- data/lib/dor/services/metadata_service.rb +4 -4
- data/lib/dor/services/public_desc_metadata_service.rb +16 -8
- data/lib/dor/services/public_xml_service.rb +7 -4
- data/lib/dor/services/registration_service.rb +25 -20
- data/lib/dor/services/reset_workspace_service.rb +4 -4
- data/lib/dor/services/sdr_ingest_service.rb +4 -2
- data/lib/dor/services/search_service.rb +8 -9
- data/lib/dor/services/suri_service.rb +3 -2
- data/lib/dor/services/technical_metadata_service.rb +15 -9
- data/lib/dor/services/thumbnail_service.rb +14 -10
- data/lib/dor/utils/hydrus_shims.rb +2 -0
- data/lib/dor/utils/ng_tidy.rb +3 -7
- data/lib/dor/utils/predicate_patch.rb +2 -0
- data/lib/dor/utils/sdr_client.rb +3 -0
- data/lib/dor/utils/solr_doc_helper.rb +4 -2
- data/lib/dor/version.rb +3 -1
- data/lib/dor/workflow/document.rb +113 -108
- data/lib/dor/workflow/process.rb +90 -87
- data/lib/tasks/rdoc.rake +4 -3
- metadata +4 -4
@@ -1,10 +1,12 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Dor
|
2
4
|
# Represents the Fedora 3 datastream that hold technical metadata
|
3
5
|
class TechnicalMetadataDS < ActiveFedora::OmDatastream
|
4
6
|
# This provides the prefix for the solr fields generated by ActiveFedora.
|
5
7
|
# Since we don't want a prefix, we override this to return an empty string.
|
6
8
|
def prefix
|
7
|
-
''
|
9
|
+
''
|
8
10
|
end
|
9
11
|
end
|
10
12
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Dor
|
2
4
|
class VersionTag
|
3
5
|
include Comparable
|
@@ -7,14 +9,17 @@ module Dor
|
|
7
9
|
def <=>(other)
|
8
10
|
diff = @major <=> other.major
|
9
11
|
return diff if diff != 0
|
12
|
+
|
10
13
|
diff = @minor <=> other.minor
|
11
14
|
return diff if diff != 0
|
15
|
+
|
12
16
|
@admin <=> other.admin
|
13
17
|
end
|
14
18
|
|
15
19
|
# @param [String] raw_tag the value of the tag attribute from a Version node
|
16
20
|
def self.parse(raw_tag)
|
17
21
|
return nil unless raw_tag =~ /(\d+)\.(\d+)\.(\d+)/
|
22
|
+
|
18
23
|
VersionTag.new $1, $2, $3
|
19
24
|
end
|
20
25
|
|
@@ -81,7 +86,7 @@ module Dor
|
|
81
86
|
ng_xml_will_change!
|
82
87
|
if find_by_terms(:version).size == 0
|
83
88
|
v = ng_xml.create_element 'version',
|
84
|
-
|
89
|
+
:versionId => '1', :tag => '1.0.0'
|
85
90
|
d = ng_xml.create_element 'description', 'Initial Version'
|
86
91
|
ng_xml.root['objectId'] = pid
|
87
92
|
ng_xml.root.add_child(v)
|
@@ -123,6 +128,7 @@ module Dor
|
|
123
128
|
ng_xml.root['objectId'] = pid
|
124
129
|
return if find_by_terms(:version).size == 1
|
125
130
|
return if opts.empty?
|
131
|
+
|
126
132
|
ng_xml_will_change!
|
127
133
|
current = current_version_node
|
128
134
|
if opts.include? :description
|
@@ -141,8 +147,8 @@ module Dor
|
|
141
147
|
else
|
142
148
|
# get rid of the current tag
|
143
149
|
tags = find_by_terms(:version, :tag)
|
144
|
-
sorted_tags = tags.map {|t| VersionTag.parse(t.value)}.sort
|
145
|
-
current_tag = sorted_tags[sorted_tags.length - 2]
|
150
|
+
sorted_tags = tags.map { |t| VersionTag.parse(t.value) }.sort
|
151
|
+
current_tag = sorted_tags[sorted_tags.length - 2] # Get the second greatest tag since we are dropping the current, greatest
|
146
152
|
current[:tag] = current_tag.increment(opts[:significance]).to_s
|
147
153
|
end
|
148
154
|
|
@@ -187,6 +193,7 @@ module Dor
|
|
187
193
|
def current_description
|
188
194
|
desc_node = current_version_node.at_xpath('description')
|
189
195
|
return desc_node.content if desc_node
|
196
|
+
|
190
197
|
''
|
191
198
|
end
|
192
199
|
|
@@ -222,12 +229,12 @@ module Dor
|
|
222
229
|
# @return [Nokogiri::XML::Node] Node representing the current version
|
223
230
|
def current_version_node
|
224
231
|
versions = find_by_terms(:version)
|
225
|
-
versions.max_by {|v| v[:versionId].to_i }
|
232
|
+
versions.max_by { |v| v[:versionId].to_i }
|
226
233
|
end
|
227
234
|
|
228
235
|
def newest_tag
|
229
236
|
tags = find_by_terms(:version, :tag)
|
230
|
-
tags.map {|t| VersionTag.parse(t.value)}.max
|
237
|
+
tags.map { |t| VersionTag.parse(t.value) }.max
|
231
238
|
end
|
232
239
|
end
|
233
240
|
end
|
@@ -1,91 +1,92 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Dor
|
2
|
-
class WorkflowDefinitionDs < ActiveFedora::OmDatastream
|
3
|
-
|
4
|
+
class WorkflowDefinitionDs < ActiveFedora::OmDatastream
|
5
|
+
include SolrDocHelper
|
4
6
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
7
|
+
set_terminology do |t|
|
8
|
+
t.root(:path => 'workflow-def', :index_as => [:not_searchable])
|
9
|
+
t.process(:index_as => [:not_searchable])
|
10
|
+
end
|
9
11
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
12
|
+
define_template :process do |builder, workflow, attrs|
|
13
|
+
prereqs = attrs.delete('prerequisite')
|
14
|
+
prereqs = prereqs.split(/\s*,\s*/) if prereqs.is_a?(String)
|
15
|
+
attrs.keys.each { |k| attrs[k.to_s.dasherize.to_sym] = attrs.delete(k) }
|
16
|
+
builder.process(attrs) do |node|
|
17
|
+
Array(prereqs).each do |prereq|
|
18
|
+
(repo, wf, prereq_name) = prereq.split(/:/)
|
19
|
+
if prereq_name.nil?
|
20
|
+
prereq_name = repo
|
21
|
+
repo = nil
|
22
|
+
end
|
23
|
+
if repo == workflow.repo && wf = workflow.name
|
24
|
+
repo = nil
|
25
|
+
wf = nil
|
26
|
+
end
|
27
|
+
attrs = (repo.nil? && wf.nil?) ? {} : { :repository => repo, :workflow => wf }
|
28
|
+
node.prereq(attrs) { node.text prereq_name }
|
20
29
|
end
|
21
|
-
if repo == workflow.repo && wf = workflow.name
|
22
|
-
repo = nil
|
23
|
-
wf = nil
|
24
|
-
end
|
25
|
-
attrs = (repo.nil? && wf.nil?) ? {} : { :repository => repo, :workflow => wf }
|
26
|
-
node.prereq(attrs) { node.text prereq_name }
|
27
30
|
end
|
28
31
|
end
|
29
|
-
end
|
30
32
|
|
31
|
-
|
32
|
-
|
33
|
-
|
33
|
+
def self.xml_template
|
34
|
+
Nokogiri::XML('<workflow-def/>')
|
35
|
+
end
|
34
36
|
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
37
|
+
def add_process(attributes)
|
38
|
+
ng_xml_will_change!
|
39
|
+
add_child_node(ng_xml.at_xpath('/workflow-def'), :process, self, attributes)
|
40
|
+
end
|
39
41
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
42
|
+
def processes
|
43
|
+
ng_xml.xpath('/workflow-def/process').collect do |node|
|
44
|
+
Workflow::Process.new(repo, name, node)
|
45
|
+
end.sort { |a, b| (a.sequence || 0) <=> (b.sequence || 0) }
|
46
|
+
end
|
45
47
|
|
46
|
-
|
47
|
-
|
48
|
-
|
48
|
+
def name
|
49
|
+
ng_xml.at_xpath('/workflow-def/@id').to_s
|
50
|
+
end
|
49
51
|
|
50
|
-
|
51
|
-
|
52
|
-
|
52
|
+
def repo
|
53
|
+
ng_xml.at_xpath('/workflow-def/@repository').to_s
|
54
|
+
end
|
53
55
|
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
56
|
+
# Creates the xml used by Dor::WorkflowService#create_workflow
|
57
|
+
# @return [String] An object's initial workflow as defined by the <workflow-def> in content
|
58
|
+
def initial_workflow
|
59
|
+
doc = Nokogiri::XML('<workflow/>')
|
60
|
+
root = doc.root
|
61
|
+
root['id'] = name
|
62
|
+
processes.each { |proc|
|
63
|
+
doc.create_element 'process' do |node|
|
64
|
+
node['name'] = proc.name
|
65
|
+
if proc.status
|
66
|
+
node['status'] = proc.status
|
67
|
+
node['attempts'] = '1'
|
68
|
+
else
|
69
|
+
node['status'] = 'waiting'
|
70
|
+
end
|
71
|
+
node['lifecycle'] = proc.lifecycle if proc.lifecycle
|
72
|
+
root.add_child node
|
68
73
|
end
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
}
|
73
|
-
Nokogiri::XML(doc.to_xml) { |x| x.noblanks }.to_xml { |config| config.no_declaration }
|
74
|
-
end
|
74
|
+
}
|
75
|
+
Nokogiri::XML(doc.to_xml) { |x| x.noblanks }.to_xml { |config| config.no_declaration }
|
76
|
+
end
|
75
77
|
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
78
|
+
def to_solr(solr_doc = {}, *args)
|
79
|
+
solr_doc = super(solr_doc, *args)
|
80
|
+
add_solr_value(solr_doc, 'workflow_name', name, :symbol, [:symbol])
|
81
|
+
processes.each do |p|
|
82
|
+
add_solr_value(solr_doc, 'process', "#{p.name}|#{p.label}", :symbol, [:displayable])
|
83
|
+
end
|
84
|
+
solr_doc
|
81
85
|
end
|
82
|
-
solr_doc
|
83
|
-
end
|
84
86
|
|
85
|
-
|
86
|
-
|
87
|
-
|
87
|
+
# maintain AF < 8 indexing behavior
|
88
|
+
def prefix
|
89
|
+
''
|
90
|
+
end
|
88
91
|
end
|
89
|
-
|
90
|
-
end
|
91
92
|
end
|
@@ -1,17 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Dor
|
2
4
|
# TODO: class docs
|
3
5
|
class WorkflowDs < ActiveFedora::OmDatastream
|
4
6
|
set_terminology do |t|
|
5
7
|
t.root(:path => 'workflows')
|
6
8
|
t.workflow {
|
7
|
-
t.workflowId(
|
9
|
+
t.workflowId(:path => { :attribute => 'id' })
|
8
10
|
t.process {
|
9
|
-
t.name_(
|
10
|
-
t.status(
|
11
|
-
t.timestamp(:path => {:attribute => 'datetime' }, :index_as => [:displayable, :not_searchable]
|
12
|
-
t.elapsed(
|
13
|
-
t.lifecycle(:path => {:attribute => 'lifecycle'}, :index_as => [:displayable, :not_searchable]
|
14
|
-
t.attempts(
|
11
|
+
t.name_(:path => { :attribute => 'name' }, :index_as => [:displayable, :not_searchable])
|
12
|
+
t.status(:path => { :attribute => 'status' }, :index_as => [:displayable, :not_searchable])
|
13
|
+
t.timestamp(:path => { :attribute => 'datetime' }, :index_as => [:displayable, :not_searchable]) # , :data_type => :date)
|
14
|
+
t.elapsed(:path => { :attribute => 'elapsed' }, :index_as => [:displayable, :not_searchable])
|
15
|
+
t.lifecycle(:path => { :attribute => 'lifecycle' }, :index_as => [:displayable, :not_searchable])
|
16
|
+
t.attempts(:path => { :attribute => 'attempts' }, :index_as => [:displayable, :not_searchable])
|
15
17
|
}
|
16
18
|
}
|
17
19
|
end
|
@@ -20,6 +22,7 @@ module Dor
|
|
20
22
|
xml = Dor::Config.workflow.client.get_workflow_xml(repo, pid, wf)
|
21
23
|
xml = Nokogiri::XML(xml)
|
22
24
|
return nil if xml.xpath('workflow').length == 0
|
25
|
+
|
23
26
|
Workflow::Document.new(xml.to_s)
|
24
27
|
end
|
25
28
|
|
@@ -39,6 +42,7 @@ module Dor
|
|
39
42
|
xml = Nokogiri::XML(%(<?xml version="1.0" encoding="UTF-8" standalone="yes"?>\n<workflows objectId="#{pid}"/>))
|
40
43
|
digital_object.datastreams.keys.each do |dsid|
|
41
44
|
next unless dsid =~ /WF$/
|
45
|
+
|
42
46
|
ds_content = Nokogiri::XML(Dor::Config.workflow.client.get_workflow_xml('dor', pid, dsid))
|
43
47
|
xml.root.add_child(ds_content.root)
|
44
48
|
end
|
@@ -55,6 +59,7 @@ module Dor
|
|
55
59
|
def current_priority
|
56
60
|
cp = workflows.detect(&:expedited?)
|
57
61
|
return 0 if cp.nil?
|
62
|
+
|
58
63
|
cp.priority.to_i
|
59
64
|
end
|
60
65
|
|
data/lib/dor/exceptions.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Dor
|
2
4
|
# Indexing provided by ActiveFedora
|
3
5
|
class DataIndexer
|
@@ -8,6 +10,20 @@ module Dor
|
|
8
10
|
@resource = resource
|
9
11
|
end
|
10
12
|
|
13
|
+
# we need to override this until https://github.com/samvera/active_fedora/pull/1371
|
14
|
+
# has been released
|
15
|
+
def to_solr(solr_doc = {}, opts = {})
|
16
|
+
super.tap do |doc|
|
17
|
+
doc['active_fedora_model_ssi'] = has_model
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
# rubocop:disable Naming/PredicateName
|
22
|
+
def has_model
|
23
|
+
resource.class.inspect
|
24
|
+
end
|
25
|
+
# rubocop:enable Naming/PredicateName
|
26
|
+
|
11
27
|
delegate :create_date, :modified_date, :state, :pid, :inner_object,
|
12
28
|
:datastreams, :relationships, to: :resource
|
13
29
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Dor
|
2
4
|
class IdentifiableIndexer
|
3
5
|
include SolrDocHelper
|
@@ -26,7 +28,7 @@ module Dor
|
|
26
28
|
add_solr_value(solr_doc, 'title_sort', resource.label, :string, [:stored_sortable])
|
27
29
|
|
28
30
|
rels_doc = Nokogiri::XML(resource.datastreams['RELS-EXT'].content)
|
29
|
-
ns_hash = {'hydra' => 'http://projecthydra.org/ns/relations#', 'fedora' => 'info:fedora/fedora-system:def/relations-external#', 'rdf' => 'http://www.w3.org/1999/02/22-rdf-syntax-ns#'}
|
31
|
+
ns_hash = { 'hydra' => 'http://projecthydra.org/ns/relations#', 'fedora' => 'info:fedora/fedora-system:def/relations-external#', 'rdf' => 'http://www.w3.org/1999/02/22-rdf-syntax-ns#' }
|
30
32
|
apos = rels_doc.search('//rdf:RDF/rdf:Description/hydra:isGovernedBy', ns_hash)
|
31
33
|
collections = rels_doc.search('//rdf:RDF/rdf:Description/fedora:isMemberOfCollection', ns_hash)
|
32
34
|
solrize_related_obj_titles(solr_doc, apos, @@apo_hash, 'apo_title', 'nonhydrus_apo_title', 'hydrus_apo_title')
|
@@ -46,28 +48,35 @@ module Dor
|
|
46
48
|
end
|
47
49
|
end
|
48
50
|
|
51
|
+
# Clears out the cache of items. Used primarily in testing.
|
52
|
+
def self.reset_cache!
|
53
|
+
@@collection_hash = {}
|
54
|
+
@@apo_hash = {}
|
55
|
+
end
|
56
|
+
|
49
57
|
private
|
50
58
|
|
51
59
|
def solrize_related_obj_titles(solr_doc, relationships, title_hash, union_field_name, nonhydrus_field_name, hydrus_field_name)
|
52
60
|
# TODO: if you wanted to get a little fancier, you could also solrize a 2 level hierarchy and display using hierarchial facets, like
|
53
61
|
# ["SOURCE", "SOURCE : TITLE"] (e.g. ["Hydrus", "Hydrus : Special Collections"], see (exploded) tags in IdentityMetadataDS#to_solr).
|
54
|
-
title_type = :symbol
|
55
|
-
title_attrs = [:stored_searchable]
|
62
|
+
title_type = :symbol # we'll get an _ssim because of the type
|
63
|
+
title_attrs = [:stored_searchable] # we'll also get a _tesim from this attr
|
56
64
|
relationships.each do |rel_node|
|
57
65
|
rel_druid = rel_node['rdf:resource']
|
58
|
-
next unless rel_druid
|
66
|
+
next unless rel_druid # TODO: warning here would also be useful
|
67
|
+
|
59
68
|
rel_druid = rel_druid.gsub('info:fedora/', '')
|
60
69
|
|
61
70
|
# populate cache if necessary
|
62
71
|
unless title_hash.key?(rel_druid)
|
63
72
|
begin
|
64
73
|
related_obj = Dor.find(rel_druid)
|
65
|
-
related_obj_title =
|
66
|
-
is_from_hydrus = (related_obj
|
67
|
-
title_hash[rel_druid] = {'related_obj_title' => related_obj_title, 'is_from_hydrus' => is_from_hydrus}
|
74
|
+
related_obj_title = related_obj_display_title(related_obj, rel_druid)
|
75
|
+
is_from_hydrus = (related_obj&.tags&.include?('Project : Hydrus'))
|
76
|
+
title_hash[rel_druid] = { 'related_obj_title' => related_obj_title, 'is_from_hydrus' => is_from_hydrus }
|
68
77
|
rescue ActiveFedora::ObjectNotFoundError
|
69
78
|
# This may happen if the given APO or Collection does not exist (bad data)
|
70
|
-
title_hash[rel_druid] = {'related_obj_title' => rel_druid, 'is_from_hydrus' => false}
|
79
|
+
title_hash[rel_druid] = { 'related_obj_title' => rel_druid, 'is_from_hydrus' => false }
|
71
80
|
end
|
72
81
|
end
|
73
82
|
|
@@ -80,5 +89,11 @@ module Dor
|
|
80
89
|
add_solr_value(solr_doc, union_field_name, title_hash[rel_druid]['related_obj_title'], title_type, title_attrs)
|
81
90
|
end
|
82
91
|
end
|
92
|
+
|
93
|
+
def related_obj_display_title(related_obj, default_title)
|
94
|
+
return default_title unless related_obj
|
95
|
+
|
96
|
+
related_obj.full_title || default_title
|
97
|
+
end
|
83
98
|
end
|
84
99
|
end
|
data/lib/dor/models/abstract.rb
CHANGED