hyrax 5.0.1 → 5.0.3

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 (247) hide show
  1. checksums.yaml +4 -4
  2. data/.circleci/config.yml +7 -176
  3. data/.dassie/.env +8 -3
  4. data/.dassie/Gemfile +13 -2
  5. data/.dassie/app/controllers/hyrax/generic_work_resources_controller.rb +17 -0
  6. data/.dassie/app/controllers/hyrax/generic_works_controller.rb +7 -1
  7. data/.dassie/app/forms/generic_work_resource_form.rb +20 -0
  8. data/.dassie/app/indexers/generic_work_resource_indexer.rb +16 -0
  9. data/.dassie/app/models/admin_set_resource.rb +9 -0
  10. data/.dassie/app/models/collection_resource.rb +2 -0
  11. data/.dassie/app/models/file_set.rb +2 -0
  12. data/.dassie/app/models/generic_work_resource.rb +10 -0
  13. data/.dassie/app/views/hyrax/generic_work_resources/_generic_work_resource.html.erb +2 -0
  14. data/.dassie/config/analytics.yml +6 -1
  15. data/.dassie/config/application.rb +24 -0
  16. data/.dassie/config/initializers/hyrax.rb +13 -3
  17. data/.dassie/config/initializers/wings.rb +109 -0
  18. data/.dassie/config/metadata/generic_work_resource.yaml +22 -0
  19. data/.dassie/config/valkyrie_index.yml +4 -10
  20. data/.dassie/db/migrate/20240506070809_valkyrie_id_to_string.rb +5 -0
  21. data/.dassie/db/schema.rb +2 -2
  22. data/.dassie/spec/indexers/generic_work_resource_indexer_spec.rb +13 -0
  23. data/.dassie/spec/models/generic_work_resource_spec.rb +12 -0
  24. data/.dassie/spec/views/generic_work_resources/_generic_work_resource.html.erb_spec.rb +7 -0
  25. data/.dockerignore +6 -4
  26. data/.github/release.yml +3 -0
  27. data/.github/workflows/lint-build-test.yml +130 -0
  28. data/.github/workflows/test-results.yml +40 -0
  29. data/.koppie/.env +7 -5
  30. data/.koppie/Gemfile +12 -1
  31. data/.koppie/config/analytics.yml +6 -1
  32. data/.koppie/config/environments/test.rb +2 -0
  33. data/.koppie/config/initializers/1_valkyrie.rb +6 -2
  34. data/.koppie/config/solr.yml +1 -1
  35. data/.regen +1 -1
  36. data/.rubocop.yml +5 -0
  37. data/Dockerfile +16 -36
  38. data/Gemfile +2 -0
  39. data/app/assets/javascripts/hydra-editor/field_manager.es6 +187 -0
  40. data/app/assets/javascripts/hyrax/analytics_events.js +48 -24
  41. data/app/assets/javascripts/hyrax/collapse.js +4 -4
  42. data/app/assets/javascripts/hyrax/file_manager/save_manager.es6 +2 -0
  43. data/app/assets/javascripts/hyrax/search.js +2 -3
  44. data/app/assets/javascripts/hyrax/select_work_type.es6 +3 -1
  45. data/app/assets/javascripts/hyrax/uploader.js +20 -18
  46. data/app/assets/javascripts/hyrax.js +1 -0
  47. data/app/assets/stylesheets/_bootstrap-default-overrides.scss +4 -0
  48. data/app/assets/stylesheets/hyrax/_card.scss +4 -0
  49. data/app/assets/stylesheets/hyrax/_catalog.scss +21 -0
  50. data/app/assets/stylesheets/hyrax/_collections.scss +1 -1
  51. data/app/assets/stylesheets/hyrax/_facets.scss +15 -3
  52. data/app/assets/stylesheets/hyrax/_featured.scss +4 -0
  53. data/app/assets/stylesheets/hyrax/_form.scss +4 -0
  54. data/app/assets/stylesheets/hyrax/_forms.scss +2 -1
  55. data/app/assets/stylesheets/hyrax/_nestable.scss +9 -8
  56. data/app/assets/stylesheets/hyrax/_select_work_type.scss +12 -0
  57. data/app/assets/stylesheets/hyrax/_styles.scss +4 -0
  58. data/app/assets/stylesheets/hyrax/_work-show.scss +3 -0
  59. data/app/controllers/concerns/hyrax/singular_subresource_controller.rb +7 -2
  60. data/app/controllers/concerns/hyrax/valkyrie_downloads_controller_behavior.rb +11 -2
  61. data/app/controllers/concerns/hyrax/works_controller_behavior.rb +9 -2
  62. data/app/controllers/hyrax/admin/analytics/collection_reports_controller.rb +2 -2
  63. data/app/controllers/hyrax/admin/analytics/work_reports_controller.rb +7 -8
  64. data/app/controllers/hyrax/dashboard/collections_controller.rb +2 -1
  65. data/app/controllers/hyrax/downloads_controller.rb +24 -3
  66. data/app/controllers/hyrax/file_sets_controller.rb +32 -6
  67. data/app/controllers/hyrax/my/works_controller.rb +20 -0
  68. data/app/controllers/hyrax/stats_controller.rb +1 -1
  69. data/app/controllers/hyrax/uploads_controller.rb +28 -2
  70. data/app/forms/hyrax/forms/admin/appearance.rb +1 -1
  71. data/app/forms/hyrax/forms/admin/collection_type_form.rb +1 -7
  72. data/app/forms/hyrax/forms/pcdm_collection_form.rb +9 -0
  73. data/app/forms/hyrax/forms/work_embargo_form.rb +6 -0
  74. data/app/forms/hyrax/forms/work_lease_form.rb +6 -0
  75. data/app/indexers/concerns/hyrax/location_indexer.rb +2 -2
  76. data/app/indexers/hyrax/indexers/file_set_indexer.rb +4 -0
  77. data/app/indexers/hyrax/indexers/resource_indexer.rb +1 -0
  78. data/app/indexers/hyrax/valkyrie_indexer.rb +3 -5
  79. data/app/jobs/migrate_files_to_valkyrie_job.rb +109 -0
  80. data/app/jobs/migrate_resources_job.rb +34 -0
  81. data/app/jobs/valkyrie_create_derivatives_job.rb +2 -1
  82. data/app/models/admin_set.rb +1 -0
  83. data/app/models/concerns/hyrax/ar_resource.rb +104 -0
  84. data/app/models/concerns/hyrax/solr_document/ordered_members.rb +2 -1
  85. data/app/models/concerns/hyrax/solr_document_behavior.rb +13 -2
  86. data/app/models/concerns/hyrax/valkyrie_lazy_migration.rb +82 -0
  87. data/app/models/file_download_stat.rb +1 -1
  88. data/app/models/file_view_stat.rb +1 -1
  89. data/app/models/hyrax/collection_type.rb +12 -4
  90. data/app/models/hyrax/file_metadata.rb +19 -0
  91. data/app/models/hyrax/file_set.rb +25 -0
  92. data/app/models/hyrax/model_registry.rb +2 -3
  93. data/app/models/hyrax/resource.rb +5 -0
  94. data/app/models/hyrax/statistic.rb +12 -37
  95. data/app/presenters/hyrax/file_set_presenter.rb +2 -1
  96. data/app/presenters/hyrax/file_usage.rb +3 -3
  97. data/app/presenters/hyrax/iiif_manifest_presenter.rb +2 -1
  98. data/app/presenters/hyrax/member_presenter_factory.rb +7 -1
  99. data/app/presenters/hyrax/menu_presenter.rb +1 -1
  100. data/app/presenters/hyrax/stats_usage_presenter.rb +2 -1
  101. data/app/presenters/hyrax/work_show_presenter.rb +13 -17
  102. data/app/presenters/hyrax/work_usage.rb +5 -2
  103. data/app/search_builders/hyrax/expired_embargo_search_builder.rb +7 -1
  104. data/app/search_builders/hyrax/expired_lease_search_builder.rb +7 -1
  105. data/app/search_builders/hyrax/filter_by_type.rb +1 -3
  106. data/app/search_builders/hyrax/valkyrie_abstract_type_relation.rb +7 -2
  107. data/app/services/hyrax/access_control_list.rb +1 -1
  108. data/app/services/hyrax/admin_set_create_service.rb +16 -5
  109. data/app/services/hyrax/admin_set_service.rb +2 -1
  110. data/app/services/hyrax/analytics/ga4/base.rb +96 -0
  111. data/app/services/hyrax/analytics/ga4/events.rb +25 -0
  112. data/app/services/hyrax/analytics/ga4/events_daily.rb +36 -0
  113. data/app/services/hyrax/analytics/ga4/visits.rb +33 -0
  114. data/app/services/hyrax/analytics/ga4/visits_daily.rb +24 -0
  115. data/app/services/hyrax/analytics/ga4.rb +204 -0
  116. data/app/services/hyrax/analytics/google.rb +16 -2
  117. data/app/services/hyrax/analytics/matomo.rb +16 -3
  118. data/app/services/hyrax/analytics/results.rb +6 -0
  119. data/app/services/hyrax/custom_queries/find_access_control.rb +1 -1
  120. data/app/services/hyrax/custom_queries/find_by_date_range.rb +6 -23
  121. data/app/services/hyrax/custom_queries/find_collections_by_type.rb +2 -2
  122. data/app/services/hyrax/custom_queries/find_count_by.rb +3 -31
  123. data/app/services/hyrax/custom_queries/find_file_metadata.rb +2 -2
  124. data/app/services/hyrax/custom_queries/find_models_by_access.rb +5 -27
  125. data/app/services/hyrax/embargo_manager.rb +2 -1
  126. data/app/services/hyrax/listeners/file_listener.rb +2 -2
  127. data/app/services/hyrax/lock_manager.rb +6 -6
  128. data/app/services/hyrax/lockable.rb +4 -3
  129. data/app/services/hyrax/simple_schema_loader.rb +1 -1
  130. data/app/services/hyrax/solr_service.rb +22 -8
  131. data/app/services/hyrax/statistics/query_service.rb +1 -1
  132. data/app/services/hyrax/statistics/works/over_time.rb +1 -1
  133. data/app/services/hyrax/thumbnail_path_service.rb +2 -0
  134. data/app/services/hyrax/user_stat_importer.rb +5 -5
  135. data/app/services/hyrax/valkyrie_upload.rb +9 -7
  136. data/app/services/hyrax/versioning_service.rb +10 -2
  137. data/app/services/hyrax/work_query_service.rb +2 -2
  138. data/app/services/migrate_resource_service.rb +55 -0
  139. data/app/views/_controls.html.erb +5 -5
  140. data/app/views/_masthead.html.erb +1 -1
  141. data/app/views/catalog/_search_form.html.erb +9 -16
  142. data/app/views/catalog/_thumbnail_list_collection.html.erb +1 -1
  143. data/app/views/catalog/_thumbnail_list_default.html.erb +2 -2
  144. data/app/views/hyrax/admin/analytics/collection_reports/index.html.erb +4 -4
  145. data/app/views/hyrax/admin/analytics/work_reports/index.html.erb +1 -1
  146. data/app/views/hyrax/admin/collection_types/_form.html.erb +4 -4
  147. data/app/views/hyrax/admin/collection_types/index.html.erb +1 -1
  148. data/app/views/hyrax/admin/features/index.html.erb +1 -1
  149. data/app/views/hyrax/base/_file_manager_actions.html.erb +1 -1
  150. data/app/views/hyrax/base/_file_manager_member.html.erb +7 -4
  151. data/app/views/hyrax/base/_file_manager_thumbnail.html.erb +1 -1
  152. data/app/views/hyrax/base/_form_files.html.erb +1 -1
  153. data/app/views/hyrax/base/_form_member_of_collections.html.erb +4 -0
  154. data/app/views/hyrax/base/_show_actions.html.erb +7 -8
  155. data/app/views/hyrax/base/_work_button_row.html.erb +1 -1
  156. data/app/views/hyrax/batch_select/_add_button.html.erb +1 -1
  157. data/app/views/hyrax/content_blocks/_form.html.erb +3 -3
  158. data/app/views/hyrax/dashboard/_sidebar.html.erb +1 -1
  159. data/app/views/hyrax/dashboard/_user_activity.html.erb +2 -2
  160. data/app/views/hyrax/dashboard/collections/_form.html.erb +4 -4
  161. data/app/views/hyrax/dashboard/collections/_form_share.html.erb +6 -4
  162. data/app/views/hyrax/dashboard/collections/_list_collections.html.erb +1 -1
  163. data/app/views/hyrax/dashboard/collections/_show_document_list_row.html.erb +1 -1
  164. data/app/views/hyrax/dashboard/show_admin.html.erb +18 -19
  165. data/app/views/hyrax/dashboard/sidebar/_activity.html.erb +1 -1
  166. data/app/views/hyrax/embargoes/_list_expired_active_embargoes.html.erb +7 -7
  167. data/app/views/hyrax/file_sets/_actions.html.erb +9 -1
  168. data/app/views/hyrax/file_sets/_permission_form.html.erb +4 -2
  169. data/app/views/hyrax/file_sets/_show_actions.html.erb +1 -1
  170. data/app/views/hyrax/homepage/_featured.html.erb +1 -1
  171. data/app/views/hyrax/homepage/_recent_document.html.erb +2 -2
  172. data/app/views/hyrax/leases/_list_expired_active_leases.html.erb +6 -6
  173. data/app/views/hyrax/my/collections/_list_collections.html.erb +1 -1
  174. data/app/views/hyrax/my/collections/_tabs.html.erb +1 -1
  175. data/app/views/hyrax/pages/_form.html.erb +8 -8
  176. data/app/views/hyrax/transfers/_received.html.erb +1 -1
  177. data/app/views/hyrax/uploads/create.json.jbuilder +2 -2
  178. data/app/views/hyrax/users/_activity_log.html.erb +15 -9
  179. data/app/views/hyrax/users/_user_row.html.erb +6 -3
  180. data/app/views/hyrax/users/_vitals.html.erb +3 -2
  181. data/app/views/layouts/_head_tag_content.html.erb +2 -0
  182. data/app/views/shared/_appearance_styles.html.erb +5 -1
  183. data/app/views/shared/_ga4.html.erb +11 -0
  184. data/app/views/shared/_select_work_type_modal.html.erb +10 -1
  185. data/bin/db-migrate-seed.sh +3 -3
  186. data/bin/dev-entrypoint.sh +7 -2
  187. data/bin/{db-wait.sh → service-wait.sh} +1 -1
  188. data/bin/worker-entrypoint.sh +8 -0
  189. data/chart/hyrax/templates/deployment-worker.yaml +2 -2
  190. data/config/locales/hyrax.en.yml +4 -2
  191. data/config/metadata/basic_metadata.yaml +20 -0
  192. data/config/metadata/hyrax_internal_metadata.yaml +1 -1
  193. data/docker-compose-dassie.yml +167 -0
  194. data/docker-compose-koppie.yml +21 -36
  195. data/docker-compose-sirenia.yml +50 -44
  196. data/docker-compose.yml +2 -183
  197. data/documentation/developing-your-hyrax-based-app.md +2 -2
  198. data/hyrax.gemspec +5 -4
  199. data/lib/freyja/custom_query_container.rb +5 -0
  200. data/lib/freyja/metadata_adapter.rb +32 -0
  201. data/lib/freyja/persister.rb +42 -0
  202. data/lib/freyja/query_service.rb +20 -0
  203. data/lib/freyja/resource_factory.rb +8 -0
  204. data/lib/freyja.rb +14 -0
  205. data/lib/frigg/custom_query_container.rb +5 -0
  206. data/lib/frigg/metadata_adapter.rb +22 -0
  207. data/lib/frigg/persister.rb +33 -0
  208. data/lib/frigg/query_service.rb +15 -0
  209. data/lib/frigg.rb +13 -0
  210. data/lib/generators/hyrax/install_generator.rb +5 -0
  211. data/lib/generators/hyrax/templates/config/analytics.yml +6 -1
  212. data/lib/generators/hyrax/templates/config/initializers/1_valkyrie.rb +6 -2
  213. data/lib/generators/hyrax/templates/config/valkyrie_index.yml +1 -1
  214. data/lib/goddess/custom_query_container.rb +71 -0
  215. data/lib/goddess/metadata.rb +13 -0
  216. data/lib/goddess/query.rb +176 -0
  217. data/lib/hyrax/configuration.rb +83 -0
  218. data/lib/hyrax/engine.rb +2 -0
  219. data/lib/hyrax/form_fields.rb +1 -3
  220. data/lib/hyrax/name.rb +5 -0
  221. data/lib/hyrax/rubocop/custom_cops.rb +30 -0
  222. data/lib/hyrax/specs/capybara.rb +10 -6
  223. data/lib/hyrax/specs/shared_specs/factories/admin_sets.rb +2 -0
  224. data/lib/hyrax/specs/shared_specs/factories/hyrax_embargo.rb +4 -0
  225. data/lib/hyrax/specs/shared_specs/factories/hyrax_lease.rb +4 -0
  226. data/lib/hyrax/specs/shared_specs/factories/hyrax_work.rb +16 -2
  227. data/lib/hyrax/specs/shared_specs/hydra_works.rb +1 -1
  228. data/lib/hyrax/transactions/admin_set_destroy.rb +2 -1
  229. data/lib/hyrax/transactions/collection_destroy.rb +2 -1
  230. data/lib/hyrax/transactions/container.rb +9 -0
  231. data/lib/hyrax/transactions/steps/add_file_sets.rb +2 -1
  232. data/lib/hyrax/transactions/steps/delete_permission_template.rb +30 -0
  233. data/lib/hyrax/transactions/steps/delete_resource.rb +1 -1
  234. data/lib/hyrax/transactions/steps/save_collection_logo.rb +2 -1
  235. data/lib/hyrax/valkyrie_can_can_adapter.rb +8 -1
  236. data/lib/hyrax/version.rb +1 -1
  237. data/lib/wings/active_fedora_converter.rb +13 -5
  238. data/lib/wings/converter_value_mapper.rb +1 -0
  239. data/lib/wings/services/custom_queries/find_collections_by_type.rb +2 -1
  240. data/lib/wings/services/custom_queries/find_file_metadata.rb +2 -2
  241. data/lib/wings/setup.rb +12 -3
  242. data/lib/wings/transformer_value_mapper.rb +5 -1
  243. data/lib/wings/valkyrie/persister.rb +3 -1
  244. data/template.rb +1 -1
  245. metadata +77 -19
  246. data/.koppie/scripts/db-migrate-seed.sh +0 -9
  247. data/.koppie/scripts/entrypoint.sh +0 -10
