ddr-core 1.1.2 → 1.3.0

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f6a1052e5e437237006cf1412ac6505d0d21daee7ca0bdaa0755d985c91eee87
4
- data.tar.gz: 4722ee74f016efbca90f93a6bdff963db1a964cd180dbbc55e0febf8c861c67c
3
+ metadata.gz: 61fa03b921f6c0f3b15a68938f568c4547f324facd5f57bf753ca5b28ae756e2
4
+ data.tar.gz: e74b196e266a1d4a21c24c90490374d2f7054fb52921623adba198bbc9948eca
5
5
  SHA512:
6
- metadata.gz: d4352b429f24959b5c52388ba562724ff782c4a2e877c565ca0716c450965bbbbfde3d1c558ae2179064ec8963a4eb1f0fb6664656c7c1535cf1dc7acb02189f
7
- data.tar.gz: 7bf7a08bad7f10c1ff6049130e42d8c8584f925f845d83222aa74286b031dca008ee61a440d2989e4734e4e5539e14b2e252d679a0ff75bd0cbdbad6bf6a06f5
6
+ metadata.gz: 95ab554ea0873c8b6f8db7ae981f0d6336d180731f91bfd57d19f8806682142fdc47bce3fb09a8aae485bd41e7e9389e0c4cf16fdfa992406032335350085041
7
+ data.tar.gz: 5239037ba065ed2034951311c260d988b19cbd45f3d2c2f6f10e78e9cd311e866e9fc7ca49619fca3de2ec791dea19942b41b4b22863383b9442456ade3ff0ac
data/README.md CHANGED
@@ -6,7 +6,7 @@ A port of ddr-models based on Valkyrie.
6
6
 
7
7
  Install docker (Docker for Mac includes all the required components).
8
8
 
9
- Change to the `.docker` directory.
9
+ Change to the `.docker` directory.
10
10
 
11
11
  Start the *development* stack:
12
12
 
data/Rakefile CHANGED
@@ -28,3 +28,8 @@ begin
28
28
  task default: :spec
29
29
  rescue LoadError
30
30
  end
31
+
32
+ # This allows our s2i builder image to work more smoothly
33
+ task 'tmp:create' do
34
+ Rake::Task['app:tmp:create'].invoke
35
+ end
@@ -15,6 +15,8 @@ module Ddr
15
15
  term_names
16
16
  end
17
17
 
18
+ # Dublin Core vocabulary comes from the rdf-vocab gem (https://github.com/ruby-rdf/rdf-vocab)
19
+ # DDR vocabulary is defined locally
18
20
  def self.vocabularies
19
21
  [RDF::Vocab::DC, Ddr::Vocab::DukeTerms].freeze
20
22
  end
@@ -34,7 +36,11 @@ module Ddr
34
36
 
35
37
  included do
36
38
  Ddr::Describable.term_names.each do |term_name|
37
- attribute term_name, Valkyrie::Types::Set
39
+ if term_name == :available # Intercept dcterms:available and set it to a single date so it can be used for embargoes
40
+ attribute term_name, Valkyrie::Types::DateTime.optional
41
+ else
42
+ attribute term_name, Valkyrie::Types::Set
43
+ end
38
44
  end
39
45
  end
40
46
 
@@ -53,7 +59,7 @@ module Ddr
53
59
  arg = args.pop
54
60
  terms = case arg.to_sym
55
61
  when :empty
56
- desc_metadata_terms.select { |t| values(t).empty? }
62
+ desc_metadata_terms.select { |t| values(t).nil? || values(t).empty? }
57
63
  when :present
58
64
  desc_metadata_terms.select { |t| values(t).present? }
59
65
  when :defined_attributes
@@ -94,15 +100,5 @@ module Ddr
94
100
  desc_metadata_terms.each { |t| set_desc_metadata_values(t, term_values_hash[t]) }
95
101
  end
96
102
 
