hyrax 3.3.0 → 3.4.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 (172) hide show
  1. checksums.yaml +4 -4
  2. data/.dassie/config/initializers/hyrax.rb +11 -1
  3. data/.gitignore +3 -0
  4. data/.regen +1 -1
  5. data/.rubocop_fixme.yml +3 -1
  6. data/Dockerfile +2 -1
  7. data/app/actors/hyrax/actors/file_actor.rb +6 -4
  8. data/app/actors/hyrax/actors/transfer_request_actor.rb +3 -7
  9. data/app/assets/javascripts/hyrax/analytics_events.js +8 -2
  10. data/app/assets/javascripts/hyrax/autocomplete/linked_data.es6 +1 -3
  11. data/app/controllers/concerns/hyrax/controller.rb +21 -0
  12. data/app/controllers/concerns/hyrax/works_controller_behavior.rb +83 -59
  13. data/app/controllers/hyrax/admin/admin_sets_controller.rb +105 -19
  14. data/app/controllers/hyrax/admin/permission_template_accesses_controller.rb +12 -19
  15. data/app/controllers/hyrax/batch_edits_controller.rb +12 -3
  16. data/app/controllers/hyrax/batch_uploads_controller.rb +4 -0
  17. data/app/controllers/hyrax/citations_controller.rb +1 -1
  18. data/app/controllers/hyrax/dashboard/collections_controller.rb +19 -10
  19. data/app/forms/hyrax/forms/administrative_set_form.rb +19 -1
  20. data/app/forms/hyrax/forms/batch_edit_form.rb +1 -1
  21. data/app/forms/hyrax/forms/dashboard/nest_collection_form.rb +21 -6
  22. data/app/forms/hyrax/forms/pcdm_collection_form.rb +1 -0
  23. data/app/forms/hyrax/forms/permission_template_form.rb +17 -9
  24. data/app/forms/hyrax/forms/resource_form.rb +9 -5
  25. data/app/helpers/hyrax/collections_helper.rb +14 -0
  26. data/app/helpers/hyrax/membership_helper.rb +1 -1
  27. data/app/helpers/hyrax/trophy_helper.rb +1 -1
  28. data/app/helpers/hyrax/url_helper.rb +1 -1
  29. data/app/indexers/hyrax/administrative_set_indexer.rb +8 -2
  30. data/app/indexers/hyrax/deep_indexing_service.rb +1 -1
  31. data/app/indexers/hyrax/file_set_indexer.rb +1 -0
  32. data/app/indexers/hyrax/pcdm_collection_indexer.rb +3 -1
  33. data/app/indexers/hyrax/thumbnail_indexer.rb +31 -0
  34. data/app/indexers/hyrax/valkyrie_file_set_indexer.rb +6 -6
  35. data/app/indexers/hyrax/valkyrie_indexer.rb +4 -2
  36. data/app/indexers/hyrax/valkyrie_work_indexer.rb +13 -0
  37. data/app/inputs/controlled_vocabulary_input.rb +2 -0
  38. data/app/jobs/change_depositor_event_job.rb +47 -0
  39. data/app/jobs/characterize_job.rb +38 -2
  40. data/app/jobs/concerns/hyrax/members_permission_job_behavior.rb +1 -1
  41. data/app/jobs/content_depositor_change_event_job.rb +2 -1
  42. data/app/jobs/hyrax/propagate_change_depositor_job.rb +32 -0
  43. data/app/jobs/inherit_permissions_job.rb +1 -1
  44. data/app/jobs/valkyrie_create_derivatives_job.rb +25 -0
  45. data/app/jobs/valkyrie_ingest_job.rb +84 -16
  46. data/app/models/admin_set.rb +2 -2
  47. data/app/models/collection_branding_info.rb +8 -6
  48. data/app/models/concerns/hyrax/collection_behavior.rb +2 -2
  49. data/app/models/concerns/hyrax/file_set/characterization.rb +7 -1
  50. data/app/models/concerns/hyrax/solr_document/metadata.rb +1 -0
  51. data/app/models/concerns/hyrax/solr_document_behavior.rb +9 -3
  52. data/app/models/hyrax/administrative_set.rb +36 -1
  53. data/app/models/hyrax/collection_type.rb +2 -2
  54. data/app/models/hyrax/file_metadata.rb +5 -1
  55. data/app/models/hyrax/file_set.rb +42 -1
  56. data/app/models/hyrax/pcdm_collection.rb +56 -0
  57. data/app/models/hyrax/permission_template.rb +11 -5
  58. data/app/models/hyrax/work.rb +91 -0
  59. data/app/models/proxy_deposit_request.rb +1 -1
  60. data/app/presenters/hyrax/admin_set_presenter.rb +2 -2
  61. data/app/presenters/hyrax/pcdm_member_presenter_factory.rb +2 -2
  62. data/app/presenters/hyrax/work_show_presenter.rb +7 -3
  63. data/app/search_builders/hyrax/dashboard/collections_search_builder.rb +2 -2
  64. data/app/search_builders/hyrax/dashboard/managed_search_filters.rb +44 -4
  65. data/app/search_builders/hyrax/dashboard/nested_collections_search_builder.rb +2 -2
  66. data/app/search_builders/hyrax/my/collections_search_builder.rb +11 -4
  67. data/app/services/hyrax/access_control_list.rb +13 -0
  68. data/app/services/hyrax/admin_set_create_service.rb +21 -37
  69. data/app/services/hyrax/change_content_depositor_service.rb +2 -2
  70. data/app/services/hyrax/change_depositor_service.rb +70 -0
  71. data/app/services/hyrax/characterization/valkyrie_characterization_service.rb +1 -1
  72. data/app/services/hyrax/collections/nested_collection_query_service.rb +23 -11
  73. data/app/services/hyrax/custom_queries/navigators/child_file_sets_navigator.rb +45 -0
  74. data/app/services/hyrax/custom_queries/navigators/child_filesets_navigator.rb +7 -2
  75. data/app/services/hyrax/custom_queries/navigators/parent_work_navigator.rb +54 -0
  76. data/app/services/hyrax/default_middleware_stack.rb +3 -0
  77. data/app/services/hyrax/file_set_derivatives_service.rb +21 -2
  78. data/app/services/hyrax/file_set_type_service.rb +2 -5
  79. data/app/services/hyrax/listeners/file_metadata_listener.rb +20 -1
  80. data/app/services/hyrax/listeners/member_cleanup_listener.rb +23 -3
  81. data/app/services/hyrax/listeners/metadata_index_listener.rb +39 -0
  82. data/app/services/hyrax/listeners/proxy_deposit_listener.rb +14 -8
  83. data/app/services/hyrax/location_service.rb +33 -0
  84. data/app/services/hyrax/multiple_membership_checker.rb +44 -1
  85. data/app/services/hyrax/resource_visibility_propagator.rb +1 -1
  86. data/app/services/hyrax/simple_schema_loader.rb +5 -1
  87. data/app/services/hyrax/solr_query_service.rb +12 -7
  88. data/app/services/hyrax/thumbnail_path_service.rb +1 -1
  89. data/app/services/hyrax/work_uploads_handler.rb +0 -10
  90. data/app/validators/hyrax/collection_membership_validator.rb +38 -0
  91. data/app/views/catalog/_index_header_list_hyrax_pcdm_collection.html.erb +4 -0
  92. data/app/views/hyrax/admin/admin_sets/_form_participant_table.html.erb +2 -2
  93. data/app/views/hyrax/admin/admin_sets/_form_participants.html.erb +2 -2
  94. data/app/views/hyrax/admin/admin_sets/_form_visibility.html.erb +2 -2
  95. data/app/views/hyrax/admin/admin_sets/_form_workflow.erb +1 -1
  96. data/app/views/hyrax/admin/collection_types/index.html.erb +1 -1
  97. data/app/views/hyrax/base/_form.html.erb +1 -1
  98. data/app/views/hyrax/base/_form_child_work_relationships.html.erb +1 -1
  99. data/app/views/hyrax/dashboard/collections/_default_group.html.erb +2 -2
  100. data/app/views/hyrax/dashboard/collections/_form.html.erb +21 -15
  101. data/app/views/hyrax/dashboard/collections/_form_discovery.html.erb +6 -3
  102. data/app/views/hyrax/dashboard/collections/_form_share.html.erb +2 -2
  103. data/app/views/hyrax/dashboard/collections/_form_share_table.html.erb +3 -3
  104. data/app/views/hyrax/dashboard/collections/_list_collections.html.erb +2 -2
  105. data/app/views/hyrax/dashboard/works/_default_group.html.erb +1 -1
  106. data/app/views/hyrax/dashboard/works/_list_works.html.erb +1 -1
  107. data/app/views/hyrax/file_sets/_actions.html.erb +2 -2
  108. data/app/views/hyrax/my/_work_action_menu.html.erb +8 -9
  109. data/app/views/hyrax/my/collections/_default_group.html.erb +2 -2
  110. data/app/views/hyrax/my/collections/_list_collections.html.erb +2 -2
  111. data/app/views/hyrax/my/collections/index.html.erb +3 -2
  112. data/app/views/hyrax/my/works/_default_group.html.erb +1 -1
  113. data/app/views/hyrax/my/works/_list_works.html.erb +1 -2
  114. data/app/views/hyrax/my/works/index.html.erb +4 -2
  115. data/chart/hyrax/Chart.yaml +2 -2
  116. data/chart/hyrax/README.md +22 -1
  117. data/config/initializers/listeners.rb +0 -1
  118. data/config/locales/hyrax.de.yml +6 -5
  119. data/config/locales/hyrax.en.yml +30 -28
  120. data/config/locales/hyrax.es.yml +10 -9
  121. data/config/locales/hyrax.fr.yml +2 -1
  122. data/config/locales/hyrax.it.yml +3 -2
  123. data/config/locales/hyrax.pt-BR.yml +2 -1
  124. data/config/locales/hyrax.zh.yml +2 -1
  125. data/config/metadata/basic_metadata.yaml +2 -0
  126. data/config/metadata/core_metadata.yaml +1 -1
  127. data/docker-compose.yml +46 -42
  128. data/documentation/developing-your-hyrax-based-app.md +1 -1
  129. data/documentation/legacyREADME.md +1 -1
  130. data/lib/hyrax/administrative_set_name.rb +18 -0
  131. data/lib/hyrax/collection_name.rb +2 -0
  132. data/lib/hyrax/configuration.rb +10 -0
  133. data/lib/hyrax/controlled_vocabularies/location.rb +9 -2
  134. data/lib/hyrax/controlled_vocabularies/resource_label_caching.rb +42 -0
  135. data/lib/hyrax/controlled_vocabularies.rb +1 -0
  136. data/lib/hyrax/publisher.rb +45 -0
  137. data/lib/hyrax/specs/capybara.rb +1 -1
  138. data/lib/hyrax/specs/shared_specs/hydra_works.rb +11 -4
  139. data/lib/hyrax/specs/shared_specs/indexers.rb +117 -3
  140. data/lib/hyrax/transactions/admin_set_create.rb +2 -1
  141. data/lib/hyrax/transactions/admin_set_destroy.rb +22 -0
  142. data/lib/hyrax/transactions/admin_set_update.rb +21 -0
  143. data/lib/hyrax/transactions/collection_destroy.rb +22 -0
  144. data/lib/hyrax/transactions/collection_update.rb +3 -2
  145. data/lib/hyrax/transactions/container.rb +87 -23
  146. data/lib/hyrax/transactions/create_work.rb +3 -0
  147. data/lib/hyrax/transactions/destroy_work.rb +3 -0
  148. data/lib/hyrax/transactions/steps/apply_collection_permission_template.rb +2 -0
  149. data/lib/hyrax/transactions/steps/apply_permission_template.rb +2 -0
  150. data/lib/hyrax/transactions/steps/apply_visibility.rb +2 -0
  151. data/lib/hyrax/transactions/steps/change_depositor.rb +46 -0
  152. data/lib/hyrax/transactions/steps/check_for_empty_admin_set.rb +36 -0
  153. data/lib/hyrax/transactions/steps/delete_access_control.rb +32 -0
  154. data/lib/hyrax/transactions/steps/delete_resource.rb +19 -3
  155. data/lib/hyrax/transactions/steps/destroy_work.rb +3 -1
  156. data/lib/hyrax/transactions/steps/ensure_permission_template.rb +2 -0
  157. data/lib/hyrax/transactions/steps/save.rb +24 -6
  158. data/lib/hyrax/transactions/steps/save_access_control.rb +2 -2
  159. data/lib/hyrax/transactions/steps/save_work.rb +3 -0
  160. data/lib/hyrax/transactions/steps/set_user_as_creator.rb +41 -0
  161. data/lib/hyrax/transactions/steps/update_work_members.rb +51 -0
  162. data/lib/hyrax/transactions/update_work.rb +4 -3
  163. data/lib/hyrax/transactions/work_create.rb +1 -1
  164. data/lib/hyrax/transactions/work_destroy.rb +2 -1
  165. data/lib/hyrax/transactions/work_update.rb +19 -0
  166. data/lib/hyrax/version.rb +1 -1
  167. data/lib/wings/attribute_transformer.rb +5 -1
  168. data/lib/wings/setup.rb +3 -1
  169. data/lib/wings/valkyrie/query_service.rb +2 -1
  170. data/lib/wings/valkyrie/storage.rb +7 -1
  171. data/template.rb +1 -1
  172. metadata +24 -3