@@ -0,0 +1,82 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Hyrax
4
+ ##
5
+ # This mixin is for {Valkyrie::Resource} objects to be able to read/write the same Solr document
6
+ # as their corresponding {ActiveFedora::Base} object.
7
+ #
8
+ # @see https://github.com/samvera/hyrax/pull/6221 Discussion about having one indexed document
9
+ module ValkyrieLazyMigration
10
+ ##
11
+ # Copy over even more of the {Wings::ModelRegistery} legacy model's model_name properties
12
+ #
13
+ # @note When we remove {Wings}, the downstream implementers will need to explicitly craft the
14
+ # properties of the model_name.
15
+ class ResourceName < ResourceName
16
+ def initialize(klass, *args)
17
+ super
18
+
19
+ legacy_model = Wings::ModelRegistry.lookup(klass)
20
+ return unless legacy_model
21
+
22
+ instance_variables.each do |ivar|
23
+ next if ivar == :@name
24
+ next if ivar == :@klass
25
+ instance_variable_set(ivar, legacy_model.model_name.send(ivar[1..-1]))
26
+ end
27
+ end
28
+ attr_reader :klass
29
+ end
30
+
31
+ ##
32
+ # This function helps configuration a work for a valkyrie migration; namely by helping re-use
33
+ # an existing SOLR document, by specifying that the given :klass is a migration :from another
34
+ # class.
35
+ #
36
+ # @note This is similar to the {Wings::ModelRegistry.register}, but is envisioned as part of
37
+ # the Frigg and Freyja adapters for Postges and Fedora lazy migrations.
38
+ #
39
+ # @param klass [Hyrax::Resource, .attribute]
40
+ # @param from [ActiveFedora::Base, .to_rdf_representation]
41
+ # @param name_class [Hyrax::Name] responsible, in part, for determining the various routing
42
+ # paths you might use.
43
+ #
44
+ # @example
45
+ # class MyWork < ActiveFedora::Base
46
+ # end
47
+ #
48
+ # class MyWorkResource < Hyrax::Resource
49
+ # Hyrax::ValkyrieLazyMigration.migrating(self, from: MyWork)
50
+ # end
51
+ def self.migrating(klass, from:, name_class: Hyrax::ValkyrieLazyMigration::ResourceName)
52
+ Wings::ModelRegistry.register(klass, from)
53
+ from.singleton_class.define_method(:migrating_from) { from }
54
+ from.singleton_class.define_method(:migrating_to) { klass }
55
+ klass.singleton_class.define_method(:migrating_from) { from }
56
+ klass.singleton_class.define_method(:migrating_to) { klass }
57
+ klass.singleton_class.define_method(:_hyrax_default_name_class) { name_class }
58
+ klass.singleton_class.define_method(:to_rdf_representation) { migrating_from.to_rdf_representation }
59
+
60
+ klass.include(self)
61
+ end
62
+
63
+ extend ActiveSupport::Concern
64
+
65
+ included do
66
+ attribute :internal_resource, Valkyrie::Types::Any.default(to_rdf_representation.freeze), internal: true
67
+ end
68
+
69
+ def members
70
+ return @members if @members.present?
71
+ @members = member_ids.map do |id|
72
+ Hyrax.query_service.find_by(id: id)
73
+ rescue Valkyrie::Persistence::ObjectNotFoundError
74
+ Rails.logger.warn("Could not find member #{id} for #{self.id}")
75
+ end
76
+ end
77
+
78
+ def to_solr
79
+ Hyrax::ValkyrieIndexer.for(resource: self).to_solr
80
+ end
81
+ end
82
+ end
@@ -21,7 +21,7 @@ class FileDownloadStat < Hyrax::Statistic
21
21
 
