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,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Hyrax
4
+ module AnnotatesContent
5
+ extend ActiveSupport::Concern
6
+
7
+ include DisplaysTranscripts
8
+
9
+ def annotation_content
10
+ transcription_content if video? || audio?
11
+ end
12
+
13
+ private
14
+
15
+ def transcription_content
16
+ transcripts.map do |doc|
17
+ options = {
18
+ type: 'Text',
19
+ motivation: 'supplementing',
20
+ body_id: transcript_url(doc, host: hostname, file_ext: file_ext(doc.mime_type)),
21
+ format: doc.mime_type,
22
+ label: doc.title_or_label
23
+ }
24
+ options[:language] = language_code(doc.language) if language_code(doc.language)
25
+ IIIFManifest::V3::AnnotationContent.new(**options)
26
+ end
27
+ end
28
+
29
+ # If you change the accepted mime types in Hyrax::TranscriptsBehavior,
30
+ # you may also want to override this method
31
+ def file_ext(_mime_type)
32
+ "vtt"
33
+ end
34
+ end
35
+ end
@@ -25,7 +25,7 @@ module Hyrax
25
25
 
26
26
  # CurationConcern methods
27
27
  delegate :stringify_keys, :human_readable_type, :collection?, :representative_id,
28
- :to_s, to: :solr_document
28
+ :to_s, :schema_version, to: :solr_document
29
29
 
30
30
  delegate(*Hyrax::CollectionType.settings_attributes, to: :collection_type, prefix: :collection_type_is)
31
31
  alias nestable? collection_type_is_nestable?
@@ -129,6 +129,10 @@ module Hyrax
129
129
  current_ability.can?(:deposit, solr_document)
130
130
  end
131
131
 
132
+ def editor?
133
+ current_ability.can?(:edit, self)
134
+ end
135
+
132
136
  def user_can_create_new_nest_collection?
133
137
  current_ability.can?(:create_collection_of_type, collection_type)
134
138
  end