@@ -5,11 +5,11 @@ module Hyrax
5
5
  load_and_authorize_resource class: 'Hyrax::PermissionTemplateAccess'
6
6
 
7
7
  def destroy
8
+ authorize! :destroy, @permission_template_access
8
9
  ActiveRecord::Base.transaction do
9
- @permission_template_access.destroy if valid_delete?
10
+ @permission_template_access.destroy
10
11
  remove_access!
11
12
  end
12
-
13
13
  if @permission_template_access.destroyed?
14
14
  after_destroy_success
15
15
  else
@@ -19,38 +19,31 @@ module Hyrax
19
19
 
20
20
  private
21
21
 
22
- # This is a controller validation rather than a model validation
23
- # because we don't want to prevent the ability to remove the whole
24
- # PermissionTemplate and all of its associated PermissionTemplateAccesses
25
- # @return [Boolean] true if it's valid
26
- def valid_delete?
27
- return true unless @permission_template_access.admin_group?
28
- @permission_template_access.errors[:base] <<
29
- t('hyrax.admin.admin_sets.form.permission_destroy_errors.admin_group')
30
- false
31
- end
32
-
33
- def after_destroy_success
22
+ def after_destroy_error
34
23
  if source.admin_set?
24
+ @permission_template_access.errors[:base] <<
25
+ t('hyrax.admin.admin_sets.form.permission_destroy_errors.participants')
35
26
  redirect_to hyrax.edit_admin_admin_set_path(source_id,
36
27
  anchor: 'participants'),