22
22
  # this is called by the parent class
23
23
  def filter(file)
24
- { file_id: file.id }
24
+ { file_id: file.id.to_s }
25
25
  end
26
26
  end
27
27
  end
@@ -6,7 +6,7 @@ class FileViewStat < Hyrax::Statistic
6
6
  class << self
7
7
  # this is called by the parent class
8
8
  def filter(file)
9
- { file_id: file.id }
9
+ { file_id: file.id.to_s }
10
10
  end
11
11
  end
12
12
  end
@@ -100,12 +100,20 @@ module Hyrax
100
100
 
101
101
  ##
102
102
  # @return [Enumerable<Collection, PcdmCollection>]
103
- def collections(use_valkyrie: Hyrax.config.use_valkyrie?)
103
+ def collections(use_valkyrie: Hyrax.config.use_valkyrie?, model: Hyrax.config.collection_class)
104
104
  return [] unless id
105
- return Hyrax.custom_queries.find_collections_by_type(global_id: to_global_id.to_s) if use_valkyrie
105
+ return Hyrax.custom_queries.find_collections_by_type(global_id: to_global_id.to_s, model:) if use_valkyrie
106
106
  ActiveFedora::Base.where(Hyrax.config.collection_type_index_field.to_sym => to_global_id.to_s)
