hyrax 5.2.0 → 5.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.
Files changed (527) hide show
  1. checksums.yaml +4 -4
  2. data/.dassie/.env +5 -1
  3. data/.dassie/Gemfile +4 -2
  4. data/.dassie/app/assets/images/bulkrax/removed.png +0 -0
  5. data/.dassie/app/assets/javascripts/application.js +2 -0
  6. data/.dassie/app/assets/stylesheets/application.css +1 -0
  7. data/.dassie/app/controllers/catalog_controller.rb +1 -0
  8. data/.dassie/app/forms/admin_set_resource_form.rb +6 -0
  9. data/.dassie/app/forms/collection_resource_form.rb +5 -2
  10. data/.dassie/app/forms/generic_work_resource_form.rb +5 -2
  11. data/.dassie/app/forms/monograph_form.rb +5 -3
  12. data/.dassie/app/indexers/admin_set_resource_indexer.rb +5 -0
  13. data/.dassie/app/indexers/collection_resource_indexer.rb +3 -3
  14. data/.dassie/app/indexers/generic_work_resource_indexer.rb +6 -3
  15. data/.dassie/app/indexers/monograph_indexer.rb +6 -2
  16. data/.dassie/app/models/ability.rb +8 -0
  17. data/.dassie/app/models/collection_resource.rb +4 -2
  18. data/.dassie/app/models/concerns/bulkrax/has_local_processing.rb +8 -0
  19. data/.dassie/app/models/generic_work_resource.rb +5 -2
  20. data/.dassie/app/models/monograph.rb +5 -2
  21. data/.dassie/app/views/blacklight_dynamic_sitemap/sitemap/show.xml.builder +22 -0
  22. data/.dassie/bin/importer +142 -0
  23. data/.dassie/bin/web +4 -0
  24. data/.dassie/config/browse_everything_providers.yml +1 -1
  25. data/.dassie/config/bulkrax_api.yml +86 -0
  26. data/.dassie/config/initializers/bulkrax.rb +92 -0
  27. data/.dassie/config/initializers/clamav.rb +2 -0
  28. data/.dassie/config/initializers/hyrax.rb +7 -1
  29. data/.dassie/config/initializers/riiif.rb +4 -80
  30. data/.dassie/config/initializers/wings.rb +1 -1
  31. data/.dassie/config/metadata_profiles/m3_profile.yaml +929 -0
  32. data/.dassie/config/puma.rb +4 -2
  33. data/.dassie/config/routes.rb +7 -0
  34. data/.dassie/config/sidekiq.yml +17 -0
  35. data/.dassie/config/uv/uv.html +63 -81
  36. data/.dassie/db/migrate/20240606205215_create_hyrax_flexible_schemas.rb +9 -0
  37. data/.dassie/db/migrate/20240606205216_add_contexts_to_hyrax_flexible_schemas.rb +5 -0
  38. data/.dassie/db/migrate/20250826142438_create_bulkrax_importers.bulkrax.rb +19 -0
  39. data/.dassie/db/migrate/20250826142439_create_bulkrax_importer_runs.bulkrax.rb +17 -0
  40. data/.dassie/db/migrate/20250826142440_create_bulkrax_entries.bulkrax.rb +17 -0
  41. data/.dassie/db/migrate/20250826142441_add_status_to_entry.bulkrax.rb +10 -0
  42. data/.dassie/db/migrate/20250826142442_add_collections_to_importer_runs.bulkrax.rb +7 -0
  43. data/.dassie/db/migrate/20250826142443_change_collection_ids_on_entries.bulkrax.rb +6 -0
  44. data/.dassie/db/migrate/20250826142444_create_bulkrax_exporters.bulkrax.rb +20 -0
  45. data/.dassie/db/migrate/20250826142445_create_bulkrax_exporter_runs.bulkrax.rb +15 -0
  46. data/.dassie/db/migrate/20250826142446_change_importer_and_exporter_to_polymorphic.bulkrax.rb +19 -0
  47. data/.dassie/db/migrate/20250826142447_add_total_collection_records_to_importer_runs.bulkrax.rb +6 -0
  48. data/.dassie/db/migrate/20250826142448_add_children_to_importer_runs.bulkrax.rb +7 -0
  49. data/.dassie/db/migrate/20250826142449_change_total_records_to_total_work_entries.bulkrax.rb +7 -0
  50. data/.dassie/db/migrate/20250826142450_change_entry_last_error.bulkrax.rb +20 -0
  51. data/.dassie/db/migrate/20250826142451_add_validate_only_to_bulkrax_importers.bulkrax.rb +6 -0
  52. data/.dassie/db/migrate/20250826142452_add_status_to_importers.bulkrax.rb +10 -0
  53. data/.dassie/db/migrate/20250826142453_remove_foreign_key_from_bulkrax_entries.bulkrax.rb +6 -0
  54. data/.dassie/db/migrate/20250826142454_add_status_to_exporters.bulkrax.rb +8 -0
  55. data/.dassie/db/migrate/20250826142455_add_invalid_record_to_importer_run.bulkrax.rb +6 -0
  56. data/.dassie/db/migrate/20250826142456_create_bulkrax_statuses.bulkrax.rb +19 -0
  57. data/.dassie/db/migrate/20250826142457_move_to_statuses.bulkrax.rb +31 -0
  58. data/.dassie/db/migrate/20250826142458_add_date_filter_and_status_to_bulkrax_exporters.bulkrax.rb +8 -0
  59. data/.dassie/db/migrate/20250826142459_add_workflow_status_to_bulkrax_exporter.bulkrax.rb +6 -0
  60. data/.dassie/db/migrate/20250826142460_remove_unused_last_error.bulkrax.rb +8 -0
  61. data/.dassie/db/migrate/20250826142461_increase_text_sizes.bulkrax.rb +13 -0
  62. data/.dassie/db/migrate/20250826142462_change_bulkrax_statuses_error_message_column_type_to_text.bulkrax.rb +6 -0
  63. data/.dassie/db/migrate/20250826142463_rename_children_counters_to_relationships.bulkrax.rb +7 -0
  64. data/.dassie/db/migrate/20250826142464_add_file_set_counters_to_importer_runs.bulkrax.rb +8 -0
  65. data/.dassie/db/migrate/20250826142465_add_import_attempts_to_entries.bulkrax.rb +6 -0
  66. data/.dassie/db/migrate/20250826142466_add_work_counters_to_importer_runs.bulkrax.rb +7 -0
  67. data/.dassie/db/migrate/20250826142467_create_bulkrax_pending_relationships.bulkrax.rb +14 -0
  68. data/.dassie/db/migrate/20250826142468_add_order_to_bulkrax_pending_relationships.bulkrax.rb +6 -0
  69. data/.dassie/db/migrate/20250826142469_add_include_thumbnails_to_bulkrax_exporters.bulkrax.rb +6 -0
  70. data/.dassie/db/migrate/20250826142470_add_generated_metadata_to_bulkrax_exporters.bulkrax.rb +6 -0
  71. data/.dassie/db/migrate/20250826142471_rename_bulkrax_importer_run_to_importer_run.bulkrax.rb +18 -0
  72. data/.dassie/db/migrate/20250826142472_add_indices_to_bulkrax.bulkrax.rb +26 -0
  73. data/.dassie/db/migrate/20250826142473_denormalize_status_message.bulkrax.rb +8 -0
  74. data/.dassie/db/migrate/20250826142474_update_identifier_index.bulkrax.rb +7 -0
  75. data/.dassie/db/migrate/20250826142475_add_index_to_metadata_bulkrax_identifier.bulkrax.rb +19 -0
  76. data/.dassie/db/migrate/20250826142476_add_file_name_to_uploaded_files.bulkrax.rb +6 -0
  77. data/.dassie/db/migrate/20250826142477_add_error_tracking_to_pending_relationships.bulkrax.rb +6 -0
  78. data/.dassie/db/migrate/20250826142478_add_last_imported_at_to_bulkrax_importers.bulkrax.rb +6 -0
  79. data/.dassie/db/migrate/20250826142479_add_next_import_at_to_bulkrax_importers.bulkrax.rb +6 -0
  80. data/.dassie/db/migrate/20250826142480_entry_error_denormalization.bulkrax.rb +8 -0
  81. data/.dassie/db/migrate/20250826142481_faster_first_entry.bulkrax.rb +8 -0
  82. data/.dassie/db/migrate/20260430000001_create_hyrax_redirect_paths.hyrax.rb +13 -0
  83. data/.dassie/db/migrate/20260521003627_add_additional_redirect_info.hyrax.rb +32 -0
  84. data/.dassie/db/schema.rb +154 -1
  85. data/.dassie/package.json +1 -1
  86. data/.dassie/rspec_split_configuration.json +835 -0
  87. data/.dassie/solr/conf/solrconfig.xml +21 -5
  88. data/.github/{ISSUE_TEMPLATE.md → ISSUE_TEMPLATE/issue_template.md} +5 -0
  89. data/.github/workflows/lint-build-test.yml +5 -1
  90. data/.github/workflows/release-charts.yml +48 -0
  91. data/.gitignore +6 -0
  92. data/.koppie/.env +5 -1
  93. data/.koppie/Gemfile +3 -1
  94. data/.koppie/app/assets/images/bulkrax/removed.png +0 -0
  95. data/.koppie/app/assets/javascripts/application.js +2 -0
  96. data/.koppie/app/assets/stylesheets/application.css +1 -0
  97. data/.koppie/app/controllers/catalog_controller.rb +1 -1
  98. data/.koppie/app/forms/collection_resource_form.rb +5 -2
  99. data/.koppie/app/forms/file_set_form.rb +5 -0
  100. data/.koppie/app/forms/generic_work_form.rb +5 -3
  101. data/.koppie/app/forms/monograph_form.rb +5 -3
  102. data/.koppie/app/indexers/collection_resource_indexer.rb +5 -2
  103. data/.koppie/app/indexers/file_set_indexer.rb +7 -0
  104. data/.koppie/app/indexers/generic_work_indexer.rb +6 -3
  105. data/.koppie/app/indexers/monograph_indexer.rb +6 -2
  106. data/.koppie/app/models/ability.rb +8 -0
  107. data/.koppie/app/models/collection_resource.rb +4 -2
  108. data/.koppie/app/models/concerns/bulkrax/has_local_processing.rb +8 -0
  109. data/.koppie/app/models/generic_work.rb +5 -2
  110. data/.koppie/app/models/monograph.rb +5 -2
  111. data/.koppie/app/views/blacklight_dynamic_sitemap/sitemap/show.xml.builder +22 -0
  112. data/.koppie/bin/importer +142 -0
  113. data/.koppie/bin/web +4 -0
  114. data/.koppie/config/browse_everything_providers.yml +2 -0
  115. data/.koppie/config/bulkrax_api.yml +86 -0
  116. data/.koppie/config/initializers/bulkrax.rb +92 -0
  117. data/.koppie/config/initializers/clamav.rb +2 -0
  118. data/.koppie/config/initializers/hyrax.rb +11 -3
  119. data/.koppie/config/initializers/riiif.rb +4 -82
  120. data/.koppie/config/metadata_profiles/m3_profile.yaml +930 -0
  121. data/.koppie/config/routes.rb +7 -0
  122. data/.koppie/config/sidekiq.yml +2 -0
  123. data/.koppie/config/uv/uv.html +63 -81
  124. data/.koppie/db/migrate/20240606205215_create_hyrax_flexible_schemas.rb +9 -0
  125. data/.koppie/db/migrate/20240606205216_add_contexts_to_hyrax_flexible_schemas.rb +5 -0
  126. data/.koppie/db/migrate/20250826142438_create_bulkrax_importers.bulkrax.rb +19 -0
  127. data/.koppie/db/migrate/20250826142439_create_bulkrax_importer_runs.bulkrax.rb +17 -0
  128. data/.koppie/db/migrate/20250826142440_create_bulkrax_entries.bulkrax.rb +17 -0
  129. data/.koppie/db/migrate/20250826142441_add_status_to_entry.bulkrax.rb +10 -0
  130. data/.koppie/db/migrate/20250826142442_add_collections_to_importer_runs.bulkrax.rb +7 -0
  131. data/.koppie/db/migrate/20250826142443_change_collection_ids_on_entries.bulkrax.rb +6 -0
  132. data/.koppie/db/migrate/20250826142444_create_bulkrax_exporters.bulkrax.rb +20 -0
  133. data/.koppie/db/migrate/20250826142445_create_bulkrax_exporter_runs.bulkrax.rb +15 -0
  134. data/.koppie/db/migrate/20250826142446_change_importer_and_exporter_to_polymorphic.bulkrax.rb +19 -0
  135. data/.koppie/db/migrate/20250826142447_add_total_collection_records_to_importer_runs.bulkrax.rb +6 -0
  136. data/.koppie/db/migrate/20250826142448_add_children_to_importer_runs.bulkrax.rb +7 -0
  137. data/.koppie/db/migrate/20250826142449_change_total_records_to_total_work_entries.bulkrax.rb +7 -0
  138. data/.koppie/db/migrate/20250826142450_change_entry_last_error.bulkrax.rb +20 -0
  139. data/.koppie/db/migrate/20250826142451_add_validate_only_to_bulkrax_importers.bulkrax.rb +6 -0
  140. data/.koppie/db/migrate/20250826142452_add_status_to_importers.bulkrax.rb +10 -0
  141. data/.koppie/db/migrate/20250826142453_remove_foreign_key_from_bulkrax_entries.bulkrax.rb +6 -0
  142. data/.koppie/db/migrate/20250826142454_add_status_to_exporters.bulkrax.rb +8 -0
  143. data/.koppie/db/migrate/20250826142455_add_invalid_record_to_importer_run.bulkrax.rb +6 -0
  144. data/.koppie/db/migrate/20250826142456_create_bulkrax_statuses.bulkrax.rb +19 -0
  145. data/.koppie/db/migrate/20250826142457_move_to_statuses.bulkrax.rb +31 -0
  146. data/.koppie/db/migrate/20250826142458_add_date_filter_and_status_to_bulkrax_exporters.bulkrax.rb +8 -0
  147. data/.koppie/db/migrate/20250826142459_add_workflow_status_to_bulkrax_exporter.bulkrax.rb +6 -0
  148. data/.koppie/db/migrate/20250826142460_remove_unused_last_error.bulkrax.rb +8 -0
  149. data/.koppie/db/migrate/20250826142461_increase_text_sizes.bulkrax.rb +13 -0
  150. data/.koppie/db/migrate/20250826142462_change_bulkrax_statuses_error_message_column_type_to_text.bulkrax.rb +6 -0
  151. data/.koppie/db/migrate/20250826142463_rename_children_counters_to_relationships.bulkrax.rb +7 -0
  152. data/.koppie/db/migrate/20250826142464_add_file_set_counters_to_importer_runs.bulkrax.rb +8 -0
  153. data/.koppie/db/migrate/20250826142465_add_import_attempts_to_entries.bulkrax.rb +6 -0
  154. data/.koppie/db/migrate/20250826142466_add_work_counters_to_importer_runs.bulkrax.rb +7 -0
  155. data/.koppie/db/migrate/20250826142467_create_bulkrax_pending_relationships.bulkrax.rb +14 -0
  156. data/.koppie/db/migrate/20250826142468_add_order_to_bulkrax_pending_relationships.bulkrax.rb +6 -0
  157. data/.koppie/db/migrate/20250826142469_add_include_thumbnails_to_bulkrax_exporters.bulkrax.rb +6 -0
  158. data/.koppie/db/migrate/20250826142470_add_generated_metadata_to_bulkrax_exporters.bulkrax.rb +6 -0
  159. data/.koppie/db/migrate/20250826142471_rename_bulkrax_importer_run_to_importer_run.bulkrax.rb +18 -0
  160. data/.koppie/db/migrate/20250826142472_add_indices_to_bulkrax.bulkrax.rb +26 -0
  161. data/.koppie/db/migrate/20250826142473_denormalize_status_message.bulkrax.rb +8 -0
  162. data/.koppie/db/migrate/20250826142474_update_identifier_index.bulkrax.rb +7 -0
  163. data/.koppie/db/migrate/20250826142475_add_index_to_metadata_bulkrax_identifier.bulkrax.rb +19 -0
  164. data/.koppie/db/migrate/20250826142476_add_file_name_to_uploaded_files.bulkrax.rb +6 -0
  165. data/.koppie/db/migrate/20250826142477_add_error_tracking_to_pending_relationships.bulkrax.rb +6 -0
  166. data/.koppie/db/migrate/20250826142478_add_last_imported_at_to_bulkrax_importers.bulkrax.rb +6 -0
  167. data/.koppie/db/migrate/20250826142479_add_next_import_at_to_bulkrax_importers.bulkrax.rb +6 -0
  168. data/.koppie/db/migrate/20250826142480_entry_error_denormalization.bulkrax.rb +8 -0
  169. data/.koppie/db/migrate/20250826142481_faster_first_entry.bulkrax.rb +8 -0
  170. data/.koppie/db/migrate/20260430000001_create_hyrax_redirect_paths.hyrax.rb +13 -0
  171. data/.koppie/db/migrate/20260521003627_add_additional_redirect_info.hyrax.rb +32 -0
  172. data/.koppie/db/schema.rb +154 -1
  173. data/.koppie/package.json +1 -1
  174. data/.koppie/rspec_split_configuration.json +835 -0
  175. data/.koppie/solr/conf/solrconfig.xml +0 -5
  176. data/.koppie/yarn.lock +5587 -752
  177. data/.rubocop.yml +1 -0
  178. data/CONTAINERS.md +2 -0
  179. data/Dockerfile +9 -22
  180. data/Gemfile +0 -1
  181. data/Gemfile.allinson +2 -0
  182. data/README.md +1 -2
  183. data/app/actors/hyrax/actors/file_actor.rb +2 -0
  184. data/app/actors/hyrax/actors/file_set_actor.rb +1 -1
  185. data/app/assets/javascripts/hyrax/app.js.erb +143 -10
  186. data/app/assets/javascripts/hyrax/collection_select.js +68 -0
  187. data/app/assets/javascripts/hyrax/copy_permalink_button.js +28 -0
  188. data/app/assets/javascripts/hyrax/redirects.js +45 -0
  189. data/app/assets/javascripts/hyrax/relationships/control.es6 +21 -1
  190. data/app/assets/javascripts/hyrax/select_work_type.es6 +7 -2
  191. data/app/assets/javascripts/hyrax.js +3 -1
  192. data/app/assets/stylesheets/hyrax/_file-show.scss +1 -0
  193. data/app/assets/stylesheets/hyrax/_viewer.scss +8 -0
  194. data/app/assets/stylesheets/hyrax/dashboard.scss +22 -1
  195. data/app/assets/stylesheets/hyrax/sidebar.scss +98 -3
  196. data/app/authorities/qa/authorities/collections.rb +9 -9
  197. data/app/controllers/concerns/hyrax/analytics_error_handling.rb +97 -0
  198. data/app/controllers/concerns/hyrax/collections_controller_behavior.rb +3 -1
  199. data/app/controllers/concerns/hyrax/ensure_migrated_behavior.rb +50 -0
  200. data/app/controllers/concerns/hyrax/flexible_catalog_behavior.rb +206 -0
  201. data/app/controllers/concerns/hyrax/flexible_schema_behavior.rb +17 -0
  202. data/app/controllers/concerns/hyrax/redirect_to_display_url.rb +54 -0
  203. data/app/controllers/concerns/hyrax/works_controller_behavior.rb +52 -10
  204. data/app/controllers/hyrax/admin/admin_sets_controller.rb +21 -13
  205. data/app/controllers/hyrax/admin/analytics/collection_reports_controller.rb +2 -0
  206. data/app/controllers/hyrax/admin/analytics/work_reports_controller.rb +1 -0
  207. data/app/controllers/hyrax/admin/metadata_profiles_controller.rb +66 -0
  208. data/app/controllers/hyrax/batch_uploads_controller.rb +2 -0
  209. data/app/controllers/hyrax/dashboard/collections_controller.rb +27 -6
  210. data/app/controllers/hyrax/dashboard/nest_collections_controller.rb +2 -1
  211. data/app/controllers/hyrax/dashboard_controller.rb +2 -1
  212. data/app/controllers/hyrax/file_sets_controller.rb +19 -1
  213. data/app/controllers/hyrax/homepage_controller.rb +7 -0
  214. data/app/controllers/hyrax/my/works_controller.rb +2 -21
  215. data/app/controllers/hyrax/permissions_controller.rb +4 -2
  216. data/app/controllers/hyrax/redirects_controller.rb +56 -0
  217. data/app/controllers/hyrax/single_use_links_viewer_controller.rb +2 -1
  218. data/app/controllers/hyrax/transcripts_controller.rb +31 -0
  219. data/app/controllers/hyrax/uploads_controller.rb +18 -9
  220. data/app/forms/concerns/hyrax/based_near_field_behavior.rb +77 -0
  221. data/app/forms/concerns/hyrax/flexible_form_behavior.rb +37 -0
  222. data/app/forms/concerns/hyrax/redirects_field_behavior.rb +95 -0
  223. data/app/forms/concerns/hyrax/transcripts_behavior.rb +40 -0
  224. data/app/forms/hyrax/forms/admin_set_form.rb +1 -1
  225. data/app/forms/hyrax/forms/administrative_set_form.rb +3 -0
  226. data/app/forms/hyrax/forms/batch_edit_form.rb +1 -3
  227. data/app/forms/hyrax/forms/batch_upload_form.rb +14 -0
  228. data/app/forms/hyrax/forms/file_set_edit_form.rb +1 -1
  229. data/app/forms/hyrax/forms/file_set_form.rb +6 -7
  230. data/app/forms/hyrax/forms/pcdm_collection_form.rb +17 -5
  231. data/app/forms/hyrax/forms/pcdm_object_form.rb +11 -3
  232. data/app/forms/hyrax/forms/resource_batch_edit_form.rb +8 -22
  233. data/app/forms/hyrax/forms/resource_form.rb +110 -10
  234. data/app/forms/hyrax/forms.rb +1 -0
  235. data/app/helpers/hyrax/attributes_helper.rb +109 -0
  236. data/app/helpers/hyrax/blacklight_override.rb +26 -0
  237. data/app/helpers/hyrax/collections_helper.rb +2 -0
  238. data/app/helpers/hyrax/file_set_form_helper.rb +24 -0
  239. data/app/helpers/hyrax/hyrax_helper_behavior.rb +51 -2
  240. data/app/helpers/hyrax/iiif_helper.rb +11 -1
  241. data/app/helpers/hyrax/permalink_helper.rb +37 -0
  242. data/app/helpers/hyrax/redirects_tab_helper.rb +29 -0
  243. data/app/helpers/hyrax/work_form_helper.rb +9 -5
  244. data/app/helpers/hyrax/works_helper.rb +14 -0
  245. data/app/indexers/concerns/hyrax/workflow_indexer.rb +30 -0
  246. data/app/indexers/hyrax/file_set_indexer.rb +1 -0
  247. data/app/indexers/hyrax/indexers/administrative_set_indexer.rb +2 -1
  248. data/app/indexers/hyrax/indexers/file_set_indexer.rb +6 -2
  249. data/app/indexers/hyrax/indexers/pcdm_collection_indexer.rb +11 -1
  250. data/app/indexers/hyrax/indexers/pcdm_object_indexer.rb +25 -4
  251. data/app/indexers/hyrax/indexers/redirects_indexer.rb +29 -0
  252. data/app/indexers/hyrax/indexers/resource_indexer.rb +4 -0
  253. data/app/indexers/hyrax/indexers.rb +2 -0
  254. data/app/inputs/controlled_vocabulary_input.rb +7 -1
  255. data/app/jobs/content_deposit_error_event_job.rb +14 -0
  256. data/app/jobs/create_work_job.rb +2 -0
  257. data/app/jobs/migrate_files_to_valkyrie_job.rb +7 -1
  258. data/app/models/batch_upload_item.rb +4 -0
  259. data/app/models/concerns/hyrax/ability/flexible_metadata_ability.rb +10 -0
  260. data/app/models/concerns/hyrax/ability.rb +3 -1
  261. data/app/models/concerns/hyrax/collection_behavior.rb +4 -0
  262. data/app/models/concerns/hyrax/file_set/transcripts.rb +16 -0
  263. data/app/models/concerns/hyrax/file_set_behavior.rb +9 -0
  264. data/app/models/concerns/hyrax/flexibility.rb +107 -0
  265. data/app/models/concerns/hyrax/redirects_normalization.rb +38 -0
  266. data/app/models/concerns/hyrax/solr_document/characterization.rb +1 -1
  267. data/app/models/concerns/hyrax/solr_document/metadata.rb +9 -0
  268. data/app/models/concerns/hyrax/solr_document/ordered_members.rb +4 -0
  269. data/app/models/concerns/hyrax/solr_document_behavior.rb +28 -6
  270. data/app/models/concerns/hyrax/suppressible.rb +1 -1
  271. data/app/models/concerns/hyrax/valkyrie_lazy_migration.rb +1 -1
  272. data/app/models/concerns/hyrax/with_embargoes_and_leases.rb +30 -0
  273. data/app/models/concerns/hyrax/work_behavior.rb +8 -0
  274. data/app/models/hyrax/administrative_set.rb +1 -1
  275. data/app/models/hyrax/change_set.rb +3 -1
  276. data/app/models/hyrax/embargo.rb +3 -1
  277. data/app/models/hyrax/file_set.rb +4 -2
  278. data/app/models/hyrax/flexible_schema.rb +219 -0
  279. data/app/models/hyrax/lease.rb +3 -1
  280. data/app/models/hyrax/pcdm_collection.rb +4 -1
  281. data/app/models/hyrax/redirect.rb +64 -0
  282. data/app/models/hyrax/redirect_path.rb +14 -0
  283. data/app/models/hyrax/resource.rb +22 -25
  284. data/app/models/hyrax/riiif/file.rb +43 -0
  285. data/app/models/hyrax/riiif/file_resolver.rb +65 -0
  286. data/app/models/hyrax/uploaded_file.rb +9 -0
  287. data/app/models/hyrax/virus_scanner.rb +16 -3
  288. data/app/models/hyrax/work.rb +4 -1
  289. data/app/models/job_io_wrapper.rb +12 -3
  290. data/app/models/user_mailbox.rb +3 -1
  291. data/app/presenters/hyrax/admin_set_presenter.rb +11 -0
  292. data/app/presenters/hyrax/annotates_content.rb +35 -0
  293. data/app/presenters/hyrax/collection_presenter.rb +5 -1
  294. data/app/presenters/hyrax/displays_content.rb +133 -0
  295. data/app/presenters/hyrax/displays_image.rb +11 -4
  296. data/app/presenters/hyrax/displays_transcripts.rb +59 -0
  297. data/app/presenters/hyrax/file_set_presenter.rb +8 -18
  298. data/app/presenters/hyrax/google_scholar_presenter.rb +39 -0
  299. data/app/presenters/hyrax/iiif_manifest_presenter.rb +22 -4
  300. data/app/presenters/hyrax/missing_method_behavior.rb +15 -0
  301. data/app/presenters/hyrax/pcdm_member_presenter_factory.rb +2 -4
  302. data/app/presenters/hyrax/work_show_presenter.rb +56 -14
  303. data/app/renderers/hyrax/renderers/attribute_renderer.rb +6 -2
  304. data/app/renderers/hyrax/renderers/license_attribute_renderer.rb +10 -10
  305. data/app/renderers/hyrax/renderers/redirects_label_attribute_renderer.rb +25 -0
  306. data/app/renderers/hyrax/renderers/rights_statement_attribute_renderer.rb +10 -11
  307. data/app/search_builders/hyrax/dashboard/nested_collections_search_builder.rb +1 -5
  308. data/app/search_builders/hyrax/filter_by_type.rb +1 -1
  309. data/app/services/hyrax/action/create_valkyrie_work.rb +3 -2
  310. data/app/services/hyrax/authority_rendering_helper.rb +28 -0
  311. data/app/services/hyrax/authority_service.rb +107 -0
  312. data/app/services/hyrax/callbacks.rb +2 -2
  313. data/app/services/hyrax/collections/nested_collection_query_service.rb +9 -3
  314. data/app/services/hyrax/collections_service.rb +17 -0
  315. data/app/services/hyrax/custom_queries/find_by_property_value.rb +89 -0
  316. data/app/services/hyrax/edit_permissions_service.rb +4 -4
  317. data/app/services/hyrax/file_set_type_service.rb +12 -4
  318. data/app/services/hyrax/flexible_schema_validator_service.rb +153 -0
  319. data/app/services/hyrax/flexible_schema_validators/class_validator.rb +123 -0
  320. data/app/services/hyrax/flexible_schema_validators/core_metadata_validator.rb +211 -0
  321. data/app/services/hyrax/flexible_schema_validators/existing_records_validator.rb +83 -0
  322. data/app/services/hyrax/flexible_schema_validators/redirects_validator.rb +116 -0
  323. data/app/services/hyrax/flexible_schema_validators/schema_validator.rb +34 -0
  324. data/app/services/hyrax/flexible_schema_validators/sort_properties_validator.rb +54 -0
  325. data/app/services/hyrax/license_service.rb +1 -1
  326. data/app/services/hyrax/listeners/metadata_index_listener.rb +11 -0
  327. data/app/services/hyrax/m3_profile_editor.rb +32 -0
  328. data/app/services/hyrax/m3_schema_loader.rb +119 -0
  329. data/app/services/hyrax/manifest_builder_service.rb +18 -24
  330. data/app/services/hyrax/permalink_path.rb +25 -0
  331. data/app/services/hyrax/qa_select_service.rb +9 -5
  332. data/app/services/hyrax/redirect_path_normalizer.rb +58 -0
  333. data/app/services/hyrax/redirects_lookup.rb +59 -0
  334. data/app/services/hyrax/resource_permissions_visibility_propagator.rb +54 -0
  335. data/app/services/hyrax/resource_types_service.rb +3 -20
  336. data/app/services/hyrax/rights_statement_service.rb +1 -1
  337. data/app/services/hyrax/schema_loader.rb +205 -0
  338. data/app/services/hyrax/search_service.rb +1 -1
  339. data/app/services/hyrax/simple_schema_loader.rb +28 -127
  340. data/app/services/hyrax/solr_service.rb +39 -2
  341. data/app/services/hyrax/tolerant_select_service.rb +5 -3
  342. data/app/services/hyrax/valkyrie_upload.rb +2 -2
  343. data/app/services/hyrax/visibility_propagator.rb +4 -1
  344. data/app/services/hyrax/work_uploads_handler.rb +17 -9
  345. data/app/services/hyrax/workflow/workflow_factory.rb +5 -4
  346. data/app/services/migrate_resource_service.rb +7 -1
  347. data/app/uploaders/hyrax/uploaded_file_uploader.rb +2 -2
  348. data/app/utils/hyrax/data_destroyers/collection_branding_destroyer.rb +1 -1
  349. data/app/utils/hyrax/required_data_seeder.rb +1 -0
  350. data/app/utils/hyrax/required_data_seeders/collection_type_seeder.rb +1 -1
  351. data/app/utils/hyrax/required_data_seeders/flexible_profile_seeder.rb +28 -0
  352. data/app/validators/hyrax/controlled_vocabulary_validator.rb +64 -0
  353. data/app/validators/hyrax/redirect_validator.rb +128 -0
  354. data/app/views/catalog/_thumbnail_list_default.html.erb +2 -2
  355. data/app/views/collections/edit_fields/_schema_version.html.erb +1 -0
  356. data/app/views/hyrax/admin/admin_sets/_form_metadata.html.erb +24 -2
  357. data/app/views/hyrax/admin/admin_sets/_show_document_list_row.html.erb +1 -1
  358. data/app/views/hyrax/admin/admin_sets/show.html.erb +1 -1
  359. data/app/views/hyrax/admin/analytics/_analytics_error.html.erb +42 -0
  360. data/app/views/hyrax/admin/analytics/collection_reports/index.html.erb +11 -9
  361. data/app/views/hyrax/admin/analytics/work_reports/index.html.erb +60 -57
  362. data/app/views/hyrax/admin/features/index.html.erb +1 -1
  363. data/app/views/hyrax/admin/metadata_profiles/_import_modal.html.erb +20 -0
  364. data/app/views/hyrax/admin/metadata_profiles/index.html.erb +54 -0
  365. data/app/views/hyrax/base/_attribute_rows.html.erb +4 -21
  366. data/app/views/hyrax/base/_citations.html.erb +1 -0
  367. data/app/views/hyrax/base/_currently_shared.html.erb +2 -1
  368. data/app/views/hyrax/base/_file_manager_thumbnail.html.erb +1 -1
  369. data/app/views/hyrax/base/_form_member_of_collections.html.erb +6 -6
  370. data/app/views/hyrax/base/_form_redirects.html.erb +125 -0
  371. data/app/views/hyrax/base/_form_share.html.erb +7 -2
  372. data/app/views/hyrax/base/_member.html.erb +1 -1
  373. data/app/views/hyrax/base/_social_media.html.erb +1 -1
  374. data/app/views/hyrax/base/iiif_viewers/_universal_viewer.html.erb +1 -1
  375. data/app/views/hyrax/base/show.html.erb +3 -0
  376. data/app/views/hyrax/collections/_default_group.html.erb +3 -0
  377. data/app/views/hyrax/collections/_list_collections.html.erb +3 -0
  378. data/app/views/hyrax/collections/_list_works.html.erb +1 -1
  379. data/app/views/hyrax/collections/_show_document_list_row.html.erb +1 -1
  380. data/app/views/hyrax/collections/show.html.erb +4 -0
  381. data/app/views/hyrax/dashboard/_sidebar.html.erb +4 -2
  382. data/app/views/hyrax/dashboard/collections/_button_for_update_collection.html.erb +2 -1
  383. data/app/views/hyrax/dashboard/collections/_default_group.html.erb +3 -0
  384. data/app/views/hyrax/dashboard/collections/_form.html.erb +17 -0
  385. data/app/views/hyrax/dashboard/collections/_form_for_select_collection.html.erb +7 -10
  386. data/app/views/hyrax/dashboard/collections/_list_collections.html.erb +3 -0
  387. data/app/views/hyrax/dashboard/collections/_list_works.html.erb +1 -1
  388. data/app/views/hyrax/dashboard/collections/_show_document_list_row.html.erb +1 -1
  389. data/app/views/hyrax/dashboard/profiles/_edit_primary.html.erb +1 -8
  390. data/app/views/hyrax/dashboard/profiles/_trophy_edit.html.erb +1 -1
  391. data/app/views/hyrax/dashboard/profiles/show.html.erb +5 -5
  392. data/app/views/hyrax/dashboard/sidebar/_metadata.html.erb +11 -0
  393. data/app/views/hyrax/dashboard/sidebar/_repository_content.html.erb +1 -0
  394. data/app/views/hyrax/dashboard/works/_default_group.html.erb +3 -0
  395. data/app/views/hyrax/dashboard/works/_list_works.html.erb +4 -2
  396. data/app/views/hyrax/file_sets/_form.html.erb +16 -3
  397. data/app/views/hyrax/file_sets/_metadata.html.erb +35 -0
  398. data/app/views/hyrax/file_sets/_valkyrie_form.html.erb +37 -0
  399. data/app/views/hyrax/file_sets/edit.html.erb +14 -8
  400. data/app/views/hyrax/file_sets/media_display/_audio.html.erb +4 -0
  401. data/app/views/hyrax/file_sets/media_display/_image.html.erb +2 -2
  402. data/app/views/hyrax/file_sets/media_display/_video.html.erb +4 -0
  403. data/app/views/hyrax/homepage/_explore_collections.html.erb +1 -1
  404. data/app/views/hyrax/homepage/_featured_fields.html.erb +1 -1
  405. data/app/views/hyrax/homepage/_recent_document.html.erb +1 -1
  406. data/app/views/hyrax/homepage/robots.text.erb +17 -0
  407. data/app/views/hyrax/my/collections/_default_group.html.erb +3 -0
  408. data/app/views/hyrax/my/collections/_list_collections.html.erb +3 -0
  409. data/app/views/hyrax/my/collections/_modal_add_subcollection.html.erb +2 -2
  410. data/app/views/hyrax/my/collections/_modal_add_to_collection.html.erb +2 -2
  411. data/app/views/hyrax/my/works/_default_group.html.erb +3 -0
  412. data/app/views/hyrax/my/works/_list_works.html.erb +5 -2
  413. data/app/views/hyrax/shared/_copy_permalink.html.erb +10 -0
  414. data/app/views/hyrax/users/_user_info.html.erb +1 -6
  415. data/app/views/layouts/_head_tag_content.html.erb +2 -0
  416. data/app/views/layouts/hyrax/dashboard.html.erb +2 -1
  417. data/app/views/records/edit_fields/_contexts.html.erb +1 -0
  418. data/app/views/records/edit_fields/_license.html.erb +17 -5
  419. data/app/views/records/edit_fields/_resource_type.html.erb +16 -2
  420. data/app/views/records/edit_fields/_rights_statement.html.erb +17 -5
  421. data/app/views/records/edit_fields/_schema_version.html.erb +1 -0
  422. data/app/views/records/edit_fields/_transcript_ids.html.erb +18 -0
  423. data/app/views/records/show_fields/_rights.html.erb +1 -1
  424. data/app/views/shared/_citations.html.erb +46 -31
  425. data/app/views/shared/_schema_version.html.erb +38 -0
  426. data/bin/dev-entrypoint.sh +14 -4
  427. data/chart/hyrax/Chart.yaml +4 -4
  428. data/chart/hyrax/templates/ingress.yaml +3 -0
  429. data/chart/hyrax/values.yaml +10 -8
  430. data/config/features.rb +27 -0
  431. data/config/initializers/blacklight_dynamic_sitemap.rb +43 -0
  432. data/config/initializers/deprecation_patch.rb +27 -0
  433. data/config/initializers/hydra_works.rb +3 -0
  434. data/config/initializers/i18n_extensions.rb +197 -0
  435. data/config/initializers/storage_adapter_initializer.rb +15 -9
  436. data/config/locales/hyrax.de.yml +172 -54
  437. data/config/locales/hyrax.en.yml +183 -66
  438. data/config/locales/hyrax.es.yml +174 -49
  439. data/config/locales/hyrax.fr.yml +169 -45
  440. data/config/locales/hyrax.it.yml +172 -48
  441. data/config/locales/hyrax.pt-BR.yml +172 -54
  442. data/config/locales/hyrax.zh.yml +145 -21
  443. data/config/metadata/basic_metadata.yaml +66 -0
  444. data/config/metadata/batch_edit_metadata.yaml +119 -0
  445. data/config/metadata/file_set_metadata.yaml +19 -0
  446. data/config/metadata/redirects.yaml +15 -0
  447. data/config/metadata_profiles/m3_json_schema.json +487 -0
  448. data/config/metadata_profiles/m3_profile.yaml +954 -0
  449. data/config/routes.rb +9 -0
  450. data/docker-compose-allinson.yml +176 -0
  451. data/docker-compose-dassie.yml +14 -6
  452. data/docker-compose-koppie.yml +18 -10
  453. data/docker-compose-sirenia.yml +20 -10
  454. data/documentation/copy_permalink.md +29 -0
  455. data/documentation/developing-your-hyrax-based-app.md +2 -2
  456. data/documentation/file-ingest-characterization.md +763 -0
  457. data/documentation/flexible_metadata.md +113 -0
  458. data/documentation/forms/field_behaviors.md +297 -0
  459. data/documentation/redirects.md +428 -0
  460. data/hyrax.gemspec +7 -2
  461. data/lib/freyja/metadata_adapter.rb +1 -1
  462. data/lib/freyja/persister.rb +25 -1
  463. data/lib/generators/hyrax/collection_resource/collection_resource_generator.rb +2 -2
  464. data/lib/generators/hyrax/collection_resource/templates/collection.rb.erb +5 -1
  465. data/lib/generators/hyrax/collection_resource/templates/collection_form.rb.erb +5 -1
  466. data/lib/generators/hyrax/collection_resource/templates/collection_indexer.rb.erb +6 -1
  467. data/lib/generators/hyrax/iiif_viewer/USAGE +22 -0
  468. data/lib/generators/hyrax/iiif_viewer/iiif_viewer_generator.rb +49 -0
  469. data/lib/generators/hyrax/iiif_viewer/templates/_clover.html.erb +7 -0
  470. data/lib/generators/hyrax/iiif_viewer/templates/clover/clover.css +3328 -0
  471. data/lib/generators/hyrax/iiif_viewer/templates/clover/clover.html +16 -0
  472. data/lib/generators/hyrax/iiif_viewer/templates/clover/clover.js +237 -0
  473. data/lib/generators/hyrax/install_generator.rb +25 -1
  474. data/lib/generators/hyrax/riiif_generator.rb +1 -1
  475. data/lib/generators/hyrax/templates/app/views/blacklight_dynamic_sitemap/sitemap/show.xml.builder +22 -0
  476. data/lib/generators/hyrax/templates/config/clamav.rb +2 -0
  477. data/lib/generators/hyrax/templates/config/initializers/riiif.rb +4 -80
  478. data/lib/generators/hyrax/templates/db/migrate/20240606205215_create_hyrax_flexible_schemas.rb.erb +9 -0
  479. data/lib/generators/hyrax/templates/db/migrate/20240606205216_add_contexts_to_hyrax_flexible_schemas.rb.erb +5 -0
  480. data/lib/generators/hyrax/templates/db/migrate/20260430000001_create_hyrax_redirect_paths.rb.erb +22 -0
  481. data/lib/generators/hyrax/templates/db/migrate/20260521003627_add_additional_redirect_info.rb.erb +39 -0
  482. data/lib/generators/hyrax/templates/package.json +1 -1
  483. data/lib/generators/hyrax/templates/uv.html +60 -82
  484. data/lib/generators/hyrax/work/templates/indexer.rb.erb +1 -1
  485. data/lib/generators/hyrax/work/templates/model.rb.erb +5 -1
  486. data/lib/generators/hyrax/work_resource/templates/form.rb.erb +5 -2
  487. data/lib/generators/hyrax/work_resource/templates/indexer.rb.erb +6 -2
  488. data/lib/generators/hyrax/work_resource/templates/work.rb.erb +5 -2
  489. data/lib/generators/hyrax/work_resource/work_resource_generator.rb +1 -0
  490. data/lib/hyrax/active_fedora_dummy_model.rb +6 -0
  491. data/lib/hyrax/configuration.rb +197 -3
  492. data/lib/hyrax/deprecation.rb +31 -0
  493. data/lib/hyrax/engine.rb +2 -0
  494. data/lib/hyrax/form_fields.rb +17 -5
  495. data/lib/hyrax/indexer.rb +31 -2
  496. data/lib/hyrax/resource_sync/resource_list_writer.rb +3 -0
  497. data/lib/hyrax/schema.rb +15 -4
  498. data/lib/hyrax/specs/shared_specs/factories/administrative_sets.rb +12 -0
  499. data/lib/hyrax/specs/shared_specs/factories/hyrax_embargo.rb +1 -1
  500. data/lib/hyrax/specs/shared_specs/factories/hyrax_file_set.rb +13 -0
  501. data/lib/hyrax/specs/shared_specs/factories/hyrax_lease.rb +5 -1
  502. data/lib/hyrax/specs/shared_specs/factories/hyrax_work.rb +14 -0
  503. data/lib/hyrax/specs/shared_specs/simple_work.rb +41 -0
  504. data/lib/hyrax/specs/shared_specs.rb +1 -0
  505. data/lib/hyrax/transactions/collection_create.rb +2 -1
  506. data/lib/hyrax/transactions/collection_destroy.rb +1 -0
  507. data/lib/hyrax/transactions/collection_update.rb +2 -1
  508. data/lib/hyrax/transactions/container.rb +28 -0
  509. data/lib/hyrax/transactions/steps/apply_permission_template_on_update.rb +65 -0
  510. data/lib/hyrax/transactions/steps/apply_workflow_on_admin_set_change.rb +97 -0
  511. data/lib/hyrax/transactions/steps/ensure_admin_set.rb +1 -1
  512. data/lib/hyrax/transactions/steps/remove_redirect_paths.rb +35 -0
  513. data/lib/hyrax/transactions/steps/save.rb +10 -0
  514. data/lib/hyrax/transactions/steps/set_default_admin_set.rb +1 -1
  515. data/lib/hyrax/transactions/steps/sync_redirect_paths.rb +141 -0
  516. data/lib/hyrax/transactions/work_create.rb +2 -1
  517. data/lib/hyrax/transactions/work_destroy.rb +1 -0
  518. data/lib/hyrax/transactions/work_update.rb +4 -1
  519. data/lib/hyrax/version.rb +1 -1
  520. data/lib/hyrax.rb +61 -0
  521. data/lib/tasks/m3_profile_cleanup.rake +35 -0
  522. data/template.rb +4 -1
  523. metadata +313 -19
  524. data/.dassie/public/robots.txt +0 -1
  525. data/.koppie/public/robots.txt +0 -1
  526. data/app/forms/concerns/hyrax/basic_metadata_form_fields_behavior.rb +0 -39
  527. data/app/views/catalog/_thumbnail_list_collection.html.erb +0 -4