37
- notice: translate('participants', scope: 'hyrax.admin.admin_sets.form.permission_update_notices')
28
+ alert: @permission_template_access.errors.full_messages.to_sentence
38
29
  else
30
+ @permission_template_access.errors[:base] <<
31
+ t('hyrax.dashboard.collections.form.permission_update_errors.sharing')
39
32
  redirect_to hyrax.edit_dashboard_collection_path(source_id,
40
33
  anchor: 'sharing'),
41
- notice: translate('sharing', scope: 'hyrax.dashboard.collections.form.permission_update_notices')
34
+ alert: @permission_template_access.errors.full_messages.to_sentence
42
35
  end
43
36
  end
44
37
 
45
- def after_destroy_error
38
+ def after_destroy_success
46
39
  if source.admin_set?
47
40
  redirect_to hyrax.edit_admin_admin_set_path(source_id,
48
41
  anchor: 'participants'),
49
- alert: @permission_template_access.errors.full_messages.to_sentence
42
+ notice: translate('participants', scope: 'hyrax.admin.admin_sets.form.permission_update_notices')
50
43
  else
51
44
  redirect_to hyrax.edit_dashboard_collection_path(source_id,
52
45
  anchor: 'sharing'),
53
- alert: @permission_template_access.errors.full_messages.to_sentence
46
+ notice: translate('sharing', scope: 'hyrax.dashboard.collections.form.permission_update_notices')
54
47
  end
55
48
  end
56
49
 
@@ -39,8 +39,11 @@ module Hyrax
39
39
 
40
40
  def destroy_collection
41
41
  batch.each do |doc_id|
42
- obj = Hyrax.query_service.find_by_alternate_identifier(alternate_identifier: doc_id, use_valkyrie: false)
43
- obj.destroy
42
+ resource = Hyrax.query_service.find_by(id: Valkyrie::ID.new(doc_id))
43
+ transactions['collection_resource.destroy']
44
+ .with_step_args('collection_resource.delete' => { user: current_user })
45
+ .call(resource)
46
+ .value!
44
47
  end