107
107
  end
108
108
 
109
+ # Query solr to see if any collections of this type exist
110
+ # This should be much more performant for certain adapters than calling collections.any?
111
+ # @return [Boolean] True if there are any collections of this collection type in the repository
112
+ def collections?
113
+ return false unless id
114
+ Hyrax::SolrQueryService.new.with_field_pairs(field_pairs: { Hyrax.config.collection_type_index_field.to_sym => to_global_id.to_s }).with_model(model: Hyrax.config.collection_class.to_rdf_representation).count > 0
115
+ end
116
+
109
117
  # @return [Boolean] True if this is the Admin Set type
110
118
  def admin_set?
111
119
  machine_id == ADMIN_SET_MACHINE_ID
@@ -154,7 +162,7 @@ module Hyrax
154
162
  end
155
163
 
156
164
  def ensure_no_collections
157
- return true unless collections.any?
165
+ return true unless collections?
158
166
  errors[:base] << I18n.t('hyrax.admin.collection_types.errors.not_empty')
159
167
  throw :abort
160
168
  end
@@ -172,7 +180,7 @@ module Hyrax
172
180
  end
173
181
 
174
182
  def ensure_no_settings_changes_if_collections_exist
175
- return true unless collections.any?
183
+ return true unless collections?
176
184
  return true unless collection_type_settings_changed?
