dor-services 2.2.4

Sign up to get free protection for your applications and to get access to all the features.
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,229 @@
1
+ <?xml version="1.0" ?>
2
+ <!--
3
+ Licensed to the Apache Software Foundation (ASF) under one or more
4
+ contributor license agreements. See the NOTICE file distributed with
5
+ this work for additional information regarding copyright ownership.
6
+ The ASF licenses this file to You under the Apache License, Version 2.0
7
+ (the "License"); you may not use this file except in compliance with
8
+ the License. You may obtain a copy of the License at
9
+
10
+ http://www.apache.org/licenses/LICENSE-2.0
11
+
12
+ Unless required by applicable law or agreed to in writing, software
13
+ distributed under the License is distributed on an "AS IS" BASIS,
14
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ See the License for the specific language governing permissions and
16
+ limitations under the License.
17
+ -->
18
+
19
+ <!-- This is the Solr schema file. This file should be named "schema.xml" and
20
+ should be in the conf directory under the solr home
21
+ (i.e. ./solr/conf/schema.xml by default)
22
+ or located where the classloader for the Solr webapp can find it.
23
+
24
+ For more information, on how to customize this file, please see
25
+ http://wiki.apache.org/solr/SchemaXml
26
+ -->
27
+
28
+ <schema name="active_fedora" version="0.1">
29
+ <!-- attribute "name" is the name of this schema and is only used for display purposes.
30
+ Applications should change this to reflect the nature of the search collection.
31
+ version="1.1" is Solr's version number for the schema syntax and semantics. It should
32
+ not normally be changed by applications.
33
+ 1.0: multiValued attribute did not exist, all fields are multiValued by nature
34
+ 1.1: multiValued attribute introduced, false by default -->
35
+
36
+ <types>
37
+ <!-- field type definitions. The "name" attribute is
38
+ just a label to be used by field definitions. The "class"
39
+ attribute and any other attributes determine the real
40
+ behavior of the fieldtype.
41
+ Class names starting with "solr" refer to java classes in the
42
+ org.apache.solr.analysis package.
43
+ -->
44
+
45
+ <!-- The StrField type is not analyzed, but indexed/stored verbatim.
46
+ - StrField and TextField support an optional compressThreshold which
47
+ limits compression (if enabled in the derived fields) to values which
48
+ exceed a certain size (in characters).
49
+ -->
50
+ <fieldtype name="string" class="solr.StrField" sortMissingLast="true" omitNorms="true"/>
51
+
52
+ <!-- boolean type: "true" or "false" -->
53
+ <fieldtype name="boolean" class="solr.BoolField" sortMissingLast="true" omitNorms="true"/>
54
+
55
+ <!-- The optional sortMissingLast and sortMissingFirst attributes are
56
+ currently supported on types that are sorted internally as strings.
57
+ - If sortMissingLast="true", then a sort on this field will cause documents
58
+ without the field to come after documents with the field,
59
+ regardless of the requested sort order (asc or desc).
60
+ - If sortMissingFirst="true", then a sort on this field will cause documents
61
+ without the field to come before documents with the field,
62
+ regardless of the requested sort order.
63
+ - If sortMissingLast="false" and sortMissingFirst="false" (the default),
64
+ then default lucene sorting will be used which places docs without the
65
+ field first in an ascending sort and last in a descending sort.
66
+ -->
67
+
68
+
69
+ <!-- numeric field types that store and index the text
70
+ value verbatim (and hence don't support range queries, since the
71
+ lexicographic ordering isn't equal to the numeric ordering) -->
72
+ <fieldtype name="integer" class="solr.IntField" omitNorms="true"/>
73
+ <fieldtype name="long" class="solr.LongField" omitNorms="true"/>
74
+ <fieldtype name="float" class="solr.FloatField" omitNorms="true"/>
75
+ <fieldtype name="double" class="solr.DoubleField" omitNorms="true"/>
76
+
77
+
78
+ <!-- Numeric field types that manipulate the value into
79
+ a string value that isn't human-readable in its internal form,
80
+ but with a lexicographic ordering the same as the numeric ordering,
81
+ so that range queries work correctly. -->
82
+ <fieldtype name="sint" class="solr.SortableIntField" sortMissingLast="true" omitNorms="true"/>
83
+ <fieldtype name="slong" class="solr.SortableLongField" sortMissingLast="true" omitNorms="true"/>
84
+ <fieldtype name="sfloat" class="solr.SortableFloatField" sortMissingLast="true" omitNorms="true"/>
85
+ <fieldtype name="sdouble" class="solr.SortableDoubleField" sortMissingLast="true" omitNorms="true"/>
86
+
87
+
88
+ <!-- The format for this date field is of the form 1995-12-31T23:59:59Z, and
89
+ is a more restricted form of the canonical representation of dateTime
90
+ http://www.w3.org/TR/xmlschema-2/#dateTime
91
+ The trailing "Z" designates UTC time and is mandatory.
92
+ Optional fractional seconds are allowed: 1995-12-31T23:59:59.999Z
93
+ All other components are mandatory.
94
+
95
+ Expressions can also be used to denote calculations that should be
96
+ performed relative to "NOW" to determine the value, ie...
97
+
98
+ NOW/HOUR
99
+ ... Round to the start of the current hour
100
+ NOW-1DAY
101
+ ... Exactly 1 day prior to now
102
+ NOW/DAY+6MONTHS+3DAYS
103
+ ... 6 months and 3 days in the future from the start of
104
+ the current day
105
+
106
+ Consult the DateField javadocs for more information.
107
+ -->
108
+ <fieldtype name="date" class="solr.DateField" sortMissingLast="true" omitNorms="true"/>
109
+
110
+ <!-- solr.TextField allows the specification of custom text analyzers
111
+ specified as a tokenizer and a list of token filters. Different
112
+ analyzers may be specified for indexing and querying.
113
+
114
+ The optional positionIncrementGap puts space between multiple fields of
115
+ this type on the same document, with the purpose of preventing false phrase
116
+ matching across fields.
117
+
118
+ For more info on customizing your analyzer chain, please see
119
+ http://wiki.apache.org/solr/AnalyzersTokenizersTokenFilters
120
+ -->
121
+
122
+ <!-- One can also specify an existing Analyzer class that has a
123
+ default constructor via the class attribute on the analyzer element
124
+ <fieldtype name="text_greek" class="solr.TextField">
125
+ <analyzer class="org.apache.lucene.analysis.el.GreekAnalyzer"/>
126
+ </fieldType>
127
+ -->
128
+
129
+ <!-- A text field that only splits on whitespace for exact matching of words -->
130
+ <fieldtype name="text_ws" class="solr.TextField" positionIncrementGap="100">
131
+ <analyzer>
132
+ <tokenizer class="solr.WhitespaceTokenizerFactory"/>
133
+ </analyzer>
134
+ </fieldtype>
135
+
136
+ <fieldtype name="text" class="solr.TextField" positionIncrementGap="100">
137
+ <analyzer>
138
+ <tokenizer class="solr.StandardTokenizerFactory"/>
139
+ <filter class="solr.StandardFilterFactory"/>
140
+ <filter class="solr.LowerCaseFilterFactory"/>
141
+ </analyzer>
142
+ </fieldtype>
143
+
144
+ <fieldtype name="text_zh" class="solr.TextField">
145
+ <analyzer class="org.apache.lucene.analysis.cn.ChineseAnalyzer"/>
146
+ </fieldtype>
147
+
148
+
149
+ <!-- Less flexible matching, but less false matches. Probably not ideal for product names,
150
+ but may be good for SKUs. Can insert dashes in the wrong place and still match. -->
151
+ <fieldtype name="textTight" class="solr.TextField" positionIncrementGap="100" >
152
+ <analyzer>
153
+ <tokenizer class="solr.WhitespaceTokenizerFactory"/>
154
+ <filter class="solr.SynonymFilterFactory" synonyms="synonyms.txt" ignoreCase="true" expand="false"/>
155
+ <filter class="solr.StopFilterFactory" ignoreCase="true" words="stopwords.txt"/>
156
+ <filter class="solr.WordDelimiterFilterFactory" generateWordParts="0" generateNumberParts="0" catenateWords="1" catenateNumbers="1" catenateAll="0"/>
157
+ <filter class="solr.LowerCaseFilterFactory"/>
158
+ <filter class="solr.EnglishPorterFilterFactory" protected="protwords.txt"/>
159
+ <filter class="solr.RemoveDuplicatesTokenFilterFactory"/>
160
+ </analyzer>
161
+ </fieldtype>
162
+
163
+ </types>
164
+
165
+
166
+ <fields>
167
+ <!-- Valid attributes for fields:
168
+ name: mandatory - the name for the field
169
+ type: mandatory - the name of a previously defined type from the <types> section
170
+ indexed: true if this field should be indexed (searchable or sortable)
171
+ stored: true if this field should be retrievable
172
+ compressed: [false] if this field should be stored using gzip compression
173
+ (this will only apply if the field type is compressable; among
174
+ the standard field types, only TextField and StrField are)
175
+ multiValued: true if this field may contain multiple values per document
176
+ omitNorms: (expert) set to true to omit the norms associated with
177
+ this field (this disables length normalization and index-time
178
+ boosting for the field, and saves some memory). Only full-text
179
+ fields or fields that need an index-time boost need norms.
180
+ -->
181
+
182
+ <field name="PID" type="string" indexed="true" stored="true"/>
183
+
184
+ <!-- catchall field, containing all other searchable text fields (implemented
185
+ via copyField further on in this schema -->
186
+ <field name="text" type="text" indexed="true" stored="false" multiValued="true"/>
187
+ <!-- catchall field, containing all other searchable text fields (implemented
188
+ via copyField further on in this schema -->
189
+ <field name="date" type="date" indexed="true" stored="false" multiValued="true"/>
190
+
191
+ <!-- Dynamic field definitions. If a field name is not found, dynamicFields
192
+ will be used if the name matches any of the patterns.
193
+ RESTRICTION: the glob-like pattern in the name attribute must have
194
+ a "*" only at the start or the end.
195
+ EXAMPLE: name="*_i" will match any field ending in _i (like myid_i, z_i)
196
+ Longer patterns will be matched first. if equal size patterns
197
+ both match, the first appearing in the schema will be used. -->
198
+ <dynamicField name="*_date" type="date" indexed="true" stored="true" multiValued="true"/>
199
+ <dynamicField name="*_name" type="date" indexed="true" stored="true" multiValued="true"/>
200
+ <dynamicField name="*_field" type="text" indexed="true" stored="true" multiValued="true"/>
201
+
202
+ <dynamicField name="*_facet" type="string" indexed="true" stored="true" multiValued="true"/>
203
+ <dynamicField name="*_zh_text" type="text_zh" indexed="true" stored="true" multiValued="true"/>
204
+ <dynamicField name="*_text" type="text" indexed="true" stored="true" multiValued="true"/>
205
+ <dynamicField name="*_display" type="text" indexed="false" stored="true" multiValued="true"/>
206
+ </fields>
207
+
208
+ <!-- field to use to determine and enforce document uniqueness. -->
209
+ <uniqueKey>PID</uniqueKey>
210
+
211
+ <!-- field for the QueryParser to use when an explicit fieldname is absent -->
212
+ <defaultSearchField>text</defaultSearchField>
213
+
214
+ <!-- SolrQueryParser configuration: defaultOperator="AND|OR" -->
215
+ <solrQueryParser defaultOperator="AND"/>
216
+
217
+ <!-- copyField commands copy one field to another at the time a document
218
+ is added to the index. It's used either to index the same field differently,
219
+ or to add multiple fields to the same field for easier/faster searching. -->
220
+ <copyField source="*_date" dest="date"/>
221
+ <copyField source="*_text" dest="text"/>
222
+ <copyField source="*_facet" dest="text"/>
223
+
224
+ <!-- Similarity is the scoring routine for each document vs. a query.
225
+ A custom similarity may be specified here, but the default is fine
226
+ for most applications. -->
227
+ <!-- <similarity class="org.apache.lucene.search.DefaultSimilarity"/> -->
228
+
229
+ </schema>
@@ -0,0 +1,32 @@
1
+ desc "Generate RDoc"
2
+ task :doc => ['doc:generate']
3
+
4
+ namespace :doc do
5
+ project_root = File.expand_path(File.join(File.dirname(__FILE__), '..', '..'))
6
+ doc_destination = File.join(project_root, 'rdoc')
7
+
8
+ begin
9
+ require 'yard'
10
+ require 'yard/rake/yardoc_task'
11
+
12
+ YARD::Rake::YardocTask.new(:generate) do |yt|
13
+ yt.files = Dir.glob(File.join(project_root, 'lib', '*.rb')) +
14
+ Dir.glob(File.join(project_root, 'lib', '**', '*.rb')) +
15
+ [ File.join(project_root, 'README.rdoc') ] +
16
+ [ File.join(project_root, 'LICENSE') ]
17
+
18
+ yt.options = ['--output-dir', doc_destination, '--readme', 'README.rdoc']
19
+ end
20
+ rescue LoadError
21
+ desc "Generate YARD Documentation"
22
+ task :generate do
23
+ abort "Please install the YARD gem to generate rdoc."
24
+ end
25
+ end
26
+
27
+ desc "Remove generated documenation"
28
+ task :clean do
29
+ rm_r doc_destination if File.exists?(doc_destination)
30
+ end
31
+
32
+ end
@@ -0,0 +1,261 @@
1
+ require 'nokogiri'
2
+
3
+ # TODO: Rewrite in OM
4
+
5
+ class Foxml
6
+ attr_reader :xml
7
+
8
+ NAMESPACES = {
9
+ "dc" => "http://purl.org/dc/elements/1.1/",
10
+ "fedora-model" => "info:fedora/fedora-system:def/model#",
11
+ "foxml" => "info:fedora/fedora-system:def/foxml#",
12
+ "oai_dc" => "http://www.openarchives.org/OAI/2.0/oai_dc/",
13
+ "rdf" => "http://www.w3.org/1999/02/22-rdf-syntax-ns#",
14
+ "rel" => "info:fedora/fedora-system:def/relations-external#",
15
+ "hydra" => "http://projecthydra.org/ns/relations#"
16
+ }
17
+
18
+ def initialize(pid=nil, label=nil, content_model=nil, identity_metadata=nil, admin_policy_object=nil, parent = nil)
19
+ @xml = Nokogiri::XML(XML_TEMPLATE) { |config| config.default_xml.noblanks }
20
+ @defined_namespaces = { '' => nil }
21
+ @xml.traverse { |node|
22
+ if node.respond_to?(:namespace_definitions)
23
+ node.namespace_definitions.each { |ns|
24
+ @defined_namespaces[ns.prefix] = ns
25
+ }
26
+ end
27
+ }
28
+
29
+ self.pid = pid.to_s
30
+ self.label = label.to_s
31
+ self.content_model = content_model
32
+ self.identity_metadata = identity_metadata
33
+ self.admin_policy_object = admin_policy_object
34
+ self.parent = parent
35
+ end
36
+
37
+ def pid
38
+ self.xpath('/foxml:digitalObject/@PID').first.value
39
+ end
40
+
41
+ def pid=(value)
42
+ self.xpath('/foxml:digitalObject/@PID').first.value = value
43
+ self.get_datastream("RELS-EXT","rdf:RDF/rdf:Description/@rdf:about").value = "info:fedora/#{value}"
44
+ update_dc_identifiers
45
+ end
46
+
47
+ def content_model
48
+ self.get_rels_ext_resource("fedora-model:hasModel")
49
+ end
50
+
51
+ def content_model=(value)
52
+ self.set_rels_ext_resource("fedora-model:hasModel",value)
53
+ end
54
+
55
+ def dublin_core
56
+ self.get_datastream("DC","oai_dc:dc")
57
+ end
58
+
59
+ def dublin_core=(value)
60
+ self.set_datastream("DC",value,'<oai_dc:dc xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:oai_dc="http://www.openarchives.org/OAI/2.0/oai_dc/"/>')
61
+ end
62
+
63
+ def identity_metadata
64
+ self.get_datastream("identityMetadata","identityMetadata")
65
+ end
66
+
67
+ def identity_metadata=(value)
68
+ self.set_datastream("identityMetadata",value,"<identityMetadata/>")
69
+ # strip the namespace Nokogiri attaches to identityMetadata
70
+ self.get_datastream("identityMetadata","*[local-name()='identityMetadata']").traverse { |n| n.namespace = nil }
71
+ update_dc_identifiers
72
+ end
73
+
74
+ def label
75
+ self.xpath('//foxml:property[@NAME="info:fedora/fedora-system:def/model#label"]/@VALUE').first
76
+ end
77
+
78
+ def label=(value)
79
+ self.xpath('//foxml:property[@NAME="info:fedora/fedora-system:def/model#label"]/@VALUE').first.value = value[0..254]
80
+ if existing_title = self.get_datastream("DC","//dc:title")
81
+ existing_title.remove
82
+ end
83
+ new_child = @xml.create_element('dc:title')
84
+ new_child.content = value
85
+ self.dublin_core.add_child(new_child)
86
+ fix_namespaces(new_child)
87
+ return new_child
88
+ end
89
+
90
+ def admin_policy_object
91
+ self.get_rels_ext_resource("hydra:isGovernedBy")
92
+ end
93
+
94
+ def admin_policy_object=(value)
95
+ self.set_rels_ext_resource("hydra:isGovernedBy",value)
96
+ end
97
+
98
+ def parent
99
+ self.get_rels_ext_resource("rel:isPartOf")
100
+ end
101
+
102
+ def parent=(value)
103
+ self.set_rels_ext_resource("rel:isPartOf",value)
104
+ end
105
+
106
+ def get_rels_ext_resource(predicate)
107
+ resource = self.get_datastream("RELS-EXT","//#{predicate}/@rdf:resource")
108
+ if resource.nil?
109
+ nil
110
+ else
111
+ resource.value.split(/\//).last
112
+ end
113
+ end
114
+
115
+ def set_rels_ext_resource(predicate,value)
116
+ if existing_parent = self.get_datastream("RELS-EXT","//#{predicate}")
117
+ existing_parent.remove
118
+ end
119
+ if value.nil?
120
+ return nil
121
+ else
122
+ new_child = @xml.create_element(predicate, 'rdf:resource' => "info:fedora/#{value}")
123
+ self.get_datastream("RELS-EXT","//rdf:Description").add_child(new_child)
124
+ fix_namespaces(new_child)
125
+ return new_child
126
+ end
127
+ end
128
+
129
+ def get_datastream(ds_name, *paths)
130
+ result = self.xpath(%{//foxml:datastream[@ID="#{ds_name}"]/foxml:datastreamVersion/foxml:xmlContent}).first
131
+ paths.each do |path|
132
+ result = result.xpath(path, NAMESPACES).first
133
+ end
134
+ return result
135
+ end
136
+
137
+ def set_datastream(ds_name, new_value, nil_value = nil)
138
+ new_value ||= nil_value
139
+ if new_value.is_a?(Nokogiri::XML::Document)
140
+ new_value = new_value.root.clone
141
+ elsif new_value.is_a?(Nokogiri::XML::Node)
142
+ new_value = new_value.clone
143
+ end
144
+ parent = self.get_datastream(ds_name)
145
+ parent.children.each { |c| c.remove }
146
+ unless new_value.nil?
147
+ parent.add_child(new_value)
148
+ end
149
+ end
150
+
151
+ def to_xml(opts = {})
152
+ undent_datastreams = opts.delete(:undent_datastreams)
153
+ @xml.traverse { |node| if node.is_a?(Nokogiri::XML::Text) and node.text.chomp.strip.empty?; node.remove; end }
154
+ result = @xml.to_xml(opts)
155
+
156
+ # Fedora FOXML ingest treats some (but not all) whitespace as significant. In order to get
157
+ # our datastreams to look pretty, we need to un-indent them. This an ugly hack, and only
158
+ # works because we've strictly controlled Nokogiri's serialization of whitespace.
159
+ if undent_datastreams
160
+ result.gsub!(/^(\s*)<foxml:xmlContent>\n(.+?)\n\s*<\/foxml:xmlContent>/m) { |m|
161
+ sp = $1
162
+ data = $2
163
+ "#{sp}<foxml:xmlContent>\n" + data.gsub(/^#{sp} /,'') + "\n#{sp}</foxml:xmlContent>"
164
+ }
165
+ end
166
+ return result
167
+ end
168
+
169
+ def xpath(path)
170
+ @xml.xpath(path, NAMESPACES)
171
+ end
172
+
173
+ def update_dc_identifiers
174
+ new_ids = self.identity_metadata.xpath('*[local-name() = "sourceId" or local-name() = "otherId"]').collect do |n|
175
+ n.attribute_nodes.first.value + ":" + n.content
176
+ end
177
+ unless self.pid.nil? or self.pid.empty?
178
+ new_ids << self.pid
179
+ end
180
+
181
+ dc = self.dublin_core
182
+ dc.xpath('dc:identifier',NAMESPACES).each { |existing_id| existing_id.remove }
183
+ new_ids.uniq.each do |id|
184
+ new_child = @xml.create_element('dc:identifier')
185
+ new_child.content = id
186
+ dc.add_child(new_child)
187
+ fix_namespaces(new_child)
188
+ end
189
+ return new_ids
190
+ end
191
+
192
+ def fix_namespaces(node)
193
+ if node.is_a?(Nokogiri::XML::CharacterData)
194
+ node.namespace = nil
195
+ else
196
+ if node.name =~ /:/
197
+ (prefix,name) = node.name.split(/:/)
198
+ node.name = name
199
+ node.namespace = @defined_namespaces[prefix]
200
+ end
201
+ unless node.is_a?(Nokogiri::XML::Attr)
202
+ node.children.each { |n| fix_namespaces(n) }
203
+ node.attribute_nodes.each { |n| fix_namespaces(n) }
204
+ end
205
+ end
206
+ end
207
+
208
+ def method_missing(sym,*args)
209
+ if @xml.respond_to?(sym)
210
+ @xml.send(sym,*args)
211
+ else
212
+ super
213
+ end
214
+ end
215
+
216
+ XML_TEMPLATE = %{
217
+ <?xml version="1.0" encoding="UTF-8"?>
218
+ <foxml:digitalObject PID="$$PID$$" VERSION="1.1"
219
+ xmlns:foxml="info:fedora/fedora-system:def/foxml#"
220
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="info:fedora/fedora-system:def/foxml# http://www.fedora.info/definitions/1/0/foxml1-1.xsd">
221
+ <foxml:objectProperties>
222
+ <foxml:property NAME="info:fedora/fedora-system:def/model#state" VALUE="Active"/>
223
+ <foxml:property NAME="info:fedora/fedora-system:def/model#label" VALUE="$$LABEL$$"/>
224
+ <foxml:property NAME="info:fedora/fedora-system:def/model#ownerId" VALUE="DOR"/>
225
+ </foxml:objectProperties>
226
+ <foxml:datastream CONTROL_GROUP="X" ID="DC" STATE="A" VERSIONABLE="false">
227
+ <foxml:datastreamVersion
228
+ FORMAT_URI="http://www.openarchives.org/OAI/2.0/oai_dc/" ID="DC1.0"
229
+ LABEL="Dublin Core Record for this object" MIMETYPE="text/xml">
230
+ <foxml:xmlContent>
231
+ <oai_dc:dc xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:oai_dc="http://www.openarchives.org/OAI/2.0/oai_dc/"/>
232
+ </foxml:xmlContent>
233
+ </foxml:datastreamVersion>
234
+ </foxml:datastream>
235
+ <foxml:datastream CONTROL_GROUP="X" ID="identityMetadata" STATE="A" VERSIONABLE="false">
236
+ <foxml:datastreamVersion
237
+ ID="identityMetadata.0" LABEL="Identity Metadata" MIMETYPE="text/xml">
238
+ <foxml:xmlContent>
239
+ <identityMetadata/>
240
+ </foxml:xmlContent>
241
+ </foxml:datastreamVersion>
242
+ </foxml:datastream>
243
+ <foxml:datastream CONTROL_GROUP="X" ID="RELS-EXT" STATE="A">
244
+ <foxml:datastreamVersion
245
+ FORMAT_URI="info:fedora/fedora-system:FedoraRELSExt-1.0" ID="RELS-EXT.0"
246
+ LABEL="RDF Statements about this object" MIMETYPE="application/rdf+xml">
247
+ <foxml:xmlContent>
248
+ <rdf:RDF xmlns:fedora-model="info:fedora/fedora-system:def/model#"
249
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
250
+ xmlns:fedora="info:fedora/fedora-system:def/relations-external#"
251
+ xmlns:hydra="http://projecthydra.org/ns/relations#">
252
+ <rdf:Description rdf:about="info:fedora/$$PID$$">
253
+ </rdf:Description>
254
+ </rdf:RDF>
255
+ </foxml:xmlContent>
256
+ </foxml:datastreamVersion>
257
+ </foxml:datastream>
258
+ </foxml:digitalObject>
259
+ }.strip
260
+
261
+ end