hyrax 3.3.0 → 3.4.2

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 (255) hide show
  1. checksums.yaml +4 -4
  2. data/.circleci/config.yml +26 -17
  3. data/.dassie/.env +2 -1
  4. data/.dassie/Gemfile +1 -1
  5. data/.dassie/app/forms/collection_resource_form.rb +8 -0
  6. data/.dassie/app/indexers/collection_resource_indexer.rb +8 -0
  7. data/.dassie/app/models/collection_resource.rb +35 -0
  8. data/.dassie/config/initializers/file_services.rb +4 -0
  9. data/.dassie/config/initializers/hyrax.rb +12 -1
  10. data/.dassie/config/metadata/collection_resource.yaml +23 -0
  11. data/.dassie/db/seeds.rb +74 -17
  12. data/.dassie/spec/forms/collection_resource_form_spec.rb +13 -0
  13. data/.dassie/spec/indexers/collection_resource_indexer_spec.rb +14 -0
  14. data/.dassie/spec/models/collection_resource_spec.rb +13 -0
  15. data/.github/release.yml +26 -0
  16. data/.gitignore +3 -0
  17. data/.regen +1 -1
  18. data/.rubocop.yml +1 -1
  19. data/.rubocop_fixme.yml +22 -3
  20. data/CONTAINERS.md +18 -13
  21. data/Dockerfile +4 -3
  22. data/app/actors/hyrax/actors/file_actor.rb +6 -4
  23. data/app/actors/hyrax/actors/transfer_request_actor.rb +3 -7
  24. data/app/assets/javascripts/hyrax/analytics_events.js +8 -2
  25. data/app/assets/javascripts/hyrax/autocomplete/linked_data.es6 +1 -3
  26. data/app/assets/javascripts/hyrax/collections_v2.es6 +13 -0
  27. data/app/controllers/concerns/hyrax/collections_controller_behavior.rb +1 -3
  28. data/app/controllers/concerns/hyrax/controller.rb +21 -0
  29. data/app/controllers/concerns/hyrax/works_controller_behavior.rb +83 -59
  30. data/app/controllers/hyrax/admin/admin_sets_controller.rb +105 -19
  31. data/app/controllers/hyrax/admin/permission_template_accesses_controller.rb +12 -19
  32. data/app/controllers/hyrax/batch_edits_controller.rb +12 -3
  33. data/app/controllers/hyrax/batch_uploads_controller.rb +4 -0
  34. data/app/controllers/hyrax/citations_controller.rb +1 -1
  35. data/app/controllers/hyrax/dashboard/collections_controller.rb +176 -83
  36. data/app/controllers/hyrax/single_use_links_viewer_controller.rb +1 -1
  37. data/app/forms/hyrax/forms/administrative_set_form.rb +19 -1
  38. data/app/forms/hyrax/forms/batch_edit_form.rb +1 -1
  39. data/app/forms/hyrax/forms/collection_form.rb +1 -1
  40. data/app/forms/hyrax/forms/dashboard/nest_collection_form.rb +21 -6
  41. data/app/forms/hyrax/forms/pcdm_collection_form.rb +30 -2
  42. data/app/forms/hyrax/forms/permission_template_form.rb +17 -9
  43. data/app/forms/hyrax/forms/resource_form.rb +23 -5
  44. data/app/forms/hyrax/forms/widgets/admin_set_visibility.rb +1 -1
  45. data/app/helpers/hyrax/collections_helper.rb +14 -0
  46. data/app/helpers/hyrax/membership_helper.rb +1 -1
  47. data/app/helpers/hyrax/trophy_helper.rb +1 -1
  48. data/app/helpers/hyrax/url_helper.rb +1 -1
  49. data/app/indexers/hyrax/administrative_set_indexer.rb +8 -2
  50. data/app/indexers/hyrax/deep_indexing_service.rb +1 -1
  51. data/app/indexers/hyrax/file_set_indexer.rb +1 -0
  52. data/app/indexers/hyrax/pcdm_collection_indexer.rb +3 -2
  53. data/app/indexers/hyrax/thumbnail_indexer.rb +31 -0
  54. data/app/indexers/hyrax/valkyrie_file_set_indexer.rb +6 -6
  55. data/app/indexers/hyrax/valkyrie_indexer.rb +4 -2
  56. data/app/indexers/hyrax/valkyrie_work_indexer.rb +13 -0
  57. data/app/inputs/controlled_vocabulary_input.rb +2 -0
  58. data/app/jobs/change_depositor_event_job.rb +47 -0
  59. data/app/jobs/characterize_job.rb +43 -3
  60. data/app/jobs/concerns/hyrax/members_permission_job_behavior.rb +1 -1
  61. data/app/jobs/content_depositor_change_event_job.rb +2 -1
  62. data/app/jobs/hyrax/propagate_change_depositor_job.rb +32 -0
  63. data/app/jobs/import_url_job.rb +4 -6
  64. data/app/jobs/inherit_permissions_job.rb +1 -1
  65. data/app/jobs/valkyrie_create_derivatives_job.rb +25 -0
  66. data/app/jobs/valkyrie_ingest_job.rb +41 -35
  67. data/app/models/admin_set.rb +10 -2
  68. data/app/models/collection_branding_info.rb +8 -6
  69. data/app/models/concerns/hyrax/collection_behavior.rb +3 -3
  70. data/app/models/concerns/hyrax/file_set/characterization.rb +7 -1
  71. data/app/models/concerns/hyrax/solr_document/metadata.rb +1 -0
  72. data/app/models/concerns/hyrax/solr_document_behavior.rb +9 -3
  73. data/app/models/concerns/hyrax/user.rb +11 -0
  74. data/app/models/concerns/hyrax/work_behavior.rb +1 -1
  75. data/app/models/featured_work_list.rb +0 -1
  76. data/app/models/hyrax/administrative_set.rb +36 -1
  77. data/app/models/hyrax/collection_type.rb +2 -2
  78. data/app/models/hyrax/file_metadata.rb +37 -3
  79. data/app/models/hyrax/file_set.rb +43 -4
  80. data/app/models/hyrax/group.rb +19 -0
  81. data/app/models/hyrax/pcdm_collection.rb +56 -1
  82. data/app/models/hyrax/permission_template.rb +11 -5
  83. data/app/models/hyrax/work.rb +91 -0
  84. data/app/models/job_io_wrapper.rb +1 -1
  85. data/app/models/proxy_deposit_request.rb +1 -1
  86. data/app/presenters/hyrax/admin_set_presenter.rb +2 -2
  87. data/app/presenters/hyrax/member_presenter_factory.rb +2 -4
  88. data/app/presenters/hyrax/pcdm_member_presenter_factory.rb +2 -2
  89. data/app/presenters/hyrax/work_show_presenter.rb +10 -6
  90. data/app/search_builders/hyrax/dashboard/collections_search_builder.rb +2 -2
  91. data/app/search_builders/hyrax/dashboard/managed_search_filters.rb +44 -4
  92. data/app/search_builders/hyrax/dashboard/nested_collections_search_builder.rb +2 -2
  93. data/app/search_builders/hyrax/my/collections_search_builder.rb +11 -4
  94. data/app/services/hyrax/access_control_list.rb +20 -6
  95. data/app/services/hyrax/adapters/nesting_index_adapter.rb +3 -3
  96. data/app/services/hyrax/admin_set_create_service.rb +21 -37
  97. data/app/services/hyrax/change_content_depositor_service.rb +2 -2
  98. data/app/services/hyrax/change_depositor_service.rb +70 -0
  99. data/app/services/hyrax/characterization/valkyrie_characterization_service.rb +4 -6
  100. data/app/services/hyrax/collections/collection_member_service.rb +3 -5
  101. data/app/services/hyrax/collections/nested_collection_query_service.rb +24 -12
  102. data/app/services/hyrax/custom_queries/navigators/child_file_sets_navigator.rb +45 -0
  103. data/app/services/hyrax/custom_queries/navigators/child_filesets_navigator.rb +7 -2
  104. data/app/services/hyrax/custom_queries/navigators/parent_work_navigator.rb +54 -0
  105. data/app/services/hyrax/default_middleware_stack.rb +3 -0
  106. data/app/services/hyrax/file_set_derivatives_service.rb +21 -2
  107. data/app/services/hyrax/file_set_type_service.rb +2 -5
  108. data/app/services/hyrax/listeners/file_metadata_listener.rb +31 -1
  109. data/app/services/hyrax/listeners/member_cleanup_listener.rb +27 -11
  110. data/app/services/hyrax/listeners/metadata_index_listener.rb +39 -0
  111. data/app/services/hyrax/listeners/proxy_deposit_listener.rb +14 -8
  112. data/app/services/hyrax/location_service.rb +33 -0
  113. data/app/services/hyrax/multiple_membership_checker.rb +46 -1
  114. data/app/services/hyrax/resource_visibility_propagator.rb +1 -1
  115. data/app/services/hyrax/simple_schema_loader.rb +5 -1
  116. data/app/services/hyrax/solr_query_service.rb +12 -7
  117. data/app/services/hyrax/thumbnail_path_service.rb +1 -1
  118. data/app/services/hyrax/valkyrie_persist_derivatives.rb +50 -0
  119. data/app/services/hyrax/valkyrie_upload.rb +94 -0
  120. data/app/services/hyrax/work_uploads_handler.rb +0 -10
  121. data/app/services/hyrax/workflow/workflow_importer.rb +7 -9
  122. data/app/services/hyrax/workflow/workflow_schema.rb +3 -5
  123. data/app/strategies/hyrax/strategies/yaml_strategy.rb +4 -6
  124. data/app/uploaders/hyrax/uploaded_file_uploader.rb +4 -4
  125. data/app/utils/hyrax/data_destroyers/collection_branding_destroyer.rb +29 -0
  126. data/app/utils/hyrax/data_destroyers/collection_types_destroyer.rb +26 -0
  127. data/app/utils/hyrax/data_destroyers/default_admin_set_id_cache_destroyer.rb +26 -0
  128. data/app/utils/hyrax/data_destroyers/featured_works_destroyer.rb +27 -0
  129. data/app/utils/hyrax/data_destroyers/permission_templates_destroyer.rb +30 -0
  130. data/app/utils/hyrax/data_destroyers/repository_metadata_destroyer.rb +42 -0
  131. data/app/utils/hyrax/data_destroyers/stats_destroyer.rb +33 -0
  132. data/app/utils/hyrax/data_maintenance.rb +51 -0
  133. data/app/utils/hyrax/required_data_seeder.rb +21 -0
  134. data/app/utils/hyrax/required_data_seeders/collection_seeder.rb +26 -0
  135. data/app/utils/hyrax/required_data_seeders/collection_type_seeder.rb +36 -0
  136. data/app/utils/hyrax/test_data_seeder.rb +24 -0
  137. data/app/utils/hyrax/test_data_seeders/collection_seeder.rb +91 -0
  138. data/app/utils/hyrax/test_data_seeders/collection_type_seeder.rb +72 -0
  139. data/app/utils/hyrax/test_data_seeders/user_seeder.rb +52 -0
  140. data/app/validators/hyrax/collection_membership_validator.rb +39 -0
  141. data/app/views/catalog/_index_header_list_default.html.erb +8 -1
  142. data/app/views/catalog/_thumbnail_list_default.html.erb +8 -3
  143. data/app/views/collections/edit_fields/_based_near.html.erb +7 -7
  144. data/app/views/hyrax/admin/admin_sets/_form_participant_table.html.erb +2 -2
  145. data/app/views/hyrax/admin/admin_sets/_form_participants.html.erb +2 -2
  146. data/app/views/hyrax/admin/admin_sets/_form_visibility.html.erb +2 -2
  147. data/app/views/hyrax/admin/admin_sets/_form_workflow.erb +1 -1
  148. data/app/views/hyrax/admin/collection_types/index.html.erb +1 -1
  149. data/app/views/hyrax/base/_form.html.erb +1 -1
  150. data/app/views/hyrax/base/_form_child_work_relationships.html.erb +1 -1
  151. data/app/views/hyrax/dashboard/collections/_default_group.html.erb +2 -2
  152. data/app/views/hyrax/dashboard/collections/_form.html.erb +24 -17
  153. data/app/views/hyrax/dashboard/collections/_form_branding.html.erb +1 -0
  154. data/app/views/hyrax/dashboard/collections/_form_discovery.html.erb +6 -3
  155. data/app/views/hyrax/dashboard/collections/_form_share.html.erb +2 -2
  156. data/app/views/hyrax/dashboard/collections/_form_share_table.html.erb +3 -3
  157. data/app/views/hyrax/dashboard/collections/_list_collections.html.erb +2 -2
  158. data/app/views/hyrax/dashboard/sidebar/_activity.html.erb +1 -1
  159. data/app/views/hyrax/dashboard/works/_default_group.html.erb +1 -1
  160. data/app/views/hyrax/dashboard/works/_list_works.html.erb +1 -1
  161. data/app/views/hyrax/file_sets/_actions.html.erb +2 -2
  162. data/app/views/hyrax/my/_facet_pagination.html.erb +12 -9
  163. data/app/views/hyrax/my/_work_action_menu.html.erb +8 -9
  164. data/app/views/hyrax/my/collections/_default_group.html.erb +2 -2
  165. data/app/views/hyrax/my/collections/_list_collections.html.erb +2 -2
  166. data/app/views/hyrax/my/collections/index.html.erb +3 -2
  167. data/app/views/hyrax/my/works/_default_group.html.erb +1 -1
  168. data/app/views/hyrax/my/works/_list_works.html.erb +1 -2
  169. data/app/views/hyrax/my/works/_tabs.html.erb +6 -1
  170. data/app/views/hyrax/my/works/index.html.erb +4 -2
  171. data/chart/hyrax/Chart.yaml +11 -7
  172. data/chart/hyrax/README.md +22 -1
  173. data/chart/hyrax/templates/_helpers.tpl +4 -0
  174. data/chart/hyrax/templates/cron-embargo.yaml +5 -0
  175. data/chart/hyrax/templates/cron-lease.yaml +5 -0
  176. data/chart/hyrax/templates/deployment-worker.yaml +11 -0
  177. data/chart/hyrax/templates/ingress.yaml +7 -6
  178. data/chart/hyrax/values.yaml +152 -0
  179. data/config/features.rb +48 -50
  180. data/config/initializers/listeners.rb +0 -1
  181. data/config/initializers/{valkryrie_storage.rb → storage_adapter_initializer.rb} +5 -0
  182. data/config/locales/hyrax.de.yml +18 -17
  183. data/config/locales/hyrax.en.yml +30 -28
  184. data/config/locales/hyrax.es.yml +10 -9
  185. data/config/locales/hyrax.fr.yml +2 -1
  186. data/config/locales/hyrax.it.yml +3 -2
  187. data/config/locales/hyrax.pt-BR.yml +2 -1
  188. data/config/locales/hyrax.zh.yml +2 -1
  189. data/config/metadata/basic_metadata.yaml +2 -0
  190. data/config/metadata/core_metadata.yaml +1 -1
  191. data/docker-compose.yml +47 -42
  192. data/documentation/developing-your-hyrax-based-app.md +1 -1
  193. data/documentation/legacyREADME.md +1 -1
  194. data/hyrax.gemspec +5 -3
  195. data/lib/generators/hyrax/collection_resource/USAGE +20 -0
  196. data/lib/generators/hyrax/collection_resource/collection_resource_generator.rb +133 -0
  197. data/lib/generators/hyrax/collection_resource/templates/collection.rb.erb +34 -0
  198. data/lib/generators/hyrax/collection_resource/templates/collection_form.rb.erb +7 -0
  199. data/lib/generators/hyrax/collection_resource/templates/collection_form_spec.rb.erb +13 -0
  200. data/lib/generators/hyrax/collection_resource/templates/collection_indexer.rb.erb +7 -0
  201. data/lib/generators/hyrax/collection_resource/templates/collection_indexer_spec.rb.erb +13 -0
  202. data/lib/generators/hyrax/collection_resource/templates/collection_metadata.yaml +22 -0
  203. data/lib/generators/hyrax/collection_resource/templates/collection_spec.rb.erb +12 -0
  204. data/lib/generators/hyrax/install_generator.rb +9 -0
  205. data/lib/hyrax/administrative_set_name.rb +18 -0
  206. data/lib/hyrax/collection_name.rb +2 -0
  207. data/lib/hyrax/configuration.rb +22 -0
  208. data/lib/hyrax/controlled_vocabularies/location.rb +9 -2
  209. data/lib/hyrax/controlled_vocabularies/resource_label_caching.rb +42 -0
  210. data/lib/hyrax/controlled_vocabularies.rb +1 -0
  211. data/lib/hyrax/publisher.rb +49 -0
  212. data/lib/hyrax/schema.rb +16 -13
  213. data/lib/hyrax/specs/capybara.rb +1 -1
  214. data/lib/hyrax/specs/shared_specs/hydra_works.rb +11 -5
  215. data/lib/hyrax/specs/shared_specs/indexers.rb +117 -3
  216. data/lib/hyrax/transactions/admin_set_create.rb +2 -1
  217. data/lib/hyrax/transactions/admin_set_destroy.rb +22 -0
  218. data/lib/hyrax/transactions/admin_set_update.rb +21 -0
  219. data/lib/hyrax/transactions/collection_destroy.rb +22 -0
  220. data/lib/hyrax/transactions/collection_update.rb +5 -2
  221. data/lib/hyrax/transactions/container.rb +97 -23
  222. data/lib/hyrax/transactions/create_work.rb +3 -0
  223. data/lib/hyrax/transactions/destroy_work.rb +3 -0
  224. data/lib/hyrax/transactions/steps/apply_collection_permission_template.rb +2 -0
  225. data/lib/hyrax/transactions/steps/apply_permission_template.rb +2 -0
  226. data/lib/hyrax/transactions/steps/apply_visibility.rb +2 -0
  227. data/lib/hyrax/transactions/steps/change_depositor.rb +46 -0
  228. data/lib/hyrax/transactions/steps/check_for_empty_admin_set.rb +36 -0
  229. data/lib/hyrax/transactions/steps/delete_access_control.rb +32 -0
  230. data/lib/hyrax/transactions/steps/delete_resource.rb +19 -3
  231. data/lib/hyrax/transactions/steps/destroy_work.rb +3 -1
  232. data/lib/hyrax/transactions/steps/ensure_permission_template.rb +2 -0
  233. data/lib/hyrax/transactions/steps/save.rb +24 -6
  234. data/lib/hyrax/transactions/steps/save_access_control.rb +2 -2
  235. data/lib/hyrax/transactions/steps/save_collection_banner.rb +59 -0
  236. data/lib/hyrax/transactions/steps/save_collection_logo.rb +109 -0
  237. data/lib/hyrax/transactions/steps/save_work.rb +3 -0
  238. data/lib/hyrax/transactions/steps/set_user_as_creator.rb +41 -0
  239. data/lib/hyrax/transactions/steps/update_work_members.rb +51 -0
  240. data/lib/hyrax/transactions/update_work.rb +4 -3
  241. data/lib/hyrax/transactions/work_create.rb +1 -1
  242. data/lib/hyrax/transactions/work_destroy.rb +2 -1
  243. data/lib/hyrax/transactions/work_update.rb +19 -0
  244. data/lib/hyrax/version.rb +1 -1
  245. data/lib/wings/active_fedora_converter/file_metadata_node.rb +48 -0
  246. data/lib/wings/active_fedora_converter/instance_builder.rb +68 -0
  247. data/lib/wings/active_fedora_converter.rb +3 -3
  248. data/lib/wings/attribute_transformer.rb +5 -1
  249. data/lib/wings/services/custom_queries/find_file_metadata.rb +19 -8
  250. data/lib/wings/setup.rb +3 -1
  251. data/lib/wings/valkyrie/persister.rb +2 -0
  252. data/lib/wings/valkyrie/query_service.rb +6 -7
  253. data/lib/wings/valkyrie/storage.rb +7 -1
  254. data/template.rb +1 -1
  255. metadata +99 -12