@@ -0,0 +1,205 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Hyrax
4
+ ##
5
+ # @api private
6
+ #
7
+ # This is a simple yaml config-driven schema loader
8
+ #
9
+ # @see config/metadata/basic_metadata.yaml for an example configuration
10
+ class SchemaLoader
11
+ class UndefinedSchemaError < ArgumentError; end
12
+
13
+ ##
14
+ # @param [Symbol] schema
15
+ #
16
+ # @return [Hash<Symbol, Dry::Types::Type>] a map from attribute names to
17
+ # types
18
+ def attributes_for(schema:, version: 1, contexts: nil)
19
+ definitions(schema, version, contexts).each_with_object({}) do |definition, hash|
20
+ hash[definition.name] = definition.type.meta(definition.config)
21
+ end
22
+ end
23
+
24
+ ##
25
+ # @param [Symbol] schema
26
+ #
27
+ # @return [Hash{Symbol => Hash{Symbol => Object}}]
28
+ def form_definitions_for(schema:, version: 1, contexts: nil)
29
+ definitions(schema, version, contexts).each_with_object({}) do |definition, hash|
30
+ next if definition.form_options.empty?
31
+
32
+ hash[definition.name] = definition.form_options
33
+ end
34
+ end
35
+
36
+ ##
37
+ # @param [Symbol] schema
38
+ #
39
+ # @return [{Symbol => Symbol}] a map from index keys to attribute names
40
+ def index_rules_for(schema:, version: 1, contexts: nil)
41
+ definitions(schema, version, contexts).each_with_object({}) do |definition, hash|
42
+ definition.index_keys.each do |key|
43
+ hash[key] = definition.name
44
+ end
45
+ end
46
+ end
47
+
48
+ def current_version
49
+ 1
50
+ end
51
+
52
+ ##
53
+ # @api private
54
+ class AttributeDefinition
55
+ ##
56
+ # @!attr_reader :config
57
+ # @return [Hash<String, Object>]
58
+ # @!attr_reader :name
59
+ # @return [#to_sym]
60
+ attr_reader :config, :name
61
+
62
+ ##
63
+ # @param [#to_sym] name
64
+ # @param [Hash<String, Object>] config
65
+ def initialize(name, config)
66
+ @config = config
67
+ @name = (config['name'] || name).to_sym
68
+ end
69
+
70
+ ##
71
+ # @return [Hash{Symbol => Object}]
72
+ def form_options
73
+ config.fetch('form', {})&.symbolize_keys || {}
74
+ end
75
+
76
+ ##
77
+ # @return [Enumerable<Symbol>]
78
+ def index_keys
79
+ (config.fetch('indexing', nil) || config.fetch('index_keys', []))&.reject { |k| ['facetable', 'stored_searchable', 'admin_only', 'editor_only'].include?(k) }&.map(&:to_sym) || []
80
+ end
81
+
82
+ ##
83
+ # @return [Hash{Symbol => Object}]
84
+ def view_options
85
+ # prefer display_label over view:label for labels, make available in the view
86
+ @view_options = config.fetch('view', {})&.with_indifferent_access || {}
87
+ Deprecation.warn('view: label is deprecated, use display_label instead') if @view_options[:label].present?
88
+ @view_options.delete(:label)
89
+ @view_options[:display_label] = display_label
90
+ @view_options[:admin_only] = admin_only?
91
+ @view_options[:editor_only] = editor_only?
92
+ @view_options
93
+ end
94
+
95
+ def display_label
96
+ return @display_label if @display_label
97
+ @display_label = config.fetch('display_label', {})&.with_indifferent_access || {}
98
+ @display_label = { default: @display_label } if @display_label.is_a?(String)
99
+ @display_label
100
+ end
101
+
102
+ def admin_only?
103
+ @admin_only ||= config.fetch('admin_only', false) || config['indexing']&.include?('admin_only')
104
+ end
105
+
106
+ def editor_only?
107
+ @editor_only ||= config.fetch('editor_only', false) || config['indexing']&.include?('editor_only')
108
+ end
109
+
110
+ ##
111
+ # @return [Dry::Types::Type]
112
+ def type
113
+ member_type = type_for(config['type'])
114
+ wrapper_type = multiple? ? Valkyrie::Types::Array.constructor(&Coerce) : Identity
115
+ wrapper_type.of(member_type)
116
+ end
117
+
118
+ # Cleans up the input before the type system sees it: drops the
119
+ # "no value provided" placeholder dry-types uses internally, then
120
+ # removes blanks. Wraps a bare Hash in a one-element array; using
121
+ # `Array(hash)` would surprise-flatten it into [[:k, v], ...] pairs.
122
+ # Valkyrie's JSONValueMapper unwraps single-element arrays on read,
123
+ # so the type sees a hash here when there was originally one entry.
124
+ Coerce = lambda do |value|
125
+ return [] if value.equal?(Dry::Types::Undefined)
126
+ wrapped = value.is_a?(::Hash) ? [value] : Array(value)
127
+ wrapped.reject { |v| v.equal?(Dry::Types::Undefined) }.select(&:present?)
128
+ end
129
+
130
+ # Determine whether this attribute allows multiple values.
131
+ def multiple?
132
+ return config['multiple'] if config.key?('multiple')
133
+ return true if config.key?('data_type') && config['data_type'] == 'array'
134
+ return false unless (card = config['cardinality'])
135
+
136
+ max = card['maximum']
137
+ max.nil? || max.to_i > 1
138
+ end
139
+
140
+ ##
141
+ # @api private
142
+ #
143
+ # Single-value wrapper that matches the multi-value branch's cleanup:
144
+ # strips the dry-types Undefined placeholder and coerces blank strings to nil.
145
+ # Booleans, numbers, and other types pass through unchanged.
146
+ #
147
+ # @example
148
+ # Identity.of(Valkyrie::Types::String) # => Valkyrie::Types::String with blank-string → nil coercion
149
+ class Identity
150
+ def self.of(type)
151
+ type.constructor do |value|
152
+ next nil if value.equal?(Dry::Types::Undefined)
153
+ value.is_a?(String) ? value.presence : value
154
+ end
155
+ end
156
+ end
157
+
158
+ private
159
+
160
+ ##
161
+ # Resolves a `type:` value from a schema YAML to a Dry::Types::Type.
162
+ #
163
+ # Recognized values:
164
+ # - `id`, `uri`, `date_time` — Valkyrie type shortcuts.
165
+ # - `hash` — for attributes whose entries carry multiple sub-fields
166
+ # (e.g. redirects, with path / canonical / sequence). Use this
167
+ # instead of nesting a Valkyrie::Resource. See
168
+ # `documentation/redirects.md` for a worked example.
169
+ # - Any primitive Valkyrie type, looked up under `Valkyrie::Types::*`
170
+ # by classified name (e.g. `string` → `Valkyrie::Types::String`).
171
+ #
172
+ # Raises `ArgumentError` if nothing matches.
173
+ #
174
+ # @param [String]
175
+ # @return [Dry::Types::Type]
176
+ def type_for(type)
177
+ case type
178
+ when 'id'
179
+ Valkyrie::Types::ID
180
+ when 'uri'
181
+ Valkyrie::Types::URI
182
+ when 'date_time'
183
+ Valkyrie::Types::DateTime
184
+ when 'hash'
185
+ Dry::Types['hash']
186
+ else
187
+ "Valkyrie::Types::#{type.classify}".safe_constantize ||
188
+ raise(ArgumentError, "Unrecognized type: #{type}")
189
+ end
190
+ end
191
+ end
192
+
193
+ class UndefinedSchemaError < ArgumentError; end
194
+
195
+ private
196
+
197
+ def definitions(_schema_name, _version, _contexts)
198
+ raise NotImplementedError, 'Implement #definitions in a child class'
199
+ end
200
+
201
+ def config_search_paths
202
+ Hyrax.config.schema_loader_config_search_paths
203
+ end
204
+ end
205
+ end
@@ -10,7 +10,7 @@ module Hyrax
10
10
 
