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
@@ -7,19 +7,18 @@ module Hyrax
7
7
  private
8
8
 
9
9
  ##
10
- # Special treatment for license/rights. A URL from the Hyrax gem's config/hyrax.rb is stored in the descMetadata of the
11
- # curation_concern. If that URL is valid in form, then it is used as a link. If it is not valid, it is used as plain text.
10
+ # Special treatment for license/rights. A URL from the Hyrax gem's
11
+ # `config/hyrax.rb` is stored in the descMetadata of the
12
+ # curation_concern. If the stored value is an absolute http/https URI,
13
+ # render it as a link with the authority's label; otherwise render the
14
+ # value as plain text to avoid emitting unsafe href values (e.g.
15
+ # `javascript:`) or broken links for free-text values.
12
16
  def attribute_value_to_html(value)
13
- begin
14
- parsed_uri = URI.parse(value)
15
- rescue URI::InvalidURIError
16
- nil
17
- end
18
- if parsed_uri.nil?
19
- ERB::Util.h(value)
20
- else
17
+ if Hyrax::AuthorityRenderingHelper.linkable_uri?(value)
21
18
  label = Hyrax.config.rights_statement_service_class.new.label(value) { value }
22
- %(<a href=#{ERB::Util.h(value)} target="_blank">#{label}</a>)
19
+ %(<a href="#{ERB::Util.h(value)}" target="_blank" rel="noopener noreferrer">#{ERB::Util.h(label)}</a>)
20
+ else
21
+ ERB::Util.h(value)
23
22
  end
24
23
  end
25
24
  end
@@ -16,11 +16,7 @@ module Hyrax
16
16
 
17
17
  # Override for Hydra::AccessControlsEnforcement
18
18
  attr_reader :discovery_permissions
19
- self.default_processor_chain += [:with_pagination, :show_only_other_collections_of_the_same_collection_type]
20
-
21
- def with_pagination(solr_parameters)
22
- solr_parameters[:rows] = 1000
23
- end
19
+ self.default_processor_chain += [:show_only_other_collections_of_the_same_collection_type]
24
20
 
25
21
  # Solr can do graph traversal without the need of special indexing with the Graph query parser so
26
22
  # use this to compute both the parents and children of the current collection then exclude them
@@ -41,7 +41,7 @@ module Hyrax
41
41
  # types from appearing in search results
42
42
  # @return [Array<Class>] the list of work types to include in searches
43
43
  def work_types
44
- Hyrax::ModelRegistry.work_classes
44
+ Hyrax::ModelRegistry.work_classes - Hyrax::ModelRegistry.file_set_classes
45
45
  end
46
46
 
47
47
  def work_classes
@@ -35,6 +35,7 @@ module Hyrax
35
35
  @params = params
36
36
  @work_attributes_key = work_attributes_key
37
37
  @work_attributes = @params.fetch(work_attributes_key, {})
38
+ @file_set_params = work_attributes[:file_set]&.map { |fs| fs.permit! } || []
38
39
  end
39
40
 
40
41
  attr_reader :form, :transactions, :user, :parent_id, :work_attributes, :uploaded_files, :params, :work_attributes_key
@@ -42,7 +43,7 @@ module Hyrax
42
43
  ##
43
44
  # @api public
44
45
  # @return [TrueClass] when the object is valid.
45
- # @return [FalseClass] when the object is valid.
46
+ # @return [FalseClass] when the object is not valid.
46
47
  def validate
47
48
  form.validate(work_attributes)
48
49
  end
@@ -63,7 +64,7 @@ module Hyrax
63
64
  def step_args
64
65
  {
65
66
  'work_resource.add_to_parent' => { parent_id: params[:parent_id], user: user },
66
- 'work_resource.add_file_sets' => { uploaded_files: uploaded_files, file_set_params: work_attributes[:file_set] },
67
+ 'work_resource.add_file_sets' => { uploaded_files: uploaded_files, file_set_params: @file_set_params },
67
68
  'change_set.set_user_as_depositor' => { user: user },
68
69
  'work_resource.change_depositor' => { user: ::User.find_by_user_key(form.on_behalf_of) },
69
70
  'work_resource.save_acl' => { permissions_params: form.input_params["permissions"] }
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+ module Hyrax
3
+ # Shared rendering helpers for authority-backed values that may have been
4
+ # stored as either a real URI or as free text (the M3 metadata profile, plus
5
+ # bulk importers such as Bulkrax and Willow Sword, can both produce
6
+ # free-text values that the UI's authority dropdown did not constrain).
7
+ module AuthorityRenderingHelper
8
+ # Schemes considered safe to emit in an `<a href>`. Anything else (notably
9
+ # `javascript:`, `data:`, custom schemes, or non-absolute strings like
10
+ # `"moomin"`) is rendered as plain text so it cannot be activated as a
11
+ # link.
12
+ LINKABLE_URI_SCHEMES = %w[http https].freeze
13
+
14
+ module_function
15
+
16
+ # @param value [String, nil]
17
+ # @return [Boolean] true when the value parses as an absolute URI with an
18
+ # allowed scheme, suitable to use as an anchor's href.
19
+ def linkable_uri?(value)
20
+ return false if value.blank?
21
+
22
+ parsed = URI.parse(value)
23
+ parsed.absolute? && LINKABLE_URI_SCHEMES.include?(parsed.scheme&.downcase)
24
+ rescue URI::InvalidURIError
25
+ false
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,107 @@
1
+ # frozen_string_literal: true
2
+ module Hyrax
3
+ # Shared behavior for authority-backed services. Module-level services
4
+ # (such as {Hyrax::ResourceTypesService}) `extend` this module to gain a
5
+ # tolerant `label` / `active?` / `include_current_value` API plus the
6
+ # declarative macros described below.
7
+ #
8
+ # The tolerant methods are intentionally forgiving of off-authority values:
9
+ # `label` falls back to the id itself, `active?` returns false for ids that
10
+ # aren't present in the authority at all, and `include_current_value`
11
+ # preserves such values in edit forms so they are not silently dropped on
12
+ # save.
13
+ #
14
+ # ## Declarative macros
15
+ #
16
+ # Hosts use the {#authority_name} and {#microdata_namespace} macros to
17
+ # declare the wrapped subauthority and the i18n namespace used for
18
+ # schema.org type lookup. These macros generate the rest of the host's
19
+ # API for free:
20
+ #
21
+ # module Hyrax
22
+ # module DisciplineService
23
+ # extend Hyrax::AuthorityService
24
+ #
25
+ # authority_name 'discipline'
26
+ # microdata_namespace 'type.'
27
+ # end
28
+ # end
29
+ #
30
+ # `authority_name` provides `authority`, `authority=`, `select_options`,
31
+ # and `select_all_options` (the latter is an alias of the former — both
32
+ # names are in use in the wild). `microdata_namespace` provides
33
+ # `microdata_type`.
34
+ #
35
+ # @note This module is intended for use via `extend` on a host module. The
36
+ # macros define singleton methods on the host and are not available via
37
+ # `include`. Hosts that need instance-level tolerant lookup should
38
+ # subclass {Hyrax::QaSelectService} instead.
39
+ module AuthorityService
40
+ # Declares which Qa::Authorities::Local subauthority this host wraps,
41
+ # generating `authority`, `authority=`, `select_options`, and
42
+ # `select_all_options` accessors on the host.
43
+ #
44
+ # @param subauthority_name [String] the YAML subauthority name (e.g.
45
+ # `'discipline'`)
46
+ def authority_name(subauthority_name)
47
+ define_singleton_method(:authority) do
48
+ @authority ||= Qa::Authorities::Local.subauthority_for(subauthority_name)
49
+ end
50
+
51
+ define_singleton_method(:authority=) do |value|
52
+ @authority = value
53
+ end
54
+
55
+ define_singleton_method(:select_all_options) do
56
+ authority.all.map { |element| [element[:label], element[:id]] }
57
+ end
58
+ singleton_class.alias_method(:select_options, :select_all_options)
59
+ end
60
+
61
+ # Declares the Microdata i18n namespace this host uses for schema.org
62
+ # type lookup, generating a `microdata_type(id)` method on the host.
63
+ #
64
+ # @param namespace [String] the Microdata key prefix (e.g. `'type.'`,
65
+ # `'resource_type.'`, `'accessibility_feature_type.'`)
66
+ def microdata_namespace(namespace)
67
+ define_singleton_method(:microdata_type) do |id|
68
+ return Hyrax.config.microdata_default_type if id.nil?
69
+ Microdata.fetch("#{namespace}#{id}", default: Hyrax.config.microdata_default_type)
70
+ end
71
+ end
72
+
73
+ # @param id [String]
74
+ # @return [String] the label for the authority entry, falling back to the
75
+ # id itself when no matching term is found.
76
+ #
77
+ # @yield when no 'term' value is present for the id
78
+ # @yieldreturn [String] an alternate label to return
79
+ def label(id, &block)
80
+ block ||= ->(_key) { id }
81
+ authority.find(id).fetch('term', &block)
82
+ end
83
+
84
+ # @return [Boolean] whether the id is an active entry. Returns false for
85
+ # ids that aren't present in the authority at all, so a caller can treat
86
+ # unknown ids as inactive (and preserve them in edit forms via
87
+ # {#include_current_value}).
88
+ def active?(id)
89
+ result = authority.find(id)
90
+ return false if result.blank?
91
+ result.fetch('active', true)
92
+ end
93
+
94
+ # Preserves an off-authority value as a forced-select option in a Simple
95
+ # Form input so it is not silently dropped on save. Intended for use as a
96
+ # Simple Form `item_helper:` callback or to augment a `collection:` array
97
+ # in a partial.
98
+ def include_current_value(value, _index, render_options, html_options)
99
+ force_select = html_options[:class].is_a?(Array) ? [' force-select'] : ' force-select'
100
+ unless value.blank? || active?(value)
101
+ html_options[:class] += force_select
102
+ render_options += [[label(value), value]]
103
+ end
104
+ [render_options, html_options]
105
+ end
106
+ end
107
+ end
@@ -48,7 +48,7 @@ module Hyrax
48
48
 
49
49
  # Defines a callback for a given hook.
50
50
  def set(hook, warn: true, &block)
51
- Deprecation.warn(self, warning_for_set) if warn
51
+ Deprecation.warn(warning_for_set) if warn
52
52
  raise NoBlockGiven, "a block is required when setting a callback" unless block_given?
53
53
  @callbacks[hook] = proc(&block)
54
54
  end
@@ -60,7 +60,7 @@ module Hyrax
60
60
 
61
61
  # Runs the callback defined for a given hook, with the arguments provided
62
62
  def run(hook, *args, warn: true, **opts)
63
- Deprecation.warn(self, warning_for_run) if warn
63
+ Deprecation.warn(warning_for_run) if warn
64
64
  raise NotEnabled unless enabled?(hook)
65
65
  return nil unless set?(hook)
66
66
  @callbacks[hook].call(*args, **opts)
@@ -16,7 +16,7 @@ module Hyrax
16
16
  def self.available_child_collections(parent:, scope:, limit_to_id: nil)
17
17
  return [] unless nestable?(collection: parent)
18
18
  return [] unless scope.can?(:deposit, parent)
19
- query_solr(collection: parent, access: :read, scope: scope, limit_to_id: limit_to_id, nest_direction: :as_child).documents
19
+ query_solr(collection: parent, access: :read, scope: scope, limit_to_id: limit_to_id, nest_direction: :as_child)
20
20
  end
21
21
 
22
22
  ##
@@ -34,7 +34,7 @@ module Hyrax
34
34
  def self.available_parent_collections(child:, scope:, limit_to_id: nil)
35
35
  return [] unless nestable?(collection: child)
36
36
  return [] unless scope.can?(:read, child)
37
- query_solr(collection: child, access: :deposit, scope: scope, limit_to_id: limit_to_id, nest_direction: :as_parent).documents
37
+ query_solr(collection: child, access: :deposit, scope: scope, limit_to_id: limit_to_id, nest_direction: :as_parent)
38
38
  end
39
39
 
40
40
  ##
@@ -65,6 +65,7 @@ module Hyrax
65
65
  # id is in the response. Useful for validation.
66
66
  # @param nest_direction [Symbol] :as_child or :as_parent
67
67
  def self.query_solr(collection:, access:, scope:, limit_to_id:, nest_direction:)
68
+ scope.blacklight_config.http_method = :post
68
69
  query_builder = Hyrax::Dashboard::NestedCollectionsSearchBuilder.new(
69
70
  access: access,
70
71
  collection: collection,
@@ -73,7 +74,12 @@ module Hyrax
73
74
  )
74
75
 
75
76
  query_builder.where(id: limit_to_id.to_s) if limit_to_id
76
- scope.blacklight_config.repository.search(query_builder.query)
77
+
78
+ Hyrax::SolrService.fetch_all do |rows, start|
79
+ scope.blacklight_config.repository.search(
80
+ query_builder.query.merge(rows: rows, start: start, fl: 'id,title_tesim,has_model_ssim')
81
+ )
82
+ end
77
83
  end
78
84
  private_class_method :query_solr
79
85
 
@@ -25,5 +25,22 @@ module Hyrax
25
25
 
26
26
  response.documents
27
27
  end
28
+
29
+ # Like {#search_results}, but pages through every matching collection
30
+ # (up to Hyrax.config.solr_max_results). Use this only for surfaces that
31
+ # need the full set, e.g. the "Add to Collection" dropdown where a user
32
+ # must see every collection they can deposit into.
33
+ #
34
+ # @param [Symbol] access :read or :edit
35
+ # @return [Array<SolrDocument>]
36
+ def all_search_results(access = nil)
37
+ builder = search_builder.with(user_params)
38
+ builder.with_access(access) if access
39
+ yield builder if block_given?
40
+
41
+ Hyrax::SolrService.fetch_all do |rows, start|
42
+ blacklight_config.repository.search(builder.query.merge(rows: rows, start: start))
43
+ end
44
+ end
28
45
  end
29
46
  end
@@ -0,0 +1,89 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Hyrax
4
+ module CustomQueries
5
+ ##
6
+ # Finds resources for arbitrary properties using the Solr index.
7
+ # @see https://github.com/samvera/valkyrie/wiki/Queries#custom-queries
8
+ class FindByPropertyValue
9
+ def self.queries
10
+ [:find_ids_by_property_pairs,
11
+ :find_many_by_property_pairs,
12
+ :find_many_by_property_value,
13
+ :find_by_property_value]
14
+ end
15
+
16
+ def initialize(query_service:, query_rows: 1_000)
17
+ @query_service = query_service
18
+ @query_rows = query_rows
19
+ end
20
+
21
+ ##
22
+ # Returns ids for resources matching all specified property/value pairs.
23
+ # @param pairs [Hash<String, String>] properties and values to match
24
+ # @param field_suffix Hash,Symbol which solr field suffix to use; Use a hash to set per field pair
25
+ # @param options Hash options to pass to solr
26
+ #
27
+ # @return [Array<String>] found ids, if any
28
+ def find_ids_by_property_pairs(pairs:, field_suffix: :ssim, **options)
29
+ options = { rows: @query_rows }.merge(options)
30
+ query = pairs.map do |k, v|
31
+ suffix = if field_suffix.is_a? Hash
32
+ field_suffix[k] || :ssim
33
+ else
34
+ field_suffix
35
+ end
36
+ "+#{k}_#{suffix}:\"#{RSolr.solr_escape(v)}\""
37
+ end
38
+ response = Hyrax::SolrService.query_result(query.join(' '), fl: 'id', **options)
39
+ response['response']['docs'].map { |doc| doc['id'] }
40
+ end
41
+
42
+ ##
43
+ # Returns resources matching all specified property/value pairs.
44
+ # @param pairs [Hash<String, String>] properties and values to match
45
+ # @param field_suffix Hash,Symbol which solr field suffix to use; Use a hash to set per field pair
46
+ # @param options Hash options to pass to solr
47
+ #
48
+ # @return [Array<Valkyrie::Resource>] found resources, if any
49
+ def find_many_by_property_pairs(pairs:, field_suffix: :ssim, **options)
50
+ found_ids = find_ids_by_property_pairs(pairs: pairs, field_suffix: field_suffix, **options)
51
+ @query_service.find_many_by_ids(ids: found_ids).to_a # Wings emits an Enumerator
52
+ end
53
+
54
+ ##
55
+ # Returns resources matching the specified property and value.
56
+ # @param property [#to_s] the name of the property we're attempting to query.
57
+ # @param value [#to_s] the property value we're trying to match.
58
+ # @param field_suffix Symbol which solr field suffix to use
59
+ # @param search_field String alt way to set property and suffix for compatibility
60
+ # @param options Hash options to pass to solr
61
+ #
62
+ # @return [Array<Valkyrie::Resource>] found resources, if any
63
+ def find_many_by_property_value(property:, value:, field_suffix: :ssim, search_field: nil, **options)
64
+ if search_field
65
+ search_field_array = search_field.split('_')
66
+ field_suffix = search_field_array.last
67
+ property = search_field_array[0...-1].join('_')
68
+ end
69
+ find_many_by_property_pairs(pairs: { property => value }, field_suffix: field_suffix, **options)
70
+ end
71
+
72
+ ##
73
+ # Returns one resource matching the specified property and value.
74
+ # @param property [#to_s] the name of the property we're attempting to query.
75
+ # @param value [#to_s] the property value we're trying to match.
76
+ # @param field_suffix Symbol which solr field suffix to use
77
+ # @param search_field String alt way to set property and suffix for compatibility
78
+ # @param options Hash options to pass to solr
79
+ #
80
+ # @return [NilClass] when no record was found
81
+ # @return [Valkyrie::Resource] when a resource was found
82
+ def find_by_property_value(property:, value:, field_suffix: :ssim, search_field: nil, **options)
83
+ options[:rows] = 1
84
+ find_many_by_property_value(property: property, value: value,
85
+ field_suffix: field_suffix, search_field: search_field, **options).first
86
+ end
87
+ end
88
+ end
89
+ end
@@ -42,11 +42,11 @@ module Hyrax
42
42
  # (+Hyrax::Forms::FileSetForm+), +:in_works_ids+ is prepopulated onto
43
43
  # the form object itself. For +Hyrax::Forms::FileSetEditForm+, the
44
44
  # +:in_works+ method is present on the wrapped +:model+.
45
- if obj.is_a?(Hyrax.config.file_set_form)
45
+ if obj.model.respond_to?(:in_works)
46
+ new(object: obj.model.in_works.first, ability: ability)
47
+ else
46
48
  object_id = obj.in_works_ids.first
47
49
  new(object: Hyrax.query_service.find_by(id: object_id), ability: ability)
48
- else
49
- new(object: obj.model.in_works.first, ability: ability)
50
50
  end
51
51
  elsif obj.file_set?
52
52
  # The provided form object is a FileSet.
@@ -171,7 +171,7 @@ module Hyrax
171
171
  unauthorized_managers = []
172
172
  unauthorized_collection_managers = []
173
173
  object_unauthorized_collection_ids.each do |id|
174
- Hyrax::PermissionTemplate.find_by(source_id: id).access_grants.each do |grant|
174
+ Hyrax::PermissionTemplate.find_by(source_id: id)&.access_grants&.each do |grant|
175
175
  if grant.access == "manage"
176
176
  unauthorized_managers << grant.agent_id
177
177
  unauthorized_collection_managers += Array.wrap({ name: grant.agent_id }.merge(id: id))
@@ -8,9 +8,6 @@ module Hyrax
8
8
  # @note this service is for `Hyrax::FileSet` Valkyrie Resources. for
9
9
  # ActiveFedora file sets see Hydra::Works::MimeTypes
10
10
  class FileSetTypeService
11
- DEFAULT_AUDIO_TYPES = ['audio/mp3', 'audio/mpeg', 'audio/wav',
12
- 'audio/x-wave', 'audio/x-wav', 'audio/ogg'].freeze
13
-
14
11
  ##
15
12
  # @!attribute [r] file_set
16
13
  # @return [Hyrax::FileSet]
@@ -42,11 +39,22 @@ module Hyrax
42
39
  audio_types.include?(mime_type)
43
40
  end
44
41
 
42
+ ##
43
+ # @return [Boolean]
44
+ def video?
45
+ video_types.include?(mime_type)
46
+ end
47
+
45
48
  private
46
49
 
47
50
  def audio_types
48
51
  return ::FileSet.audio_mime_types if defined?(::FileSet) && ::FileSet.respond_to?(:audio_mime_types)
49
- DEFAULT_AUDIO_TYPES
52
+ Hyrax.config.mime_types_map.fetch(:audio_mime_types)
53
+ end
54
+
55
+ def video_types
56
+ return ::FileSet.video_mime_types if defined?(::FileSet) && ::FileSet.respond_to?(:video_mime_types)
57
+ Hyrax.config.mime_types_map.fetch(:video_mime_types)
50
58
  end
51
59
  end
52
60
  end
@@ -0,0 +1,153 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Hyrax
4
+ class FlexibleSchemaValidatorService
5
+ DEFAULT_SCHEMA = Hyrax::Engine.root.join('config', 'metadata_profiles', 'm3_json_schema.json')
6
+ REQUIRED_CLASSES = [
7
+ Hyrax.config.admin_set_model,
8
+ Hyrax.config.collection_model,
9
+ Hyrax.config.file_set_model
10
+ ].map { |str| str.gsub(/^::/, '') }
11
+
12
+ attr_reader :profile, :schema, :schemer, :errors, :warnings
13
+
14
+ # Initializes a new FlexibleSchemaValidatorService.
15
+ #
16
+ # @param profile [Hash] the flexible metadata profile to validate
17
+ # @param schema [Pathname, String] the JSON schema to validate against.
18
+ # Defaults to {DEFAULT_SCHEMA}.
19
+ # @return [void]
20
+ def initialize(profile:, schema: default_schema)
21
+ @profile = profile
22
+ @schema = schema
23
+ @schemer = JSONSchemer.schema(schema)
24
+ @errors = []
25
+ @warnings = []
26
+ end
27
+
28
+ # Execute all validation routines and populate {#errors} with any
29
+ # issues discovered.
30
+ #
31
+ # @return [void]
32
+ def validate!
33
+ validate_required_classes
34
+ validate_class_availability
35
+ validate_available_on_classes_defined
36
+ validate_existing_records_classes_defined
37
+ validate_schema
38
+ validate_label_prop
39
+ validate_core_metadata
40
+ validate_sort_properties
41
+ validate_redirects
42
+ end
43
+
44
+ # The default JSON schema used when no custom schema is provided.
45
+ #
46
+ # @return [Pathname]
47
+ def default_schema
48
+ DEFAULT_SCHEMA
49
+ end
50
+
51
+ # Classes that MUST be present in every flexible metadata profile.
52
+ #
53
+ # @return [Array<String>]
54
+ def required_classes
55
+ REQUIRED_CLASSES
56
+ end
57
+
58
+ private
59
+
60
+ # Validates core metadata requirements using the CoreMetadataValidator.
61
+ #
62
+ # This delegates to CoreMetadataValidator to check that essential metadata
63
+ # properties are properly configured across all classes in the profile.
64
+ #
65
+ # @return [void]
66
+ def validate_core_metadata
67
+ FlexibleSchemaValidators::CoreMetadataValidator.new(profile: profile, errors: @errors).validate!
68
+ end
69
+
70
+ # Runs JSON schema validation and translates resulting errors into
71
+ # user-friendly messages appended to {#errors}.
72
+ #
73
+ # @return [void]
74
+ def validate_schema
75
+ FlexibleSchemaValidators::SchemaValidator.new(schemer, profile, @errors).validate!
76
+ end
77
+
78
+ # Ensures that all required classes are defined in the profile.
79
+ #
80
+ # @return [void]
81
+ def validate_required_classes
82
+ missing_classes = clean_class_names(required_classes) - clean_class_names(profile['classes'].keys)
83
+ return if missing_classes.empty?
84
+
85
+ @errors << "Missing required classes: #{missing_classes.join(', ')}."
86
+ end
87
+
88
+ def clean_class_names(names)
89
+ names.map { |name| name.to_s.strip.gsub(/^::/, '') }
90
+ end
91
+
92
+ # Checks that any class referenced in the profile is a registered
93
+ # Hyrax curation concern type.
94
+ #
95
+ # @return [void]
96
+ def validate_class_availability
97
+ FlexibleSchemaValidators::ClassValidator.new(profile, required_classes, @errors).validate_availability!
98
+ end
99
+
100
+ # Validates that every class referenced under `available_on.class` is also
101
+ # defined in the profile's top-level `classes` section.
102
+ #
103
+ # @return [void]
104
+ def validate_available_on_classes_defined
105
+ FlexibleSchemaValidators::ClassValidator.new(profile, required_classes, @errors).validate_references!
106
+ end
107
+
108
+ # Delegates to {ExistingRecordsValidator} to ensure that no classes with
109
+ # existing repository records have been removed from the profile.
110
+ #
111
+ # @return [void]
112
+ def validate_existing_records_classes_defined
113
+ FlexibleSchemaValidators::ExistingRecordsValidator.new(profile, required_classes, @errors).validate!
114
+ end
115
+
116
+ # Validates that any properties needed to support sorting catalog search results.
117
+ #
118
+ # @return [void]
119
+ def validate_sort_properties
120
+ FlexibleSchemaValidators::SortPropertiesValidator.new(profile, @warnings).validate!
121
+ end
122
+
123
+ # Validates that the `redirects` property is declared on every work and
124
+ # collection class when both `Hyrax.config.redirects_enabled?` and
125
+ # `Flipflop.redirects?` are true. When either gate is closed, this
126
+ # validator is a no-op.
127
+ #
128
+ # @return [void]
129
+ def validate_redirects
130
+ FlexibleSchemaValidators::RedirectsValidator.new(
131
+ profile: profile, errors: @errors, warnings: @warnings
132
+ ).validate!
133
+ end
134
+
135
+ # Validates that a `label` property exists and that it is available on
136
+ # `Hyrax.config.file_set_model`.
137
+ #
138
+ # @return [void]
139
+ def validate_label_prop
140
+ label_prop = profile.dig('properties', 'label')
141
+ unless label_prop
142
+ @errors << "A `label` property is required."
143
+ return
144
+ end
145
+
146
+ available_on_classes = label_prop.dig('available_on', 'class')
147
+ file_set_model_name = Hyrax.config.file_set_model.gsub(/^::/, '')
148
+ return if available_on_classes&.include?(file_set_model_name)
149
+
150
+ @errors << "Label must be available on #{file_set_model_name}."
151
+ end
152
+ end
153
+ end