45
48
  flash[:notice] = "Batch delete complete"
46
49
  after_destroy_collection
@@ -83,7 +86,13 @@ module Hyrax
83
86
  end
84
87
 
85
88
  def destroy_batch
86
- batch.each { |id| Hyrax.query_service.find_by_alternate_identifier(alternate_identifier: id, use_valkyrie: false).destroy }
89
+ batch.each do |id|
90
+ resource = Hyrax.query_service.find_by(id: Valkyrie::ID.new(id))
91
+ transactions['work_resource.destroy']
92
+ .with_step_args('work_resource.delete' => { user: current_user })
93
+ .call(resource)
94
+ .value!
95
+ end
87
96
  after_update
88
97
  end
89
98
 
@@ -44,6 +44,10 @@ module Hyrax
44
44
  end
45
45
 
46
46
  def build_form
47
+ add_breadcrumb t('hyrax.controls.home'), root_path
48
+ add_breadcrumb t('hyrax.dashboard.breadcrumbs.admin'), hyrax.dashboard_path
49
+ add_breadcrumb t('hyrax.admin.sidebar.works'), hyrax.my_works_path
50
+ add_breadcrumb t('hyrax.batch_uploads.new.breadcrumb'), request.path
47
51
  super
48
52
  @form.payload_concern = params[:payload_concern]
49
53
  end
@@ -2,8 +2,8 @@
2
2
  module Hyrax
3
3
  class CitationsController < ApplicationController
4
4
  include WorksControllerBehavior
5
+ include DenyAccessOverrideBehavior
5
6
  include Breadcrumbs
6
- include SingularSubresourceController
7
7
 
8
8
  # Overrides decide_layout from WorksControllerBehavior
9
9
  with_themed_layout '1_column'
@@ -43,13 +43,10 @@ module Hyrax
43
43
  # The search builder to find the collections' members
44
44
  self.membership_service_class = Collections::CollectionMemberSearchService
45
45
 
46
- load_and_authorize_resource except: [:index],
46
+ load_and_authorize_resource except: [:index, :create],
47
47
  instance_name: :collection,
48
48
  class: Hyrax.config.collection_model
49
49
 
50
- skip_load_resource only: :create if
51
- Hyrax.config.collection_class < ActiveFedora::Base
52
-
53
50
  def deny_collection_access(exception)
54
51
  if exception.action == :edit
55
52
  redirect_to(url_for(action: 'show'), alert: 'You do not have sufficient privileges to edit this document')
@@ -83,6 +80,7 @@ module Hyrax
83
80
 
84
81
  def edit
85
82
  form
83
+ collection_type
86
84
  end
87
85
 
88
86
  def after_create
@@ -109,12 +107,13 @@ module Hyrax
109
107
  end
110
108
 
111
109
  def create
112
- return valkyrie_create if @collection.is_a?(Valkyrie::Resource)
113
110
  # Manual load and authorize necessary because Cancan will pass in all
114
111
  # form attributes. When `permissions_attributes` are present the
115
112
  # collection is saved without a value for `has_model.`
116
113
  @collection = Hyrax.config.collection_class.new
117
114
  authorize! :create, @collection
115
+ return valkyrie_create if @collection.is_a?(Valkyrie::Resource)
116
+
118
117
  # Coming from the UI, a collection type gid should always be present. Coming from the API, if a collection type gid is not specified,
119
118
  # use the default collection type (provides backward compatibility with versions < Hyrax 2.1.0)
120
119
  @collection.collection_type_gid = params[:collection_type_gid].presence || default_collection_type.to_global_id
@@ -168,10 +167,10 @@ module Hyrax
168
167
  # leaving id to avoid changing the method's parameters prior to release
169
168
  respond_to do |format|
170
169
  format.html do
171
- redirect_to my_collections_path,
170
+ redirect_to hyrax.my_collections_path,
172
171
  notice: t('hyrax.dashboard.my.action.collection_delete_success')
173
172
  end
174
- format.json { head :no_content, location: my_collections_path }
173
+ format.json { head :no_content, location: hyrax.my_collections_path }
175
174
  end
176
175
  end
177
176
 
@@ -188,8 +187,7 @@ module Hyrax
188
187
  def destroy
189
188
  case @collection
190
189
  when Valkyrie::Resource
191
- Hyrax.persister.delete(resource: @collection)
192
- after_destroy(params[:id])
190
+ valkyrie_destroy
193
191
  else
194
192
  if @collection.destroy
195
193
  after_destroy(params[:id])
@@ -241,6 +239,14 @@ module Hyrax
241
239
  after_update
242
240
  end
243
241
 
242
+ def valkyrie_destroy
243
+ if transactions['collection_resource.destroy'].call(@collection).success?
244
+ after_destroy(params[:id])
245
+ else
246
+ after_destroy_error(params[:id])
247
+ end
248
+ end
249
+
244
250
  def default_collection_type
245
251
  Hyrax::CollectionType.find_or_create_default_collection_type
246
252
  end
@@ -332,7 +338,10 @@ module Hyrax
332
338
  def process_logo_records(uploaded_file_ids)
333
339
  public_files = []
334
340
  uploaded_file_ids.each_with_index do |ufi, i|
335
- if ufi.include?('public')
341
+ # If the user has chosen a new logo, the ufi will be an integer
342
+ # If the logo was previously chosen, the ufi will be a path
343
+ # If it is a path, update the rec, else create a new rec
344
+ if !ufi.match(/\D/).nil?
336
345
  update_logo_info(ufi, params["alttext"][i], verify_linkurl(params["linkurl"][i]))