11
11
  def method_missing(method_name, *arguments, &block)
12
12
  if scope&.respond_to?(method_name)
13
- Deprecation.warn(self.class, "Calling `#{method_name}` on scope " \
13
+ Deprecation.warn("Calling `#{method_name}` on scope " \
14
14
  'is deprecated and will be removed in Blacklight 8. Call #to_h first if you ' \
15
15
  ' need to use hash methods (or, preferably, use your own SearchState implementation)')
16
16
  scope&.public_send(method_name, *arguments, &block)
@@ -7,41 +7,13 @@ module Hyrax
7
7
  # This is a simple yaml config-driven schema loader
8
8
  #
9
9
  # @see config/metadata/basic_metadata.yaml for an example configuration
10
- class SimpleSchemaLoader
11
- ##
12
- # @param [Symbol] schema
13
- #
14
- # @return [Hash<Symbol, Dry::Types::Type>] a map from attribute names to
15
- # types
16
- def attributes_for(schema:)
17
- definitions(schema).each_with_object({}) do |definition, hash|
18
- hash[definition.name] = definition.type.meta(definition.config)
19
- end
20
- end
21
-
22
- ##
23
- # @param [Symbol] schema
24
- #
25
- # @return [Hash{Symbol => Hash{Symbol => Object}}]
26
- def form_definitions_for(schema:)
27
- definitions(schema).each_with_object({}) do |definition, hash|
28
- next if definition.form_options.empty?
29
-
30
- hash[definition.name] = definition.form_options
31
- end
32
- end
33
-
34
- ##
35
- # @param [Symbol] schema
36
- #
37
- # @return [{Symbol => Symbol}] a map from index keys to attribute names
38
- def index_rules_for(schema:)
39
- definitions(schema).each_with_object({}) do |definition, hash|
40
- definition.index_keys.each do |key|
41
- hash[key] = definition.name
42
- end
10
+ class SimpleSchemaLoader < Hyrax::SchemaLoader
11
+ def view_definitions_for(schema:, version: 1, contexts: nil) # rubocop:disable Lint/UnusedMethodArgument
12
+ schema.each_with_object({}) do |property, metadata|
13
+ view_options = property.meta['view']
14
+ metadata[property.name.to_s] = view_options.with_indifferent_access unless view_options.nil?
43
15
  end
44
- end
16
+ end # rubocop:enable Lint/UnusedMethodArgument
45
17
 
46
18
  def permissive_schema_for_valkrie_adapter
47
19
  metadata_files.each_with_object({}) do |schema_name, ret_hsh|
@@ -49,95 +21,12 @@ module Hyrax
49
21
  end
50
22
  end
51
23
 
52
- ##
53
- # @api private
54
- class AttributeDefinition
55
- ##
56
- # @!attr_reader :config
57
- # @return [Hash<String, Object>]
58
- # @!attr_reader :name
59
- # @return [#to_sym]
60
- attr_reader :config, :name
61
-
62
- ##
63
- # @param [#to_sym] name
64
- # @param [Hash<String, Object>] config
65
- def initialize(name, config)
66
- @config = config
67
- @name = name.to_sym
68
- end
69
-
70
- ##
71
- # @return [Hash{Symbol => Object}]
72
- def form_options
73
- config.fetch('form', {}).symbolize_keys
74
- end
75
-
76
- ##
77
- # @return [Enumerable<Symbol>]
78
- def index_keys
79
- config.fetch('index_keys', []).map(&:to_sym)
80
- end
81
-
82
- ##
83
- # @return [Dry::Types::Type]
84
- def type
85
- collection_type = if config['multiple']
86
- Valkyrie::Types::Array.constructor { |v| Array(v).select(&:present?) }
87
- else
88
- Identity
89
- end
90
- collection_type.of(type_for(config['type']))
91
- end
92
-
93
- ##
94
- # @api private
95
- #
96
- # This class acts as a Valkyrie/Dry::Types collection with typed members,
97
- # but instead of wrapping the given type with itself as the collection type
98
- # (as in `Valkyrie::Types::Array.of(MyType)`), it returns the given type.
99
- #
100
- # @example
101
- # Identity.of(Valkyrie::Types::String) # => Valkyrie::Types::String
102
- #
103
- class Identity
104
- ##
105
- # @param [Dry::Types::Type]
106
- # @return [Dry::Types::Type] the type passed in
107
- def self.of(type)
108
- type
109
- end
110
- end
111
-
112
- private
113
-
114
- ##
115
- # Maps a configuration string value to a `Valkyrie::Type`.
116
- #
117
- # @param [String]
118
- # @return [Dry::Types::Type]
119
- def type_for(type)
120
- case type
121
- when 'id'
122
- Valkyrie::Types::ID
123
- when 'uri'
124
- Valkyrie::Types::URI
125
- when 'date_time'
126
- Valkyrie::Types::DateTime
127
- else
128
- "Valkyrie::Types::#{type.capitalize}".constantize
129
- end
130
- end
131
- end
132
-
133
- class UndefinedSchemaError < ArgumentError; end
134
-
135
24
  private
136
25
 
137
26
  ##
138
27
  # @param [#to_s] schema_name
139
28
  # @return [Enumerable<AttributeDefinition]
140
- def definitions(schema_name)
29
+ def definitions(schema_name, _version, _contexts = nil)
141
30
  schema_config(schema_name)['attributes'].map do |name, config|
142
31
  AttributeDefinition.new(name, config)
143
32
  end
@@ -147,6 +36,8 @@ module Hyrax
147
36
  # @param [#to_s] schema_name
148
37
  # @return [Hash]
149
38
  def schema_config(schema_name)
39
+ raise(UndefinedSchemaError, "No schema defined: #{schema_name}") if disabled_schemas.include?(schema_name.to_s)
40
+
150
41
  schema_config_path = config_paths(schema_name).find { |path| File.exist? path }
151
42
  raise(UndefinedSchemaError, "No schema defined: #{schema_name}") unless schema_config_path
152
43
 
@@ -157,14 +48,24 @@ module Hyrax
157
48
  config_search_paths.collect { |root_path| root_path.to_s + "/config/metadata/#{schema_name}.yaml" }
158
49
  end
159
50
 
160
- def config_search_paths
161
- Hyrax.config.simple_schema_loader_config_search_paths
162
- end
163
-
164
51
  def metadata_files
165
52
  file_name_arr = []
166
53
  config_search_paths.each { |root_path| file_name_arr += Dir.entries(root_path.to_s + "/config/metadata/") }
167
- file_name_arr.reject { |fn| !fn.include?('.yaml') }.uniq.map { |y| y.gsub('.yaml', '') }
54
+ file_name_arr
55
+ .reject { |fn| !fn.include?('.yaml') }
56
+ .uniq
57
+ .map { |y| y.gsub('.yaml', '') }
58
+ .reject { |schema_name| disabled_schemas.include?(schema_name) }
59
+ end
60
+
61
+ # Names of schemas in `config/metadata/` that the host app has not
62
+ # opted in to. {#metadata_files} skips them and {#schema_config}
63
+ # raises `UndefinedSchemaError` for them, so they behave as if the
64
+ # YAML did not exist.
65
+ def disabled_schemas
66
+ disabled = []
67
+ disabled << 'redirects' unless Hyrax.config.redirects_enabled?
68
+ disabled
168
69
  end
169
70
 
170
71
  def predicate_pairs(ret_hsh, schema_name)
@@ -179,10 +80,10 @@ module Hyrax
179
80
  end
180
81
 
181
82
  def multiple_predicate_message(name, existing, incoming)
182
- message = "The attribute of #{name} has been assigned a predicate multiple times " \
183
- "within the metadata YAMLs. Please be aware that once the attribute's " \
184
- "predicate value is first assigned, any other value will be ignored. " \
185
- "The existing value is #{existing} preventing the use of #{incoming}"
83
+ message = "The attribute of #{name} has been assigned a predicate multiple times " \
84
+ "within the metadata YAMLs. Please be aware that once the attribute's " \
85
+ "predicate value is first assigned, any other value will be ignored. " \
86
+ "The existing value is #{existing} preventing the use of #{incoming}"
186
87
  Hyrax.logger.warn(message)
187
88
  end
188
89
  end
@@ -6,7 +6,7 @@ module Hyrax
6
6
  #
7
7
  # This class replaces `ActiveFedora::SolrService`, which is deprecated for
8
8
  # internal use.
9
- class SolrService
9
+ class SolrService # rubocop:disable Metrics/ClassLength
10
10
  COMMIT_PARAMS = { softCommit: true }.freeze
11
11
 
12
12
  ##
@@ -39,7 +39,7 @@ module Hyrax
39
39
 
40
40
  def instance
41
41
  # Deprecation warning for calling from outside of the Hyrax::SolrService class
42
- Deprecation.warn(self, rsolr_call_warning) unless caller[1].include?("#{self.class.name.underscore}.rb")
42
+ Deprecation.warn(rsolr_call_warning) unless caller[1].include?("#{self.class.name.underscore}.rb")
43
43
 
44
44
  @old_service.instance
45
45
  end
@@ -125,6 +125,43 @@ module Hyrax
125
125
  end
126
126
  end
127
127
 
128
+ # Pages through a caller-supplied Solr search, returning matching
129
+ # documents as a single Array. The block is yielded once with (0, 0) as
130
+ # a count probe, then once per page with (rows, start) where rows is
131
+ # bounded by Hyrax.config.solr_rows_per_request. Results are truncated
132
+ # to Hyrax.config.solr_max_results; when numFound exceeds the cap, a
133
+ # warning is logged and only the first solr_max_results docs are
134
+ # returned. The block must return a response object responding to
135
+ # +response['numFound']+ and +documents+.
136
+ #
137
+ # @return [Array] matching documents, truncated at solr_max_results
138
+ def self.fetch_all
139
+ rows_per_request = Hyrax.config.solr_rows_per_request
140
+ target = fetch_all_target(yield(0, 0))
141
+ return [] if target.zero?
142
+
143
+ documents = []
144
+ (0...target).step(rows_per_request) do |start|
145
+ rows = [rows_per_request, target - start].min
146
+ documents.concat(yield(rows, start).documents)
147
+ end
148
+ documents
149
+ end
150
+
151
+ # @api private
152
+ # Returns the number of documents to fetch, capped at
153
+ # Hyrax.config.solr_max_results. Logs a warning when truncating.
154
+ def self.fetch_all_target(count_response)
155
+ total = count_response.response['numFound'].to_i
156
+ max = Hyrax.config.solr_max_results
157
+ return total if total <= max
158
+
159
+ Hyrax.logger.warn("Hyrax::SolrService.fetch_all truncated: numFound=#{total} exceeds " \
160
+ "Hyrax.config.solr_max_results=#{max}. Returning the first #{max} documents.")
161
+ max
162
+ end
163
+ private_class_method :fetch_all_target
164
+
128
165
  # Wraps rsolr :delete_by_query
129
166
  def delete_by_query(query, **args)
130
167
  connection.delete_by_query(query, params: args)
@@ -6,10 +6,12 @@ module Hyrax
6
6
  class TolerantSelectService < QaSelectService
7
7
  ##
8
8
  # @return [Boolean] indicates whether the term is active;
9
- # false if the term is inactive or does not exist; defaults to true when
10
- # no key is given
9
+ # false if the term is inactive or is not present in the authority;
10
+ # defaults to true when the term is present but no `active:` key is given.
11
11
  def active?(id)
12
- authority.find(id)&.fetch('active', true)
12
+ result = authority.find(id)
13
+ return false if result.blank?
14
+ result.fetch('active', true)
13
15
  end
14
16
 
15
17
  ##
@@ -38,7 +38,7 @@ class Hyrax::ValkyrieUpload
38
38
  @file_set_file_service = file_set_file_service
39
39
  end
40
40
 
41
- def upload(filename:, file_set:, io:, use: Hyrax::FileMetadata::Use::ORIGINAL_FILE, user: nil, mime_type: nil, skip_derivatives: false) # rubocop:disable Metrics/AbcSize
41
+ def upload(filename:, file_set:, io:, use: Hyrax::FileMetadata::Use::ORIGINAL_FILE, user: nil, mime_type: nil, skip_derivatives: false) # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity
42
42
  return version_upload(file_set: file_set, io: io, user: user) if use == Hyrax::FileMetadata::Use::ORIGINAL_FILE && file_set.original_file_id && storage_adapter.supports?(:versions)
43
43
  streamfile = storage_adapter.upload(file: io, original_filename: filename, resource: file_set)
44
44
  file_metadata = Hyrax::FileMetadata(streamfile)
@@ -50,7 +50,7 @@ class Hyrax::ValkyrieUpload
50
50
 
51
51
  if use == Hyrax::FileMetadata::Use::ORIGINAL_FILE
52
52
  # Set file set label.
53
- reset_title = file_set.title.first == file_set.label
53
+ reset_title = (file_set.title.first == file_set.label || (file_set.title.blank? && file_set.label.blank?))
54
54
  # set title to label if that's how it was before this characterization
55
55
  file_set.title = file_metadata.original_filename if reset_title
56
56
  # always set the label to the original_name
@@ -13,7 +13,10 @@ module Hyrax
13
13
  when ActiveFedora::Base # ActiveFedora
14
14
  FileSetVisibilityPropagator.new(source: source)
15
15
  when Hyrax::Resource # Valkyrie
16
- ResourceVisibilityPropagator.new(source: source)
16
+ # Due to performance issues for a Hyrax::Resource in Fedora 6,
17
+ # use a job that copies both permissions and visibility instead
18
+ # of reloading and iterating again.
19
+ ResourcePermissionsVisibilityPropagator.new(source: source)
17
20
  else
18
21
  NullVisibilityPropagator.new(source: source)
19
22
  end
@@ -68,7 +68,9 @@ module Hyrax
68
68
  # (for job retries). this is for legacy/AttachFilesToWorkJob
69
69
  # compatibility, but could stand for a robust reimplementation.
70
70
  #
71
- # @param [Enumberable<Hyrax::UploadedFile>] files files to add
71
+ # @param [Enumerable<Hyrax::UploadedFile>] files files to add
72
+ #
73
+ # @param [Enumerable<Hash>] file_set_params additional parameters for each file_set
72
74
  #
73
75
  # @return [WorkFileSetManager] self
74
76
  # @raise [ArgumentError] if any of the uploaded files are not an
@@ -76,21 +78,24 @@ module Hyrax
76
78
  def add(files:, file_set_params: [])
77
79
  validate_files(files) &&
78
80
  @files = Array.wrap(files).reject { |f| f.file_set_uri.present? }
79
- @file_set_params = file_set_params
81
+ @file_set_params = file_set_params || []
80
82
  self
81
83
  end
82
84
 
83
85
  ##
84
86
  # @api public
85
87
  #
86
- # Create filesets for each added file
88
+ # Create filesets for each added file, and add some additional metadata passed in file_set_params
89
+ # Additional metadata will only be set if it is not overriden by the `file_set_args` hash, and if it is a valid part of the schema
87
90
  #
88
91
  # @return [Boolean] true if all requested files were attached
89
92
  def attach
90
93
  return true if Array.wrap(files).empty? # short circuit to avoid aquiring a lock we won't use
91
94
 
92
95
  acquire_lock_for(work.id) do
93
- event_payloads = files.each_with_object([]) { |file, arry| arry << make_file_set_and_ingest(file) }
96
+ event_payloads = files.each_with_object([]).with_index do |(file, arry), index|
97
+ arry << make_file_set_and_ingest(file, @file_set_params[index] || {})
98
+ end
94
99
  @persister.save(resource: work)
95
100
  Hyrax.publisher.publish('object.metadata.updated', object: work, user: files.first.user)
96
101
  event_payloads.each do |payload|
@@ -104,8 +109,8 @@ module Hyrax
104
109
 
105
110
  ##
106
111
  # @api private
107
- def make_file_set_and_ingest(file)
108
- file_set = @persister.save(resource: Hyrax::FileSet.new(file_set_args(file)))
112
+ def make_file_set_and_ingest(file, file_set_params = {})
113
+ file_set = @persister.save(resource: Hyrax.config.valkyrie_file_set_class.new(file_set_args(file, file_set_params)))
109
114
  Hyrax.publisher.publish('object.deposited', object: file_set, user: file.user)
110
115
  file.add_file_set!(file_set)
111
116
 
@@ -133,14 +138,17 @@ module Hyrax
133
138
  ##
134
139
  # @api private
135
140
  #
141
+ # @note the second hash overrides values in the first hash
142
+ #
143
+ #
136
144
  # @return [Hash{Symbol => Object}]
137
- def file_set_args(file)
145
+ def file_set_args(file, file_set_params = {})
138
146
  { depositor: file.user.user_key,
139
- creator: file.user.user_key,
147
+ creator: Array.wrap(file.user.user_key),
140
148
  date_uploaded: file.created_at,
141
149
  date_modified: Hyrax::TimeService.time_in_utc,
142
150
  label: file.uploader.filename,
143
- title: file.uploader.filename }
151
+ title: Array.wrap(file.uploader.filename) }.merge(file_set_params)
144
152
  end