97
- ################
98
- # I do not believe this method is needed any longer since I can't find any usage of it in ddr-models, ddr-batch,
99
- # dul-hydra, ddr-public, or ddr-portals
100
- ################
101
- # module ClassMethods
102
- # def find_by_identifier(identifier)
103
- # find(Ddr::Index::Fields::IDENTIFIER_ALL => identifier)
104
- # end
105
- # end
106
-
107
103
  end
108
104
  end
@@ -0,0 +1,28 @@
1
+ module Ddr
2
+ module Embargoable
3
+ extend ActiveSupport::Concern
4
+
5
+ def embargo
6
+ result = available.present? ? available : parent&.available
7
+ normalize(result)
8
+ end
9
+
10
+ def embargoed?
11
+ !embargo.nil? && embargo > DateTime.now
12
+ end
13
+
14
+ private
15
+
16
+ def normalize(value)
17
+ case value
18
+ when ::Time
19
+ value.to_datetime
20
+ when ::Array
21
+ value.first
22
+ else
23
+ value
24
+ end
25
+ end
26
+
27
+ end
28
+ end
@@ -7,6 +7,7 @@ module Ddr
7
7
  affiliation: Valkyrie::Types::Set,
8
8
  aleph_id: Valkyrie::Types::Strict::String.optional,
9
9
  aspace_id: Valkyrie::Types::Strict::String.optional,
10
+ contentdm_id: Valkyrie::Types::Strict::String.optional,
10
11
  depositor: Valkyrie::Types::Strict::String.optional,
11
12
  display_format: Valkyrie::Types::Strict::String.optional,
12
13
  doi: Valkyrie::Types::Strict::String.optional,
@@ -54,6 +55,11 @@ module Ddr
54
55
  workflow_state == Ddr::Workflow::UNPUBLISHED
55
56
  end
56
57
 
58
+ # Usually won't be called directly. See `publishable?` on Resource and its derivatives
59
+ def nonpublishable?
60
+ workflow_state == Ddr::Workflow::NONPUBLISHABLE
61
+ end
62
+
57
63
  def resource_roles
58
64
  roles.select(&:in_resource_scope?)
59
65
  end
@@ -7,12 +7,24 @@ module Ddr
7
7
  attribute :parent_id, Valkyrie::Types::ID.optional
8
8
  end
9
9
 
10
+ def has_parent?
11
+ parent_id.present?
12
+ end
13
+
10
14
  def parent
11
- Ddr.query_service.find_by(id: parent_id) if parent_id
15
+ Ddr.query_service.find_by(id: parent_id) if has_parent?
12
16
  end
13
17
 
18
+ # Resources with parents (currently, Items and Components) are publishable only if they have not been marked
19
+ # nonpublishable and their parent is published
14
20
  def publishable?
15
- parent&.published? || false
21
+ !nonpublishable? && parental_publication_guard
22
+ end
23
+
24
+ private
25
+
26
+ def parental_publication_guard
27
+ parent.present? && parent.published?
16
28
  end
17
29
 
18
30
  end
@@ -21,6 +21,9 @@ module Ddr
21
21
  end
22
22
 
23
23
  def find_by_permanent_id(ark)
24
+ if ark.blank?
25
+ raise ArgumentError, "ARK argument must be present."
26
+ end
24
27
  query = Ddr::Index::Query.new do
25
28
  q *:*
26
29
  where permanent_id: ark
@@ -294,6 +297,14 @@ module Ddr
294
297
  resource.children
295
298
  end
296
299
 
300
+ def embargo
301
+ resource.embargo
302
+ end
303
+
304
+ def embargoed?
305
+ resource.embargoed?
306
+ end
307
+
297
308
  private
298
309
 
299
310
  def query_service
@@ -16,12 +16,13 @@ module Ddr
16
16
  query.docs
17
17
  end
18
18
 
19
- def targets
20
- Ddr.query_service.find_inverse_references_by(resource: self, property: 'for_collection_id')
19
+ # Collection resources are publishable unless they have been marked nonpublishable
20
+ def publishable?
21
+ !nonpublishable?
21
22
  end