@@ -43,13 +43,10 @@ module Hyrax
43
43
  # The search builder to find the collections' members
44
44
  self.membership_service_class = Collections::CollectionMemberSearchService
45
45
 
46
- load_and_authorize_resource except: [:index],
46
+ load_and_authorize_resource except: [:index, :create],
47
47
  instance_name: :collection,
48
48
  class: Hyrax.config.collection_model
49
49
 
50
- skip_load_resource only: :create if
51
- Hyrax.config.collection_class < ActiveFedora::Base
52
-
53
50
  def deny_collection_access(exception)
54
51
  if exception.action == :edit
55
52
  redirect_to(url_for(action: 'show'), alert: 'You do not have sufficient privileges to edit this document')
@@ -83,95 +80,66 @@ module Hyrax
83
80
 
84
81
  def edit
85
82
  form
83
+ collection_type
86
84
  end
87
85
 
88
86
  def after_create
89
- if @collection.is_a?(ActiveFedora::Base)
90
- form
91
- set_default_permissions
92
- # if we are creating the new collection as a subcollection (via the nested collections controller),
93
- # we pass the parent_id through a hidden field in the form and link the two after the create.
94
- link_parent_collection(params[:parent_id]) unless params[:parent_id].nil?
95
- end
96
- respond_to do |format|
97
- Hyrax::SolrService.commit
98
- format.html { redirect_to edit_dashboard_collection_path(@collection), notice: t('hyrax.dashboard.my.action.collection_create_success') }
99
- format.json { render json: @collection, status: :created, location: dashboard_collection_path(@collection) }
100
- end
87
+ Deprecation.warn("Method `#after_create` will be removed in Hyrax 4.0.")
88
+ after_create_response # call private method for processing
101
89
  end
