ddr-core 0.3.0 → 1.2.0.rc1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +1 -1
- data/app/models/concerns/ddr/#search_builder_behavior.rb# +10 -0
- data/app/models/concerns/ddr/describable.rb +8 -12
- data/app/models/concerns/ddr/has_admin_metadata.rb +13 -65
- data/app/models/concerns/ddr/has_children.rb +8 -0
- data/app/models/concerns/ddr/has_content.rb +1 -0
- data/app/models/concerns/ddr/has_parent.rb +14 -2
- data/app/models/concerns/ddr/search_builder_behavior.rb~ +9 -0
- data/app/models/concerns/ddr/solr_document_behavior.rb +11 -90
- data/app/models/ddr/#admin_set.rb# +26 -0
- data/app/models/ddr/#auxiliary_resource_cache.rb# +34 -0
- data/app/models/ddr/admin_set.rb +3 -7
- data/app/models/ddr/alert.rb +16 -0
- data/app/models/ddr/auxiliary_resource.rb +24 -0
- data/app/models/ddr/auxiliary_resource.rb~ +13 -0
- data/app/models/ddr/cacheable_auxiliary_resource.rb~ +20 -0
- data/app/models/ddr/collection.rb +5 -4
- data/app/models/ddr/component.rb +8 -0
- data/app/models/ddr/contact.rb +1 -5
- data/app/models/ddr/finding_aid.rb +61 -0
- data/app/models/ddr/item.rb +0 -14
- data/app/models/ddr/language.rb +1 -6
- data/app/models/ddr/resource.rb +14 -0
- data/app/models/ddr/rights_statement.rb +2 -6
- data/config/locales/ddr-core.en.yml +4 -1
- data/db/migrate/20200207194453_add_default_to_lock_version.rb +6 -0
- data/lib/ddr/auth/#duke_person.rb# +9 -0
- data/lib/ddr/auth/ability_definitions/publication_ability_definitions.rb +7 -0
- data/lib/ddr/auth/ability_definitions/role_based_ability_definitions.rb +2 -2
- data/lib/ddr/auth/ability_definitions/superuser_ability_definitions.rb +3 -0
- data/lib/ddr/auth/duke_directory.rb~ +7 -0
- data/lib/ddr/auth/effective_permissions.rb +2 -1
- data/lib/ddr/auth/effective_roles.rb +6 -2
- data/lib/ddr/auth/failure_app.rb +1 -1
- data/lib/ddr/auth/grouper_gateway.rb +2 -2
- data/lib/ddr/auth/permissions.rb +14 -11
- data/lib/ddr/auth/role_based_access_controls_enforcement.rb +1 -37
- data/lib/ddr/auth/roles.rb +15 -3
- data/lib/ddr/auth/roles/role.rb +27 -69
- data/lib/ddr/auth/roles/role_types.rb +13 -6
- data/lib/ddr/core.rb +9 -0
- data/lib/ddr/core/version.rb +1 -1
- data/lib/ddr/fits.rb +0 -91
- data/lib/ddr/index.rb +2 -2
- data/lib/ddr/index/csv_query_result.rb +27 -20
- data/lib/ddr/index/fields.rb +5 -0
- data/lib/ddr/managers/technical_metadata_manager.rb +9 -4
- data/lib/ddr/structure.rb +2 -1
- data/lib/ddr/vocab/asset.rb +4 -0
- data/lib/ddr/workflow.rb +1 -0
- metadata +35 -9
@@ -1,7 +1,8 @@
|
|
1
1
|
module Ddr::Auth
|
2
2
|
class EffectivePermissions
|
3
3
|
|
4
|
-
# @param obj [Object] an object that receives :roles and returns
|
4
|
+
# @param obj [Object] an object that receives :roles and returns an Array
|
5
|
+
# of Ddr::Auth::Roles::Role.
|
5
6
|
# @param agents [String, Array<String>] agent(s) to match roles
|
6
7
|
# @return [Array<Symbol>]
|
7
8
|
def self.call(obj, agents)
|
@@ -1,8 +1,12 @@
|
|
1
1
|
module Ddr::Auth
|
2
2
|
class EffectiveRoles
|
3
3
|
|
4
|
-
def self.call(obj, agents)
|
5
|
-
( obj.
|
4
|
+
def self.call(obj, agents = nil)
|
5
|
+
( obj.roles | obj.inherited_roles ).tap do |roles|
|
6
|
+
if agents
|
7
|
+
roles.select! { |r| agents.include?(r.agent) }
|
8
|
+
end
|
9
|
+
end
|
6
10
|
end
|
7
11
|
|
8
12
|
end
|
data/lib/ddr/auth/failure_app.rb
CHANGED
@@ -25,7 +25,7 @@ module Ddr
|
|
25
25
|
|
26
26
|
# List of all grouper groups for the repository
|
27
27
|
def repository_groups(raw = false)
|
28
|
-
repo_groups = groups(
|
28
|
+
repo_groups = groups(Ddr::Auth.repository_group_filter)
|
29
29
|
if ok?
|
30
30
|
return repo_groups if raw
|
31
31
|
repo_groups.map do |g|
|
@@ -52,7 +52,7 @@ module Ddr
|
|
52
52
|
result = response["WsGetGroupsResults"]["results"].first
|
53
53
|
# Have to manually filter results b/c Grouper WS version 1.5 does not support filter parameter
|
54
54
|
if result && result["wsGroups"]
|
55
|
-
groups = result["wsGroups"].select { |g| g["name"] =~ /^#{
|
55
|
+
groups = result["wsGroups"].select { |g| g["name"] =~ /^#{Ddr::Auth.repository_group_filter}/ }
|
56
56
|
end
|
57
57
|
end
|
58
58
|
rescue StandardError => e
|
data/lib/ddr/auth/permissions.rb
CHANGED
@@ -1,18 +1,21 @@
|
|
1
1
|
module Ddr::Auth
|
2
2
|
class Permissions
|
3
3
|
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
4
|
+
DISCOVER = :discover
|
5
|
+
READ = :read
|
6
|
+
DOWNLOAD = :download
|
7
|
+
ADD_CHILDREN = :add_children
|
8
|
+
UPDATE = :update
|
9
|
+
REPLACE = :replace
|
10
|
+
ARRANGE = :arrange
|
11
|
+
PUBLISH = :publish
|
12
|
+
UNPUBLISH = :unpublish
|
13
|
+
MAKE_NONPUBLISHABLE = :make_nonpublishable
|
14
|
+
AUDIT = :audit
|
15
|
+
GRANT = :grant
|
14
16
|
|
15
|
-
ALL = [ READ, DOWNLOAD, ADD_CHILDREN, UPDATE,
|
17
|
+
ALL = [ DISCOVER, READ, DOWNLOAD, ADD_CHILDREN, UPDATE,
|
18
|
+
REPLACE, ARRANGE, PUBLISH, UNPUBLISH, MAKE_NONPUBLISHABLE, AUDIT, GRANT ]
|
16
19
|
|
17
20
|
end
|
18
21
|
end
|
@@ -1,11 +1,5 @@
|
|
1
1
|
module Ddr
|
2
2
|
module Auth
|
3
|
-
#
|
4
|
-
# Hydra controller mixin for role-based access control
|
5
|
-
#
|
6
|
-
# Overrides Hydra::AccessControlsEnforcement#gated_discovery_filters
|
7
|
-
# to apply role filters instead of permissions filters.
|
8
|
-
#
|
9
3
|
module RoleBasedAccessControlsEnforcement
|
10
4
|
|
11
5
|
def self.included(controller)
|
@@ -17,38 +11,8 @@ module Ddr
|
|
17
11
|
@current_ability ||= AbilityFactory.call(current_user, request.env)
|
18
12
|
end
|
19
13
|
|
20
|
-
# List of URIs for policies on which any of the current user's agent has a role in policy scope
|
21
|
-
def policy_role_policies
|
22
|
-
@policy_role_policies ||= Array.new.tap do |uris|
|
23
|
-
filters = current_ability.agents.map do |agent|
|
24
|
-
"#{Ddr::Index::Fields::POLICY_ROLE}:\"#{agent}\""
|
25
|
-
end.join(" OR ")
|
26
|
-
query = "#{Ddr::Index::Fields::ACTIVE_FEDORA_MODEL}:Collection AND (#{filters})"
|
27
|
-
results = ActiveFedora::SolrService.query(query, rows: Collection.count, fl: Ddr::Index::Fields::INTERNAL_URI)
|
28
|
-
results.each_with_object(uris) { |r, memo| memo << r[Ddr::Index::Fields::INTERNAL_URI] }
|
29
|
-
end
|
30
|
-
end
|
31
|
-
|
32
|
-
def policy_role_filters
|
33
|
-
if policy_role_policies.present?
|
34
|
-
rels = policy_role_policies.map { |pid| [:is_governed_by, pid] }
|
35
|
-
ActiveFedora::SolrService.construct_query_for_rel(rels, "OR")
|
36
|
-
end
|
37
|
-
end
|
38
|
-
|
39
|
-
def resource_role_filters
|
40
|
-
current_ability.agents.map do |agent|
|
41
|
-
ActiveFedora::SolrService.raw_query(Ddr::Index::Fields::RESOURCE_ROLE, agent)
|
42
|
-
end.join(" OR ")
|
43
|
-
end
|
44
|
-
|
45
|
-
def gated_discovery_filters
|
46
|
-
[resource_role_filters, policy_role_filters].compact
|
47
|
-
end
|
48
|
-
|
49
|
-
# Overrides Hydra::AccessControlsEnforcement
|
50
14
|
def enforce_show_permissions
|
51
|
-
authorize!
|
15
|
+
authorize! Permissions::DISCOVER, params[:id]
|
52
16
|
end
|
53
17
|
|
54
18
|
end
|
data/lib/ddr/auth/roles.rb
CHANGED
@@ -10,18 +10,30 @@ module Ddr::Auth
|
|
10
10
|
|
11
11
|
RESOURCE_SCOPE = "resource".freeze
|
12
12
|
POLICY_SCOPE = "policy".freeze
|
13
|
-
SCOPES = [RESOURCE_SCOPE, POLICY_SCOPE].freeze
|
13
|
+
SCOPES = [ RESOURCE_SCOPE, POLICY_SCOPE ].freeze
|
14
|
+
|
15
|
+
ORDERED_ROLE_TYPES = [
|
16
|
+
CURATOR,
|
17
|
+
EDITOR,
|
18
|
+
METADATA_EDITOR,
|
19
|
+
CONTRIBUTOR,
|
20
|
+
DOWNLOADER,
|
21
|
+
VIEWER,
|
22
|
+
METADATA_VIEWER
|
23
|
+
]
|
14
24
|
|
15
25
|
class << self
|
16
|
-
|
17
26
|
def type_map
|
18
27
|
@type_map ||= role_types.map { |role_type| [role_type.to_s, role_type] }.to_h
|
19
28
|
end
|
20
29
|
|
21
30
|
def role_types
|
22
|
-
|
31
|
+
ORDERED_ROLE_TYPES
|
23
32
|
end
|
24
33
|
|
34
|
+
def titles
|
35
|
+
@titles ||= role_types.map(&:title)
|
36
|
+
end
|
25
37
|
end
|
26
38
|
|
27
39
|
end
|
data/lib/ddr/auth/roles/role.rb
CHANGED
@@ -8,67 +8,38 @@ module Ddr
|
|
8
8
|
|
9
9
|
DEFAULT_SCOPE = Roles::RESOURCE_SCOPE
|
10
10
|
|
11
|
-
ValidScope = Valkyrie::Types::
|
12
|
-
ValidRoleType = Valkyrie::Types::Strict::String.enum(*(Roles
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
# @return [Role] the role
|
23
|
-
# @example
|
24
|
-
# Role.build type: "Curator", agent: "bob", scope: "resource"
|
25
|
-
def build(args={})
|
26
|
-
new.tap do |role|
|
27
|
-
args[:role_type] ||= args.delete(:type)
|
28
|
-
args[:agent] ||= nil # Triggers a constraint error
|
29
|
-
args[:agent] = args[:agent].to_s # Coerce Ddr::Auth:Group to string
|
30
|
-
|
31
|
-
args.each do |attr, val|
|
32
|
-
role.set_value(attr, val)
|
33
|
-
end
|
11
|
+
ValidScope = Valkyrie::Types::Coercible::String.default(DEFAULT_SCOPE).enum(*(Roles::SCOPES))
|
12
|
+
ValidRoleType = Valkyrie::Types::Strict::String.enum(*(Roles.titles))
|
13
|
+
ValidAgent = Valkyrie::Types::Coercible::String.constrained(min_size: 1)
|
14
|
+
|
15
|
+
# Symbolize input keys
|
16
|
+
# https://dry-rb.org/gems/dry-struct/1.0/recipes/#symbolize-input-keys
|
17
|
+
transform_keys do |key|
|
18
|
+
_k = key.to_sym
|
19
|
+
# For backwards compat allow :type as an alias for :role_type
|
20
|
+
_k == :type ? :role_type : _k
|
21
|
+
end
|
34
22
|
|
35
|
-
|
23
|
+
# Make nils use default values
|
24
|
+
# https://dry-rb.org/gems/dry-struct/1.0/recipes/#resolving-default-values-on-code-nil-code
|
25
|
+
transform_types do |type|
|
26
|
+
if type.default?
|
27
|
+
type.constructor do |value|
|
28
|
+
value.nil? ? Dry::Types::Undefined : value
|
36
29
|
end
|
30
|
+
else
|
31
|
+
type
|
37
32
|
end
|
33
|
+
end
|
38
34
|
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
#
|
43
|
-
# # Deserialize a Role from JSON
|
44
|
-
# # @param json [String] the JSON string
|
45
|
-
# # @return [Role] the role
|
46
|
-
# def from_json(json)
|
47
|
-
# build JSON.parse(json)
|
48
|
-
# end
|
49
|
-
|
50
|
-
# alias_method :deserialize, :from_json
|
51
|
-
|
52
|
-
private
|
53
|
-
|
54
|
-
#
|
55
|
-
# DELETEME
|
56
|
-
#
|
57
|
-
# def build_attributes(args={})
|
58
|
-
# # symbolize keys and stringify values
|
59
|
-
# attrs = args.each_with_object({}) do |(k, v), memo|
|
60
|
-
# memo[k.to_sym] = Array(v).first.to_s
|
61
|
-
# end
|
62
|
-
# # set default scope if necessary
|
63
|
-
# attrs[:scope] ||= DEFAULT_SCOPE
|
64
|
-
# # accept :type key for role_type attribute
|
65
|
-
# if attrs.key?(:type)
|
66
|
-
# attrs[:role_type] = attrs.delete(:type)
|
67
|
-
# end
|
68
|
-
# attrs
|
69
|
-
# end
|
35
|
+
attribute :agent, ValidAgent
|
36
|
+
attribute :role_type, ValidRoleType
|
37
|
+
attribute :scope, ValidScope
|
70
38
|
|
71
|
-
|
39
|
+
# DEPRECATED: Use constructor
|
40
|
+
def self.build(args={})
|
41
|
+
new(args)
|
42
|
+
end
|
72
43
|
|
73
44
|
# Roles are considered equal (==) if they
|
74
45
|
# are of the same type and have the same agent and scope.
|
@@ -96,19 +67,6 @@ module Ddr
|
|
96
67
|
"agent=#{agent.inspect}, scope=#{scope.inspect}>"
|
97
68
|
end
|
98
69
|
|
99
|
-
# TODO refactor up?
|
100
|
-
def proper_attributes
|
101
|
-
attributes.slice(self.class.fields - self.class.reserved_attributes)
|
102
|
-
end
|
103
|
-
|
104
|
-
###############
|
105
|
-
# FIXME or remove serialization/deserialization
|
106
|
-
###############
|
107
|
-
#
|
108
|
-
# delegate :to_json, to: :proper_attributes
|
109
|
-
#
|
110
|
-
# alias_method :serialize, :to_json
|
111
|
-
|
112
70
|
# Returns the permissions associated with the role
|
113
71
|
# @return [Array<Symbol>] the permissions
|
114
72
|
def permissions
|
@@ -14,36 +14,43 @@ module Ddr
|
|
14
14
|
"Editor",
|
15
15
|
"The Editor role conveys reponsibility for managing the content, " \
|
16
16
|
"description and structural arrangement of a resource.",
|
17
|
-
[ Permissions::
|
18
|
-
Permissions::
|
17
|
+
[ Permissions::DISCOVER, Permissions::READ, Permissions::DOWNLOAD,
|
18
|
+
Permissions::ADD_CHILDREN, Permissions::UPDATE,
|
19
|
+
Permissions::REPLACE, Permissions::ARRANGE ]
|
19
20
|
)
|
20
21
|
|
21
22
|
METADATA_EDITOR = RoleType.new(
|
22
23
|
"MetadataEditor",
|
23
24
|
"The Metadata Editor role conveys responsibility for " \
|
24
25
|
"managing the description of a resource.",
|
25
|
-
[ Permissions::READ, Permissions::DOWNLOAD, Permissions::UPDATE ]
|
26
|
+
[ Permissions::DISCOVER, Permissions::READ, Permissions::DOWNLOAD, Permissions::UPDATE ]
|
26
27
|
)
|
27
28
|
|
28
29
|
CONTRIBUTOR = RoleType.new(
|
29
30
|
"Contributor",
|
30
31
|
"The Contributor role conveys responsibility for adding related " \
|
31
32
|
"resources to a resource, such as works to a collection.",
|
32
|
-
[ Permissions::READ, Permissions::ADD_CHILDREN ]
|
33
|
+
[ Permissions::DISCOVER, Permissions::READ, Permissions::ADD_CHILDREN ]
|
33
34
|
)
|
34
35
|
|
35
36
|
DOWNLOADER = RoleType.new(
|
36
37
|
"Downloader",
|
37
38
|
"The Downloader role conveys access to the \"master\" file " \
|
38
39
|
"(original content bitstream) of a resource.",
|
39
|
-
[ Permissions::READ, Permissions::DOWNLOAD ]
|
40
|
+
[ Permissions::DISCOVER, Permissions::READ, Permissions::DOWNLOAD ]
|
40
41
|
)
|
41
42
|
|
42
43
|
VIEWER = RoleType.new(
|
43
44
|
"Viewer",
|
44
45
|
"The Viewer role conveys access to the description and \"access\" " \
|
45
46
|
"files (e.g., derivative bitstreams) of a resource.",
|
46
|
-
[ Permissions::READ ]
|
47
|
+
[ Permissions::DISCOVER, Permissions::READ ]
|
48
|
+
)
|
49
|
+
|
50
|
+
METADATA_VIEWER = RoleType.new(
|
51
|
+
"MetadataViewer",
|
52
|
+
"The MetadataViewer role coveys access to the description of a resource.",
|
53
|
+
[ Permissions::DISCOVER ]
|
47
54
|
)
|
48
55
|
|
49
56
|
end
|
data/lib/ddr/core.rb
CHANGED
@@ -9,6 +9,7 @@ module Ddr
|
|
9
9
|
autoload :Files
|
10
10
|
autoload :NotFoundError, 'ddr/error'
|
11
11
|
autoload :Fits
|
12
|
+
autoload :FindingAid
|
12
13
|
autoload :Index
|
13
14
|
autoload :Managers
|
14
15
|
autoload :Utils
|
@@ -103,6 +104,14 @@ module Ddr
|
|
103
104
|
false
|
104
105
|
end
|
105
106
|
|
107
|
+
mattr_accessor :ddr_aux_api_url do
|
108
|
+
ENV["DDR_AUX_API_URL"]
|
109
|
+
end
|
110
|
+
|
111
|
+
mattr_accessor :ead_xml_base_url do
|
112
|
+
ENV["EAD_XML_BASE_URL"]
|
113
|
+
end
|
114
|
+
|
106
115
|
module Core
|
107
116
|
|
108
117
|
end
|
data/lib/ddr/core/version.rb
CHANGED
data/lib/ddr/fits.rb
CHANGED
@@ -94,96 +94,5 @@ module Ddr
|
|
94
94
|
xpath('//fits:fits', fits: FITS_XMLNS).first
|
95
95
|
end
|
96
96
|
|
97
|
-
### DDRevo ##########################################
|
98
|
-
# From Ddr::Datastreams::FitsDatastream. Code above accounts for proxy terms from below and has a private 'root'
|
99
|
-
# method but does not (yet) provide methods for all the terms in the terminology. Thought it was better to wait
|
100
|
-
# and see which ones are actually needed.
|
101
|
-
### DDRevo ##########################################
|
102
|
-
# set_terminology do |t|
|
103
|
-
# t.root(path: "fits",
|
104
|
-
# xmlns: FITS_XMLNS,
|
105
|
-
# schema: FITS_SCHEMA)
|
106
|
-
# t.version(path: {attribute: "version"})
|
107
|
-
# t.timestamp(path: {attribute: "timestamp"})
|
108
|
-
# t.identification {
|
109
|
-
# t.identity {
|
110
|
-
# t.mimetype(path: {attribute: "mimetype"})
|
111
|
-
# t.format_label(path: {attribute: "format"})
|
112
|
-
# t.version
|
113
|
-
# t.externalIdentifier
|
114
|
-
# t.pronom_identifier(path: "externalIdentifier", attributes: {type: "puid"})
|
115
|
-
# }
|
116
|
-
# }
|
117
|
-
# t.fileinfo {
|
118
|
-
# t.created
|
119
|
-
# t.creatingApplicationName
|
120
|
-
# t.creatingos
|
121
|
-
# t.filename
|
122
|
-
# t.filepath
|
123
|
-
# t.fslastmodified
|
124
|
-
# t.lastmodified
|
125
|
-
# t.md5checksum
|
126
|
-
# t.size
|
127
|
-
# }
|
128
|
-
# t.filestatus {
|
129
|
-
# t.valid
|
130
|
-
# t.well_formed(path: "well-formed")
|
131
|
-
# }
|
132
|
-
# t.metadata {
|
133
|
-
# t.image {
|
134
|
-
# t.imageWidth
|
135
|
-
# t.imageHeight
|
136
|
-
# t.colorSpace
|
137
|
-
# t.iccProfileName
|
138
|
-
# t.iccProfileVersion
|
139
|
-
# }
|
140
|
-
# t.document {
|
141
|
-
# # TODO - configure to get from Tika?
|
142
|
-
# # t.encoding
|
143
|
-
# }
|
144
|
-
# t.text
|
145
|
-
# t.audio
|
146
|
-
# t.video
|
147
|
-
# }
|
148
|
-
#
|
149
|
-
# ## proxy terms
|
150
|
-
#
|
151
|
-
# # identification / identity
|
152
|
-
# t.format_label proxy: [:identification, :identity, :format_label]
|
153
|
-
# t.format_version proxy: [:identification, :identity, :version]
|
154
|
-
# t.media_type proxy: [:identification, :identity, :mimetype]
|
155
|
-
# t.pronom_identifier proxy: [:identification, :identity, :pronom_identifier]
|
156
|
-
#
|
157
|
-
# # filestatus
|
158
|
-
# t.valid proxy: [:filestatus, :valid]
|
159
|
-
# t.well_formed proxy: [:filestatus, :well_formed]
|
160
|
-
#
|
161
|
-
# # fileinfo
|
162
|
-
# t.created proxy: [:fileinfo, :created]
|
163
|
-
# t.creating_application proxy: [:fileinfo, :creatingApplicationName]
|
164
|
-
# t.extent proxy: [:fileinfo, :size]
|
165
|
-
# t.md5 proxy: [:fileinfo, :md5checksum]
|
166
|
-
#
|
167
|
-
# # image metadata
|
168
|
-
# t.color_space proxy: [:metadata, :image, :colorSpace]
|
169
|
-
# t.icc_profile_name proxy: [:metadata, :image, :iccProfileName]
|
170
|
-
# t.icc_profile_version proxy: [:metadata, :image, :iccProfileVersion]
|
171
|
-
# t.image_height proxy: [:metadata, :image, :imageHeight]
|
172
|
-
# t.image_width proxy: [:metadata, :image, :imageWidth]
|
173
|
-
# end
|
174
|
-
#
|
175
|
-
# def self.xml_template
|
176
|
-
# builder = Nokogiri::XML::Builder.new do |xml|
|
177
|
-
# xml.fits("xmlns"=>FITS_XMLNS,
|
178
|
-
# "xmlns:xsi"=>"http://www.w3.org/2001/XMLSchema-instance",
|
179
|
-
# "xsi:schemaLocation"=>"http://hul.harvard.edu/ois/xml/ns/fits/fits_output http://hul.harvard.edu/ois/xml/xsd/fits/fits_output.xsd")
|
180
|
-
# end
|
181
|
-
# builder.doc
|
182
|
-
# end
|
183
|
-
#
|
184
|
-
# def prefix
|
185
|
-
# "fits__"
|
186
|
-
# end
|
187
|
-
|
188
97
|
end
|
189
98
|
end
|