22
23
 
23
- def publishable?
24
- true
24
+ def targets
25
+ Ddr.query_service.find_inverse_references_by(resource: self, property: 'for_collection_id')
25
26
  end
26
27
 
27
28
  end
@@ -2,6 +2,7 @@ module Ddr
2
2
  class Component < Resource
3
3
 
4
4
  include Captionable
5
+ include Embargoable
5
6
  include HasContent
6
7
  include HasExtractedText
7
8
  include HasIntermediateFile
@@ -29,5 +30,13 @@ module Ddr
29
30
  Ddr.query_service.find_by(id: target_id) if target_id
30
31
  end
31
32
 
33
+ def inherited_roles
34
+ if has_parent?
35
+ super | parent.policy_roles
36
+ else
37
+ super
38
+ end
39
+ end
40
+
32
41
  end
33
42
  end
@@ -1,61 +1,53 @@
1
- # TODO: 'https://duldev.atlassian.net/browse/DDR-1755'
2
-
3
1
  module Ddr
4
2
  class FindingAid
5
3
  attr_reader :ead_id
6
4
 
7
- EAD_XMLNS = "urn:isbn:1-931666-22-9"
8
-
9
5
  def initialize(ead_id)
10
6
  @ead_id = ead_id
11
7
  end
12
8
 
9
+ # TODO: use permalinks in the future when all finding aids have ARKs
13
10
  def url
14
- doc.css("eadid").attr("url").text
11
+ [Ddr.finding_aid_base_url, '/catalog/', ead_id].join
15
12
  end
16
13
 
17
- # The finding aid title
18
14
  def title
19
- doc.css("titleproper").children.first.text.strip
15
+ doc.fetch('normalized_title_ssm',[])&.first
20
16
  end
21
17
 
22
18
  def repository
23
- collection.xpath('ead:did/ead:repository/ead:corpname', ead: EAD_XMLNS).text
19
+ doc.fetch('repository_ssm',[])&.first
24
20
  end
25
21
 
26
22
  def collection_date_span
27
- collection.xpath('ead:did/ead:unitdate[@type="inclusive"]', ead: EAD_XMLNS).text
23
+ doc.fetch('normalized_date_ssm',[])&.first
28
24
  end
29
25
 
30
26
  def collection_number
31
- collection.xpath('ead:did/ead:unitid', ead: EAD_XMLNS).text
27
+ doc.fetch('unitid_ssm',[])&.first
32
28
  end
33
29
 
34
30
  def collection_title
35
- collection.xpath('ead:did/ead:unittitle', ead: EAD_XMLNS).text
31
+ doc.fetch('title_ssm',[])&.first
36
32
  end
37
33
 
38
34
  def extent
39
- collection.xpath('ead:did/ead:physdesc/ead:extent', ead: EAD_XMLNS).map(&:text).join("; ")
35
+ doc.fetch('extent_ssm',[]).join("; ")
40
36
  end
41
37
 
42
38
  def abstract
43
- collection.xpath('ead:did/ead:abstract', ead: EAD_XMLNS).text
39
+ first_abstract = doc.fetch('abstract_tesim',[])&.first
40
+ ActionController::Base.helpers.strip_tags(first_abstract)
44
41
  end
45
42
 
46
43
  private
47
44
 
48
- def collection
49
- doc.xpath('//ead:archdesc[@level="collection"]', ead: EAD_XMLNS)
50
- end
51
-
52
- # @raise [OpenURI::HTTPError] if 404, etc.
53
45
  def doc
54
- @doc ||= Nokogiri::XML(open(ead_xml_url))
46
+ @doc ||= JSON.parse(open(arclight_collection_data_url).read)
55
47
  end
56
48
 
57
- def ead_xml_url
58
- Ddr.ead_xml_base_url + ead_id + ".xml"
49
+ def arclight_collection_data_url
50
+ [Ddr.finding_aid_base_url, '/catalog/', ead_id, '/raw.json'].join
59
51
  end
60
52
  end