145
153
 
146
154
  ##
@@ -50,9 +50,10 @@ module Hyrax
50
50
  private
51
51
 
52
52
  def create_workflow_entity!
53
- Sipity::Entity.find_or_create_by!(proxy_for_global_id: Hyrax::GlobalID(work).to_s,
54
- workflow: workflow_for(work),
55
- workflow_state: nil)
53
+ entity = Sipity::Entity.find_by(proxy_for_global_id: Hyrax::GlobalID(work).to_s)
54
+ entity || Sipity::Entity.create!(proxy_for_global_id: Hyrax::GlobalID(work).to_s,
55
+ workflow: workflow_for(work),
56
+ workflow_state: nil)
56
57
  end
57
58
 
58
59
  def assign_specific_roles_to(entity:)
@@ -63,7 +64,7 @@ module Hyrax
63
64
  end
64
65
 
65
66
  def run_workflow_action!(action:)
66
- subject = WorkflowActionInfo.new(work, user)
67
+ subject = Hyrax::WorkflowActionInfo.new(work, user)
67
68
  Workflow::WorkflowActionService.run(subject: subject,
68
69
  action: action)
69
70
  end
@@ -25,7 +25,13 @@ class MigrateResourceService
25
25
  end
26
26
 
27
27
  def resource_form