177
185
  errors[:base] << I18n.t('hyrax.admin.collection_types.errors.no_settings_change_if_not_empty')
178
186
  throw :abort
@@ -42,6 +42,18 @@ module Hyrax
42
42
 
43
43
  THUMBNAIL = ::Valkyrie::Vocab::PCDMUse.ThumbnailImage # for compatibility with earlier versions of Hyrax; prefer +THUMBNAIL_IMAGE+
44
44
 
45
+ # @return [Array<RDF::URI>] list of all uses
46
+ def use_list
47
+ [ORIGINAL_FILE,
48
+ THUMBNAIL_IMAGE,
49
+ EXTRACTED_TEXT,
50
+ INTERMEDIATE_FILE,
51
+ PRESERVATION_FILE,
52
+ SERVICE_FILE,
53
+ TRANSCRIPT]
54
+ end
55
+ module_function :use_list
56
+
45
57
  ##
46
58
  # @param use [RDF::URI, Symbol]
47
59
  #
@@ -166,6 +178,13 @@ module Hyrax
166
178
  pcdm_use.include?(Use::EXTRACTED_TEXT)
167
179
  end
168
180
 
181
+ ##
182
+ # Filters out uses not recognized by Hyrax (e.g. http://fedora.info/definitions/v4/repository#Binary)
183
+ # @return [Array]
184
+ def filtered_pcdm_use
185
+ pcdm_use.select { |use| Use.use_list.include?(use) }
186
+ end
187
+
169
188
  ##
170
189
  # @return [String]
171
190
  def to_rdf_representation
@@ -106,6 +106,31 @@ module Hyrax
106
106
  extracted_text&.id
107
107
  end
108
108
 
109
+ ##
110
+ # @return [Array] All ids, extensions, mime types, names, and uses
111
+ # @example
112
+ # [{:id=>"123", :extension=>"pdf", :mime_type=>"application/pdf", :name=>nil, :use=>"OriginalFile"},
113
+ # {:id=>"234", :extension=>"jpeg", :mime_type=>"application/octet-stream", :name=>"thumbnail", :use=>"ThumbnailImage"}]
114
+ # rubocop:disable Metrics/MethodLength
115
+ def extensions_and_mime_types
116
+ return [] if file_ids.empty?
117
+ Hyrax.custom_queries.find_files(file_set: self).each_with_object([]) do |fm, arr|
118
+ next unless fm.original_filename
119
+ extension = File.extname(fm.original_filename)
120
+ next if extension.empty?
121
+ use = fm.filtered_pcdm_use.first.to_s.split("#").last
122
+ name = use == 'OriginalFile' ? nil : File.basename(fm.original_filename, extension).split('-').last
123
+ arr << {
124
+ id: fm.id.to_s,
125
+ extension: extension[1..], # remove leading '.'
126
+ mime_type: fm.mime_type,
127
+ name: name,
128
+ use: use
129
+ }
130
+ end
131
+ # rubocop:enable Metrics/MethodLength
132
+ end
133
+
109
134
  ##
110
135
  # @return [Valkyrie::ID]
111
136
  def representative_id
@@ -85,7 +85,8 @@ module Hyrax
85
85
  #
86
86
  # @todo Consider the Wings::ModelRegistry and how we perform mappings.
87
87
  def self.work_class_names
88
- Hyrax.config.registered_curation_concern_types
88
+ @work_class_names ||= (Hyrax.config.registered_curation_concern_types +
89
+ Array(Rails.application.class.try(:work_types))).map(&:to_s).uniq
89
90
  end
90
91
 
91
92
  def self.work_classes
@@ -101,11 +102,9 @@ module Hyrax
101
102
  def self.classes_from(strings)
