dor-services 2.2.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. data/lib/datastreams/content_metadata_ds.rb +12 -0
  2. data/lib/datastreams/embargo_metadata_ds.rb +107 -0
  3. data/lib/datastreams/events_ds.rb +58 -0
  4. data/lib/datastreams/identity_metadata_ds.rb +28 -0
  5. data/lib/datastreams/ng_tidy.rb +19 -0
  6. data/lib/datastreams/simple_dublin_core_ds.rb +23 -0
  7. data/lib/datastreams/workflow_definition_ds.rb +105 -0
  8. data/lib/datastreams/workflow_ds.rb +16 -0
  9. data/lib/dor-services.rb +19 -0
  10. data/lib/dor/admin_policy_object.rb +11 -0
  11. data/lib/dor/base.rb +81 -0
  12. data/lib/dor/cleanup_service.rb +32 -0
  13. data/lib/dor/config.rb +45 -0
  14. data/lib/dor/digital_stacks_service.rb +82 -0
  15. data/lib/dor/druid_utils.rb +41 -0
  16. data/lib/dor/embargo.rb +41 -0
  17. data/lib/dor/exceptions.rb +13 -0
  18. data/lib/dor/item.rb +141 -0
  19. data/lib/dor/metadata_handlers/catalog_handler.rb +22 -0
  20. data/lib/dor/metadata_handlers/mdtoolkit_handler.rb +42 -0
  21. data/lib/dor/metadata_service.rb +88 -0
  22. data/lib/dor/mods2dc.xslt +447 -0
  23. data/lib/dor/provenance_metadata_service.rb +65 -0
  24. data/lib/dor/registration_service.rb +87 -0
  25. data/lib/dor/rsolr.rb +27 -0
  26. data/lib/dor/sdr_ingest_service.rb +117 -0
  27. data/lib/dor/search_service.rb +86 -0
  28. data/lib/dor/suri_service.rb +37 -0
  29. data/lib/dor/tei2dc.xslt +102 -0
  30. data/lib/dor/workflow_object.rb +13 -0
  31. data/lib/dor/workflow_service.rb +111 -0
  32. data/lib/gsearch/demoFoxmlToSolr.xslt +384 -0
  33. data/lib/gsearch/schema.xml +229 -0
  34. data/lib/tasks/rdoc.rake +32 -0
  35. data/lib/xml_models/foxml.rb +261 -0
  36. data/lib/xml_models/identity_metadata/dublin_core.rb +119 -0
  37. data/lib/xml_models/identity_metadata/identity_metadata.rb +288 -0
  38. metadata +462 -0