28
- @resource_form ||= Hyrax::Forms::ResourceForm.for(resource: resource)
28
+ # needs to validate the form to prevent potential frozen errors in custom transaction steps
29
+ @resource_form ||= begin
30
+ form_params = resource.attributes.to_h.with_indifferent_access
31
+ form = Hyrax::Forms::ResourceForm.for(resource: resource)
32
+ form.validate(form_params)
33
+ form
34
+ end
29
35
  end
30
36
 
31
37
  def model_events(model)
@@ -4,11 +4,11 @@ module Hyrax
4
4
  # Override the directory where uploaded files will be stored.
5
5
  # This is a sensible default for uploaders that are meant to be mounted:
6
6
  def store_dir
7
- (configured_upload_path / model.class.to_s.underscore / mounted_as.to_s / model.id.to_s).to_s
7
+ (configured_upload_path / model.class.name.underscore / mounted_as.to_s / model.id.to_s).to_s
8
8
  end
9
9
 
10
10
  def cache_dir
11
- (configured_cache_path / model.class.to_s.underscore / mounted_as.to_s / model.id.to_s).to_s
11
+ (configured_cache_path / model.class.name.underscore / mounted_as.to_s / model.id.to_s).to_s
12
12
  end
13
13
 
14
14
  private
@@ -20,7 +20,7 @@ module Hyrax
20
20
 
