hyrax 3.0.2 → 3.1.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.
Files changed (162) hide show
  1. checksums.yaml +4 -4
  2. data/.circleci/config.yml +22 -0
  3. data/.dassie/Gemfile +10 -5
  4. data/.dassie/config/initializers/hyrax.rb +5 -0
  5. data/.dockerignore +3 -0
  6. data/.env +0 -1
  7. data/.rubocop.yml +4 -0
  8. data/CONTAINERS.md +1 -1
  9. data/Dockerfile +12 -6
  10. data/Gemfile +21 -27
  11. data/app/actors/hyrax/actors/base_actor.rb +1 -1
  12. data/app/actors/hyrax/actors/create_with_remote_files_actor.rb +85 -63
  13. data/app/actors/hyrax/actors/create_with_remote_files_ordered_members_actor.rb +7 -42
  14. data/app/controllers/concerns/hyrax/collections_controller_behavior.rb +20 -8
  15. data/app/controllers/concerns/hyrax/embargoes_controller_behavior.rb +21 -9
  16. data/app/controllers/concerns/hyrax/leases_controller_behavior.rb +14 -5
  17. data/app/controllers/concerns/hyrax/works_controller_behavior.rb +22 -3
  18. data/app/controllers/hyrax/admin/workflows_controller.rb +8 -2
  19. data/app/controllers/hyrax/dashboard/collection_members_controller.rb +13 -9
  20. data/app/controllers/hyrax/dashboard/collections_controller.rb +12 -10
  21. data/app/controllers/hyrax/file_sets_controller.rb +49 -13
  22. data/app/controllers/hyrax/permissions_controller.rb +3 -4
  23. data/app/controllers/hyrax/workflow_actions_controller.rb +3 -1
  24. data/app/forms/hyrax/forms/collection_form.rb +7 -3
  25. data/app/forms/hyrax/forms/dashboard/nest_collection_form.rb +24 -2
  26. data/app/forms/hyrax/forms/file_set_form.rb +46 -0
  27. data/app/forms/hyrax/forms/permission.rb +23 -0
  28. data/app/forms/hyrax/forms/permission_template_form.rb +8 -2
  29. data/app/forms/hyrax/forms/resource_form.rb +10 -17
  30. data/app/forms/hyrax/forms/work_form.rb +5 -2
  31. data/app/helpers/hyrax/batch_edits_helper.rb +3 -1
  32. data/app/helpers/hyrax/collections_helper.rb +88 -2
  33. data/app/helpers/hyrax/dashboard_helper_behavior.rb +3 -7
  34. data/app/helpers/hyrax/file_set_helper.rb +25 -6
  35. data/app/helpers/hyrax/work_form_helper.rb +53 -0
  36. data/app/indexers/hyrax/administrative_set_indexer.rb +18 -0
  37. data/app/indexers/hyrax/valkyrie_indexer.rb +3 -3
  38. data/app/inputs/controlled_vocabulary_input.rb +2 -5
  39. data/app/jobs/attach_files_to_work_job.rb +19 -10
  40. data/app/jobs/attach_files_to_work_with_ordered_members_job.rb +6 -5
  41. data/app/jobs/inherit_permissions_job.rb +9 -5
  42. data/app/models/admin_set.rb +6 -25
  43. data/app/models/concerns/hyrax/ability.rb +3 -1
  44. data/app/models/concerns/hyrax/collection_behavior.rb +17 -44
  45. data/app/models/concerns/hyrax/file_set/characterization.rb +18 -12
  46. data/app/models/concerns/hyrax/solr_document_behavior.rb +9 -52
  47. data/app/models/concerns/hyrax/suppressible.rb +5 -0
  48. data/app/models/concerns/hyrax/user.rb +9 -3
  49. data/app/models/hyrax/file_set.rb +6 -0
  50. data/app/models/hyrax/pcdm_collection.rb +1 -0
  51. data/app/models/hyrax/permission_template.rb +98 -12
  52. data/app/models/hyrax/virus_scanner.rb +27 -18
  53. data/app/models/sipity/agent.rb +1 -0
  54. data/app/models/sipity/entity.rb +30 -8
  55. data/app/models/sipity/workflow.rb +1 -0
  56. data/app/models/sipity.rb +42 -0
  57. data/app/presenters/hyrax/admin_set_options_presenter.rb +2 -10
  58. data/app/presenters/hyrax/admin_set_presenter.rb +5 -1
  59. data/app/presenters/hyrax/admin_set_selection_presenter.rb +116 -0
  60. data/app/presenters/hyrax/collection_presenter.rb +31 -6
  61. data/app/presenters/hyrax/file_set_presenter.rb +6 -1
  62. data/app/presenters/hyrax/file_usage.rb +3 -2
  63. data/app/presenters/hyrax/stats_usage_presenter.rb +2 -1
  64. data/app/presenters/hyrax/trophy_presenter.rb +33 -4
  65. data/app/presenters/hyrax/user_profile_presenter.rb +11 -1
  66. data/app/presenters/hyrax/version_list_presenter.rb +19 -0
  67. data/app/presenters/hyrax/version_presenter.rb +3 -2
  68. data/app/presenters/hyrax/work_show_presenter.rb +25 -4
  69. data/app/presenters/hyrax/work_usage.rb +5 -3
  70. data/app/renderers/hyrax/renderers/attribute_renderer.rb +10 -2
  71. data/app/search_builders/hyrax/admin_set_search_builder.rb +1 -1
  72. data/app/search_builders/hyrax/my/collections_search_builder.rb +1 -1
  73. data/app/services/hyrax/admin_set_create_service.rb +3 -1
  74. data/app/services/hyrax/collections/collection_member_search_service.rb +72 -0
  75. data/app/services/hyrax/collections/collection_member_service.rb +112 -27
  76. data/app/services/hyrax/collections/migration_service.rb +4 -2
  77. data/app/services/hyrax/collections/nested_collection_persistence_service.rb +12 -13
  78. data/app/services/hyrax/collections/nested_collection_query_service.rb +2 -0
  79. data/app/services/hyrax/collections/permissions_create_service.rb +6 -4
  80. data/app/services/hyrax/contextual_path.rb +23 -0
  81. data/app/services/hyrax/custom_queries/find_file_metadata.rb +7 -5
  82. data/app/services/hyrax/custom_queries/navigators/parent_collections_navigator.rb +46 -0
  83. data/app/services/hyrax/edit_permissions_service.rb +27 -20
  84. data/app/services/hyrax/find_objects_via_solr_service.rb +11 -7
  85. data/app/services/hyrax/multiple_membership_checker.rb +51 -31
  86. data/app/services/hyrax/resource_status.rb +7 -0
  87. data/app/services/hyrax/search_service.rb +4 -2
  88. data/app/services/hyrax/solr_query_builder_service.rb +29 -6
  89. data/app/services/hyrax/solr_query_service.rb +224 -0
  90. data/app/services/hyrax/solr_service.rb +8 -1
  91. data/app/services/hyrax/statistics/depositors/summary.rb +2 -1
  92. data/app/services/hyrax/work_uploads_handler.rb +17 -2
  93. data/app/services/hyrax/workflow/actionable_objects.rb +70 -0
  94. data/app/services/hyrax/workflow/object_in_workflow_decorator.rb +31 -0
  95. data/app/services/hyrax/workflow/status_list_service.rb +43 -13
  96. data/app/views/hyrax/base/_form_relationships.html.erb +1 -2
  97. data/app/views/hyrax/base/_form_rendering.html.erb +1 -1
  98. data/app/views/hyrax/base/_form_representative.html.erb +1 -1
  99. data/app/views/hyrax/base/_form_thumbnail.html.erb +1 -1
  100. data/app/views/hyrax/base/_guts4form.html.erb +2 -2
  101. data/app/views/hyrax/base/_representative_media.html.erb +1 -1
  102. data/app/views/hyrax/base/_show_actions.html.erb +1 -1
  103. data/app/views/hyrax/dashboard/collections/_form.html.erb +3 -3
  104. data/app/views/hyrax/dashboard/collections/_list_collections.html.erb +1 -1
  105. data/app/views/hyrax/dashboard/collections/edit.html.erb +4 -2
  106. data/app/views/hyrax/dashboard/collections/new.html.erb +4 -2
  107. data/app/views/hyrax/dashboard/collections/show.html.erb +1 -1
  108. data/app/views/hyrax/file_sets/edit.html.erb +1 -1
  109. data/app/views/hyrax/file_sets/media_display/_audio.html.erb +1 -1
  110. data/app/views/hyrax/file_sets/media_display/_default.html.erb +1 -1
  111. data/app/views/hyrax/file_sets/media_display/_image.html.erb +1 -1
  112. data/app/views/hyrax/file_sets/media_display/_office_document.html.erb +1 -1
  113. data/app/views/hyrax/file_sets/media_display/_pdf.html.erb +1 -1
  114. data/app/views/hyrax/file_sets/media_display/_video.html.erb +1 -1
  115. data/app/views/hyrax/file_sets/show.html.erb +1 -1
  116. data/app/views/hyrax/my/_admin_set_action_menu.html.erb +0 -11
  117. data/app/views/hyrax/my/_collection_action_menu.html.erb +1 -2
  118. data/app/views/hyrax/my/collections/_list_collections.html.erb +1 -1
  119. data/app/views/hyrax/my/collections/_modal_add_subcollection.html.erb +3 -5
  120. data/bin/solrcloud-assign-configset.sh +8 -5
  121. data/bin/solrcloud-upload-configset.sh +4 -2
  122. data/chart/hyrax/Chart.yaml +3 -3
  123. data/chart/hyrax/README.md +47 -1
  124. data/chart/hyrax/templates/_helpers.tpl +1 -1
  125. data/chart/hyrax/templates/configmap-env.yaml +1 -3
  126. data/chart/hyrax/templates/deployment-worker.yaml +6 -3
  127. data/chart/hyrax/templates/deployment.yaml +8 -3
  128. data/chart/hyrax/values.yaml +12 -0
  129. data/config/brakeman.ignore +2 -2
  130. data/config/locales/hyrax.de.yml +1 -1
  131. data/config/locales/hyrax.en.yml +1 -1
  132. data/config/locales/hyrax.es.yml +1 -1
  133. data/config/locales/hyrax.fr.yml +1 -1
  134. data/config/locales/hyrax.it.yml +1 -1
  135. data/config/locales/hyrax.pt-BR.yml +1 -1
  136. data/config/locales/hyrax.zh.yml +1 -1
  137. data/docker-compose.yml +1 -0
  138. data/documentation/developing-your-hyrax-based-app.md +1 -1
  139. data/documentation/legacyREADME.md +1 -1
  140. data/lib/generators/hyrax/templates/config/initializers/hyrax.rb +5 -0
  141. data/lib/hyrax/active_fedora_dummy_model.rb +62 -0
  142. data/lib/hyrax/configuration.rb +8 -0
  143. data/lib/hyrax/engine.rb +1 -0
  144. data/lib/hyrax/errors.rb +2 -0
  145. data/lib/hyrax/specs/capybara.rb +3 -1
  146. data/lib/hyrax/specs/shared_specs/valkyrie_storage_versions.rb +9 -0
  147. data/lib/hyrax/transactions/container.rb +21 -0
  148. data/lib/hyrax/transactions/file_set_destroy.rb +21 -0
  149. data/lib/hyrax/transactions/steps/add_file_sets.rb +3 -2
  150. data/lib/hyrax/transactions/steps/add_to_parent.rb +36 -0
  151. data/lib/hyrax/transactions/steps/remove_file_set_from_work.rb +47 -0
  152. data/lib/hyrax/transactions/work_create.rb +2 -1
  153. data/lib/hyrax/valkyrie_can_can_adapter.rb +1 -0
  154. data/lib/hyrax/version.rb +1 -1
  155. data/lib/hyrax.rb +9 -0
  156. data/lib/tasks/collection_type_global_id.rake +1 -1
  157. data/lib/tasks/regenerate_derivatives.rake +12 -0
  158. data/lib/wings/orm_converter.rb +18 -2
  159. data/lib/wings/setup.rb +1 -0
  160. data/lib/wings/valkyrie/storage.rb +56 -1
  161. data/template.rb +1 -1
  162. metadata +17 -2