337
346
  public_files << ufi
338
347
  else # brand new one, insert in the database
@@ -6,6 +6,20 @@ module Hyrax
6
6
  # @api public
7
7
  # @see https://github.com/samvera/valkyrie/wiki/ChangeSets-and-Dirty-Tracking
8
8
  class AdministrativeSetForm < Valkyrie::ChangeSet
9
+ ##
10
+ # @api private
11
+ AdminSetMembersPopulator = lambda do |_options|
12
+ self.member_ids =
13
+ if model.new_record
14
+ []
15
+ else
16
+ Hyrax
17
+ .query_service
18
+ .find_inverse_references_by(property: :admin_set_id, resource: model)
19
+ .map(&:id)
20
+ end
21
+ end
22
+
9
23
  property :title, required: true, primary: true
10
24
  property :description, primary: true
11
25
 
@@ -13,7 +27,11 @@ module Hyrax
13
27
  property :date_modified, readable: false
14
28
  property :date_uploaded, readable: false
15
29
 
16
- property :depositor
30
+ property :creator
31
+
32
+ validates :title, presence: true
33
+
34
+ property :member_ids, virtual: true, default: [], prepopulator: AdminSetMembersPopulator
17
35
 
18
36
  class << self
19
37
  def model_class
@@ -65,7 +65,7 @@ module Hyrax
65
65
  def initialize_combined_fields
66
66
  # For each of the files in the batch, set the attributes to be the concatenation of all the attributes
67
67
  batch_document_ids.each_with_object({}) do |doc_id, combined_attributes|
68
- work = ActiveFedora::Base.find(doc_id)
68
+ work = Hyrax.query_service.find_by(id: doc_id)
69
69
  terms.each do |field|
70
70
  combined_attributes[field] ||= []
71
71
  combined_attributes[field] = (combined_attributes[field] + work[field].to_a).uniq
@@ -26,11 +26,11 @@ module Hyrax
26
26
  context:,
27
27
  query_service: default_query_service,
28
28
  persistence_service: default_persistence_service)
29
- self.parent = parent || (parent_id.present? && Hyrax.config.collection_class.find(parent_id))
30
- self.child = child || (child_id.present? && Hyrax.config.collection_class.find(child_id))
31
29
  self.context = context
32
30
  self.query_service = query_service
33
31
  self.persistence_service = persistence_service
32
+ self.parent = parent || (parent_id.present? && find_parent(parent_id))
33
+ self.child = child || (child_id.present? && find_child(child_id))
34
34
  end # rubocop:enable Metrics/ParameterLists
35
35
 
36
36
  attr_accessor :parent, :child
@@ -83,7 +83,7 @@ module Hyrax
83
83
  # rerouting to new_dashboard_collection_path to add the new collection as
84
84
  # a child. Since we don't yet have a child collection, the valid? option can't be used here.
85
85
  def validate_add
86
- if parent.try(:nestable?)
86
+ if nestable?(parent)
87
87
  nesting_within_maximum_depth
88
88
  else
89
89
  errors.add(:parent, :cannot_have_child_nested)
@@ -116,15 +116,30 @@ module Hyrax
116
116
  end
117
117
 
118
118
  def parent_and_child_can_be_nested
119
- if parent.try(:nestable?) && child.try(:nestable?)
119
+ if nestable?(parent) && nestable?(child)
120
120
  return true if query_service.parent_and_child_can_nest?(parent: parent, child: child, scope: context)
121
121
  errors.add(:parent, :cannot_have_child_nested)
122
122
  errors.add(:child, :cannot_nest_in_parent)
123
123
  else
124
- errors.add(:parent, :is_not_nestable) unless parent.try(:nestable?)
125
- errors.add(:child, :is_not_nestable) unless child.try(:nestable?)
124
+ errors.add(:parent, :is_not_nestable) unless nestable?(parent)
125
+ errors.add(:child, :is_not_nestable) unless nestable?(child)
126
126
  end
127
127
  end
128
+
129
+ def find_parent(parent_id)
130
+ Hyrax.query_service.find_by(id: parent_id)
131
+ end
132
+
133
+ def find_child(child_id)
134
+ Hyrax.query_service.find_by(id: child_id)
135
+ end
136
+
137
+ def nestable?(collection)
138
+ return false if collection.blank?
139
+ return collection.nestable? if collection.respond_to? :nestable?
140
+ collection_type = Hyrax::CollectionType.find_by_gid!(collection.collection_type_gid)
141
+ collection_type.nestable?
142
+ end
128
143
  end
129
144
  end
130
145
  end
@@ -14,6 +14,7 @@ module Hyrax
14
14
 
15
15
  property :depositor, required: true
16
16
  property :collection_type_gid, required: true
17
+ property :visibility, default: VisibilityIntention::PRIVATE
17
18
 
18
19
  property :member_of_collection_ids, default: [], type: Valkyrie::Types::Array
19
20
 
@@ -7,16 +7,12 @@ module Hyrax
7
7
  self.model_class = PermissionTemplate
8
8
  self.terms = []
9
9
  delegate :access_grants, :access_grants_attributes=, :release_date, :release_period, :visibility, to: :model
10
- delegate :available_workflows, :active_workflow, :source_model, to: :model
11
-
12
- # @return [#to_s] the primary key of the associated admin_set or collection
13
- # def source_id (because you might come looking for this method)
14
- delegate :id, to: :source_model, prefix: :source
10
+ delegate :available_workflows, :active_workflow, :source, :source_id, to: :model
15
11
 
16
12
  ##
17
- # @deprecated use PermissionTemplate#reset_access_controls instead.
13
+ # @deprecated use PermissionTemplate#reset_access_controls_for instead.
18
14
  def reset_access_controls!