61
53
  end
@@ -1,6 +1,7 @@
1
1
  module Ddr
2
2
  class Item < Resource
3
3
 
4
+ include Embargoable
4
5
  include HasChildren
5
6
  include HasParent
6
7
  include HasStructMetadata
@@ -90,10 +90,23 @@ module Ddr
90
90
  send(f)&.file_identifier.present?
91
91
  end
92
92
 
93
+ # By default, no resources are publishable. To enable publication of a particular class of resource, (1) include
94
+ # the `HasAdminMetadata` concern in the model class, which defines a `nonpublishable?` method which returns true
95
+ # if the workflow_state is "nonpublishable" and (2) override the `publishable?` method to provide the logic
96
+ # for determining if a particular resource is publishable.
93
97
  def publishable?
94
98
  false
95
99
  end
96
100
 
101
+ # Embargoes are enforced by the `Embargoable` concern, which overrides the `embargo` and `embargoed?` methods
102
+ def embargo
103
+ nil
104
+ end
105
+
106
+ def embargoed?
107
+ false
108
+ end
109
+
97
110
  def has_admin_policy?
98
111
  governable? && admin_policy_id.present?
99
112
  end
@@ -18,6 +18,8 @@ en:
18
18
  common_model_name:
19
19
  label: Model
20
20
  heading: model
21
+ contentdm_id:
22
+ label: "CONTENTdm ID"
21
23
  doi:
22
24
  label: DOI
23
25
  ead_id:
@@ -35,6 +35,7 @@ module Ddr
35
35
  autoload :CollectionAbilityDefinitions
36
36
  autoload :ComponentAbilityDefinitions
37
37
  autoload :ItemAbilityDefinitions
38
+ autoload :EmbargoAbilityDefinitions
38
39
  autoload :PublicationAbilityDefinitions
39
40
  autoload :LockAbilityDefinitions
40
41
  autoload :RoleBasedAbilityDefinitions
@@ -8,6 +8,7 @@ module Ddr
8
8
  ComponentAbilityDefinitions,
9
9
  AttachmentAbilityDefinitions,
10
10
  RoleBasedAbilityDefinitions,
11
+ EmbargoAbilityDefinitions,
11
12
  PublicationAbilityDefinitions,
12
13
  LockAbilityDefinitions,
13
14
  AdminSetAbilityDefinitions,
@@ -0,0 +1,18 @@
1
+ module Ddr
2
+ module Auth
3
+ class EmbargoAbilityDefinitions < AbilityDefinitions
4
+
5
+ def call
6
+ cannot :read, [::SolrDocument, Ddr::Resource] do |obj|
7
+ obj.embargoed? && cannot?(:update, obj)
8
+ end
9
+
10
+ cannot :download, [::SolrDocument, Ddr::Resource] do |obj|
11
+ obj.embargoed? && cannot?(:update, obj)
12
+ end
13
+
14
+ end
15
+
16
+ end
17
+ end
18
+ end
@@ -2,12 +2,19 @@ module Ddr
2
2
  module Auth
3
3
  class PublicationAbilityDefinitions < AbilityDefinitions
4
4
 
5
+ # An object can't be published if it's already published or not publishable
6
+ # It can't be unpublished if it's already unpublished
7
+ # It can't be made nonpublishable if it's already published (must be unpublished
8
+ # first) or already nonpublishable
5
9
  def call
6
10
  cannot :publish, Ddr::Resource do |obj|
7
11
  obj.published? || !obj.publishable?
8
12
  end
9
13
  cannot :unpublish, Ddr::Resource do |obj|
10
- !obj.published?
14
+ !obj.published? && !obj.nonpublishable?
15
+ end
16
+ cannot :make_nonpublishable, Ddr::Resource do |obj|
17
+ obj.published? || !obj.publishable?
11
18
  end
12
19
  end
13
20
 
@@ -1,6 +1,9 @@
1
1
  module Ddr::Auth
2
2
  class SuperuserAbilityDefinitions < AbilityDefinitions