@@ -10,6 +10,11 @@ module Hyrax
10
10
  end
11
11
 
12
12
  ##
13
+ # @deprecated use `Hyrax::ResourceStatus` instead. in most cases,
14
+ # {#suppressed?} is being called on a {SolrDocumentBehavior}. we continue
15
+ # to index `suppressed_bsi` and expose its value as an attribute on solr
16
+ # document objects.
17
+ #
13
18
  # Used to restrict visibility on search results for a work that is inactive. If the state is not set, the
14
19
  # default behavior is to consider the work not to be suppressed.
15
20
  #
@@ -31,7 +31,7 @@ module Hyrax::User
31
31
 
32
32
  scope :guests, ->() { where(guest: true) }
33
33
  scope :registered, ->() { where(guest: false) }
34
- scope :without_system_accounts, -> { where("#{::User.user_key_field} not in (?)", [::User.batch_user_key, ::User.audit_user_key]) }
34
+ scope :without_system_accounts, -> { where("#{::User.user_key_field} not in (?)", [::User.batch_user_key, ::User.audit_user_key, ::User.system_user_key]) }
35
35
 
36
36
  # Validate and normalize ORCIDs
37
37
  validates_with OrcidValidator
@@ -145,7 +145,14 @@ module Hyrax::User
145
145
  end