@@ -0,0 +1,133 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Hyrax
4
+ # rubocop:disable Metrics/ModuleLength
5
+ module DisplaysContent
6
+ extend ActiveSupport::Concern
7
+ # Creates a display content only where FileSet is an image, audio, or video.
8
+ #
9
+ # @return [IIIFManifest::V3::DisplayContent] the display content required by the manifest builder.
10
+ def display_content
11
+ return nil unless ability.can?(:read, object)
12
+
13
+ return image_content if image?
14
+ return video_content if video? && Flipflop.iiif_av?
15
+ return audio_content if audio? && Flipflop.iiif_av?
16
+ return pdf_content if pdf? && Flipflop.iiif_pdf?
17
+ end
18
+
19
+ private
20
+
21
+ def content_supported?
22
+ video? || audio? || image? || pdf?
23
+ end
24
+
25
+ def image_content
26
+ return nil unless latest_file_id
27
+ # Only provide v3 content when using v3 factory
28
+ # Otherwise let display_image handle v2
29
+ return nil unless Hyrax.config.iiif_manifest_factory == ::IIIFManifest::V3::ManifestFactory
30
+
31
+ url = Hyrax.config.iiif_image_url_builder.call(
32
+ latest_file_id,
33
+ hostname,
34
+ Hyrax.config.iiif_image_size_default,
35
+ image_format(alpha_channels)
36
+ )
37
+
38
+ image_content_v3(url)
39
+ end
40
+
41
+ def image_content_v3(url)
42
+ # @see https://github.com/samvera-labs/iiif_manifest
43
+ IIIFManifest::V3::DisplayContent.new(
44
+ url,
45
+ format: image_format(alpha_channels),
46
+ width: width,
47
+ height: height,
48
+ type: 'Image',
49
+ iiif_endpoint: iiif_endpoint(latest_file_id)
50
+ )
51
+ end
52
+
53
+ def video_content
54
+ # @see https://github.com/samvera-labs/iiif_manifest
55
+ IIIFManifest::V3::DisplayContent.new(
56
+ download_path('mp4'),
57
+ label: 'mp4',
58
+ width: Array(width).first.try(:to_i),
59
+ height: Array(height).first.try(:to_i),
60
+ duration: conformed_duration,
61
+ type: 'Video',
62
+ format: mime_type,
63
+ thumbnail: thumbnail
64
+ )
65
+ end
66
+
67
+ def audio_content
68
+ IIIFManifest::V3::DisplayContent.new(
69
+ download_path('mp3'),
70
+ label: 'mp3',
71
+ duration: conformed_duration,
72
+ type: 'Sound',
73
+ # I think UV has a bug where if it's 'audio/mpeg' then it would load, so adding this
74
+ # workaround to use 'audio/mp3' (which isn't even an official MIME type).
75
+ format: Hyrax.config.iiif_av_viewer == :universal_viewer ? 'audio/mp3' : mime_type,
76
+ thumbnail: [{
77
+ id: "#{hostname}/assets/audio.png",
78
+ type: 'Image',
79
+ format: 'image/png'
80
+ }]
81
+ )
82
+ end
83
+
84
+ def pdf_content
85
+ IIIFManifest::V3::DisplayContent.new(
86
+ download_path('pdf'),
87
+ label: 'pdf',
88
+ type: 'Text',
89
+ format: mime_type,
90
+ thumbnail: thumbnail
91
+ )
92
+ end
93
+
94
+ def download_path(extension)
95
+ Hyrax::Engine.routes.url_helpers.download_url(object, file: extension, host: hostname)
96
+ end
97
+
98
+ def thumbnail
99
+ [{
100
+ id: download_path('thumbnail'),
101
+ type: 'Image',
102
+ format: 'image/jpeg'
103
+ }]
104
+ end
105
+
106
+ # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
107
+ def conformed_duration
108
+ duration_string = Array(object.duration).first
109
+ return nil if duration_string.blank?
110
+
111
+ # Handle values with explicit seconds unit (e.g., "25 s")
112
+ return duration_string.to_f if duration_string.match?(/\A[\d.]+\s*s\z/)
113
+ # Handle plain millisecond values (e.g., "120000")
114
+ return duration_string.to_f / 1000.0 unless duration_string.include?(':')
115
+
116
+ # Parse time-formatted strings
117
+ parts = duration_string.split(':').map(&:to_i)
118
+
119
+ case parts.length
120
+ when 2 # MM:SS
121
+ parts[0] * 60.0 + parts[1]
122
+ when 3 # H:MM:SS
123
+ parts[0] * 3600.0 + parts[1] * 60.0 + parts[2]
124
+ when 4 # H:MM:SS:mmm (milliseconds)
125
+ parts[0] * 3600.0 + parts[1] * 60.0 + parts[2] + (parts[3] / 1000.0)
126
+ else
127
+ duration_string.to_f # fallback
128
+ end
129
+ end
130
+ # rubocop:enable Metrics/AbcSize, Metrics/MethodLength
131
+ end
132
+ # rubocop:enable Metrics/ModuleLength
133
+ end
@@ -15,7 +15,7 @@ module Hyrax
15
15
  return nil unless latest_file_id
16
16
 
17
17
  # @see https://github.com/samvera-labs/iiif_manifest