102
90
 
103
91
  def after_create_error
104
- form
105
- respond_to do |format|
106
- format.html { render action: 'new' }
107
- format.json { render json: @collection.errors, status: :unprocessable_entity }
108
- end
92
+ Deprecation.warn("Method `#after_create_error` will be removed in Hyrax 4.0.")
93
+ after_create_errors("") # call private method for processing
109
94
  end
110
95
 
111
96
  def create
112
- return valkyrie_create if @collection.is_a?(Valkyrie::Resource)
113
97
  # Manual load and authorize necessary because Cancan will pass in all
114
98
  # form attributes. When `permissions_attributes` are present the
115
99
  # collection is saved without a value for `has_model.`
116
100
  @collection = Hyrax.config.collection_class.new
117
101
  authorize! :create, @collection
118
- # Coming from the UI, a collection type gid should always be present. Coming from the API, if a collection type gid is not specified,
119
- # use the default collection type (provides backward compatibility with versions < Hyrax 2.1.0)
120
- @collection.collection_type_gid = params[:collection_type_gid].presence || default_collection_type.to_global_id
121
- @collection.attributes = collection_params.except(:members, :parent_id, :collection_type_gid)
122
- @collection.apply_depositor_metadata(current_user.user_key)
123
- @collection.visibility = Hydra::AccessControls::AccessRight::VISIBILITY_TEXT_VALUE_PRIVATE unless @collection.discoverable?
124
- if @collection.save
125
- after_create
126
- add_members_to_collection unless batch.empty?
102
+
103
+ case @collection
104
+ when ActiveFedora::Base
105
+ create_active_fedora_collection
127
106
  else