146
146
 
147
147
  module ClassMethods
148
- # Override this method if you aren't using email/password
148
+ def system_user
149
+ find_or_create_system_user(system_user_key)
150
+ end
151
+
152
+ def system_user_key
153
+ Hyrax.config.system_user_key
154
+ end
155
+
149
156
  def audit_user
150
157
  find_or_create_system_user(audit_user_key)
151
158
  end
@@ -158,7 +165,6 @@ module Hyrax::User
158
165
  Hydra.config.user_key_field
159
166
  end
160
167
 
161
- # Override this method if you aren't using email/password
162
168
  def batch_user
163
169
  find_or_create_system_user(batch_user_key)
164
170
  end
@@ -9,6 +9,12 @@ module Hyrax
9
9
  include Hyrax::Schema(:core_metadata)
10
10
  include Hyrax::Schema(:basic_metadata)
11
11
 
12
+ def self.model_name(name_class: Hyrax::Name)
13
+ @_model_name ||= begin
14
+ name_class.new(self, nil, 'FileSet')
15
+ end
16
+ end
17
+
12
18
  attribute :file_ids, Valkyrie::Types::Array.of(Valkyrie::Types::ID) # id for FileMetadata resources
13
19
  attribute :original_file_id, Valkyrie::Types::ID # id for FileMetadata resource