18
- IIIFManifest::DisplayImage.new(display_image_url(request.base_url),
18
+ IIIFManifest::DisplayImage.new(display_image_url,
19
19
  format: image_format(alpha_channels),
20
20
  width: width,
21
21
  height: height,
@@ -24,7 +24,12 @@ module Hyrax
24
24
 
25
25
  private
26
26
 
27
- def display_image_url(base_url)
27
+ def base_url_for_iiif
28
+ respond_to?(:request) && request ? request.base_url : hostname
29
+ end
30
+
31
+ def display_image_url(base_url = nil)
32
+ base_url ||= base_url_for_iiif
28
33
  Hyrax.config.iiif_image_url_builder.call(
29
34
  latest_file_id,
30
35
  base_url,
@@ -33,8 +38,10 @@ module Hyrax
33
38
  )
34
39
  end
35
40
 
36
- def iiif_endpoint(file_id, base_url: request.base_url)
41
+ def iiif_endpoint(file_id, base_url: nil)
37
42
  return unless Hyrax.config.iiif_image_server?
43
+ base_url ||= base_url_for_iiif
44
+
38
45
  IIIFManifest::IIIFEndpoint.new(
39
46
  Hyrax.config.iiif_info_url_builder.call(file_id, base_url),
40
47
  profile: Hyrax.config.iiif_image_compliance_level_uri
@@ -42,7 +49,7 @@ module Hyrax
42
49
  end
43
50
 
44
51
  def image_format(channels)
45
- channels&.include?('rgba') ? 'png' : 'jpg'
52
+ channels&.include?('rgba') ? 'image/png' : 'image/jpeg'
46
53
  end
47
54
 
48
55
  ##
@@ -0,0 +1,59 @@
1
+ # frozen_string_literal: true
2
+ module Hyrax
3
+ module DisplaysTranscripts
4
+ extend ActiveSupport::Concern
5
+
6
+ # @return [Array<SolrDocument>] the solr documents represented by
7
+ # the audio/video file set's transcript_ids
8
+ def transcripts
9
+ return [] if transcript_ids.blank?
10
+ @transcripts ||= begin
11
+ results = Hyrax::SolrQueryService.new
12
+ .accessible_by(
13
+ ability: (try(:current_ability) || ability),
14
+ action: :read
15
+ )
16
+ .with_ids(ids: transcript_ids)
17
+ .solr_documents
18
+ sort_transcripts_by_language(results)
19
+ end
20
+ end
21
+
22
+ def transcript_url(document, host: request.base_url, file_ext: "vtt")
23
+ Hyrax::Engine.routes.url_helpers.transcript_url(document.id, host: host, file_ext: file_ext)
24
+ end
25
+
26
+ # Try our best to convert language field to an ISO 639-1 code for use in the IIIF manifest.
27
+ # @param [Array<String>] - a solr document's language field. Parseable values include an ISO 639-1 code,
28
+ # an ISO 639-3 code, or the English name for a language
29
+ # Examples: https://github.com/scsmith/language_list#examples
30
+ # @return [String or NilClass] - the 2-letter code, or nil if no value or value is unparseable
31
+ def language_code(language)
32
+ return if language.empty?
33
+ value = language.first
34
+ if URI.parse(value).scheme
35
+ # This is probably a Library of Congress languages URI
36
+ # like http://id.loc.gov/vocabulary/iso639-3/eng, which can
37
+ # be configured with the Questioning Authority gem.
38
+ # Try to extract the code from the URI.
39
+ LanguageList::LanguageInfo.find(value.split("/").last).try(:iso_639_1)
40
+ else
41
+ # Otherwise, assume it is a language code or name and try
42
+ # to convert it to a 2-letter code
43
+ LanguageList::LanguageInfo.find(value).try(:iso_639_1)
44
+ end
45
+ end
46
+
47
+ private
48
+
49
+ def sort_transcripts_by_language(results)
50
+ current_locale = I18n.locale.to_s
51
+
52
+ # Sort alphabetically by language code
53
+ sorted = results.sort_by { |doc| language_code(doc.language) || '' }
54
+
55
+ # Move current locale to front
56
+ sorted.partition { |doc| language_code(doc.language) == current_locale }.flatten
57
+ end
58
+ end
59
+ end
@@ -6,6 +6,8 @@ module Hyrax
6
6
  include CharacterizationBehavior
7
7
  include WithEvents
8
8
  include DisplaysImage
9
+ include MissingMethodBehavior
10
+ include DisplaysTranscripts
9
11
 
10
12
  attr_accessor :solr_document, :current_ability, :request
11
13
 
@@ -18,29 +20,17 @@ module Hyrax
18
20
  @request = request
19
21
  end
20
22
 
21
- # CurationConcern methods
22
- delegate :stringify_keys, :human_readable_type, :collection?, :image?, :video?,
23
- :audio?, :pdf?, :office_document?, :representative_id, :to_s,
24
- :extensions_and_mime_types, to: :solr_document
25
-
26
- # Methods used by blacklight helpers
27
- delegate :has?, :first, :fetch, to: :solr_document
28
-
29
- # Metadata Methods
30
- delegate :title, :label, :description, :creator, :contributor, :subject,
31
- :publisher, :language, :date_uploaded,
32
- :embargo_release_date, :lease_expiration_date,
33
- :depositor, :keyword, :title_or_label, :keyword,
34
- :date_created, :date_modified, :itemtype,
35
- :original_file_id,
36
- to: :solr_document
37
-
23
+ delegate :to_s, to: :solr_document
38
24
  delegate :member_of_collection_ids, to: :parent
39
25
 
40
26
  def workflow
41
27
  nil
42
28
  end
43
29
 
30
+ def model
31
+ solr_document.to_model
32
+ end
33
+
44
34
  def single_use_links
45
35
  @single_use_links ||= SingleUseLink.where(item_id: id).map { |link| link_presenter_class.new(link) }
46
36
  end
@@ -109,7 +99,7 @@ module Hyrax
109
99
  ##
110
100
  # @return [Array<String>]
111
101
  def show_partials
112
- ['show_details']
102
+ %w[show_details metadata]
113
103
  end
114
104
 
115
105
  private
@@ -82,5 +82,44 @@ module Hyrax
82
82
  def title
83
83
  Array(object.try(:title)).sort.first || ""
84
84
  end
85
+
86
+ ##
87
+ # @return [String] a string representing the degree granting institution(s)
88
+ # as a semicolon delimited list
89
+ def degree_grantor
90
+ Array(object.try(:degree_grantor)).join('; ')
91
+ end
92
+
93
+ ##
94
+ # @return [String] a string representing the journal title
95
+ def journal_title
96
+ Array(object.try(:journal_title)).first || ''
97
+ end
98
+
99
+ ##
100
+ # @return [String] a string representing the journal volume
101
+ def volume
102
+ Array(object.try(:journal_volume)).first || ''
103
+ end
104
+
105
+ ##
106
+ # @return [String] a string representing the journal issue
107
+ def issue
108
+ Array(object.try(:journal_issue)).first || ''
109
+ end
110
+
111
+ ##
112
+ # @return [String] a string representing the first page
113
+ def first_page
114
+ Array(object.try(:page_start)).first || ''
115
+ end
116
+ alias firstpage first_page
117
+
118
+ ##
119
+ # @return [String] a string representing the last page
120
+ def last_page
121
+ Array(object.try(:page_end)).first || ''
122
+ end
123
+ alias lastpage last_page
85
124
  end
86
125
  end
@@ -63,7 +63,7 @@ module Hyrax
63
63
  ##
64
64
  # @return [Array<DisplayImagePresenter>]
65
65
  def file_set_presenters
66
- member_presenters.select(&:file_set?)
66
+ member_presenters.select { |p| p.file_set? && (p.display_image || p.display_content) }
67
67
  end
68
68
 
69
69
  ##
@@ -119,14 +119,29 @@ module Hyrax
119
119
  ##
120
120
  # @return [Array<Hash{String => String}>]
121
121
  def sequence_rendering
122
- Array(try(:rendering_ids)).map do |file_set_id|
123
- rendering = file_set_presenters.find { |p| p.id == file_set_id }
122
+ Array(try(:rendering_ids)).filter_map do |file_set_id|
123
+ rendering = member_presenters.find { |p| p.file_set? && p.id == file_set_id }
124
124
  next unless rendering
125
125
 
126
126
  { '@id' => Hyrax::Engine.routes.url_helpers.download_url(rendering.id, host: hostname),
127
127
  'format' => rendering.mime_type.presence || I18n.t("hyrax.manifest.unknown_mime_text"),
128
128
  'label' => I18n.t("hyrax.manifest.download_text") + (rendering.label || '') }
129
- end.flatten
129
+ end
130
+ end
131
+
132
+ ##
133
+ # Provides a link to the work page as the homepage for the object
134
+ # represented by the IIIF manifest (see
135
+ # https://iiif.io/api/cookbook/recipe/0047-homepage/).
136
+ # This can be helpful if the IIIF manifest is reused on an external site.
137
+ # @return [Array<Hash{String => String}>]
138
+ def homepage
139
+ [{
140
+ 'id' => Rails.application.routes.url_helpers.polymorphic_url(model, host: hostname),
141
+ 'type' => 'Text',
142
+ 'format' => 'text/html',
143
+ 'label' => { 'none' => [Array(title).first || ''] }
144
+ }]
130
145
  end
131
146
 
132
147
  ##
@@ -188,6 +203,8 @@ module Hyrax
188
203
  delegate_all
189
204
 
190
205
  include Hyrax::DisplaysImage
206
+ include Hyrax::DisplaysContent
207
+ include Hyrax::AnnotatesContent
191
208
 
192
209
  ##
193
210
  # @!attribute [w] ability
@@ -201,6 +218,7 @@ module Hyrax
201
218
  #
202
219
  # @return [IIIFManifest::DisplayImage] the display image required by the manifest builder.
203
220
  def display_image
221
+ return nil if Hyrax.config.iiif_manifest_factory == ::IIIFManifest::V3::ManifestFactory
204
222
  return nil unless model.image?
205
223
  return nil unless latest_file_id
206
224
 
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+ module Hyrax
3
+ module MissingMethodBehavior
4
+ private
5
+
6
+ def method_missing(method_name, *args, &block)
7
+ return solr_document.public_send(method_name, *args, &block) if solr_document.respond_to?(method_name)
8
+ super
9
+ end
10
+
11
+ def respond_to_missing?(method_name, include_private = false)
12
+ solr_document.respond_to?(method_name, include_private) || super
13
+ end
14
+ end
15
+ end
@@ -108,12 +108,10 @@ module Hyrax
108
108
 
109
109
  def query_docs(generic_type: nil, ids: object.member_ids)
110
110
  query = "{!terms f=id}#{ids.join(',')}"
111
- query += "{!term f=generic_type_si}#{generic_type}" if generic_type
112
- # works created via ActiveFedora use the _sim field
113
- query += "{!term f=generic_type_sim}#{generic_type}" if generic_type
111
+ query = "(generic_type_si:#{generic_type} OR generic_type_sim:#{generic_type}) AND #{query}" if generic_type
114
112
 
115
113
  Hyrax::SolrService
116
- .post(q: query, rows: 10_000)
114
+ .post(query, rows: 10_000)
117
115
  .fetch('response')
118
116
  .fetch('docs')
119
117
  end
@@ -3,6 +3,7 @@ module Hyrax
3
3
  class WorkShowPresenter
4
4
  include ModelProxy
5
5
  include PresentsAttributes
6
+ include MissingMethodBehavior
6
7
 
7
8
  ##
8
9
  # @!attribute [w] member_presenter_factory
@@ -24,12 +25,45 @@ module Hyrax
24
25
  @solr_document = Hyrax::SolrDocument::OrderedMembers.decorate(solr_document)
25
26
  @current_ability = current_ability
26
27
  @request = request
28
+ define_dynamic_methods if @solr_document.flexible?
27
29
  end
28
30
 
31
+ def define_dynamic_methods # rubocop:disable Metrics/CyclomaticComplexity, Metrics/MethodLength
32
+ Hyrax::FlexibleSchema.default_properties.each do |prop|
33
+ method_name = prop.to_s
34
+ property_details = Hyrax::FlexibleSchema.current_version["properties"][method_name]
35
+ next unless property_details
36
+
37
+ index_keys = property_details["indexing"]
38
+ next unless index_keys
39
+
40
+ multi_value = property_details['multiple'] || (property_details['data_type'] == 'array')
41
+
42
+ next if self.class.method_defined?(method_name) && solr_document.respond_to?(method_name)
43
+ # Define the method on the SolrDocument class
44
+ Hyrax::SolrDocument::OrderedMembers.send(:define_method, method_name) do |*_args|
45
+ index_keys.each do |index_key|
46
+ value = self[index_key]
47
+ return (multi_value ? Array.wrap(value) : value) if value.present?
48
+ end
49
+ multi_value ? [] : ""
50
+ end
51
+
52
+ # Define the method on the Presenter class
53
+ self.class.send(:define_method, method_name) do |*args|
54
+ @solr_document.send(method_name, *args)
55
+ end
56
+ end
57
+ end # rubocop:enable Metrics/MethodLength
58
+
29
59
  # We cannot rely on the method missing to catch this delegation. Because
30
60
  # most all objects implicitly implicitly implement #to_s
31
61
  delegate :to_s, to: :solr_document
32
62
 
63
+ def schema_version
64
+ solr_document[:schema_version_ssi]
65
+ end
66
+
33
67
  def page_title
34
68
  "#{human_readable_type} | #{title.first} | ID: #{id} | #{I18n.t('hyrax.product_name')}"
35
69
  end
@@ -50,11 +84,9 @@ module Hyrax
50
84
 
51
85
  # @return [Boolean] render a IIIF viewer
52
86
  def iiif_viewer?
53
- Hyrax.config.iiif_image_server? &&
54
- representative_id.present? &&
87
+ representative_id.present? &&
55
88
  representative_presenter.present? &&
56
- representative_presenter.image? &&
57
- members_include_viewable_image?
89
+ (av_viewable? || image_viewable? || pdf_viewable?)
58
90
  end
59
91
 
60
92
  alias universal_viewer? iiif_viewer?
@@ -74,7 +106,11 @@ module Hyrax
74
106
  # <h3>My IIIF Viewer!</h3>
75
107
  # <a href=<%= main_app.polymorphic_url([main_app, :manifest, presenter], { locale: nil }) %>>Manifest</a>
76
108
  def iiif_viewer
77
- :universal_viewer
109
+ if representative_presenter.present? && av_viewable?
110
+ Hyrax.config.iiif_av_viewer
111
+ else
112
+ :universal_viewer
113
+ end
78
114
  end
79
115
 
80
116
  # @return FileSetPresenter presenter for the representative FileSets
@@ -246,15 +282,6 @@ module Hyrax
246
282
 
247
283
  private
248
284
 
249
- def method_missing(method_name, *args, &block)
250
- return solr_document.public_send(method_name, *args, &block) if solr_document.respond_to?(method_name)
251
- super
252
- end
253
-
254
- def respond_to_missing?(method_name, include_private = false)
255
- solr_document.respond_to?(method_name, include_private) || super
256
- end
257
-
258
285
  # list of item ids to display is based on ordered_ids
259
286
  def authorized_item_ids(filter_unreadable: Flipflop.hide_private_items?)
260
287
  @member_item_list_ids ||=
@@ -320,5 +347,20 @@ module Hyrax
320
347
  def members_include_viewable_image?
321
348
  file_set_presenters.any? { |presenter| presenter.image? && current_ability.can?(:read, presenter.id) }
322
349
  end
350
+
351
+ def av_viewable?
352
+ return false unless Flipflop.iiif_av?
353
+ representative_presenter.video? || representative_presenter.audio?
354
+ end
355
+
356
+ def image_viewable?
357
+ return false unless Hyrax.config.iiif_image_server?
358
+ representative_presenter.image? && members_include_viewable_image?
359
+ end
360
+
361
+ def pdf_viewable?
362
+ return false unless Flipflop.iiif_pdf?
363
+ representative_presenter.pdf?
364
+ end
323
365
  end
324
366
  end
@@ -27,7 +27,7 @@ module Hyrax
27
27
 
28
28
  # Draw the table row for the attribute
29
29
  def render
30
- return '' if values.blank? && !options[:include_empty]
30
+ return '' if blank_values? && !options[:include_empty]
31
31
 
32
32
  markup = %(<tr><th>#{label}</th>\n<td><ul class='tabular'>)
33
33
 
@@ -47,7 +47,7 @@ module Hyrax
47
47
 
48
48
  # Draw the dl row for the attribute
49
49
  def render_dl_row
50
- return '' if values.blank? && !options[:include_empty]
50
+ return '' if blank_values? && !options[:include_empty]
51
51
 
52
52
  markup = %(<dt>#{label}</dt>\n<dd><ul class='tabular'>)
53
53
 
@@ -107,6 +107,10 @@ module Hyrax
107
107
  def work_type_label_key
108
108
  options[:work_type] ? options[:work_type].underscore : nil
109
109
  end
110
+
111
+ def blank_values?
112
+ Array(values).all?(&:blank?)
113
+ end
110
114
  end
111
115
  end
112
116
  end
@@ -7,18 +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)
17
+ if Hyrax::AuthorityRenderingHelper.linkable_uri?(value)
18
+ label = Hyrax.config.license_service_class.new.label(value) { value }
19
+ %(<a href="#{ERB::Util.h(value)}" target="_blank" rel="noopener noreferrer">#{ERB::Util.h(label)}</a>)
20
20
  else
21
- %(<a href=#{ERB::Util.h(value)} target="_blank">#{Hyrax.config.license_service_class.new.label(value)}</a>)
21
+ ERB::Util.h(value)
22
22
  end
23
23
  end
24
24
  end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Hyrax
4
+ module Renderers
5
+ # Renders each redirect path as a clickable link. The link text is the full
6
+ # absolute URL (host + path); the href is the path alone, which the browser
7
+ # resolves against the current document's host. Used on show pages via the
8
+ # `redirects` schema's `render_as: redirects_label` view option.
9
+ class RedirectsLabelAttributeRenderer < AttributeRenderer
10
+ private
11
+
12
+ def li_value(value)
13
+ path = value.to_s
14
+ return ERB::Util.h(path) if path.blank?
15
+
16
+ display = base_url.present? ? "#{base_url}#{path}" : path
17
+ link_to(ERB::Util.h(display), path)
18
+ end
19
+
20
+ def base_url
21
+ options[:base_url].to_s.sub(%r{/+\z}, '')
22
+ end
23
+ end
24
+ end
25
+ end