102
103
  strings.map(&:safe_constantize).compact.uniq
103
104
  end
104
- private_class_method :classes_from
105
105
 
106
106
  def self.rdf_representations_from(klasses)
107
107
  klasses.map { |klass| klass.respond_to?(:to_rdf_representation) ? klass.to_rdf_representation : klass.name }.uniq
108
108
  end
109
- private_class_method :rdf_representations_from
110
109
  end
111
110
  end
@@ -149,6 +149,11 @@ module Hyrax
149
149
  self.class.work?
150
150
  end
151
151
 
152
+ # Its nice to know if a record is still in AF or not
153
+ def wings?
154
+ respond_to?(:head) && respond_to?(:tail)
155
+ end
156
+
152
157
  def ==(other)
153
158
  attributes.except(:created_at, :updated_at) == other.attributes.except(:created_at, :updated_at) if other.respond_to?(:attributes)
154
159
  end
@@ -25,47 +25,22 @@ module Hyrax
25
25
  combined_stats object, start_date, cache_column, event_type, user_id
26
26
  end
27
27
 
28
- # Hyrax::Download is sent to Hyrax::Analytics.profile as #hyrax__download
29
- # see Legato::ProfileMethods.method_name_from_klass
30
- def ga_statistics(start_date, object)
31
- path = polymorphic_path(object)
32
- profile = Hyrax::Analytics.profile
33
- unless profile
34
- Hyrax.logger.error("Google Analytics profile has not been established. Unable to fetch statistics.")
35
- return []
36
- end
37
- profile.hyrax__pageview(sort: 'date',
38
- start_date: start_date,
39
- end_date: Date.yesterday,
40
- limit: 10_000)
41
- .for_path(path)
42
- end
43
-
44
28
  def query_works(query)
45
- models = Hyrax.config.curation_concerns.map { |m| "\"#{m}\"" }
46
- Hyrax::SolrService.query("has_model_ssim:(#{models.join(' OR ')})", fl: query, rows: 100_000)
29
+ models = Hyrax::ModelRegistry.work_rdf_representations.map { |m| "\"#{m}\"" }
30
+ response = Hyrax::SolrService.get(fq: "has_model_ssim:(#{models.join(' OR ')})", 'facet.field': query, 'facet.missing': true, rows: 0)
31
+ Hash[*response['facet_counts']['facet_fields'][query]]
47
32
  end
48
33
 
49
34
  def work_types
50
- results = query_works("human_readable_type_tesim")
51
- results.group_by { |result| result['human_readable_type_tesim'].join('') }.transform_values(&:count)
35
+ types = query_works("human_readable_type_sim")
36
+ types['Unknown'] = types.delete(nil)
37
+ types
52
38
  end
53
39
 
54
40
  def resource_types
55
- results = query_works("resource_type_tesim")
56
- resource_types = []
57
- results.each do |y|
58
- if y["resource_type_tesim"].nil? || (y["resource_type_tesim"] == [""])
59
- resource_types.push("Unknown")
60
- elsif y["resource_type_tesim"].count > 1
61
- y["resource_type_tesim"].each do |t|
62
- resource_types.push(t)
63
- end
64
- else
65
- resource_types.push(y["resource_type_tesim"].join(""))
66
- end
67
- end
68
- resource_types.group_by { |rt| rt }.transform_values(&:count)
41
+ types = query_works("resource_type_sim")
42
+ types['Unknown'] = types.delete(nil)
43
+ types
69
44
  end
70
45
 
71
46
  private
@@ -80,10 +55,10 @@ module Hyrax
80
55
  stat_cache_info = cached_stats(object, start_date, object_method)
81
56
  stats = stat_cache_info[:cached_stats]
82
57
  if stat_cache_info[:ga_start_date] < Time.zone.today
83
- ga_stats = ga_statistics(stat_cache_info[:ga_start_date], object)
84
- ga_stats.each do |stat|
58
+ page_stats = Hyrax::Analytics.page_statistics(stat_cache_info[:ga_start_date], object)
59
+ page_stats.each do |stat|
85
60
  lstat = build_for(object, date: stat[:date], object_method => stat[ga_key], user_id: user_id)
86
- lstat.save unless Date.parse(stat[:date]) == Time.zone.today
61
+ lstat.save unless stat[:date].to_date == Time.zone.today
87
62
  stats << lstat
88
63
  end
89
64
  end
@@ -20,7 +20,8 @@ module Hyrax
20
20
 
21
21
  # CurationConcern methods
22
22
  delegate :stringify_keys, :human_readable_type, :collection?, :image?, :video?,
23
- :audio?, :pdf?, :office_document?, :representative_id, :to_s, to: :solr_document
23
+ :audio?, :pdf?, :office_document?, :representative_id, :to_s,
24
+ :extensions_and_mime_types, to: :solr_document
24
25
 
25
26
  # Methods used by blacklight helpers
26
27
  delegate :has?, :first, :fetch, to: :solr_document
@@ -5,7 +5,7 @@ module Hyrax
5
5
  # and prepares it for visualization in /app/views/stats/file.html.erb
6
6
  class FileUsage < StatsUsagePresenter
7
7
  def initialize(id)
8
- self.model = ::FileSet.find(id)
8
+ self.model = Hyrax.query_service.find_by(id: id)
9
9
  end
10
10
 
11
11
  alias file model
@@ -29,11 +29,11 @@ module Hyrax
29
29
  private
30
30
 
31
31
  def downloads
32
- to_flots(FileDownloadStat.statistics(model, created, user_id))
32
+ @downloads ||= to_flots(FileDownloadStat.statistics(model, created, user_id))
33
33
  end
34
34
 
35
35
  def pageviews