14
20
  attribute :thumbnail_id, Valkyrie::Types::ID # id for FileMetadata resource
@@ -11,6 +11,7 @@ module Hyrax
11
11
 
12
12
  attribute :collection_type_gid, Valkyrie::Types::String
13
13
  attribute :member_ids, Valkyrie::Types::Array.of(Valkyrie::Types::ID).meta(ordered: true)
14
+ attribute :member_of_collection_ids, Valkyrie::Types::Set.of(Valkyrie::Types::ID)
14
15
 
15
16
  ##
16
17
  # @api private
@@ -1,20 +1,61 @@
1
1
  # frozen_string_literal: true
2
2
  module Hyrax
3
3
  ##
4
- # Defines behavior that is applied to objects added as members of an AdminSet
4
+ # Holds policy data about the workflow and permissions applied objects when
5
+ # they are deposited through an Administrative Set or a Collection. Each
6
+ # template record has a {#source} (through {#source_id}); the template's
7
+ # rules inform the behavior of objects deposited through that {#source_model}.
5
8
  #
6
- # * access rights to stamp on each object
7
- # * calculate embargo/lease release dates
9
+ # The {PermissionTemplate} specifies:
8
10
  #
9
- # There is an interplay between an AdminSet and a PermissionTemplate.
11
+ # - an {#active_workflow} that the object will enter and be processed through.
12
+ # - {#access_grants} that can be applied to each object (especially at deposit
13
+ # time).
14
+ # - an embargo configuration ({#release_date} {#release_period}) for default
15
+ # embargo behavior.
10
16
  #
11
- # @see Hyrax::AdminSet for further discussion
12
- class PermissionTemplate < ActiveRecord::Base
17
+ # Additionally, the {PermissionTemplate} grants authority to perform actions
18
+ # that relate to the Administrative Set/Collection itself. Rules for who can
19
+ # deposit to, view(?!), or manage the admin set are governed by related
20
+ # {PermissionTemplateAccess} records. Administrat Sets should have a manager
21
+ # granted by some such record.
22
+ #
23
+ # @todo write up what "default embargo behavior", when it is applied, and how
24
+ # it interacts with embargoes specified by user input.
25
+ #
26
+ # @example cerating a permission template and manager for an admin set
27
+ # admin_set = Hyrax::AdministrativeSet.new(title: 'My Admin Set')
28
+ # admin_set = Hyrax.persister.save(resource: admin_set)
29
+ #
30
+ # template = PermissionTemplate.create!(source_id: admin_set.id.to_s)
31
+ # Hyrax::PermissionTemplateAccess.create!(permission_template: template,
32
+ # agent_type: Hyrax::PermissionTemplateAccess::USER,
33
+ # agent_id: user.user_key,
34
+ # access: Hyrax::PermissionTemplateAccess::MANAGE)
35
+ #
36
+ # @see Hyrax::AdministrativeSet
37
+ class PermissionTemplate < ActiveRecord::Base # rubocop:disable Metrics/ClassLength
13
38
  self.table_name = 'permission_templates'
14
39
 