128
- after_create_error
107
+ create_valkyrie_collection
129
108
  end
130
109
  end
131
110
 
132
111
  def after_update
133
- respond_to do |format|
134
- format.html { redirect_to update_referer, notice: t('hyrax.dashboard.my.action.collection_update_success') }
135
- format.json { render json: @collection, status: :updated, location: dashboard_collection_path(@collection) }
136
- end
112
+ Deprecation.warn("Method `#after_update` will be removed in Hyrax 4.0.")
113
+ after_update_response # call private method for processing
137
114
  end
138
115
 
139
116
  def after_update_error
140
- form
141
- respond_to do |format|
142
- format.html { render action: 'edit' }
143
- format.json { render json: @collection.errors, status: :unprocessable_entity }
144
- end
117
+ Deprecation.warn("Method `#after_update_error` will be removed in Hyrax 4.0.")
118
+ after_update_errors(@collection.errors) # call private method for processing
145
119
  end
146
120
 
147
121
  def update
148
- unless params[:update_collection].nil?
149
- process_banner_input
150
- process_logo_input
151
- end
152
-
153
- process_member_changes
154
-
155
- return valkyrie_update if @collection.is_a?(Valkyrie::Resource)
156
-
157
- @collection.visibility = Hydra::AccessControls::AccessRight::VISIBILITY_TEXT_VALUE_PRIVATE unless @collection.discoverable?
158
- # we don't have to reindex the full graph when updating collection
159
- @collection.reindex_extent = Hyrax::Adapters::NestingIndexAdapter::LIMITED_REINDEX
160
- if @collection.update(collection_params.except(:members))
161
- after_update
122
+ case @collection
123
+ when ActiveFedora::Base
124
+ update_active_fedora_collection
162
125
  else