3
3
 
4
+ # This bypasses all checks, including ones that look at whether an
5
+ # action is possible, not just allowed. So superusers may initiate
6
+ # actions which will fail.
4
7
  def call
5
8
  can :manage, :all
6
9
  end
@@ -2,7 +2,7 @@ module Ddr::Auth
2
2
  class EffectiveRoles
3
3
 
4
4
  def self.call(obj, agents = nil)
5
- ( obj.resource_roles | obj.inherited_roles ).tap do |roles|
5
+ ( obj.roles | obj.inherited_roles ).tap do |roles|
6
6
  if agents
7
7
  roles.select! { |r| agents.include?(r.agent) }
8
8
  end
@@ -1,18 +1,21 @@
1
1
  module Ddr::Auth
2
2
  class Permissions
3
3
 
4
- READ = :read
5
- DOWNLOAD = :download
6
- ADD_CHILDREN = :add_children
7
- UPDATE = :update
8
- REPLACE = :replace
9
- ARRANGE = :arrange
10
- PUBLISH = :publish
11
- UNPUBLISH = :unpublish
12
- AUDIT = :audit
13
- GRANT = :grant
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, REPLACE, ARRANGE, PUBLISH, UNPUBLISH, AUDIT, GRANT ]
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
@@ -12,7 +12,7 @@ module Ddr
12
12
  end
13
13
 
14
14
  def enforce_show_permissions
15
- authorize! :read, params[:id]
15
+ authorize! Permissions::DISCOVER, params[:id]
16
16
  end
17
17
 
18
18
  end
@@ -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
- @role_types ||= RoleTypes.constants(false).map { |const| RoleTypes.const_get(const) }
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
@@ -8,35 +8,38 @@ module Ddr
8
8
 
9
9
  DEFAULT_SCOPE = Roles::RESOURCE_SCOPE
10
10
 
11
- ValidScope = Valkyrie::Types::Strict::String.enum(*(Roles::SCOPES))
12
- ValidRoleType = Valkyrie::Types::Strict::String.enum(*(Roles::role_types.map(&:title)))
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)
13
14
 
14
- attribute :agent, Valkyrie::Types::Strict::String.constrained(min_size: 1)
15
- attribute :role_type, ValidRoleType
16
- attribute :scope, ValidScope
17
-
18
- class << self
19
-
20
- # Build a Role instance from hash attributes
21
- # @param args [Hash] the attributes
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
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
- role.scope ||= DEFAULT_SCOPE
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
- end # class << self
35
+ attribute :agent, ValidAgent
36
+ attribute :role_type, ValidRoleType
37
+ attribute :scope, ValidScope
38
+
39
+ # DEPRECATED: Use constructor
40
+ def self.build(args={})
41
+ new(args)
42
+ end
40
43
 
41
44
  # Roles are considered equal (==) if they
42
45
  # are of the same type and have the same agent and scope.
@@ -14,36 +14,44 @@ 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::READ, Permissions::DOWNLOAD, Permissions::ADD_CHILDREN,
18
- Permissions::UPDATE, Permissions::REPLACE, Permissions::ARRANGE ]
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,
27
+ Permissions::UPDATE ]
26
28
  )
27
29
 
28
30
  CONTRIBUTOR = RoleType.new(
29
31
  "Contributor",
30
32
  "The Contributor role conveys responsibility for adding related " \
31
33
  "resources to a resource, such as works to a collection.",
32
- [ Permissions::READ, Permissions::ADD_CHILDREN ]
34
+ [ Permissions::DISCOVER, Permissions::READ, Permissions::ADD_CHILDREN ]
33
35
  )
34
36
 
35
37
  DOWNLOADER = RoleType.new(
36
38
  "Downloader",
37
39
  "The Downloader role conveys access to the \"master\" file " \
38
40
  "(original content bitstream) of a resource.",
39
- [ Permissions::READ, Permissions::DOWNLOAD ]
41
+ [ Permissions::DISCOVER, Permissions::READ, Permissions::DOWNLOAD ]
40
42
  )