40
+ ##
41
+ # @!attribute [rw] source_id
42
+ # @return [String] identifier for the {Collection} or {AdministrativeSet}
43
+ # to which this template applies.
44
+ # @!attribute [rw] access_grants
45
+ # @return [Hyrax::PermissionTemplateAccess]
46
+ # @!attribute [rw] active_workflow
47
+ # @return [Sipity::Workflow]
48
+ # @!attribute [rw] available_workflows
49
+ # @return [Enumerable<Sipity::Workflow>]
15
50
  has_many :access_grants, class_name: 'Hyrax::PermissionTemplateAccess', dependent: :destroy
16
51
  accepts_nested_attributes_for :access_grants, reject_if: :all_blank
17
52
 
53
+ # The list of workflows that could be activated; It includes the active workflow
54
+ has_many :available_workflows, class_name: 'Sipity::Workflow', dependent: :destroy
55
+
56
+ # In a perfect world, there would be a join table that enforced uniqueness on the ID.
57
+ has_one :active_workflow, -> { where(active: true) }, class_name: 'Sipity::Workflow', foreign_key: :permission_template_id
58
+
18
59
  ##
19
60
  # @api public
20
61
  #
@@ -28,12 +69,6 @@ module Hyrax
28
69
  access_grants.where(agent_type: agent_type, access: access).pluck(:agent_id)
29
70
  end
30
71
 
31
- # The list of workflows that could be activated; It includes the active workflow
32
- has_many :available_workflows, class_name: 'Sipity::Workflow', dependent: :destroy
33
-
34
- # In a perfect world, there would be a join table that enforced uniqueness on the ID.
35
- has_one :active_workflow, -> { where(active: true) }, class_name: 'Sipity::Workflow', foreign_key: :permission_template_id
36
-
37
72
  ##
38
73
  # @note this is a convenience method for +Hyrax.query_service.find_by(id: template.source_id)+
39
74
  #
@@ -153,6 +188,57 @@ module Hyrax
153
188
  visibility == value
154
189
  end
155
190
 
191
+ ##
192
+ # @return [Array<String>]
193
+ def edit_users
194
+ agent_ids_for(access: 'manage', agent_type: 'user')
195
+ end
196
+
197
+ ##
198
+ # @return [Array<String>]
199
+ def edit_groups
200
+ agent_ids_for(access: 'manage', agent_type: 'group')
201
+ end
202
+
203
+ ##
204
+ # @return [Array<String>]
205
+ def read_users
206
+ (agent_ids_for(access: 'view', agent_type: 'user') +
207
+ agent_ids_for(access: 'deposit', agent_type: 'user')).uniq
208
+ end
209
+
210
+ ##
211
+ # @return [Array<String>]
212
+ def read_groups
213
+ (agent_ids_for(access: 'view', agent_type: 'group') +
214
+ agent_ids_for(access: 'deposit', agent_type: 'group')).uniq -
215
+ [::Ability.registered_group_name, ::Ability.public_group_name]
216
+ end
217
+
218
+ ##
219
+ # @return [Boolean]
220
+ def reset_access_controls(interpret_visibility: false)
221
+ reset_access_controls_for(collection: source_model,
222
+ interpret_visibility: interpret_visibility)
223
+ end
224
+
225
+ ##
226
+ # @return [Boolean]
227
+ def reset_access_controls_for(collection:, interpret_visibility: false)
228
+ interpreted_read_groups = read_groups
229
+
230
+ if interpret_visibility
231
+ visibilities = Hyrax::VisibilityMap.instance
232
+ interpreted_read_groups -= visibilities.deletions_for(visibility: collection.visibility)
233
+ interpreted_read_groups += visibilities.additions_for(visibility: collection.visibility)
234
+ end
235
+
236
+ collection.update!(edit_users: edit_users,
237
+ edit_groups: edit_groups,
238
+ read_users: read_users,
239
+ read_groups: interpreted_read_groups.uniq)
240
+ end
241
+
156
242
  private
157
243
 
158
244
  # If template requires no delays, check if date is exactly today
@@ -1,23 +1,28 @@
1
1
  # frozen_string_literal: true
2
- # The default virus scanner Hyrax::Works, ported from hydra_works.
3
- # If ClamAV is present, it will be used to check for the presence of a virus. If ClamAV is not
4
- # installed or otherwise not available to your application, Hyrax::Works does no virus checking
5
- # add assumes files have no viruses.
6
- #
7
- # @example to use a virus checker other than Hyrax::VirusScanner:
8
- # class MyScanner < Hyrax::Works::VirusScanner
9
- # def infected?
10
- # my_result = Scanner.check_for_viruses(file)
11
- # [return true or false]
12
- # end
13
- # end
14
- #
15
- # # Then set Hyrax::Works to use your scanner either in a config file or initializer:
16
- # Hyrax.config.virus_scanner = MyScanner
2
+
17
3
  module Hyrax