@@ -0,0 +1,65 @@
1
+ require 'nokogiri'
2
+ require 'date'
3
+ require 'time'
4
+
5
+ module Dor
6
+ class ProvenanceMetadataService
7
+
8
+ def self.add_provenance(dor_item, workflow_id, event_text)
9
+ druid = dor_item.pid
10
+ # workflow_xml = get_workflow_xml(druid, workflow_id)
11
+ workflow_provenance = create_workflow_provenance(druid, workflow_id, event_text)
12
+ dsname = 'provenanceMetadata'
13
+ if dor_item.datastream_names.include?(dsname)
14
+ ds = dor_item.datastreams[dsname]
15
+ old_provenance = ds.content
16
+ ds.ng_xml = update_provenance(old_provenance, workflow_provenance)
17
+ else
18
+ ds = dor_item.datastreams[dsname]
19
+ ds.label = 'Provenance Metadata'
20
+ ds.ng_xml = workflow_provenance
21
+ end
22
+ ds.save
23
+ end
24
+
25
+ # not used
26
+ def self.get_workflow_xml(druid, workflow_id)
27
+ Dor::WorkflowService.get_workflow_xml('dor', druid, workflow_id)
28
+ end
29
+
30
+ # @return [String]
31
+ def self.create_workflow_provenance(druid, workflow_id, event_text)
32
+ builder = Nokogiri::XML::Builder.new do |xml|
33
+ xml.provenanceMetadata(:objectId => druid) {
34
+ xml.agent(:name => 'DOR') {
35
+ xml.what(:object => druid) {
36
+ xml.event(:who => "DOR-#{workflow_id}", :when => Time.new.iso8601) {
37
+ xml.text(event_text)
38
+ }
39
+ }
40
+ }
41
+ }
42
+ end
43
+ builder.doc
44
+ end
45
+
46
+ # Reformat the XML
47
+ # @param[String]
48
+ def self.parse_xml_remove_blank_nodes(old_provenance)
49
+ # http://blog.slashpoundbang.com/post/1454850669/how-to-pretty-print-xml-with-nokogiri
50
+ Nokogiri::XML(old_provenance) { |x| x.noblanks }
51
+ end
52
+
53
+ # Append new stanzas in the contentMetadata for the googleMETS and technicalMetadata files
54
+ # @param[String, Hash<Symbol,String>]
55
+ def self.update_provenance(old_provenance, workflow_provenance)
56
+ pm_xml = Nokogiri::XML(old_provenance)
57
+ builder = Nokogiri::XML::Builder.with(pm_xml.at 'provenanceMetadata') do |xml|
58
+ xml << workflow_provenance.xpath('/provenanceMetadata/agent').first.to_xml
59
+ end
60
+ parse_xml_remove_blank_nodes(builder.to_xml)
61
+ end
62
+
63
+ end
64
+
65
+ end
@@ -0,0 +1,87 @@
1
+ require 'active_fedora'
2
+ require 'uuidtools'
3
+ require 'xml_models/foxml'
4
+ require 'xml_models/identity_metadata/identity_metadata'
5
+
6
+ require 'dor/search_service'
7
+
8
+ module Dor
9
+
10
+ class RegistrationService
11
+
12
+ class << self
13
+ def register_object(params = {})
14
+ [:object_type, :label].each do |required_param|
15
+ unless params[required_param]
16
+ raise Dor::ParameterError, "#{required_param.inspect} must be specified in call to #{self.name}.register_object"
17
+ end
18
+ end
19
+
20
+ object_type = params[:object_type]
21
+ content_model = params[:content_model]
22
+ admin_policy = params[:admin_policy]
23
+ label = params[:label]
24
+ source_id = params[:source_id] || {}
25
+ other_ids = params[:other_ids] || {}
26
+ tags = params[:tags] || []
27
+ parent = params[:parent]
28
+ pid = nil
29
+ if params[:pid]
30
+ pid = params[:pid]
31
+ existing_pid = SearchService.query_by_id(pid).first
32
+ unless existing_pid.nil?
33
+ raise Dor::DuplicateIdError.new(existing_pid), "An object with the PID #{pid} has already been registered."
34
+ end
35
+ else
36
+ pid = Dor::SuriService.mint_id
37
+ end
38
+
39
+ if (other_ids.has_key?(:uuid) or other_ids.has_key?('uuid')) == false
40
+ other_ids[:uuid] = UUIDTools::UUID.timestamp_create.to_s
41
+ end
42
+
43
+ apo_object = Dor::AdminPolicyObject.load_instance(admin_policy)
44
+ adm_xml = apo_object.datastreams['administrativeMetadata'].ng_xml
45
+ agreement_id = adm_xml.at('/administrativeMetadata/registration/agreementId/text()').to_s
46
+
47
+ idmd = IdentityMetadata.new
48
+
49
+ unless source_id.empty?
50
+ source_name = source_id.keys.first
51
+ source_value = source_id[source_name]
52
+ existing_pid = SearchService.query_by_id("#{source_name}:#{source_value}").first
53
+ unless existing_pid.nil?
54
+ raise Dor::DuplicateIdError.new(existing_pid), "An object with the source ID '#{source_name}:#{source_value}' has already been registered."
55
+ end
56
+ idmd.sourceId.source = source_name
57
+ idmd.sourceId.value = source_value
58
+ end
59
+
60
+ idmd.objectId = pid
61
+ idmd.objectCreators << 'DOR'
62
+ idmd.objectLabels << label
63
+ idmd.objectTypes << object_type
64
+ idmd.adminPolicy = admin_policy
65
+ idmd.agreementId = agreement_id
66
+ other_ids.each_pair { |name,value| idmd.add_identifier(name,value) }
67
+ tags.each { |tag| idmd.add_tag(tag) }
68
+
69
+ foxml = Foxml.new(pid, label, content_model, idmd.to_xml, parent)
70
+ foxml.admin_policy_object = admin_policy
71
+ rdf = foxml.xml.at('//rdf:Description', { 'rdf' => 'http://www.w3.org/1999/02/22-rdf-syntax-ns#' })
72
+ rels = adm_xml.xpath('/administrativeMetadata/relationships/*')
73
+ rels.each { |rel| rdf.add_child(rel.clone) }
74
+
75
+ repo = ActiveFedora.fedora
76
+ http_response = repo.ingest(foxml.to_xml(:undent_datastreams => true))
77
+ result = {
78
+ :response => http_response,
79
+ :pid => pid
80
+ }
81
+ return(result)
82
+ end
83
+ end
84
+
85
+ end
86
+
87
+ end
@@ -0,0 +1,27 @@
1
+ require 'rest-client'
2
+
3
+ module Dor
4
+
5
+ class RSolrConnection
6
+
7
+ def execute client, request_context
8
+ $stderr.puts request_context.inspect
9
+ resource = RestClient::Resource.new(
10
+ request_context[:uri].to_s,
11
+ :ssl_client_cert => OpenSSL::X509::Certificate.new(File.read(Dor::Config.fedora.cert_file)),
12
+ :ssl_client_key => OpenSSL::PKey::RSA.new(File.read(Dor::Config.fedora.key_file), Dor::Config.fedora.key_pass)
13
+ )
14
+ result = {}
15
+ resource.send(request_context[:method]) { |response, request, result, &block|
16
+ result = {
17
+ :status => response.net_http_res.code.to_i,
18
+ :headers => response.net_http_res.to_hash,
19
+ :body => response.net_http_res.body
20
+ }
21
+ }
22
+ result
23
+ end
24
+
25
+ end
26
+
27
+ end
@@ -0,0 +1,117 @@
1
+ require 'lyber-utils'
2
+
3
+ module Dor
4
+ class SdrIngestService
5
+
6
+ Config.declare(:sdr) do
7
+ local_workspace_root '/dor/workspace'
8
+ local_export_home '/dor/export'
9
+ datastreams do
10
+ contentMetadata 'required'
11
+ descMetadata 'required'
12
+ identityMetadata 'required'
13
+ provenanceMetadata 'required'
14
+ relationshipMetadata 'required'
15
+ technicalMetadata 'required'
16
+ rightsMetadata 'optional'
17
+ sourceMetadata 'optional'
18
+ end
19
+ end
20
+
21
+ # Some boilerplace entries for the bagit metadata file
22
+ METADATA_INFO = {
23
+ 'Source-Organization' => 'Stanford University Libraries',
24
+ 'Stanford-Content-Metadata' => 'data/metadata/contentMetadata.xml',
25
+ 'Stanford-Identity-Metadata' => 'data/metadata/identityMetadata.xml',
26
+ 'Stanford-Provenance-Metadata' => 'data/metadata/provenanceMetadata.xml'
27
+ }
28
+
29
+ # Create a bagit object and fill it with content
30
+ # Then tar it
31
+ # @param [LyberCore::Robots::WorkItem]
32
+ def self.transfer(dor_item, agreement_id)
33
+ druid = dor_item.pid
34
+ content_dir = Druid.new(druid).path(Config.sdr.local_workspace_root)
35
+
36
+ # Create the bag
37
+ bag_dir = File.join(Config.sdr.local_export_home, druid)
38
+ export_bag = LyberUtils::BagitBag.new(bag_dir)
39
+
40
+ # Fill the bag
41
+ export_bag.add_content_files(content_dir, use_links=true)
42
+ add_metadata_datastreams(dor_item, export_bag)
43
+ export_bag.write_metadata_info(metadata_info(druid, agreement_id))
44
+ export_bag.write_manifests()
45
+ export_bag.validate()
46
+
47
+ unless LyberUtils::FileUtilities.tar_object(bag_dir)
48
+ raise 'Unable to tar the bag'
49
+ end
50
+
51
+ # Now bootstrap SDR workflow queue to start SDR robots
52
+ Dor::WorkflowService.create_workflow('sdr', druid, 'sdrIngestWF', read_sdr_workflow_xml(), {:create_ds => false})
53
+ end
54
+
55
+ # Read in the XML file needed to initialize the SDR workflow
56
+ # @return [String]
57
+ def self.read_sdr_workflow_xml()
58
+ return IO.read(File.join("#{ROBOT_ROOT}", "config", "workflows", "sdrIngestWF", "sdrIngestWF.xml"))
59
+ end
60
+
61
+ # For each of the metadata files or datastreams, create a file in in the bag's data/metadata folder
62
+ # @param[String, LyberUtils::BagitBag]
63
+ def self.add_metadata_datastreams(dor_item, export_bag)
64
+ Config.sdr.datastreams.to_hash.each_pair do |ds_name, required|
65
+ # ds_name in this context is a symbol, so convert it to a string
66
+ filename = "#{ds_name.to_s}.xml"
67
+ metadata_string = self.get_datastream_content(dor_item, ds_name.to_s, required)
68
+ self.export_metadata_string(metadata_string, filename, export_bag) unless metadata_string.nil?
69
+ end
70
+ end
71
+
72
+ # create a link to a metadata file in the bag's data/metadata folder
73
+ def self.export_metadata_file(metadata_dir, filename, bag)
74
+ metadata_file = File.join(metadata_dir, filename)
75
+ bag_metadata_dir = File.join(bag.bag_dir, 'data', 'metadata')
76
+ if (File.exist?(metadata_file))
77
+ bag_file = File.join(bag_metadata_dir, filename)
78
+ File.link(metadata_file, bag_file)
79
+ return true
80
+ else
81
+ return false
82
+ end
83
+ end
84
+
85
+ # return the content of the specied datastream if it exists
86
+ # if non-existant, return nil or raise exception depending on value of required
87
+ def self.get_datastream_content(dor_item, ds_name, required)
88
+ ds = (ds_name == 'relationshipMetadata' ? 'RELS-EXT' : ds_name)
89
+ if dor_item.datastreams_in_fedora.keys.include?(ds)
90
+ return dor_item.datastreams[ds].content
91
+ elsif (required == 'optional')
92
+ return nil
93
+ else
94
+ raise "required datastream #{ds_name} not found in DOR"
95
+ end
96
+ end
97
+
98
+ # create a file in the bag's data/metadata folder containing the metadata string
99
+ # @param[String, String, LyberUtils::BagitBag]
100
+ def self.export_metadata_string(metadata_string, filename, bag)
101
+ bag.add_metadata_file_from_string(metadata_string, filename)
102
+ end
103
+
104
+ # merge item-specific data into the standard hash of metadata information
105
+ # @param [String, String]
106
+ def self.metadata_info(druid, agreement_id)
107
+ item_info = {
108
+ 'External-Identifier' => druid,
109
+ 'Stanford-Agreement-ID' => agreement_id
110
+ }
111
+ merged_info = item_info.merge(METADATA_INFO)
112
+ return merged_info
113
+ end
114
+
115
+ end
116
+
117
+ end
@@ -0,0 +1,86 @@
1
+ require 'json'
2
+ require 'active_support/core_ext'
3
+
4
+ module Dor
5
+
6
+ class SearchService
7
+
8
+ RISEARCH_TEMPLATE = "select $object from <#ri> where $object <dc:identifier> '%s'"
9
+
10
+ Config.declare(:gsearch) {
11
+ url nil
12
+ instance_eval do
13
+ def client
14
+ RestClient::Resource.new(
15
+ self.url,
16
+ :ssl_client_cert => OpenSSL::X509::Certificate.new(File.read(Config.fedora.cert_file)),
17
+ :ssl_client_key => OpenSSL::PKey::RSA.new(File.read(Config.fedora.key_file), Config.fedora.key_pass)
18
+ )
19
+ end
20
+ end
21
+ }
22
+
23
+ class << self
24
+
25
+ def reindex(*pids)
26
+ fedora_client = Config.fedora.client
27
+ solr_client = Config.gsearch.client
28
+ xsl = Nokogiri::XSLT(File.read(File.expand_path('../../gsearch/demoFoxmlToSolr.xslt', __FILE__)))
29
+ pids.in_groups_of(20, false) do |group|
30
+ doc = Nokogiri::XML('<update/>')
31
+ group.each do |pid|
32
+ begin
33
+ foxml = Dor::Base.get_foxml(pid,true)
34
+ doc.root.add_child(xsl.transform(foxml).root)
35
+ rescue RestClient::ResourceNotFound
36
+ doc.root.add_child("<delete><id>#{pid}</id></delete>")
37
+ end
38
+ end
39
+ yield group if block_given?
40
+ solr_client['update'].post(doc.to_xml, :content_type => 'application/xml')
41
+ end
42
+ pids
43
+ end
44
+
45
+ def risearch(query, opts = {})
46
+ client = Config.fedora.client['risearch']
47
+ client.options[:timeout] = opts.delete(:timeout)
48
+ query_params = {
49
+ :type => 'tuples',
50
+ :lang => 'itql',
51
+ :format => 'CSV',
52
+ :limit => '1000',
53
+ :stream => 'on',
54
+ :query => query
55
+ }.merge(opts)
56
+ result = client.post(query_params)
57
+ result.split(/\n/)[1..-1].collect { |pid| pid.chomp.sub(/^info:fedora\//,'') }
58
+ end
59
+
60
+ def gsearch(params)
61
+ client = Config.gsearch.client
62
+ query_params = params.merge(:wt => 'json')
63
+ query_string = query_params.collect { |k,v|
64
+ if v.is_a?(Array)
65
+ v.collect { |vv| "#{k}=#{URI.encode(vv.to_s)}" }.join('&')
66
+ else
67
+ "#{k}=#{URI.encode(v.to_s)}"
68
+ end
69
+ }.join('&')
70
+ result = JSON.parse(client["select?#{query_string}"].get)
71
+ end
72
+
73
+ def query_by_id(id)
74
+ if id.is_a?(Hash) # Single valued: { :google => 'STANFORD_0123456789' }
75
+ id = id.collect { |*v| v.join(':') }.first
76
+ elsif id.is_a?(Array) # Two values: [ 'google', 'STANFORD_0123456789' ]
77
+ id = id.join(':')
78
+ end
79
+ self.risearch(RISEARCH_TEMPLATE % id)
80
+ end
81
+
82
+ end
83
+
84
+ end
85
+
86
+ end
@@ -0,0 +1,37 @@
1
+ require 'rest-client'
2
+ require 'active_fedora'
3
+
4
+ module Dor
5
+ class SuriService
6
+
7
+ Config.declare(:suri) do
8
+ url nil
9
+ user nil
10
+ pass nil
11
+ id_namespace 'druid'
12
+ mint_ids false
13
+ end
14
+
15
+ # If Dor::Config.suri.mint_ids is set to true, then this method
16
+ # returns Config.suri.id_namespace:id_from_suri
17
+ # Throws an exception if there were any problems
18
+ def self.mint_id
19
+ unless(Config.suri.mint_ids)
20
+ return Fedora::Repository.instance.nextid
21
+ end
22
+
23
+ #Post with no body
24
+ resource = RestClient::Resource.new("#{Config.suri.url}/suri2/namespaces/#{Config.suri.id_namespace}/identifiers",
25
+ :user => Config.suri.user, :password => Config.suri.pass)
26
+ id = resource.post('').chomp
27
+
28
+ return "#{Config.suri.id_namespace}:#{id.strip}"
29
+
30
+ # rescue Exception => e
31
+ # Rails.logger.error("Unable to mint id from suri: #{e.to_s}")
32
+ # raise e
33
+ end
34
+
35
+
36
+ end
37
+ end
@@ -0,0 +1,102 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
3
+ xmlns:tei="http://www.tei-c.org/ns/1.0"
4
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
5
+ xmlns:dcterms="http://purl.org/dc/terms/"
6
+ xmlns:oai_dc="http://www.openarchives.org/OAI/2.0/oai_dc/"
7
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
8
+ exclude-result-prefixes="tei xsl"
9
+ version="1.0">
10
+
11
+ <xsl:output xml:space="default" indent="yes"/>
12
+
13
+ <xsl:template match="/">
14
+ <oai_dc:dc xsi:schemaLocation="http://www.openarchives.org/OAI/2.0/oai_dc/ http://www.openarchives.org/OAI/2.0/oai_dc.xsd">
15
+ <xsl:apply-templates/>
16
+ </oai_dc:dc>
17
+ </xsl:template>
18
+
19
+ <xsl:template match="tei:teiHeader">
20
+ <xsl:apply-templates/>
21
+ </xsl:template>
22
+
23
+ <xsl:template match="tei:fileDesc/tei:titleStmt/tei:title">
24
+ <xsl:call-template name="ptext"><xsl:with-param name="element-name">dc:title</xsl:with-param></xsl:call-template>
25
+ </xsl:template>
26
+
27
+ <xsl:template match="tei:fileDesc/tei:titleStmt/tei:author">
28
+ <xsl:call-template name="ptext"><xsl:with-param name="element-name">dc:creator</xsl:with-param></xsl:call-template>
29
+ </xsl:template>
30
+
31
+ <xsl:template match="tei:profileDesc/tei:textClass/tei:keywords/tei:list/tei:item">
32
+ <xsl:call-template name="ptext"><xsl:with-param name="element-name">dc:subject</xsl:with-param></xsl:call-template>
33
+ </xsl:template>
34
+
35
+ <xsl:template match="tei:encodingDesc/tei:refsDecl|tei:encodingDesc/tei:projectDesc|tei:encodingDesc/tei:editorialDesc">
36
+ <xsl:call-template name="ptext"><xsl:with-param name="element-name">dc:description</xsl:with-param></xsl:call-template>
37
+ </xsl:template>
38
+
39
+ <xsl:template match="tei:fileDesc/tei:publicationStmt/tei:publisher/tei:publisher|tei:fileDesc/tei:publicationStmt/tei:publisher/tei:pubPlace">
40
+ <xsl:call-template name="ptext"><xsl:with-param name="element-name">dc:publisher</xsl:with-param></xsl:call-template>
41
+ </xsl:template>
42
+
43
+ <xsl:template match="tei:fileDesc/tei:titleStmt/tei:editor|tei:fileDesc/tei:titleStmt/tei:funder|tei:fileDesc/tei:titleStmt/tei:sponsor|tei:fileDesc/tei:titleStmt/tei:principle">
44
+ <xsl:call-template name="ptext"><xsl:with-param name="element-name">dc:contributor</xsl:with-param></xsl:call-template>
45
+ </xsl:template>
46
+
47
+ <xsl:template match="tei:fileDesc/tei:publicationStmt/tei:date">
48
+ <xsl:call-template name="ptext"><xsl:with-param name="element-name">dc:date</xsl:with-param></xsl:call-template>
49
+ </xsl:template>
50
+
51
+ <xsl:template match="tei:extent/tei:seg[@type='size']">
52
+ <xsl:call-template name="ptext"><xsl:with-param name="element-name">dcterms:extent</xsl:with-param></xsl:call-template>
53
+ </xsl:template>
54
+
55
+ <xsl:template match="tei:fileDesc/tei:publicationStmt/tei:idno[@type='ARK']">
56
+ <xsl:call-template name="ptext"><xsl:with-param name="element-name">dc:identifier</xsl:with-param></xsl:call-template>
57
+ </xsl:template>
58
+
59
+ <xsl:template match="tei:sourceDesc/tei:bibful/tei:publicationStmt/tei:publisher|tei:sourceDesc/tei:bibful/tei:publicationStmt/tei:pubPlace|tei:sourceDesc/tei:bibful/tei:publicationStmt/tei:date|tei:sourceDesc/tei:bibl">
60
+ <xsl:call-template name="ptext"><xsl:with-param name="element-name">dc:source</xsl:with-param></xsl:call-template>
61
+ </xsl:template>
62
+
63
+ <xsl:template match="tei:profileDesc/tei:langUsage/tei:language">
64
+ <xsl:call-template name="ptext"><xsl:with-param name="element-name">dc:language</xsl:with-param></xsl:call-template>
65
+ </xsl:template>
66
+
67
+ <xsl:template match="tei:fileDesc/tei:seriesStmt/tei:title">
68
+ <xsl:call-template name="ptext"><xsl:with-param name="element-name">dc:relation</xsl:with-param></xsl:call-template>
69
+ </xsl:template>
70
+
71
+ <xsl:template match="tei:fileDesc/tei:publicationStmt/tei:availability">
72
+ <xsl:call-template name="ptext"><xsl:with-param name="element-name">dcterms:accessRights</xsl:with-param></xsl:call-template>
73
+ </xsl:template>
74
+
75
+ <xsl:template match="tei:notesStmt/tei:note[@type='summary']">
76
+ <xsl:call-template name="ptext"><xsl:with-param name="element-name">dcterms:abstract</xsl:with-param></xsl:call-template>
77
+ </xsl:template>
78
+
79
+ <!--
80
+ <xsl:template match="tei:notesStmt/tei:note[not(@type)]">
81
+ <xsl:call-template name="ptext"><xsl:with-param name="element-name">dcterms:note</xsl:with-param></xsl:call-template>
82
+ </xsl:template>
83
+ -->
84
+
85
+ <xsl:template name="ptext">
86
+ <xsl:param name="element-name"/>
87
+ <xsl:variable name="text">
88
+ <xsl:for-each select=".|./tei:p">
89
+ <xsl:variable name="t" select="normalize-space(./text())"/>
90
+ <xsl:if test="string-length($t) &gt; 0">
91
+ <xsl:value-of select="$t"/>
92
+ </xsl:if>
93
+ </xsl:for-each>
94
+ </xsl:variable>
95
+ <xsl:if test="string-length($text) &gt; 0">
96
+ <xsl:element name="{$element-name}"><xsl:value-of select="$text"/></xsl:element>
97
+ </xsl:if>
98
+ </xsl:template>
99
+
100
+ <xsl:template match="text()|@*"/>
101
+
102
+ </xsl:stylesheet>