163
- after_update_error
126
+ update_valkyrie_collection
164
127
  end
165
128
  end
166
129
 
130
+ def process_branding
131
+ process_banner_input
132
+ process_logo_input
133
+ end
134
+
167
135
  def after_destroy(_id)
168
136
  # leaving id to avoid changing the method's parameters prior to release
169
137
  respond_to do |format|
170
138
  format.html do
171
- redirect_to my_collections_path,
139
+ redirect_to hyrax.my_collections_path,
172
140
  notice: t('hyrax.dashboard.my.action.collection_delete_success')
173
141
  end
174
- format.json { head :no_content, location: my_collections_path }
142
+ format.json { head :no_content, location: hyrax.my_collections_path }
175
143
  end
176
144
  end
177
145
 
@@ -188,8 +156,7 @@ module Hyrax
188
156
  def destroy
189
157
  case @collection
190
158
  when Valkyrie::Resource
191
- Hyrax.persister.delete(resource: @collection)
192
- after_destroy(params[:id])
159
+ valkyrie_destroy
193
160
  else
194
161
  if @collection.destroy
195
162
  after_destroy(params[:id])
@@ -217,28 +184,82 @@ module Hyrax
217
184
 
218
185
  private
219
186
 
220
- def valkyrie_create
221
- form.validate(collection_params) &&
222
- @collection = transactions['change_set.create_collection']
223
- .with_step_args(
224
- 'change_set.set_user_as_depositor' => { user: current_user },
225
- 'change_set.add_to_collections' => { collection_ids: Array(params[:parent_id]) },
226
- 'collection_resource.apply_collection_type_permissions' => { user: current_user }
187
+ def create_active_fedora_collection
188
+ # Coming from the UI, a collection type gid should always be present. Coming from the API, if a collection type gid is not specified,
189
+ # use the default collection type (provides backward compatibility with versions < Hyrax 2.1.0)
190
+ @collection.collection_type_gid = params[:collection_type_gid].presence || default_collection_type.to_global_id
191
+ @collection.attributes = collection_params.except(:members, :parent_id, :collection_type_gid)
192
+ @collection.apply_depositor_metadata(current_user.user_key)
193
+ @collection.visibility = Hydra::AccessControls::AccessRight::VISIBILITY_TEXT_VALUE_PRIVATE unless @collection.discoverable?
194
+ if @collection.save
195
+ after_create_response
196
+ else
197
+ after_create_errors(@collection.errors)
198
+ end
199
+ end
200
+
201
+ def create_valkyrie_collection
202
+ return after_create_errors(form_err_msg(form)) unless form.validate(collection_params)
203
+
204
+ result =
205
+ transactions['change_set.create_collection']
206
+ .with_step_args(
207
+ 'change_set.set_user_as_depositor' => { user: current_user },
208
+ 'change_set.add_to_collections' => { collection_ids: Array(params[:parent_id]) },
209
+ 'collection_resource.apply_collection_type_permissions' => { user: current_user }
210
+ )
211
+ .call(form)
212
+
213
+ @collection = result.value_or { return after_create_errors(result.failure.first) }
214
+ after_create_response
215
+ end
216
+
217
+ def update_active_fedora_collection
218
+ process_member_changes
219
+ process_branding
220
+
221
+ @collection.visibility = Hydra::AccessControls::AccessRight::VISIBILITY_TEXT_VALUE_PRIVATE unless @collection.discoverable?
222
+ # we don't have to reindex the full graph when updating collection
223
+ @collection.reindex_extent = Hyrax::Adapters::NestingIndexAdapter::LIMITED_REINDEX
224
+ if @collection.update(collection_params.except(:members))
225
+ after_update_response
226
+ else
227
+ after_update_errors(@collection.errors)
228
+ end
229
+ end
230
+
231
+ def update_valkyrie_collection
232
+ return after_update_errors(form_err_msg(form)) unless form.validate(collection_params)
233
+
234
+ result = transactions['change_set.update_collection']
235
+ .with_step_args(
236
+ 'collection_resource.save_collection_banner' => { update_banner_file_ids: params["banner_files"],
237
+ banner_unchanged_indicator: params["banner_unchanged"] },
238
+ 'collection_resource.save_collection_logo' => { update_logo_file_ids: params["logo_files"],
239
+ alttext_values: params["alttext"],
240
+ linkurl_values: params["linkurl"] }
227
241
  )
228
- .call(form)
229
- .value_or { return after_create_error }
242
+ .call(form)
243
+ @collection = result.value_or { return after_update_errors(result.failure.first) }
230
244
 
231
- after_create
232
- add_members_to_collection unless batch.empty?
233
- @collection
245
+ process_member_changes
246
+ after_update_response
247
+ end
248
+
249
+ def valkyrie_destroy
250
+ if transactions['collection_resource.destroy'].call(@collection).success?
251
+ after_destroy(params[:id])
252
+ else
253
+ after_destroy_error(params[:id])
254
+ end
234
255
  end
235
256
 
236
- def valkyrie_update
237
- form.validate(collection_params) &&
238
- @collection = transactions['change_set.update_collection']
239
- .call(form)
240
- .value_or { return after_update_error }
241
- after_update
257
+ def form_err_msg(form)
258
+ errmsg = []
259
+ form.errors.messages.each do |fld, err|
260
+ errmsg << "#{fld} #{err.to_sentence}"
261
+ end
262
+ errmsg.to_sentence
242
263
  end
243
264
 
244
265
  def default_collection_type
@@ -332,7 +353,10 @@ module Hyrax
332
353
  def process_logo_records(uploaded_file_ids)
333
354
  public_files = []
334
355
  uploaded_file_ids.each_with_index do |ufi, i|
335
- if ufi.include?('public')
356
+ # If the user has chosen a new logo, the ufi will be an integer
357
+ # If the logo was previously chosen, the ufi will be a path
358
+ # If it is a path, update the rec, else create a new rec
359
+ if !ufi.match(/\D/).nil?
336
360
  update_logo_info(ufi, params["alttext"][i], verify_linkurl(params["linkurl"][i]))
337
361
  public_files << ufi
338
362
  else # brand new one, insert in the database
@@ -367,9 +391,7 @@ module Hyrax
367
391
  end
368
392
 
369
393
  def presenter
370
- @presenter ||= begin
371
- presenter_class.new(curation_concern, current_ability)
372
- end
394
+ @presenter ||= presenter_class.new(curation_concern, current_ability)
373
395
  end
374
396
 
375
397
  def curation_concern
@@ -490,7 +512,9 @@ module Hyrax
490
512
  @form ||=
491
513
  case @collection
492
514
  when Valkyrie::Resource
493
- Hyrax::Forms::ResourceForm.for(@collection)
515
+ form = Hyrax::Forms::ResourceForm.for(@collection)
516
+ form.prepopulate!
517
+ form
494
518
  else
495
519
  form_class.new(@collection, current_ability, repository)
496
520
  end
@@ -553,6 +577,75 @@ module Hyrax
553
577
  def valid_url?(url)
554
578
  (url =~ URI.regexp(['http', 'https']))
555
579
  end
580
+
581
+ def after_create_response
582
+ if @collection.is_a?(ActiveFedora::Base)
583
+ form
584
+ set_default_permissions
585
+ # if we are creating the new collection as a subcollection (via the nested collections controller),
586
+ # we pass the parent_id through a hidden field in the form and link the two after the create.
587
+ link_parent_collection(params[:parent_id]) unless params[:parent_id].nil?
588
+ end
589
+ respond_to do |format|
590
+ Hyrax::SolrService.commit
591
+ format.html { redirect_to edit_dashboard_collection_path(@collection), notice: t('hyrax.dashboard.my.action.collection_create_success') }
592
+ format.json { render json: @collection, status: :created, location: dashboard_collection_path(@collection) }
593
+ end
594
+ add_members_to_collection unless batch.empty?
595
+ end
596
+
597
+ def after_create_errors_for_active_fedora(errors)
598
+ form
599
+ respond_to do |format|
600
+ format.html do
601
+ flash[:error] = errors.to_s
602
+ render action: 'new'
603
+ end
604
+ format.json { render json: @collection.errors, status: :unprocessable_entity }
605
+ end
606
+ end
607
+
608
+ def after_create_errors(errors) # for valkyrie
609
+ return after_create_errors_for_active_fedora(errors) if @collection.is_a? ActiveFedora::Base
610
+ respond_to do |wants|
611
+ wants.html do
612
+ flash[:error] = errors.to_s
613
+ render 'new', status: :unprocessable_entity
614
+ end
615
+ wants.json do
616
+ render_json_response(response_type: :unprocessable_entity, options: { errors: errors })
617
+ end
618
+ end
619
+ end
620
+
621
+ def after_update_response
622
+ respond_to do |format|
623
+ format.html { redirect_to update_referer, notice: t('hyrax.dashboard.my.action.collection_update_success') }
624
+ format.json { render json: @collection, status: :updated, location: dashboard_collection_path(@collection) }
625
+ end
626
+ end
627
+
628
+ def after_update_errors_for_active_fedora(errors)
629
+ form
630
+ respond_to do |format|
631
+ format.html do
632
+ flash[:error] = errors.to_s
633
+ render action: 'edit'
634
+ end
635
+ format.json { render json: @collection.errors, status: :unprocessable_entity }
636
+ end
637
+ end
638
+
639
+ def after_update_errors(errors) # for valkyrie
640
+ return after_update_errors_for_active_fedora(errors) if @collection.is_a? ActiveFedora::Base
641
+ respond_to do |wants|
642
+ wants.html do
643
+ flash[:error] = errors.to_s
644
+ render 'edit', status: :unprocessable_entity
645
+ end
646
+ wants.json { render_json_response(response_type: :unprocessable_entity, options: { errors: errors }) }
647
+ end
648
+ end
556
649
  end
557
650
  end
558
651
  end
@@ -75,7 +75,7 @@ module Hyrax
75
75
 
76
76
  def render_single_use_error(exception)
77
77
  logger.error("Rendering PAGE due to exception: #{exception.inspect} - #{exception.backtrace if exception.respond_to? :backtrace}")
78
- render 'single_use_error', layout: "error", status: 404
78
+ render 'single_use_error', layout: "error", status: :not_found
79
79
  end
80
80
 
81
81
  def _prefixes
@@ -6,6 +6,20 @@ module Hyrax
6
6
  # @api public
7
7
  # @see https://github.com/samvera/valkyrie/wiki/ChangeSets-and-Dirty-Tracking
8
8
  class AdministrativeSetForm < Valkyrie::ChangeSet
9
+ ##
10
+ # @api private
11
+ AdminSetMembersPopulator = lambda do |_options|
12
+ self.member_ids =
13
+ if model.new_record
14
+ []
15
+ else
16
+ Hyrax
17
+ .query_service
18
+ .find_inverse_references_by(property: :admin_set_id, resource: model)
19
+ .map(&:id)
20
+ end
21
+ end
22
+
9
23
  property :title, required: true, primary: true
10
24
  property :description, primary: true
11
25
 
@@ -13,7 +27,11 @@ module Hyrax
13
27
  property :date_modified, readable: false
14
28
  property :date_uploaded, readable: false
15
29
 
16
- property :depositor
30
+ property :creator
31
+
32
+ validates :title, presence: true
33
+
34
+ property :member_ids, virtual: true, default: [], prepopulator: AdminSetMembersPopulator
17
35
 
18
36
  class << self
19
37
  def model_class
@@ -65,7 +65,7 @@ module Hyrax
65
65
  def initialize_combined_fields
66
66
  # For each of the files in the batch, set the attributes to be the concatenation of all the attributes
67
67
  batch_document_ids.each_with_object({}) do |doc_id, combined_attributes|
68
- work = ActiveFedora::Base.find(doc_id)
68
+ work = Hyrax.query_service.find_by(id: doc_id)
69
69
  terms.each do |field|
70
70
  combined_attributes[field] ||= []
71
71
  combined_attributes[field] = (combined_attributes[field] + work[field].to_a).uniq
@@ -9,7 +9,7 @@ module Hyrax
9
9
  attr_reader :scope
10
10
 
11
11
  delegate :id, :depositor, :permissions, :human_readable_type, :member_ids, :nestable?,
12
- :alternative_title, to: :model
12
+ :alternative_title, :visibility, to: :model
13
13
 
14
14
  class_attribute :membership_service_class
15
15
 
@@ -26,11 +26,11 @@ module Hyrax
26
26
  context:,
27
27
  query_service: default_query_service,
28
28
  persistence_service: default_persistence_service)