4
+ ##
5
+ # The default virus scanner ported from +Hyrax::Works+.
6
+ #
7
+ # If ClamAV is present, it will be used to check for the presence of a virus.
8
+ # If ClamAV is not installed or otherwise not available to your application,
9
+ # +Hyrax::Works+ does no virus checking add assumes files have no viruses.
10
+ #
11
+ # @example to use a virus checker other than Hyrax::VirusScanner:
12
+ # class MyScanner < Hyrax::Works::VirusScanner
13
+ # def infected?
14
+ # my_result = Scanner.check_for_viruses(file)
15
+ # [return true or false]
16
+ # end
17
+ # end
18
+ #
19
+ # # Then set Hyrax::Works to use your scanner either in a config file or initializer:
20
+ # Hyrax.config.virus_scanner = MyScanner
21
+ #
18
22
  class VirusScanner
19
23
  attr_reader :file
20
24
 
25
+ ##
21
26
  # @api public
22
27
  # @param file [String]
23
28
  def self.infected?(file)
@@ -28,7 +33,9 @@ module Hyrax
28
33
  @file = file
29
34
  end
30
35
 
31
- # Override this method to use your own virus checking software
36
+ ##
37
+ # @note Override this method to use your own virus checking software
38
+ #
32
39
  # @return [Boolean]
33
40
  def infected?
34
41
  defined?(ClamAV) ? clam_av_scanner : null_scanner
@@ -41,8 +48,10 @@ module Hyrax
41
48
  true
42
49
  end
43
50
 
44
- # Always return zero if there's nothing available to check for viruses. This means that
45
- # we assume all files have no viruses because we can't conclusively say if they have or not.
51
+ ##
52
+ # Always return zero if there's nothing available to check for viruses.
53
+ # This means that we assume all files have no viruses because we can't
54
+ # conclusively say if they have or not.
46
55
  def null_scanner
47
56
  warning "Unable to check #{file} for viruses because no virus scanner is defined" unless
48
57
  Rails.env.test?
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
  module Sipity
3
+ ##
3
4
  # A proxy for something that can take an action.
4
5
  #
5
6
  # * A User can be an agent
@@ -1,19 +1,39 @@
1
1
  # frozen_string_literal: true
2
2
  module Sipity
3
- # A proxy for the entity that is being processed via workflows.
3
+ ##
4
+ # A proxy for the entity (e.g. a repository Object) that is being processed
5
+ # via workflows.
4
6
  #
5
- # By using a proxy, we need not worry about polluting the proxy's concerns
6
- # with things related to workflow processing.
7
+ # Objects of this class (and their underlying database records) represent
8
+ # another object in the context of the workflow process. By using a proxy,
9
+ # we can avoid polluting the repository object's data and behavior with
10
+ # things related to workflow processing. This means we can move an object into
11
+ # and through a workflow without making changes independently from curatorial
12
+ # considerations (e.g. metadata changes).
13
+ #
14
+ # Keeping this object and interface separate also presents a clear seam for
15
+ # alternative solutions.
16
+ #
17
+ # The {Sipity::Entity} relates to repository objects via a +GlobalID+ URI via
18
+ # {#proxy_for_global_id}. Since the repository objects aren't assumed to be
19
+ # +ActiveRecord+ or +ActiveModel+ compatible, we use the URI-based system
20
+ # provided by +GlobalID+ to ensure that this relationship functions independent
21
+ # of the modelling system used for the respository objects. {#proxy_for} is
22
+ # provided as a convenience method for retrieving the underlying repository
23
+ # object.
24
+ #
25
+ # Each {Sipity::Entity} holds a relationship to a {Sipity::Workflow}, which is
26
+ # the active workflow on the object represented by the {Entity}. It also holds
27
+ # a reference to a {Sipity::WorkflowState}, which is the current state of the
28
+ # object within the workflow.
7
29
  #
8
- # The goal is to keep this behavior separate, so that we can possibly
9
- # extract the information.
10
30
  # @example To get the Sipity::Entity for a work
11
31
  # work = GenericWork.first
12
- # work_global_id = Hyrax::GlobalID(work).to_s
13
- # => "gid://whatever/GenericWork/3x816m604"
14
- # Sipity::Entity.where(proxy_for_global_id: work_global_id).first
32
+ # Sipity::Entity(work)
15
33
  # => #<Sipity::Entity id: 1, proxy_for_global_id: "gid://whatever/GenericWork/3x816m604",