21
21
  logger.info("Destroying collection branding...")
22
22
 
23
- Hyrax::CollectionBrandingInfo.destroy_all
23
+ CollectionBrandingInfo.destroy_all
24
24
  logger.info(" collection branding -- DESTROYED")
25
25
  end
26
26
  end
@@ -14,6 +14,7 @@ module Hyrax
14
14
  end
15
15
 
16
16
  def generate_seed_data
17
+ Hyrax::RequiredDataSeeders::FlexibleProfileSeeder.generate_seeds(logger: logger) if Hyrax.config.flexible?
17
18
  Hyrax::RequiredDataSeeders::CollectionTypeSeeder.generate_seeds(logger: logger)
18
19
  Hyrax::RequiredDataSeeders::CollectionSeeder.generate_seeds(logger: logger)
19
20
  end
@@ -20,7 +20,7 @@ module Hyrax
20
20
  logger.info(" #{as_ct.title} -- FOUND OR CREATED")
21
21
 
22
22
  user_ct = Hyrax::CollectionType.find_or_create_default_collection_type
23
- set_badge_color(user_ct, '#0099cc')
23
+ set_badge_color(user_ct, '#007EA8')
24
24
  logger.info(" #{user_ct.title} -- FOUND OR CREATED")
25
25
  end
26
26