29
- self.parent = parent || (parent_id.present? && Hyrax.config.collection_class.find(parent_id))
30
- self.child = child || (child_id.present? && Hyrax.config.collection_class.find(child_id))
31
29
  self.context = context
32
30
  self.query_service = query_service
33
31
  self.persistence_service = persistence_service
32
+ self.parent = parent || (parent_id.present? && find_parent(parent_id))
33
+ self.child = child || (child_id.present? && find_child(child_id))
34
34
  end # rubocop:enable Metrics/ParameterLists
35
35
 
36
36
  attr_accessor :parent, :child
@@ -83,7 +83,7 @@ module Hyrax
83
83
  # rerouting to new_dashboard_collection_path to add the new collection as
84
84
  # a child. Since we don't yet have a child collection, the valid? option can't be used here.
85
85
  def validate_add
86
- if parent.try(:nestable?)
86
+ if nestable?(parent)
87
87
  nesting_within_maximum_depth
88
88
  else
89
89
  errors.add(:parent, :cannot_have_child_nested)
@@ -116,15 +116,30 @@ module Hyrax
116
116
  end
117
117
 
118
118
  def parent_and_child_can_be_nested
119
- if parent.try(:nestable?) && child.try(:nestable?)
119
+ if nestable?(parent) && nestable?(child)
120
120
  return true if query_service.parent_and_child_can_nest?(parent: parent, child: child, scope: context)