16
34
  # workflow_id: 8, workflow_state_id: 20, created_at: "2017-07-07 13:39:42", updated_at: "2017-07-07 13:39:42">
35
+ #
36
+ # @see https://github.com/rails/globalid Rails' GlobalID library
17
37
  class Entity < ActiveRecord::Base
18
38
  self.table_name = 'sipity_entities'
19
39
 
@@ -35,6 +55,8 @@ module Sipity
35
55
  # Defines the method #workflow_name
36
56
  delegate :name, to: :workflow, prefix: :workflow
37
57
 
58
+ ##
59
+ # @return [Object] the thing this +Entity+ represents.
38
60
  def proxy_for
39
61
  @proxy_for ||= GlobalID::Locator.locate(proxy_for_global_id)
40
62
  end
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
  module Sipity
3
+ ##
3
4
  # A named workflow for processing an entity. Originally I had thought of
4
5
  # calling this a Type, but once I extracted the Processing submodule,
5
6
  # type felt to much of a noun, not conveying potentiality. Workflow
data/app/models/sipity.rb CHANGED
@@ -1,6 +1,44 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ ##
4
+ # {Sipity} is a workflow/state engine.
5
+ #
6
+ # This module and the classes namespaced within it provide a domain model and
7
+ # implementation for managing the movement of repository objects through a
8
+ # defined workflow. The workflows themselves are configured using JSON documents
9
+ # and loaded into the database/domain model as {Sipity::Workflow}. Each workflow
10
+ # can be understood as finite sets of {Sipity::WorkflowState},
11
+ # {Sipity::WorkflowAction} (transitions from state to state), and {Sipity::Role}
12
+ # authorized to carry out each action.
13
+ #
14
+ # Any uniquely identifiable object can be managed by a {Sipity::Workflow}.
15
+ # Normally Hyrax uses workflows to handle the deposit process and maintenance
16
+ # lifecycle of repository objects at the level of the Work (within the Hydra
17
+ # Works model). Objects are represented within the Sipity engine's domain model
18
+ # by a {Sipity::Entity}. Each object has at most one {Sipity::Entity}, is
19
+ # governed by one {Sipity::Workflow}, and in one {Sipity::WorkflowState} at any
20
+ # given time.
21
+ #
22
+ # Some use cases for Sipity workflows include:
23
+ #
24
+ # * Simple unmediated deposit with on-deposit notifications and actions;
25
+ # * Mediated deposit with one or more review steps;
26
+ # * Publication workflows requiring multiple levels of editorial approval
27
+ # and/or peer review;
28
+ # * Preservation processes involving post-deposit selection of objects for
29
+ # replication to external preservation platforms and/or required action
30
+ # in case of failed fixity checks;
31
+ # * Electronic Thesis & Dissertation submission processes involving (e.g.)
32
+ # student deposit, committee and/or departmental approval, centralized/
33
+ # graduate school review, and a final graduation step.
34
+ #
3
35
  module Sipity
36
+ ##
37
+ # Cast a given input (e.g. a +::User+ or {Hyrax::Group} to a {Sipity::Agent}).
38
+ #
39
+ # @param input [Object]
40
+ #
41
+ # @return [Sipity::Agent]
4
42
  def Agent(input, &block) # rubocop:disable Naming/MethodName
5
43
  result = case input
6
44
  when Sipity::Agent
@@ -13,6 +51,10 @@ module Sipity
13
51
 
14
52
  ##
15
53
  # Cast an object to an Entity
54
+ #
55
+ # @param input [Object]
56
+ #
57
+ # @return [Sipity::Entity]
16
58
  # rubocop:disable Naming/MethodName, Metrics/CyclomaticComplexity, Metrics/MethodLength
17
59
  def Entity(input, &block)
18
60
  result = case input
@@ -59,16 +59,8 @@ module Hyrax
59
59
  # > remove files from a work, and add new works to the set.
60
60
  return true if @current_ability.can?(:manage, permission_template)
61
61
 
62
- # Otherwise, we check if the workflow was setup, active, and
63
- # allows_access_grants.
64
- wf = workflow(permission_template: permission_template)
65
- return false unless wf
66
- wf.allows_access_grant?
67
- end
68
-
69
- def workflow(permission_template:)
70
- return unless permission_template.active_workflow
71
- Sipity::Workflow.find_by!(id: permission_template.active_workflow.id)
62
+ # Otherwise, we check if the active workflow allows access grants
63
+ !!permission_template.active_workflow&.allows_access_grant?
72
64
  end