19
- Deprecation.warn("reset_access_controls! is deprecated; use PermissionTemplate#reset_access_controls instead.")
15
+ Deprecation.warn("reset_access_controls! is deprecated; use PermissionTemplate#reset_access_controls_for instead.")
20
16
  source_model.reset_access_controls!
21
17
  end
22
18
 
@@ -73,8 +69,8 @@ module Hyrax
73
69
  # Copy this access to the permissions of the Admin Set or Collection and to
74
70
  # the WorkflowResponsibilities of the active workflow if this is an Admin Set
75
71
  def update_access(remove_agent: false)
76
- source_model.reset_access_controls!
77
- update_workflow_responsibilities(remove_agent: remove_agent) if source_model.is_a?(AdminSet)
72
+ model.reset_access_controls_for(collection: source)
73
+ update_workflow_responsibilities(remove_agent: remove_agent) if source.is_a?(Hyrax::AdministrativeSet)
78
74
  end
79
75
 
80
76
  # This method is used to revoke access to a Collection or Admin Set and its workflows
@@ -85,6 +81,18 @@ module Hyrax
85
81
  update_access(remove_agent: true)
86
82
  end
87
83
 
84
+ ##
85
+ # A bit of an analogue for a `belongs_to :source_model` as it crosses from Fedora to the DB
86
+ # @return [AdminSet, ::Collection]
87
+ # @raise [Hyrax::ObjectNotFoundError] when neither an AdminSet or Collection is found
88
+ # @note This method will eventually be replaced by #source which returns a Hyrax::Resource
89
+ # object. Many methods are equally able to process both Hyrax::Resource and
90
+ # ActiveFedora::Base. Only call this method if you need the ActiveFedora::Base object.
91
+ # @see #source
92
+ def source_model # rubocop:disable Rails/Delegate
93
+ model.source_model
94
+ end
95
+
88
96
  private
89
97
 
90
98
  # @return [String]
@@ -33,7 +33,7 @@ module Hyrax
33
33
  class ResourceForm < Hyrax::ChangeSet # rubocop:disable Metrics/ClassLength
34
34
  ##
35
35
  # @api private
36
- InWorksPopulator = lambda do |_options|
36
+ InWorksPrepopulator = lambda do |_options|
37
37
  self.in_works_ids =
38
38
  if persisted?
39
39
  Hyrax.query_service
@@ -52,7 +52,7 @@ module Hyrax
52
52
  # with `etag`-driven, application-side lock checks. for non-wings adapters
53
53
  # we want to move away from application side lock validation and rely
54
54
  # on the adapter/database features instead.
55
- LockKeyPopulator = lambda do |_options|
55
+ LockKeyPrepopulator = lambda do |_options|
56
56
  if Hyrax.config.disable_wings || !Hyrax.metadata_adapter.is_a?(Wings::Valkyrie::MetadataAdapter)
57
57
  Hyrax.logger.info "trying to prepopulate a lock token for " \
58
58
  "#{self.class.inspect}, but optimistic locking isn't " \
@@ -95,10 +95,12 @@ module Hyrax
95
95
  property :visibility_during_lease, virtual: true, prepopulator: ->(_opts) { self.visibility_during_lease = model.lease&.visibility_during_lease }
96
96
 
97
97
  # pcdm relationships
98
- property :admin_set_id, prepopulator: ->(_opts) { self.admin_set_id = AdminSet::DEFAULT_ID }
99
- property :in_works_ids, virtual: true, prepopulator: InWorksPopulator
98
+ property :admin_set_id, prepopulator: ->(_opts) { self.admin_set_id = Hyrax::AdminSetCreateService.find_or_create_default_admin_set.id.to_s }
99
+ property :in_works_ids, virtual: true, prepopulator: InWorksPrepopulator
100
100
  property :member_ids, default: [], type: Valkyrie::Types::Array
101
101
  property :member_of_collection_ids, default: [], type: Valkyrie::Types::Array
102
+ property :member_of_collections_attributes, virtual: true
103
+ validates_with CollectionMembershipValidator
102
104
 
103
105
  property :representative_id, type: Valkyrie::Types::String
104
106
  property :thumbnail_id, type: Valkyrie::Types::String
@@ -112,7 +114,7 @@ module Hyrax
112
114
  # the model is disabled
113
115
  #
114
116
  # @see https://github.com/samvera/valkyrie/wiki/Optimistic-Locking
115
- property :version, virtual: true, prepopulator: LockKeyPopulator
117
+ property :version, virtual: true, prepopulator: LockKeyPrepopulator
116
118
 
117
119
  # backs the child work search element;
118
120
  # @todo: look for a way for the view template not to depend on this
@@ -131,6 +133,8 @@ module Hyrax
131
133
  "#{resource.class.name}Form".constantize.new(resource)
132
134
  rescue NameError => _err
133
135
  case resource
136
+ when Hyrax::AdministrativeSet
137
+ Hyrax::Forms::AdministrativeSetForm.new(resource)
134
138
  when Hyrax::FileSet
135
139
  Hyrax::Forms::FileSetForm.new(resource)
136
140
  when Hyrax::PcdmCollection
@@ -191,6 +191,20 @@ module Hyrax
191
191
  CollectionType.find_or_create_default_collection_type.title
192
192
  end
193
193
 
194
+ ##
195
+ # @param collection [Object]
196
+ #
197
+ # @return [PermissionTemplateForm]
198
+ def collection_permission_template_form_for(form:)
199
+ case form
200
+ when Valkyrie::ChangeSet
201
+ template_model = Hyrax::PermissionTemplate.find_or_create_by(source_id: form.id.to_s)
202
+ Hyrax::Forms::PermissionTemplateForm.new(template_model)
203
+ else
204
+ form.permission_template
205
+ end
206
+ end
207
+
194
208
  private