121
121
  errors.add(:parent, :cannot_have_child_nested)
122
122
  errors.add(:child, :cannot_nest_in_parent)
123
123
  else
124
- errors.add(:parent, :is_not_nestable) unless parent.try(:nestable?)
125
- errors.add(:child, :is_not_nestable) unless child.try(:nestable?)
124
+ errors.add(:parent, :is_not_nestable) unless nestable?(parent)
125
+ errors.add(:child, :is_not_nestable) unless nestable?(child)
126
126
  end
127
127
  end
128
+
129
+ def find_parent(parent_id)
130
+ Hyrax.query_service.find_by(id: parent_id)
131
+ end
132
+
133
+ def find_child(child_id)
134
+ Hyrax.query_service.find_by(id: child_id)
135
+ end
136
+
137
+ def nestable?(collection)
138
+ return false if collection.blank?
139
+ return collection.nestable? if collection.respond_to? :nestable?
140
+ collection_type = Hyrax::CollectionType.find_by_gid!(collection.collection_type_gid)
141
+ collection_type.nestable?
142
+ end
128
143
  end
129
144
  end
130
145
  end
@@ -6,7 +6,32 @@ module Hyrax
6
6
  # @api public
7
7
  # @see https://github.com/samvera/valkyrie/wiki/ChangeSets-and-Dirty-Tracking
8
8
  class PcdmCollectionForm < Valkyrie::ChangeSet # rubocop:disable Metrics/ClassLength
9
- property :title, required: true, primary: true
9
+ include Hyrax::FormFields(:core_metadata)
10
+
11
+ BannerInfoPrepopulator = lambda do |_options|
12
+ self.banner_info ||= begin
13
+ banner_info = CollectionBrandingInfo.where(collection_id: id.to_s, role: "banner")
14
+ banner_file = File.split(banner_info.first.local_path).last unless banner_info.empty?
15
+ alttext = banner_info.first.alt_text unless banner_info.empty?
16
+ file_location = banner_info.first.local_path unless banner_info.empty?
17
+ relative_path = "/" + banner_info.first.local_path.split("/")[-4..-1].join("/") unless banner_info.empty?
18
+ { file: banner_file, full_path: file_location, relative_path: relative_path, alttext: alttext }
19
+ end
20
+ end
21
+
22
+ LogoInfoPrepopulator = lambda do |_options|
23
+ self.logo_info ||= begin
24
+ logos_info = CollectionBrandingInfo.where(collection_id: id.to_s, role: "logo")
25
+
26
+ logos_info.map do |logo_info|
27
+ logo_file = File.split(logo_info.local_path).last
28
+ relative_path = "/" + logo_info.local_path.split("/")[-4..-1].join("/")
29
+ alttext = logo_info.alt_text
30
+ linkurl = logo_info.target_url
31
+ { file: logo_file, full_path: logo_info.local_path, relative_path: relative_path, alttext: alttext, linkurl: linkurl }
32
+ end
33
+ end
34
+ end
10
35
 
11
36
  property :human_readable_type, writable: false
12
37
  property :date_modified, readable: false
@@ -14,12 +39,15 @@ module Hyrax
14
39
 
15
40
  property :depositor, required: true
16
41
  property :collection_type_gid, required: true
42
+ property :visibility, default: VisibilityIntention::PRIVATE
17
43
 
18
44
  property :member_of_collection_ids, default: [], type: Valkyrie::Types::Array
19
45
 
20
- validates :title, presence: true
21
46
  validates :collection_type_gid, presence: true
22
47
 
48
+ property :banner_info, virtual: true, prepopulator: BannerInfoPrepopulator
49
+ property :logo_info, virtual: true, prepopulator: LogoInfoPrepopulator
50
+
23
51
  class << self
24
52
  def model_class
25
53
  Hyrax::PcdmCollection
@@ -7,16 +7,12 @@ module Hyrax
7
7
  self.model_class = PermissionTemplate
8
8
  self.terms = []
9
9
  delegate :access_grants, :access_grants_attributes=, :release_date, :release_period, :visibility, to: :model
10
- delegate :available_workflows, :active_workflow, :source_model, to: :model
11
-
12
- # @return [#to_s] the primary key of the associated admin_set or collection
13
- # def source_id (because you might come looking for this method)
14
- delegate :id, to: :source_model, prefix: :source
10
+ delegate :available_workflows, :active_workflow, :source, :source_id, to: :model
15
11
 
16
12
  ##
17
- # @deprecated use PermissionTemplate#reset_access_controls instead.
13
+ # @deprecated use PermissionTemplate#reset_access_controls_for instead.
18
14
  def reset_access_controls!
19
- Deprecation.warn("reset_access_controls! is deprecated; use PermissionTemplate#reset_access_controls instead.")
15
+ Deprecation.warn("reset_access_controls! is deprecated; use PermissionTemplate#reset_access_controls_for instead.")
20
16
  source_model.reset_access_controls!
21
17
  end
22
18
 