36
- to_flots(FileViewStat.statistics(model, created, user_id))
36
+ @pageviews ||= to_flots(FileViewStat.statistics(model, created, user_id))
37
37
  end
38
38
  end
39
39
  end
@@ -56,7 +56,8 @@ module Hyrax
56
56
  ##
57
57
  # @return [Boolean]
58
58
  def file_set?
59
- model.try(:file_set?) || Array(model[:has_model_ssim]).include?('FileSet') || Array(model[:has_model_ssim]).include?('Hyrax::FileSet')
59
+ return true if model.try(:file_set?)
60
+ (Array(model[:has_model_ssim]) & Hyrax::ModelRegistry.file_set_rdf_representations).any?
60
61
  end
61
62
 
62
63
  ##
@@ -47,7 +47,9 @@ module Hyrax
47
47
  # in order.
48
48
  # Arbitrarily maxed at 10 thousand; had to specify rows due to solr's default of 10
49
49
  def file_set_ids
50
- @file_set_ids ||= Hyrax::SolrService.query("{!field f=has_model_ssim}FileSet",
50
+ pairs = file_set_models.map { |model| ["has_model_ssim", model] }
51
+ query = Hyrax::SolrQueryBuilderService.construct_query(pairs, "OR")
52
+ @file_set_ids ||= Hyrax::SolrService.query(query,
51
53
  rows: 10_000,
52
54
  fl: Hyrax.config.id_field,
53
55
  fq: "{!join from=ordered_targets_ssim to=id}id:\"#{id}/list_source\"")
@@ -61,5 +63,9 @@ module Hyrax
61
63
  def composite_presenter_class
62
64
  CompositePresenterFactory.new(file_presenter_class, work_presenter_class, ordered_ids & file_set_ids)
63
65
  end
66
+
67
+ def file_set_models
68
+ Hyrax::ModelRegistry.file_set_rdf_representations
69
+ end
64
70
  end
65
71
  end
@@ -33,7 +33,7 @@ module Hyrax
33
33
  def nav_link(options, also_active_for: nil, **link_html_options)
34
34
  active_urls = [options, also_active_for].compact
35
35
  list_options = active_urls.any? { |url| current_page?(url) } ? { class: 'active nav-item' } : { class: 'nav-item' }
36
- tag.li(list_options) do
36
+ tag.li(**list_options) do
37
37
  link_to(options, link_html_options) do
38
38
  yield
39
39
  end
@@ -30,12 +30,13 @@ module Hyrax
30
30
  def date_for_analytics
31
31
  earliest = Hyrax.config.analytic_start_date
32
32
  date_uploaded = string_to_date(model.date_uploaded)
33
- date_analytics = date_uploaded ? date_uploaded : model.create_date
33
+ date_analytics = date_uploaded ? date_uploaded : (model.create_date || model.created_at)
34
34
  return date_analytics if earliest.blank?
35
35
  earliest > date_analytics ? earliest : date_analytics
36
36
  end
37
37
 
38
38
  def string_to_date(date_str)
39
+ return date_str if date_str.is_a?(Date)
39
40
  Time.zone.parse(date_str)
40
41
  rescue ArgumentError, TypeError
41
42
  nil
@@ -16,13 +16,6 @@ module Hyrax
16
16
  self.collection_presenter_class = CollectionPresenter
17
17
  self.presenter_factory_class = MemberPresenterFactory
18
18
 
19
- # Methods used by blacklight helpers
20
- delegate :has?, :first, :fetch, :export_formats, :export_as, to: :solr_document
21
-
22
- # delegate fields from Hyrax::Works::Metadata to solr_document
23
- delegate :based_near_label, :related_url, :depositor, :identifier, :resource_type,
24
- :keyword, :itemtype, :admin_set, :rights_notes, :access_right, :abstract, to: :solr_document
25
-
26
19
  # @param [SolrDocument] solr_document
27
20
  # @param [Ability] current_ability
28
21
  # @param [ActionDispatch::Request] request the http request context. Used so
@@ -33,20 +26,14 @@ module Hyrax
33
26
  @request = request
34
27
  end
35
28
 
29
+ # We cannot rely on the method missing to catch this delegation. Because
30
+ # most all objects implicitly implicitly implement #to_s
31
+ delegate :to_s, to: :solr_document
32
+
36
33
  def page_title
37
34
  "#{human_readable_type} | #{title.first} | ID: #{id} | #{I18n.t('hyrax.product_name')}"
38
35
  end
39
36
 
40
- # CurationConcern methods
41
- delegate :stringify_keys, :human_readable_type, :collection?, :to_s, :suppressed?,
42
- to: :solr_document
43
-
44
- # Metadata Methods
45
- delegate :title, :date_created, :description,
46
- :creator, :contributor, :subject, :publisher, :language, :embargo_release_date,
47
- :lease_expiration_date, :license, :source, :rights_statement, :thumbnail_id, :representative_id,
48
- :rendering_ids, :member_of_collection_ids, :alternative_title, :bibliographic_citation, to: :solr_document
49
-
50
37
  def workflow
51
38
  @workflow ||= WorkflowPresenter.new(solr_document, current_ability)
52
39
  end
@@ -259,6 +246,15 @@ module Hyrax
259
246
 
260
247
  private
261
248
 
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
+
262
258
  # list of item ids to display is based on ordered_ids
263
259
  def authorized_item_ids(filter_unreadable: Flipflop.hide_private_items?)
264
260
  @member_item_list_ids ||=
@@ -8,11 +8,14 @@ module Hyrax
8
8
  # and prepares it for visualization in /app/views/stats/work.html.erb
9
9
  class WorkUsage < StatsUsagePresenter
10
10
  def initialize(id)
11
- self.model = Hyrax::WorkRelation.new.find(id)
11
+ self.model = Hyrax.query_service.find_by(id: id)
12
12
  end
13
13
 
14
14
  alias work model
15
- delegate :to_s, to: :model
15
+
16
+ def to_s
17
+ model.title.first
18
+ end
16
19
 
17
20
  def total_pageviews
18
21
  pageviews.reduce(0) { |total, result| total + result[1].to_i }
@@ -6,7 +6,13 @@ module Hyrax
6
6
 
7
7
  def only_expired_embargoes(solr_params)
8
8
  solr_params[:fq] ||= []
9
- solr_params[:fq] = 'embargo_release_date_dtsi:[* TO NOW]'
9
+ solr_params[:fq] = "embargo_release_date_dtsi:[* TO #{now}]"
10
+ end
11
+
12
+ private
13
+
14
+ def now
15
+ Hyrax::TimeService.time_in_utc.utc.xmlschema
10
16
  end
11
17
  end
12
18
  end
@@ -6,7 +6,13 @@ module Hyrax
6
6
 
7
7
  def only_expired_leases(solr_params)
8
8
  solr_params[:fq] ||= []
9
- solr_params[:fq] = 'lease_expiration_date_dtsi:[* TO NOW]'
9
+ solr_params[:fq] = "lease_expiration_date_dtsi:[* TO #{now}]"
10
+ end
11
+
12
+ private
13
+
14
+ def now
15
+ Hyrax::TimeService.time_in_utc.utc.xmlschema
10
16
  end
11
17
  end
12
18
  end
@@ -30,9 +30,7 @@ module Hyrax
30
30
  end
31
31
 
32
32
  def models_to_solr_clause
33
- models.map do |model|
34
- model.respond_to?(:to_rdf_representation) ? model.to_rdf_representation : model.name
35
- end.join(',')
33
+ Hyrax::ModelRegistry.rdf_representations_from(models).join(',')
36
34
  end
37
35
 
38
36
  def generic_type_field
@@ -19,8 +19,13 @@ module Hyrax
19
19
  Hyrax.query_service.custom_queries.find_count_by(models: allowable_types)
20
20
  end
21
21
 
22
- def where(hash)
23
- Hyrax.query_service.find_references_by(resource: hash.values.first, property: hash.keys.first)
22
+ def where(hash_or_string)
23
+ case hash_or_string
24
+ when String
25
+ Hyrax::SolrService.query(hash_or_string)
26
+ else
27
+ Hyrax.query_service.find_references_by(resource: hash_or_string.values.first, property: hash_or_string.keys.first)
28
+ end
24
29
  end
25
30
 
26
31
  def ==(other)
@@ -136,7 +136,7 @@ module Hyrax
136
136
  #
137
137
  # @return [Boolean]
138
138
  def pending_changes?
139
- change_set.changed?
139
+ !change_set.resource.persisted? || change_set.changed?
140
140
  end
141
141
 
142
142
  ##
@@ -10,7 +10,7 @@ module Hyrax
10
10
  # @see Hyrax::PermissionTemplate
11
11
  # @see Sipity::Workflow
12
12
  class AdminSetCreateService # rubocop:disable Metrics/ClassLength
13
- DEFAULT_ID = 'admin_set/default'
13
+ DEFAULT_ID = 'admin_set_default'
14
14
  DEFAULT_TITLE = ['Default Admin Set'].freeze
15
15
 
16
16
  class_attribute :permissions_create_service, :default_admin_set_persister
@@ -124,11 +124,22 @@ module Hyrax
124
124
  # do not support hardcoded IDs (e.g. postgres)
125
125
  # @return [Hyrax::AdministrativeSet] the default admin set; nil if not found
126
126
  def find_unsaved_default_admin_set
127
- admin_set = Hyrax.query_service.find_by(id: DEFAULT_ID)
128
- default_admin_set_persister.update(default_admin_set_id: DEFAULT_ID) if save_default?
127
+ admin_set = begin
128
+ # to support repositories still using the deprecated 'admin_set/default' as DEFAULT_ID
129
+ Hyrax.query_service.find_by(id: 'admin_set/default')
130
+ rescue Ldp::BadRequest, Valkyrie::Persistence::ObjectNotFoundError
131
+ # Fedora 6.5+ does not support slashes in IDs, hence the need to rescue Ldp::BadRequest
132
+ # if an admin set with deprecated ID 'admin_set/default' does not exist, check again for admin set with DEFAULT_ID
133
+ begin
134
+ Hyrax.query_service.find_by(id: DEFAULT_ID)
135
+ rescue Valkyrie::Persistence::ObjectNotFoundError
136
+ nil
137
+ end
138
+ end
139
+
140
+ default_admin_set_persister.update(default_admin_set_id: admin_set.id.to_s) if admin_set.present? && save_default?
141
+
129
142
  admin_set
130
- rescue Valkyrie::Persistence::ObjectNotFoundError
131
- # a default admin set hasn't been created yet
132
143
  end
133
144
 
134
145
  # @return [String | nil] the default admin set id; returns nil if not set
@@ -51,9 +51,10 @@ module Hyrax
51
51
  # @return [Hash] admin set id keys and file count values
52
52
  def count_files(admin_sets)
53
53
  file_counts = Hash.new(0)
54
+ file_set_models = Hyrax::ModelRegistry.file_set_rdf_representations
54
55
  admin_sets.each do |admin_set|
55
56
  query = "{!join from=member_ids_ssim to=id}isPartOf_ssim:#{admin_set.id}"
56
- file_results = Hyrax::SolrService.get(fq: [query, "{!terms f=has_model_ssim}FileSet,Hyrax::FileSet"], rows: 0)
57
+ file_results = Hyrax::SolrService.get(fq: [query, "{!terms f=has_model_ssim}#{file_set_models.join(',')}"], rows: 0)
57
58
  file_counts[admin_set.id] = file_results['response']['numFound']
58
59
  end
59
60
  file_counts