195
209
 
196
210
  # add hidden fields to a form for performing an action on a single document on a collection
@@ -41,7 +41,7 @@ module Hyrax
41
41
  Hyrax.custom_queries.find_child_works(resource: resource).map do |member|
42
42
  { id: member.id.to_s,
43
43
  label: member.title.first,
44
- path: url_for(member) }
44
+ path: main_app.url_for([member, { only_path: true }]) }
45
45
  end.to_json
46
46
  end
47
47
  end
@@ -15,7 +15,7 @@ module Hyrax
15
15
  args[:data]['remove-text'] = args[:remove_text]
16
16
 
17
17
  args[:data][:url] = hyrax.trophy_work_path(id)
18
- link_to '#', class: args[:class], data: args[:data] do
18
+ link_to '#', id: 'action-highlight-work', class: args[:class], data: args[:data] do
19
19
  yield(text)
20
20
  end
21
21
  end
@@ -3,7 +3,7 @@ module Hyrax
3
3
  module UrlHelper
4
4
  # generated models get registered as curation concerns and need a
5
5
  # track_model_path to render Blacklight-related views
6
- (['FileSet', 'Collection'] + Hyrax.config.registered_curation_concern_types).each do |concern|
6
+ (['FileSet', 'Collection', 'Hyrax::AdministrativeSet'] + Hyrax.config.registered_curation_concern_types).each do |concern|
7
7
  model = concern.safe_constantize
8
8
  model_name = model.respond_to?(:model_name) && model.model_name
9
9
  next unless model_name
@@ -4,14 +4,20 @@ module Hyrax
4
4
  ##
5
5
  # Indexes Hyrax::AdministrativeSet objects
6
6
  class AdministrativeSetIndexer < Hyrax::ValkyrieIndexer
7
+ include Hyrax::ResourceIndexer
8
+ include Hyrax::PermissionIndexer
9
+ include Hyrax::VisibilityIndexer
7
10
  include Hyrax::Indexer(:core_metadata)
8
11
 
9
12
  def to_solr # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength
10
13
  super.tap do |solr_doc|
11
- solr_doc[:generic_type_si] = 'Admin Set'
14
+ solr_doc[Hyrax.config.collection_type_index_field.to_sym] = Array(resource.try(:collection_type_gid)&.to_s)
12
15
  solr_doc[:alternative_title_tesim] = resource.alternative_title
13
- solr_doc[:creator_ssim] = resource.creator
16
+ solr_doc[:creator_ssim] = [resource.creator]
17
+ solr_doc[:creator_tesim] = [resource.creator]
14
18
  solr_doc[:description_tesim] = resource.description
19
+ solr_doc[:generic_type_sim] = ['Admin Set']
20
+ solr_doc[:thumbnail_path_ss] = Hyrax::CollectionThumbnailPathService.call(resource)
15
21
  end
16
22
  end
17
23
  end
@@ -41,7 +41,7 @@ module Hyrax
41
41
 
42
42
  def fetch_with_persistence(resource)
43
43
  old_label = resource.rdf_label.first
44
- return unless old_label == resource.rdf_subject.to_s || old_label.nil?
44
+ return unless old_label == resource.rdf_subject.to_s
45
45
  fetch_value(resource)
46
46
  return if old_label == resource.rdf_label.first || resource.rdf_label.first == resource.rdf_subject.to_s
47
47
  resource.persist! # Stores the fetched values into our marmotta triplestore
@@ -30,6 +30,7 @@ module Hyrax
30
30
  solr_doc['original_checksum_tesim'] = object.original_checksum
31
31
  solr_doc['alpha_channels_ssi'] = object.alpha_channels
32
32
  solr_doc['original_file_id_ssi'] = original_file_id
33
+ solr_doc['generic_type_si'] = 'FileSet'
33
34
  end
34
35
  end
35
36
 
@@ -7,14 +7,16 @@ module Hyrax
7
7
  include Hyrax::ResourceIndexer
8
8
  include Hyrax::PermissionIndexer
9
9
  include Hyrax::VisibilityIndexer
10
+ include Hyrax::ThumbnailIndexer
10
11
  include Hyrax::Indexer(:core_metadata)
11
12
  include Hyrax::Indexer(:basic_metadata)
12
13
 
14
+ self.thumbnail_path_service = CollectionThumbnailPathService
15
+
13
16
  def to_solr
14
17
  super.tap do |index_document|
15
18
  index_document[Hyrax.config.collection_type_index_field.to_sym] = Array(resource.try(:collection_type_gid)&.to_s)
16
19
  index_document[:generic_type_sim] = ['Collection']
17
- index_document[:thumbnail_path_ss] = Hyrax::CollectionThumbnailPathService.call(resource)
18
20
  index_document[:depositor_ssim] = [resource.depositor]
19
21
  index_document[:depositor_tesim] = [resource.depositor]
20
22
  end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+ module Hyrax
3
+ module ThumbnailIndexer
4
+ extend ActiveSupport::Concern
5
+
6
+ included do
7
+ class_attribute :thumbnail_path_service
8
+ self.thumbnail_path_service = ThumbnailPathService
9
+ class_attribute :thumbnail_field
10
+ self.thumbnail_field = :thumbnail_path_ss
11
+ end
12
+
13
+ # Adds thumbnail indexing to the solr document of a valkyrie resource
14
+ def to_solr
15
+ super.tap do |solr_doc|
16
+ index_thumbnails(solr_doc)
17
+ end
18
+ end
19
+
20
+ # Write the thumbnail paths into the solr_document
21
+ # @param [Hash] solr_document the solr document to add the field to
22
+ def index_thumbnails(solr_document)
23
+ solr_document[thumbnail_field] = thumbnail_path.to_s
24
+ end
25
+
26
+ # Returns the value for the thumbnail path to put into the solr document
27
+ def thumbnail_path
28
+ self.class.thumbnail_path_service.call(resource)
29
+ end
30
+ end
31
+ end
@@ -7,20 +7,20 @@ module Hyrax
7
7
  include Hyrax::ResourceIndexer