41
43
 
42
44
  VIEWER = RoleType.new(
43
45
  "Viewer",
44
46
  "The Viewer role conveys access to the description and \"access\" " \
45
47
  "files (e.g., derivative bitstreams) of a resource.",
46
- [ Permissions::READ ]
48
+ [ Permissions::DISCOVER, Permissions::READ ]
49
+ )
50
+
51
+ METADATA_VIEWER = RoleType.new(
52
+ "MetadataViewer",
53
+ "The MetadataViewer role conveys access to the description of a resource.",
54
+ [ Permissions::DISCOVER ]
47
55
  )
48
56
 
49
57
  end
@@ -11,12 +11,16 @@ module Ddr::Auth
11
11
 
12
12
  # @return [Array<String>]
13
13
  def affiliation
14
- split_env("affiliation").map { |a| a.sub(/@duke\.edu\z/, "") }
14
+ if anonymous?
15
+ super
16
+ else
17
+ split_env("affiliation").map { |a| a.sub(/@duke\.edu\z/, "") }
18
+ end
15
19
  end
16
20
 
17
21
  # @return [Array<String>]
18
22
  def ismemberof
19
- split_env("isMemberOf")
23
+ anonymous? ? super : split_env("isMemberOf")
20
24
  end
21
25
 
22
26
  private
@@ -84,10 +84,13 @@ module Ddr
84
84
  '.aac' => 'audio/mp4',
85
85
  '.f4a' => 'audio/mp4',
86
86
  '.flv' => 'video/flv',
87
+ '.m2t' => 'video/vnd.dlna.mpeg-tts',
88
+ '.m2ts' => 'video/m2ts',
87
89
  '.m4a' => 'audio/mp4',
88
90
  '.mov' => 'video/quicktime',
89
91
  '.mp3' => 'audio/mpeg',
90
92
  '.mp4' => 'video/mp4',
93
+ '.mts' => 'video/vnd.dlna.mpeg-tts',
91
94
  '.oga' => 'audio/ogg',
92
95
  '.ogg' => 'audio/ogg',
93
96
  '.srt' => 'text/plain',
@@ -108,10 +111,15 @@ module Ddr
108
111
  ENV["DDR_AUX_API_URL"]
109
112
  end
110
113
 
114
+ # Deprecated in ddr-core 1.2.0. Remove in future.
111
115
  mattr_accessor :ead_xml_base_url do
112
116
  ENV["EAD_XML_BASE_URL"]
113
117
  end
114
118
 
119
+ mattr_accessor :finding_aid_base_url do
120
+ ENV["FINDING_AID_BASE_URL"] || 'https://archives.lib.duke.edu'
121
+ end
122
+
115
123
  module Core
116
124
 
117
125
  end
@@ -1,5 +1,5 @@
1
1
  module Ddr
2
2
  module Core
3
- VERSION = '1.1.2'
3
+ VERSION = '1.3.0'
4
4
  end
5
5
  end
@@ -6,8 +6,35 @@ module Ddr::Index
6
6
  MAX_ROWS = 10**8 # Just set to a really high number :)
7
7
  CSV_MV_SEPARATOR = ";"
8
8
 