@@ -73,8 +69,8 @@ module Hyrax
73
69
  # Copy this access to the permissions of the Admin Set or Collection and to
74
70
  # the WorkflowResponsibilities of the active workflow if this is an Admin Set
75
71
  def update_access(remove_agent: false)
76
- source_model.reset_access_controls!
77
- update_workflow_responsibilities(remove_agent: remove_agent) if source_model.is_a?(AdminSet)
72
+ model.reset_access_controls_for(collection: source)
73
+ update_workflow_responsibilities(remove_agent: remove_agent) if source.is_a?(Hyrax::AdministrativeSet)
78
74
  end
79
75
 
80
76
  # This method is used to revoke access to a Collection or Admin Set and its workflows
@@ -85,6 +81,18 @@ module Hyrax
85
81
  update_access(remove_agent: true)
86
82
  end
87
83
 
84
+ ##
85
+ # A bit of an analogue for a `belongs_to :source_model` as it crosses from Fedora to the DB
86
+ # @return [AdminSet, ::Collection]
87
+ # @raise [Hyrax::ObjectNotFoundError] when neither an AdminSet or Collection is found
88
+ # @note This method will eventually be replaced by #source which returns a Hyrax::Resource
89
+ # object. Many methods are equally able to process both Hyrax::Resource and
90
+ # ActiveFedora::Base. Only call this method if you need the ActiveFedora::Base object.
91
+ # @see #source
92
+ def source_model # rubocop:disable Rails/Delegate
93
+ model.source_model
94
+ end
95
+
88
96
  private
89
97
 
90
98
  # @return [String]
@@ -33,7 +33,7 @@ module Hyrax
33
33
  class ResourceForm < Hyrax::ChangeSet # rubocop:disable Metrics/ClassLength
34
34
  ##
35
35
  # @api private
36
- InWorksPopulator = lambda do |_options|
36
+ InWorksPrepopulator = lambda do |_options|
37
37
  self.in_works_ids =
38
38
  if persisted?
39
39
  Hyrax.query_service
@@ -52,7 +52,7 @@ module Hyrax
52
52
  # with `etag`-driven, application-side lock checks. for non-wings adapters
53
53
  # we want to move away from application side lock validation and rely
54
54
  # on the adapter/database features instead.
55
- LockKeyPopulator = lambda do |_options|
55
+ LockKeyPrepopulator = lambda do |_options|
56
56
  if Hyrax.config.disable_wings || !Hyrax.metadata_adapter.is_a?(Wings::Valkyrie::MetadataAdapter)
57
57
  Hyrax.logger.info "trying to prepopulate a lock token for " \
58
58
  "#{self.class.inspect}, but optimistic locking isn't " \
@@ -95,10 +95,12 @@ module Hyrax
95
95
  property :visibility_during_lease, virtual: true, prepopulator: ->(_opts) { self.visibility_during_lease = model.lease&.visibility_during_lease }
96
96
 
97
97
  # pcdm relationships
98
- property :admin_set_id, prepopulator: ->(_opts) { self.admin_set_id = AdminSet::DEFAULT_ID }
99
- property :in_works_ids, virtual: true, prepopulator: InWorksPopulator
98
+ property :admin_set_id, prepopulator: ->(_opts) { self.admin_set_id = Hyrax::AdminSetCreateService.find_or_create_default_admin_set.id.to_s }
99
+ property :in_works_ids, virtual: true, prepopulator: InWorksPrepopulator
100
100
  property :member_ids, default: [], type: Valkyrie::Types::Array
101
101
  property :member_of_collection_ids, default: [], type: Valkyrie::Types::Array
102
+ property :member_of_collections_attributes, virtual: true, populator: :in_collections_populator
103
+ validates_with CollectionMembershipValidator
102
104
 
103
105
  property :representative_id, type: Valkyrie::Types::String
104
106
  property :thumbnail_id, type: Valkyrie::Types::String
@@ -112,7 +114,7 @@ module Hyrax
112
114
  # the model is disabled
113
115
  #
114
116
  # @see https://github.com/samvera/valkyrie/wiki/Optimistic-Locking
115
- property :version, virtual: true, prepopulator: LockKeyPopulator
117
+ property :version, virtual: true, prepopulator: LockKeyPrepopulator
116
118
 
117
119
  # backs the child work search element;
118
120
  # @todo: look for a way for the view template not to depend on this
@@ -131,6 +133,8 @@ module Hyrax
131
133
  "#{resource.class.name}Form".constantize.new(resource)
132
134
  rescue NameError => _err
133
135
  case resource
136
+ when Hyrax::AdministrativeSet
137
+ Hyrax::Forms::AdministrativeSetForm.new(resource)
134
138
  when Hyrax::FileSet
135
139
  Hyrax::Forms::FileSetForm.new(resource)
136
140
  when Hyrax::PcdmCollection
@@ -204,6 +208,20 @@ module Hyrax
204
208
 
205
209
  private
206
210
 
211
+ def in_collections_populator(fragment:, **_options)
212
+ adds = []
213
+ deletes = []
214
+ fragment.each do |_, h|
215
+ if h["_destroy"] == "true"
216
+ deletes << Valkyrie::ID.new(h["id"])
217
+ else
218
+ adds << Valkyrie::ID.new(h["id"])
219
+ end
220
+ end
221
+
222
+ self.member_of_collection_ids = ((member_of_collection_ids + adds) - deletes).uniq
223
+ end
224
+
207
225
  # https://trailblazer.to/2.1/docs/reform.html#reform-populators-populator-collections
208
226
  def permission_populator(collection:, index:, **)
209
227
  Hyrax::Forms::Permission.new(collection[index])
@@ -6,7 +6,7 @@ module Hyrax
6
6
  # Visibility options for permission templates
7
7
  def options
8
8
  i18n_prefix = "hyrax.admin.admin_sets.form_visibility.visibility"
9
- # Note: Visibility 'varies' = '' implies no constraints
9
+ # NOTE: Visibility 'varies' = '' implies no constraints
10
10
  [[Hydra::AccessControls::AccessRight::VISIBILITY_TEXT_VALUE_PUBLIC, I18n.t('.everyone', scope: i18n_prefix)],
11
11
  ['', I18n.t('.varies', scope: i18n_prefix)],
12
12
  [Hydra::AccessControls::AccessRight::VISIBILITY_TEXT_VALUE_AUTHENTICATED, I18n.t('.institution', scope: i18n_prefix)],