8
8
  include Hyrax::PermissionIndexer
9
9
  include Hyrax::VisibilityIndexer
10
+ include Hyrax::ThumbnailIndexer
10
11
  include Hyrax::Indexer(:core_metadata)
11
12
  include Hyrax::Indexer(:basic_metadata)
12
13
 
13
- # include Hyrax::IndexesThumbnails # TODO: Is there a Valkyrie version of a thumbnail indexer?
14
-
15
14
  def to_solr # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
16
15
  super.tap do |solr_doc| # rubocop:disable Metrics/BlockLength
17
16
  solr_doc['generic_type_si'] = 'FileSet'
18
17
 
19
18
  # Metadata from the FileSet
20
- solr_doc['file_ids_ssim'] = resource.file_ids&.map(&:to_s)
21
- solr_doc['original_file_id_ssi'] = resource.original_file_id.to_s
22
- solr_doc['thumbnail_id_ssi'] = resource.thumbnail_id.to_s
23
- solr_doc['extracted_text_id_ssi'] = resource.extracted_text_id.to_s
19
+ solr_doc['file_ids_ssim'] = resource.file_ids&.map(&:to_s)
20
+ solr_doc['original_file_id_ssi'] = resource.original_file_id.to_s
21
+ solr_doc['extracted_text_id_ssi'] = resource.extracted_text_id.to_s
22
+ solr_doc['hasRelatedMediaFragment_ssim'] = resource.representative_id.to_s
23
+ solr_doc['hasRelatedImage_ssim'] = resource.thumbnail_id.to_s
24
24
 
25
25
  # Add in metadata from the original file.
26
26
  file_metadata = original_file
@@ -64,14 +64,16 @@ module Hyrax
64
64
 
65
65
  ##
66
66
  # @api public
67
- # @return [Hash<Symbol, Object>]
67
+ # @return [HashWithIndifferentAccess<Symbol, Object>]
68
68
  def to_solr
69
69
  {
70
70
  "id": resource.id.to_s,
71
71
  "date_uploaded_dtsi": resource.created_at,
72
72
  "date_modified_dtsi": resource.updated_at,
73
+ "system_create_dtsi": resource.created_at,
74
+ "system_modified_dtsi": resource.updated_at,
73
75
  "has_model_ssim": resource.internal_resource
74
- }
76
+ }.with_indifferent_access
75
77
  end
76
78
 
77
79
  ##
@@ -7,6 +7,7 @@ module Hyrax
7
7
  include Hyrax::ResourceIndexer
8
8
  include Hyrax::PermissionIndexer
9
9
  include Hyrax::VisibilityIndexer
10
+ include Hyrax::ThumbnailIndexer
10
11
  include Hyrax::Indexer(:core_metadata)
11
12
 
12
13
  def to_solr # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength
@@ -14,10 +15,16 @@ module Hyrax
14
15
  solr_doc['generic_type_si'] = 'Work'
15
16
  solr_doc['suppressed_bsi'] = suppressed?(resource)
16
17
  solr_doc['admin_set_id_ssim'] = [resource.admin_set_id.to_s]
18
+ admin_set_label = admin_set_label(resource)
19
+ solr_doc['admin_set_sim'] = admin_set_label
20
+ solr_doc['admin_set_tesim'] = admin_set_label
21
+ solr_doc["#{Hyrax.config.admin_set_predicate.qname.last}_ssim"] = [resource.admin_set_id.to_s]
17
22
  solr_doc['member_of_collection_ids_ssim'] = resource.member_of_collection_ids.map(&:to_s)
18
23
  solr_doc['member_ids_ssim'] = resource.member_ids.map(&:to_s)
19
24
  solr_doc['depositor_ssim'] = [resource.depositor]
20
25
  solr_doc['depositor_tesim'] = [resource.depositor]
26
+ solr_doc['hasRelatedMediaFragment_ssim'] = [resource.representative_id.to_s]
27
+ solr_doc['hasRelatedImage_ssim'] = [resource.thumbnail_id.to_s]
21
28
  end
22
29
  end
23
30
 
@@ -26,5 +33,11 @@ module Hyrax
26
33
  def suppressed?(resource)
27
34
  Hyrax::ResourceStatus.new(resource: resource).inactive?
28
35
  end
36
+
37
+ def admin_set_label(resource)
38
+ return if resource.admin_set_id.blank?
39
+ admin_set = Hyrax.query_service.find_by(id: resource.admin_set_id)
40
+ admin_set.title
41
+ end
29
42
  end
30
43
  end
@@ -62,10 +62,12 @@ class ControlledVocabularyInput < MultiValueInput
62
62
 
63
63
  def build_options_for_new_row(_attribute_name, _index, options)
64
64
  options[:value] = ''
65
+ options[:data][:label] = ''
65
66
  end
66
67
 
67
68
  def build_options_for_existing_row(_attribute_name, _index, value, options)
68
69
  options[:value] = value.rdf_label.first || "Unable to fetch label for #{value.rdf_subject}"
70
+ options[:data][:label] = value.full_label || value.rdf_label
69
71
  options[:readonly] = true
70
72
  end
71
73