73
65
  end
74
66
  end
@@ -12,7 +12,11 @@ module Hyrax
12
12
  end
13
13
 
14
14
  def total_viewable_items
15
- ActiveFedora::Base.where("isPartOf_ssim:#{id}").accessible_by(current_ability).count
15
+ field_pairs = { "isPartOf_ssim" => id.to_s }
16
+ SolrQueryService.new
17
+ .with_field_pairs(field_pairs: field_pairs)
18
+ .accessible_by(ability: current_ability)
19
+ .count
16
20
  end
17
21
 
18
22
  # AdminSet cannot be deleted if default set or non-empty
@@ -0,0 +1,116 @@
1
+ # frozen_string_literal: true
2
+ module Hyrax
3
+ ##
4
+ # @api public
5
+ # @since 3.1.0
6
+ #
7
+ # Presents select options for admin sets.
8
+ #
9
+ # Each entry in the {#select_options} return value provides a label for
10
+ # display, an id to serve as the value, and a data hash which is used as HTML5
11
+ # data entries. The data entries can be used as hooks for Javascript
12
+ # to control input validation taking into account the Admin Set and
13
+ # `PermissionTemplate` rules (`visibility_component.es6` does this, for
14
+ # example).
15
+ #
16
+ # @note this supersedes the older +Hyrax::AdminSetOptionsPresenter+, which
17
+ # actied more like a "service" sending database queries to Solr and
18
+ # ActiveRecord. this version seeks only to present the input data and
19
+ # relies on its caller to pass in the right data.
20
+ class AdminSetSelectionPresenter
21
+ ##
22
+ # @param [Array<#id>]
23
+ def initialize(admin_sets:)
24
+ @admin_sets = admin_sets
25
+ end
26
+
27
+ ##
28
+ # @return [Array<Array<String, String, Hash>>] an array suitable for a form
29
+ # input `collection:` parameter. it should contain a label, an id, and a
30
+ # hash of HTML5 data attributes.
31
+ def select_options
32
+ @admin_sets.map do |admin_set|
33
+ case admin_set
34
+ when OptionsEntry
35
+ admin_set.result
36
+ else
37
+ OptionsEntry.new(admin_set: admin_set).result
38
+ end
39
+ end
40
+ end
41
+
42
+ ##
43
+ # @api public
44
+ class OptionsEntry
45
+ ##
46
+ # @!attribute [rw] admin_set
47
+ # @return [AdministrativeSet, SolrDocument]
48
+ # @!attribute [rw] permission_template
49
+ # @return [PermissionTemplate]
50
+ # @!attribute [rw] permit_sharing
51
+ # @return [Boolean]
52
+ attr_accessor :admin_set, :permission_template, :permit_sharing
53
+
54
+ ##
55
+ # @param [AdministrativeSet, SolrDocument] admin_set
56
+ # @param [PermissionTemplate] permission_template
57
+ # @param [Boolean] permit_sharing
58
+ def initialize(admin_set:, permission_template: nil, permit_sharing: false)
59
+ @admin_set = admin_set
60
+ @permission_template = permission_template
61
+ @permit_sharing = permit_sharing
62
+ end
63
+
64
+ ##
65
+ # @return [Array<String, String, Hash>]
66
+ def result
67
+ [label, id, data]
68
+ end
69
+
70
+ ##
71
+ # @return [String]
72
+ def label
73
+ Array(admin_set.title).first || admin_set.to_s
74
+ end
75
+
76
+ ##
77
+ # @return [String]
78
+ def id
79
+ admin_set.id.to_s
80
+ end
81
+
82
+ ##
83
+ # @return [Hash{String => Object}]
84
+ def data
85
+ {}.tap do |data|
86
+ data['data-sharing'] = permit_sharing
87
+
88
+ if permission_template
89
+ data.merge!(data_for(permission_template))
90
+ else
91
+ data['data-release-no-delay'] = true
92
+ end
93
+ end
94
+ end
95
+
96
+ private
97
+
98
+ ##
99
+ # @api private
100
+ def data_for(template)
101
+ {}.tap do |data|
102
+ if template.release_no_delay?
103
+ data['data-release-no-delay'] = true
104
+ elsif template.release_date.present?
105
+ data['data-release-date'] = template.release_date
106
+ end
107
+
108
+ data['data-release-before-date'] = true if
109
+ template.release_before_date?
110
+ data['data-visibility'] = template.visibility if
111
+ template.visibility.present?
112
+ end
113
+ end
114
+ end
115
+ end
116
+ end