9
+ DEFAULT_CSV_OPTIONS = {
10
+ headers: :first_row,
11
+ return_headers: false,
12
+ write_headers: true,
13
+ converters: [
14
+ # convert semicolons
15
+ lambda { |f| f.gsub(/\\#{CSV_MV_SEPARATOR}/, CSV_MV_SEPARATOR) rescue f },
16
+ # convert escaped newlines
17
+ lambda { |f| f.gsub(/\\r/, "\r").gsub(/\\n/, "\n") rescue f }
18
+ ],
19
+ }.freeze
20
+
9
21
  delegate :headers, :to_s, :to_csv, to: :table
10
22
 
23
+ attr_reader :csv_opts
24
+
25
+ #
26
+ # See Ruby docs on CSV::new for details on available keys
27
+ # and values for the optional `csv_options' Hash parameter.
28
+ #
29
+ # N.B. If you want to *add* a converter and retain the
30
+ # default converters, append DEFAULT_CSV_OPTIONS[:converters]
31
+ # to your Array of converters.
32
+ #
33
+ def initialize(query, csv_opts: {})
34
+ super(query)
35
+ @csv_opts = DEFAULT_CSV_OPTIONS.merge(csv_opts)
36
+ end
37
+
11
38
  def delete_empty_columns!
12
39
  table.by_col!.delete_if { |c, vals| vals.all?(&:nil?) }
13
40
  end
@@ -24,14 +51,6 @@ module Ddr::Index
24
51
  @table ||= CSV.parse(data, csv_opts)
25
52
  end
26
53
 
27
- def csv_opts
28
- { headers: csv_headers,
29
- converters: [convert_semicolons, convert_escaped_newlines],
30
- return_headers: false,
31
- write_headers: true,
32
- }
33
- end
34
-
35
54
  def solr_csv_opts
36
55
  { "csv.mv.separator" => CSV_MV_SEPARATOR,
37
56
  "csv.header" => solr_csv_header?,
@@ -44,10 +63,6 @@ module Ddr::Index
44
63
  query.fields.map { |f| f.respond_to?(:heading) ? f.heading : f.to_s }
45
64
  end
46
65
 
47
- def csv_headers
48
- :first_row
49
- end
50
-
51
66
  def solr_csv_header?
52
67
  query.fields.empty?
53
68
  end
@@ -72,13 +87,5 @@ module Ddr::Index
72
87
  Connection.get("select", params: solr_csv_params)
73
88
  end
74
89
 
75
- def convert_semicolons
76
- lambda { |f| f.gsub(/\\#{CSV_MV_SEPARATOR}/, CSV_MV_SEPARATOR) rescue f }
77
- end
78
-
79
- def convert_escaped_newlines
80
- lambda { |f| f.gsub(/\\r/, "\r").gsub(/\\n/, "\n") rescue f }
81
- end
82
-
83
90
  end
84
91
  end
@@ -16,6 +16,7 @@ module Ddr::Index
16
16
  ASPACE_ID = Field.new :aspace_id, :stored_sortable
17
17
  ATTACHED_FILES_HAVING_CONTENT = Field.new :attached_files_having_content, :symbol
18
18
  ATTACHED_TO_ID = Field.new :attached_to_id, :symbol
19
+ AVAILABLE = Field.new :available, :symbol
19
20
  BIBLICAL_BOOK_FACET = Field.new :biblical_book_facet, :facetable
20
21
  BOX_NUMBER_FACET = Field.new :box_number_facet, :facetable
21
22
  CATEGORY_FACET = Field.new :category_facet, :facetable
@@ -29,6 +30,7 @@ module Ddr::Index
29
30
  CONTENT_CREATE_DATE = Field.new :content_create_date, :stored_sortable, type: :date
30
31
  CONTENT_SIZE = Field.new :content_size, solr_name: "content_size_lsi"
31
32
  CONTENT_SIZE_HUMAN = Field.new :content_size_human, :symbol
33
+ CONTENTDM_ID = Field.new :contentdm_id, :stored_sortable
32
34
  CONTRIBUTOR_FACET = Field.new :contributor_facet, :facetable
33
35
  CREATOR_FACET = Field.new :creator_facet, :facetable
34
36
  DATE_FACET = Field.new :date_facet, :facetable
@@ -16,9 +16,9 @@ module Ddr::Index
16
16
 
17
17
  values do
18
18
  attribute :field, FieldAttribute
19
- attribute :value, String
20
- attribute :quote_value, Boolean, default: false
21
- attribute :template, String, default: STANDARD_QUERY
19
+ attribute :value, String, required: true
20
+ attribute :quote_value, Boolean, default: false
21
+ attribute :template, String, default: STANDARD_QUERY
22
22
  end
23
23
 
24
24
  def to_s
@@ -46,7 +46,7 @@ module Ddr::Index
46
46
  alias_method :id, :unique_key
47
47
 
48
48
  def where(field, value)
49
- values = Array(value)
49
+ values = Array(value)
50
50
  if values.size > 1
51
51
  disjunction(field, values)
52
52
  else
@@ -47,5 +47,9 @@ module Ddr::Vocab
47
47
  label: "Nested Path",
48
48
  comment: "The nested/tree path to the object to be reflected in structural metadata."
49
49
 
50
+ property "contentdmId",
51
+ label: "CONTENTdm ID"
52
+ comment: "The CONTENTdm reference corresponding to the object."
53
+
50
54
  end
51
55
  end
@@ -3,6 +3,7 @@ module Ddr
3
3
 
4
4
  PUBLISHED = 'published'
5
5
  UNPUBLISHED = 'unpublished'
6
+ NONPUBLISHABLE = 'nonpublishable'
6
7
 
7
8
  end
8
9
  end
metadata CHANGED
@@ -1,17 +1,17 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ddr-core
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.2
4
+ version: 1.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jim Coble
8
8
  - David Chandek-Stark
9
9
  - Ayse Durmaz
10
10
  - Hugh Cayless
11
- autorequire:
11
+ autorequire:
12
12
  bindir: bin
13
13
  cert_chain: []
14
- date: 2020-02-07 00:00:00.000000000 Z
14
+ date: 2020-10-27 00:00:00.000000000 Z
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
17
17
  name: activeresource
@@ -89,14 +89,14 @@ dependencies:
89
89
  requirements:
90
90
  - - "~>"
91
91
  - !ruby/object:Gem::Version
92
- version: 2.0.0
92
+ version: 2.1.0
93
93
  type: :runtime
94
94
  prerelease: false
95
95
  version_requirements: !ruby/object:Gem::Requirement
96
96
  requirements:
97
97
  - - "~>"
98
98
  - !ruby/object:Gem::Version
99
- version: 2.0.0
99
+ version: 2.1.0
100
100
  - !ruby/object:Gem::Dependency
101
101
  name: grouper-rest-client
102
102
  requirement: !ruby/object:Gem::Requirement
@@ -308,6 +308,7 @@ files:
308
308
  - app/controllers/users/sessions_controller.rb
309
309
  - app/models/concerns/ddr/captionable.rb
310
310
  - app/models/concerns/ddr/describable.rb
311
+ - app/models/concerns/ddr/embargoable.rb
311
312
  - app/models/concerns/ddr/governable.rb
312
313
  - app/models/concerns/ddr/has_admin_metadata.rb
313
314
  - app/models/concerns/ddr/has_attachments.rb
@@ -352,6 +353,7 @@ files:
352
353
  - lib/ddr/auth/ability_definitions/attachment_ability_definitions.rb
353
354
  - lib/ddr/auth/ability_definitions/collection_ability_definitions.rb
354
355
  - lib/ddr/auth/ability_definitions/component_ability_definitions.rb
356
+ - lib/ddr/auth/ability_definitions/embargo_ability_definitions.rb
355
357
  - lib/ddr/auth/ability_definitions/item_ability_definitions.rb
356
358
  - lib/ddr/auth/ability_definitions/lock_ability_definitions.rb
357
359
  - lib/ddr/auth/ability_definitions/publication_ability_definitions.rb
@@ -439,7 +441,7 @@ licenses:
439
441
  - BSD-3-Clause
440
442
  metadata:
441
443
  allowed_push_host: https://rubygems.org
442
- post_install_message:
444
+ post_install_message:
443
445
  rdoc_options: []
444
446
  require_paths:
445
447
  - lib
@@ -455,7 +457,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
455
457
  version: '0'
456
458
  requirements: []
457
459
  rubygems_version: 3.0.6
458
- signing_key:
460
+ signing_key:
459
461
  specification_version: 4
460
462
  summary: Models used in the Duke Digital Repository
461
463
  test_files: []