geoblacklight_admin 0.5.0 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (162) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +24 -11
  3. data/Rakefile +83 -47
  4. data/app/assets/javascripts/geoblacklight_admin/chosen.js +1 -0
  5. data/app/assets/stylesheets/geoblacklight_admin/_core.scss +24 -0
  6. data/app/assets/stylesheets/geoblacklight_admin/modules/_nav.scss +0 -5
  7. data/app/assets/stylesheets/geoblacklight_admin/modules/_tables.scss +1 -1
  8. data/app/controllers/admin/admin_controller.rb +16 -0
  9. data/app/controllers/admin/advanced_search_controller.rb +7 -1
  10. data/app/controllers/admin/assets_controller.rb +47 -32
  11. data/app/controllers/admin/bookmarks_controller.rb +17 -5
  12. data/app/controllers/admin/bulk_actions_controller.rb +32 -1
  13. data/app/controllers/admin/document_accesses_controller.rb +38 -0
  14. data/app/controllers/admin/document_assets_controller.rb +47 -89
  15. data/app/controllers/admin/document_distributions_controller.rb +172 -0
  16. data/app/controllers/admin/documents_controller.rb +43 -57
  17. data/app/controllers/admin/elements_controller.rb +24 -0
  18. data/app/controllers/admin/form_elements_controller.rb +33 -0
  19. data/app/controllers/admin/ids_controller.rb +6 -0
  20. data/app/controllers/admin/import_documents_controller.rb +11 -1
  21. data/app/controllers/admin/imports_controller.rb +34 -4
  22. data/app/controllers/admin/mappings_controller.rb +15 -0
  23. data/app/controllers/admin/notifications_controller.rb +27 -0
  24. data/app/controllers/admin/reference_types_controller.rb +106 -0
  25. data/app/controllers/admin/search_controller.rb +7 -0
  26. data/app/controllers/admin/users_controller.rb +10 -0
  27. data/app/helpers/asset_helper.rb +6 -0
  28. data/app/helpers/bulk_actions_helper.rb +10 -1
  29. data/app/helpers/document_helper.rb +36 -0
  30. data/app/helpers/geoblacklight_admin_helper.rb +88 -8
  31. data/app/helpers/mappings_helper.rb +26 -0
  32. data/app/indexers/document_indexer.rb +22 -2
  33. data/app/javascript/channels/consumer.js +6 -0
  34. data/app/javascript/channels/export_channel.js +30 -0
  35. data/app/javascript/channels/index.js +3 -0
  36. data/app/javascript/controllers/results_controller.js +14 -0
  37. data/app/javascript/index.js +8 -2
  38. data/app/jobs/bulk_action_revert_document_job.rb +4 -12
  39. data/app/jobs/bulk_action_run_document_job.rb +2 -10
  40. data/app/jobs/export_job.rb +35 -8
  41. data/app/jobs/geoblacklight_admin/delete_thumbnail_job.rb +19 -0
  42. data/app/jobs/geoblacklight_admin/remove_parent_dct_references_uri_job.rb +16 -0
  43. data/app/jobs/geoblacklight_admin/set_parent_dct_references_uri_job.rb +17 -0
  44. data/app/jobs/geoblacklight_admin/store_image_job.rb +22 -0
  45. data/app/jobs/import_document_job.rb +1 -4
  46. data/app/jobs/import_run_job.rb +1 -1
  47. data/app/models/admin/bookmark.rb +1 -1
  48. data/app/models/asset.rb +20 -0
  49. data/app/models/bulk_action.rb +2 -1
  50. data/app/models/bulk_actions/change_publication_state.rb +0 -25
  51. data/app/models/bulk_actions/delete_thumbnails.rb +6 -0
  52. data/app/models/bulk_actions/draft_document.rb +6 -0
  53. data/app/models/bulk_actions/harvest_thumbnails.rb +6 -0
  54. data/app/models/bulk_actions/publish_document.rb +6 -0
  55. data/app/models/bulk_actions/unpublish_document.rb +6 -0
  56. data/app/models/document/geom_validator.rb +8 -0
  57. data/app/models/document/reference.rb +65 -65
  58. data/app/models/document.rb +129 -72
  59. data/app/models/document_distribution.rb +145 -0
  60. data/app/models/element.rb +2 -0
  61. data/app/models/geoblacklight_admin/schema.rb +10 -2
  62. data/app/models/import/csv_duplicates_validator.rb +2 -0
  63. data/app/models/import/csv_header_validator.rb +3 -1
  64. data/app/models/import_btaa_aardvark.rb +2 -2
  65. data/app/models/import_document_state_machine.rb +1 -0
  66. data/app/models/import_gblv1.rb +2 -2
  67. data/app/models/reference_type.rb +40 -0
  68. data/app/models/user.rb +4 -2
  69. data/app/services/export_csv_document_distributions_service.rb +61 -0
  70. data/app/services/geoblacklight_admin/image_service/iiif_manifest.rb +39 -43
  71. data/app/services/geoblacklight_admin/image_service/tms.rb +0 -8
  72. data/app/services/geoblacklight_admin/image_service.rb +1 -1
  73. data/app/services/geoblacklight_admin/item_viewer.rb +4 -4
  74. data/app/views/admin/bookmarks/index.html.erb +19 -14
  75. data/app/views/admin/bulk_actions/show.html.erb +1 -1
  76. data/app/views/admin/document_accesses/import.html.erb +6 -2
  77. data/app/views/admin/document_assets/_assets_table.html.erb +49 -0
  78. data/app/views/admin/document_assets/_form.html.erb +2 -3
  79. data/app/views/admin/document_assets/index.html.erb +1 -47
  80. data/app/views/admin/document_distributions/_document_distribution.html.erb +39 -0
  81. data/app/views/admin/document_distributions/_document_distribution.json.jbuilder +2 -0
  82. data/app/views/admin/document_distributions/_form.html.erb +34 -0
  83. data/app/views/admin/document_distributions/destroy_all.html.erb +82 -0
  84. data/app/views/admin/document_distributions/edit.html.erb +12 -0
  85. data/app/views/admin/document_distributions/import.html.erb +80 -0
  86. data/app/views/admin/document_distributions/index.html.erb +143 -0
  87. data/app/views/admin/document_distributions/index.json.jbuilder +1 -0
  88. data/app/views/admin/document_distributions/new.html.erb +11 -0
  89. data/app/views/admin/document_distributions/show.html.erb +10 -0
  90. data/app/views/admin/document_distributions/show.json.jbuilder +1 -0
  91. data/app/views/admin/documents/_document.html.erb +1 -3
  92. data/app/views/admin/documents/_form.html.erb +2 -4
  93. data/app/views/admin/documents/_form_control.html.erb +5 -2
  94. data/app/views/admin/documents/_form_nav.html.erb +14 -5
  95. data/app/views/admin/documents/_form_nav_kithe.html.erb +4 -1
  96. data/app/views/admin/documents/_json_aardvark.jbuilder +1 -1
  97. data/app/views/admin/documents/_json_gbl_v1.jbuilder +1 -1
  98. data/app/views/admin/documents/_result_selected_options.html.erb +5 -2
  99. data/app/views/admin/documents/admin.html.erb +5 -5
  100. data/app/views/admin/documents/features/_document_references.html.erb +23 -0
  101. data/app/views/admin/documents/features/_multiple_download_links.html.erb +29 -26
  102. data/app/views/admin/ids/fetch.json.jbuilder +0 -2
  103. data/app/views/admin/ids/index.json.jbuilder +0 -2
  104. data/app/views/admin/imports/_form.html.erb +1 -1
  105. data/app/views/admin/imports/show.html.erb +1 -1
  106. data/app/views/admin/layouts/application.html.erb +4 -2
  107. data/app/views/admin/mappings/index.html.erb +1 -1
  108. data/app/views/admin/notifications/_notification.html.haml +29 -28
  109. data/app/views/admin/reference_types/_form.html.erb +25 -0
  110. data/app/views/admin/reference_types/_reference_type.html.erb +52 -0
  111. data/app/views/admin/reference_types/_reference_type.json.jbuilder +2 -0
  112. data/app/views/admin/reference_types/edit.html.erb +12 -0
  113. data/app/views/admin/reference_types/index.html.erb +52 -0
  114. data/app/views/admin/reference_types/index.json.jbuilder +1 -0
  115. data/app/views/admin/reference_types/new.html.erb +11 -0
  116. data/app/views/admin/reference_types/show.html.erb +3 -0
  117. data/app/views/admin/reference_types/show.json.jbuilder +1 -0
  118. data/app/views/admin/shared/_footer.html.erb +5 -2
  119. data/app/views/admin/shared/_js_behaviors.html.erb +2 -3
  120. data/app/views/admin/shared/_navbar.html.erb +9 -2
  121. data/app/views/admin/users/index.html.erb +0 -1
  122. data/app/views/catalog/_show_gbl_admin.html.erb +1 -1
  123. data/config/initializers/defaults.yml +310 -0
  124. data/config/initializers/rails_config.rb +8 -0
  125. data/config/locales/documents.en.yml +14 -0
  126. data/config/routes.rb +30 -5
  127. data/db/import_references_schema_support.numbers +0 -0
  128. data/db/migrate/20230316183001_add_geoblacklight_admin_gem.rb +0 -12
  129. data/db/migrate/20241009200524_create_admin_reference_types.rb +13 -0
  130. data/db/migrate/20241010161420_create_document_references.rb +14 -0
  131. data/db/migrate/20241120238823_rename_references_to_distributions.rb +5 -0
  132. data/db/seeds.rb +5 -0
  133. data/db/seeds_elements.csv +1 -1
  134. data/db/seeds_elements.numbers +0 -0
  135. data/db/seeds_reference_types.csv +29 -0
  136. data/db/seeds_reference_types.numbers +0 -0
  137. data/db/structure.sql +1 -38
  138. data/lib/compose.yml +31 -0
  139. data/lib/generators/geoblacklight_admin/config_generator.rb +53 -12
  140. data/lib/generators/geoblacklight_admin/install_generator.rb +8 -0
  141. data/lib/generators/geoblacklight_admin/templates/btaa_sample_records.csv +5 -0
  142. data/lib/generators/geoblacklight_admin/templates/config/database.yml +1 -1
  143. data/lib/generators/geoblacklight_admin/templates/config/initializers/devise.rb +0 -2
  144. data/lib/generators/geoblacklight_admin/templates/config/initializers/mime_types.rb +1 -0
  145. data/lib/generators/geoblacklight_admin/templates/demo-app/Dockerfile +31 -0
  146. data/lib/generators/geoblacklight_admin/templates/demo-app/compose.yml +42 -0
  147. data/lib/generators/geoblacklight_admin/templates/demo-app/start-server.sh +21 -0
  148. data/lib/geoblacklight_admin/engine.rb +4 -0
  149. data/lib/geoblacklight_admin/rake_task.rb +2 -0
  150. data/lib/geoblacklight_admin/tasks/distributions.rake +69 -0
  151. data/lib/geoblacklight_admin/tasks/images.rake +1 -0
  152. data/lib/geoblacklight_admin/tasks/solr.rake +31 -0
  153. data/lib/geoblacklight_admin/version.rb +1 -1
  154. data/lib/geoblacklight_admin.rb +4 -0
  155. metadata +137 -53
  156. data/app/helpers/import_documents_helper.rb +0 -5
  157. data/app/javascript/entrypoints/engine.js +0 -8
  158. data/config/locales/devise_invitable.en.yml +0 -31
  159. data/lib/generators/geoblacklight_admin/templates/devise/invitations/edit.html.erb +0 -15
  160. data/lib/generators/geoblacklight_admin/templates/devise/invitations/new.html.erb +0 -15
  161. data/lib/generators/geoblacklight_admin/templates/devise/mailer/invitation_instructions.html.erb +0 -11
  162. data/lib/generators/geoblacklight_admin/templates/devise/mailer/invitation_instructions.text.erb +0 -11
@@ -9,7 +9,7 @@ class Document < Kithe::Work
9
9
  delegate :viewer_endpoint, to: :item_viewer
10
10
 
11
11
  def item_viewer
12
- GeoblacklightAdmin::ItemViewer.new(references)
12
+ GeoblacklightAdmin::ItemViewer.new(distributions)
13
13
  end
14
14
 
15
15
  attr_accessor :skip_callbacks
@@ -34,18 +34,20 @@ class Document < Kithe::Work
34
34
  has_many :document_downloads, primary_key: "friendlier_id", foreign_key: "friendlier_id", autosave: false, dependent: :destroy,
35
35
  inverse_of: :document
36
36
 
37
+ # - DocumentDistributions
38
+ has_many :document_distributions, primary_key: "friendlier_id", foreign_key: "friendlier_id", autosave: false, dependent: :destroy,
39
+ inverse_of: :document
40
+
37
41
  # DocumentAssets - Thumbnails, Attachments, etc
38
42
  # @TODO: Redundant? Kithe also includes a members association
39
43
  def document_assets
40
44
  scope = Kithe::Asset
41
- scope = scope.where(parent_id: id)
42
-
43
- # scope = scope.page(params[:page]).per(20).order(created_at: :desc)
45
+ scope = scope.where(parent_id: id).order(position: :asc)
44
46
  scope.includes(:parent)
45
47
  end
46
48
 
47
- def downloadable_assets
48
- document_assets.select { |a| a.dct_references_uri_key == "download" }
49
+ def distributable_assets
50
+ document_assets.select { |a| a.dct_references_uri_key.present? }
49
51
  end
50
52
 
51
53
  include Statesman::Adapters::ActiveRecordQueries[
@@ -87,7 +89,7 @@ class Document < Kithe::Work
87
89
 
88
90
  # Downloadable Resouce
89
91
  def a_downloadable_resource?
90
- references_json.include?("downloadUrl")
92
+ distributions_json.include?("downloadUrl")
91
93
  end
92
94
 
93
95
  validates_with Document::DateValidator
@@ -96,55 +98,109 @@ class Document < Kithe::Work
96
98
  validates_with Document::GeomValidator
97
99
 
98
100
  # Definte our AttrJSON attributes
99
- Element.all.each do |attribute|
100
- next if attribute.solr_field == "dct_references_s"
101
+ if ActiveRecord::Base.connection.table_exists?("elements")
102
+ Element.all.each do |attribute|
103
+ next if attribute.solr_field == "dct_references_s"
101
104
 
102
- if attribute.repeatable?
103
- attr_json attribute.solr_field.to_sym, attribute.field_type.to_sym, array: true, default: -> { [] }
104
- else
105
- attr_json attribute.solr_field.to_sym, attribute.field_type.to_sym, default: ""
105
+ if attribute.repeatable?
106
+ attr_json attribute.solr_field.to_sym, attribute.field_type.to_sym, array: true, default: -> { [] }
107
+ else
108
+ attr_json attribute.solr_field.to_sym, attribute.field_type.to_sym, default: ""
109
+ end
106
110
  end
107
111
  end
108
112
 
109
113
  attr_json :dct_references_s, Document::Reference.to_type, array: true, default: -> { [] }
110
114
 
111
- # Index Transformations - *_json functions
112
- def references
113
- references = ActiveSupport::HashWithIndifferentAccess.new
115
+ # Distributions
116
+ # - BEFORE DocumentDistributions
117
+ # - Use dct_references_s
118
+ # - Use multiple downloads
119
+ # - Use distributable assets
120
+ # - AFTER DocumentDistributions
121
+ # - Use document_distributions
122
+ # - Use distributable assets
123
+ # @TODO: Remove BEFORE path once we've migrated to DocumentDistributions
124
+ def distributions
125
+ distributions = ActiveSupport::HashWithIndifferentAccess.new
126
+
127
+ # AFTER - Add DocumentDistributions to distributions
128
+ if ENV["GBL_ADMIN_REFERENCES_MIGRATED"] == "true"
129
+ distributions = document_distributions.to_aardvark_distributions
130
+ end
114
131
 
115
- # Prep value arrays
132
+ # BEFORE - Prep value arrays
133
+ # @TODO: Remove this once we've migrated to DocumentDistributions
116
134
  send(GeoblacklightAdmin::Schema.instance.solr_fields[:reference]).each do |ref|
117
- references[Document::Reference::REFERENCE_VALUES[ref.category.to_sym][:uri]] = []
135
+ if ref.category.present?
136
+ distributions[Document::Reference::REFERENCE_VALUES[ref.category.to_sym][:uri]] = []
137
+ end
118
138
  end
119
139
 
120
- # Seed value arrays
140
+ # BEFORE - Seed value arrays
141
+ # @TODO: Remove this once we've migrated to DocumentDistributions
121
142
  send(GeoblacklightAdmin::Schema.instance.solr_fields[:reference]).each do |ref|
122
- # @TODO: Need to support multiple entries per key here
123
- references[Document::Reference::REFERENCE_VALUES[ref.category.to_sym][:uri]] << ref.value
143
+ if ref.category.present?
144
+ distributions[Document::Reference::REFERENCE_VALUES[ref.category.to_sym][:uri]] << ref.value
145
+ end
124
146
  end
147
+ logger.debug("\n\nDocument#distributions > seeded: #{distributions}")
125
148
 
126
- logger.debug("\n\nDocument#references > seeded: #{references}")
127
-
128
- # Apply Downloads
129
- references = apply_downloads(references)
149
+ # BEFORE - Apply Multiple Downloads
150
+ # @TODO: Remove this once we've migrated to DocumentDistributions
151
+ if ENV["GBL_ADMIN_REFERENCES_MIGRATED"] == "false"
152
+ distributions = apply_downloads(distributions)
153
+ logger.debug("Document#distributions > downloads: #{distributions}")
154
+ end
130
155
 
131
- logger.debug("Document#references > downloads: #{references}\n\n")
156
+ # BEFORE & AFTER - Apply Distributable Assets
157
+ distributions = apply_assets(distributions)
158
+ logger.debug("Document#distributions > assets: #{distributions}")
132
159
 
133
160
  # Need to flatten the arrays here to avoid the following potential error:
134
161
  # - ArgumentError: Please use symbols for polymorphic route arguments.
135
162
  # - Via: app/helpers/geoblacklight_helper.rb:224:in `render_references_url'
136
- references.each do |key, value|
163
+ distributions.each do |key, value|
137
164
  next if key == "http://schema.org/downloadUrl"
138
165
  if value.is_a?(Array) && value.length == 1
139
- references[key] = value.first
166
+ distributions[key] = value.first
140
167
  end
141
168
  end
142
169
 
143
- references
170
+ distributions
171
+ end
172
+
173
+ # Distributions JSON
174
+ # - Indexes to Solr as dct_distributions_s
175
+ def distributions_json
176
+ if ENV["GBL_ADMIN_REFERENCES_MIGRATED"] == "true"
177
+ logger.debug("Document#distributions_json > using document_distributions")
178
+ distributions = document_distributions.to_aardvark_distributions
179
+ distributions = apply_assets(distributions)
180
+ distributions.to_json
181
+ else
182
+ logger.debug("Document#distributions > #{distributions.inspect}")
183
+ logger.debug("Document#distributions_json > using distributions")
184
+ logger.warn("Deprecation warning: AttrJSON-based dct_references_s will not be supported soon.")
185
+ self.distributions.to_json
186
+ end
144
187
  end
145
188
 
146
- def references_json
147
- references.to_json
189
+ def distributions_csv
190
+ # Initialize CSV
191
+ # - [document_id, category, value, label]
192
+ csv = []
193
+
194
+ distributions.each do |key, value|
195
+ if key == "http://schema.org/downloadUrl"
196
+ value.each do |download|
197
+ csv << [friendlier_id, ReferenceType.find_by(reference_uri: key).name, download["url"], download["label"]]
198
+ end
199
+ else
200
+ csv << [friendlier_id, ReferenceType.find_by(reference_uri: key)&.name, value, nil]
201
+ end
202
+ end
203
+ csv
148
204
  end
149
205
 
150
206
  def asset_label(asset)
@@ -155,14 +211,33 @@ class Document < Kithe::Work
155
211
  end
156
212
  end
157
213
 
158
- # Apply Downloads
214
+ def apply_assets(distributions)
215
+ # Distributable Document Assets
216
+ # - Via DocumentAssets (Assets)
217
+ # - With Downloadable URI
218
+ if distributable_assets.present?
219
+ distributable_assets.each do |asset|
220
+ if asset.dct_references_uri_key == "download"
221
+ distributions["http://schema.org/downloadUrl"] ||= []
222
+ distributions["http://schema.org/downloadUrl"] << asset.to_aardvark_reference["http://schema.org/downloadUrl"]
223
+ else
224
+ distributions.merge!(asset.to_aardvark_reference)
225
+ end
226
+ end
227
+ end
228
+
229
+ distributions
230
+ end
231
+
232
+ # BEFORE - Apply Downloads
233
+ # @TODO: Remove this once we've migrated to DocumentDistributions
159
234
  # 1. Native Aardvark Downloads
160
235
  # 2. Multiple Document Download Links
161
236
  # 3. Downloadable Document Assets
162
- def apply_downloads(references)
237
+ def apply_downloads(distributions)
163
238
  multiple_downloads = []
164
239
 
165
- dct_downloads = references["http://schema.org/downloadUrl"]
240
+ dct_downloads = distributions["http://schema.org/downloadUrl"]
166
241
 
167
242
  logger.debug("Document#dct_downloads > init: #{dct_downloads}\n\n")
168
243
 
@@ -180,35 +255,21 @@ class Document < Kithe::Work
180
255
  # Multiple Document Download Links
181
256
  # - Via DocumentDownloads
182
257
  if document_downloads.present?
183
- multiple_downloads << multiple_downloads_array
258
+ multiple_downloads_array.each do |download|
259
+ multiple_downloads << download
260
+ end
184
261
  end
185
262
 
186
263
  logger.debug("Document#dct_downloads > document_downloads: #{multiple_downloads.inspect}\n\n")
187
264
 
188
- # Downloadable Document Assets
189
- # - Via DocumentAssets (Assets)
190
- # - With Downloadable URI
191
- if downloadable_assets.present?
192
- downloadable_assets.each do |asset|
193
- logger.debug("\n\n Document#dct_downloads > dupe?: #{multiple_downloads.detect { |d| d[:url].include?(asset.file.url) }}\n\n")
194
-
195
- if multiple_downloads.detect { |d| d[:url].include?(asset.file.url) }
196
- logger.debug("\n\n Detected duplicate download URL: #{asset.file.url}\n\n")
197
- index = multiple_downloads.index { |d| d[:url].include?(asset.file.url) }
198
- multiple_downloads[index] = {label: asset_label(asset), url: asset.file.url}
199
- else
200
- logger.debug("\n\n No duplicate found - Adding downloadable asset: #{asset.file.url}\n\n")
201
- multiple_downloads << {label: asset_label(asset), url: asset.file.url}
202
- end
203
- end
204
- end
265
+ multiple_downloads = multiple_downloads.uniq { |d| [d[:label], d[:url]] } unless multiple_downloads.empty?
205
266
 
206
- logger.debug("Document#dct_downloads > downloadable_assets: #{multiple_downloads.inspect}\n\n")
207
-
208
- references[:"http://schema.org/downloadUrl"] = multiple_downloads.flatten unless multiple_downloads.empty?
209
- references
267
+ distributions[:"http://schema.org/downloadUrl"] = multiple_downloads.flatten unless multiple_downloads.empty?
268
+ distributions
210
269
  end
211
270
 
271
+ # BEFORE - Multiple Downloads Array
272
+ # @TODO: Remove this once we've migrated to DocumentDistributions
212
273
  def multiple_downloads_array
213
274
  document_downloads.collect { |d| {label: d.label, url: d.value} }
214
275
  end
@@ -230,14 +291,9 @@ class Document < Kithe::Work
230
291
  #
231
292
  def download_text(format)
232
293
  download_format = proper_case_format(format)
233
- prefix = "Original "
234
- begin
235
- format = download_format
236
- rescue
237
- # Need to rescue if format doesn't exist
238
- end
239
- value = prefix + format.to_s
240
- value.html_safe
294
+ download_format.to_s.html_safe
295
+ rescue
296
+ format.to_s.html_safe
241
297
  end
242
298
 
243
299
  ##
@@ -257,7 +313,7 @@ class Document < Kithe::Work
257
313
 
258
314
  def local?
259
315
  local = send(Settings.FIELDS.PROVIDER) || ""
260
- local.casecmp(Settings.INSTITUTION_LOCAL_NAME)&.zero?
316
+ local.casecmp(Settings.INSTITUTION)&.zero?
261
317
  end
262
318
 
263
319
  def restricted?
@@ -273,7 +329,7 @@ class Document < Kithe::Work
273
329
  end
274
330
 
275
331
  def direct_download
276
- references.download.to_hash if references.download.present?
332
+ distributions.download.to_hash if distributions.download.present?
277
333
  end
278
334
 
279
335
  def display_note
@@ -281,11 +337,11 @@ class Document < Kithe::Work
281
337
  end
282
338
 
283
339
  def hgl_download
284
- references.hgl.to_hash if references.hgl.present?
340
+ distributions.hgl.to_hash if distributions.hgl.present?
285
341
  end
286
342
 
287
343
  def oembed
288
- references.oembed.endpoint if references.oembed.present?
344
+ distributions.oembed.endpoint if distributions.oembed.present?
289
345
  end
290
346
 
291
347
  def same_institution?
@@ -294,15 +350,15 @@ class Document < Kithe::Work
294
350
  end
295
351
 
296
352
  def iiif_download
297
- references.iiif.to_hash if references.iiif.present?
353
+ distributions.iiif.to_hash if distributions.iiif.present?
298
354
  end
299
355
 
300
356
  def data_dictionary_download
301
- references.data_dictionary.to_hash if references.data_dictionary.present?
357
+ distributions.data_dictionary.to_hash if distributions.data_dictionary.present?
302
358
  end
303
359
 
304
360
  def external_url
305
- references.url&.endpoint
361
+ distributions.url&.endpoint
306
362
  end
307
363
 
308
364
  def itemtype
@@ -332,7 +388,7 @@ class Document < Kithe::Work
332
388
  # :type => a string which if its a Geoblacklight::Constants::URI key
333
389
  # will return a coresponding Geoblacklight::Reference
334
390
  def checked_endpoint(type)
335
- type = references.send(type)
391
+ type = distributions.send(type)
336
392
  type.endpoint if type.present?
337
393
  end
338
394
 
@@ -397,6 +453,7 @@ class Document < Kithe::Work
397
453
  elsif value[:destination] == "b1g_publication_state_s"
398
454
  send(:current_state)
399
455
  else
456
+ next if send(value[:destination]).blank?
400
457
  send(value[:destination])
401
458
  end
402
459
  end
@@ -0,0 +1,145 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "csv"
4
+
5
+ # DocumentDistribution
6
+ #
7
+ # This class represents a distribution of a document, which includes a URL and a distribution type.
8
+ # It belongs to a document and a reference type, and it supports CSV import and export.
9
+ #
10
+ # Associations:
11
+ # - belongs_to :document
12
+ # - belongs_to :reference_type
13
+ #
14
+ # Callbacks:
15
+ # - after_save :reindex_document
16
+ #
17
+ # Validations:
18
+ # - Validates presence of :friendlier_id, :reference_type_id, :url
19
+ # - Validates uniqueness of :url scoped to :friendlier_id and :reference_type_id
20
+ #
21
+ # Scopes:
22
+ # - to_aardvark_distributions: Converts distributions to aardvark format
23
+ # - to_csv: Converts distributions to CSV format
24
+ class DocumentDistribution < ApplicationRecord
25
+ belongs_to :document, foreign_key: :friendlier_id, primary_key: :friendlier_id
26
+ belongs_to :reference_type
27
+ after_save :reindex_document
28
+
29
+ # Validations
30
+ validates :friendlier_id, :reference_type_id, :url, presence: true
31
+ validates :url, uniqueness: {scope: [:friendlier_id, :reference_type_id]}
32
+
33
+ # Scopes
34
+ scope :to_aardvark_distributions, -> {
35
+ distributions = where(friendlier_id: pluck(:friendlier_id)).map(&:to_aardvark_distribution)
36
+ merged = {}
37
+ distributions.each do |dist|
38
+ if dist.keys.first == "http://schema.org/downloadUrl"
39
+ merged["http://schema.org/downloadUrl"] ||= []
40
+ merged["http://schema.org/downloadUrl"] << {
41
+ "url" => dist.values.first,
42
+ "label" => dist[:label]
43
+ }
44
+ else
45
+ merged[dist.keys.first] = dist.values.first
46
+ end
47
+ end
48
+ merged
49
+ }
50
+ scope :to_csv, -> { where(friendlier_id: pluck(:friendlier_id)).map(&:to_csv) }
51
+
52
+ # CSV Column Names
53
+ #
54
+ # Returns an array of column names for CSV export.
55
+ #
56
+ # @return [Array<String>] the CSV column names
57
+ def self.csv_column_names
58
+ ["friendlier_id", "reference_type", "distribution_url", "label"]
59
+ end
60
+
61
+ # Import
62
+ #
63
+ # Imports document distributions from a CSV file.
64
+ #
65
+ # @param file [File] the CSV file to import
66
+ # @return [Boolean] true if import is successful
67
+ def self.import(file)
68
+ logger.debug("CSV Import")
69
+ ::CSV.foreach(file.path, headers: true) do |row|
70
+ logger.debug("CSV Row: #{row.to_hash}")
71
+ document_distribution = DocumentDistribution.find_or_initialize_by(
72
+ friendlier_id: row.to_hash["friendlier_id"],
73
+ reference_type_id: ReferenceType.find_by(name: row.to_hash["reference_type"]).id,
74
+ url: row.to_hash["distribution_url"],
75
+ label: row.to_hash["label"]
76
+ )
77
+
78
+ logger.debug("Document Distribution: #{document_distribution.inspect}")
79
+
80
+ document_distribution.update!(
81
+ friendlier_id: row.to_hash["friendlier_id"],
82
+ reference_type_id: ReferenceType.find_by(name: row.to_hash["reference_type"]).id,
83
+ url: row.to_hash["distribution_url"],
84
+ label: row.to_hash["label"]
85
+ )
86
+ end
87
+ true
88
+ end
89
+
90
+ # Destroy All
91
+ #
92
+ # Destroys document distributions based on a CSV file.
93
+ #
94
+ # @param file [File] the CSV file to process
95
+ # @return [Boolean] true if destroy is successful
96
+ def self.destroy_all(file)
97
+ logger.debug("CSV Destroy")
98
+ ::CSV.foreach(file.path, headers: true) do |row|
99
+ logger.debug("CSV Row: #{row.to_hash}")
100
+ if DocumentDistribution.destroy_by(
101
+ friendlier_id: row.to_hash["friendlier_id"],
102
+ reference_type_id: ReferenceType.find_by(name: row.to_hash["reference_type"]).id,
103
+ url: row.to_hash["distribution_url"]
104
+ )
105
+ logger.debug("Destroyed: #{row.to_hash}")
106
+ else
107
+ logger.debug("Not Destroyed: #{row.to_hash}")
108
+ end
109
+ end
110
+ true
111
+ end
112
+
113
+ # To CSV
114
+ #
115
+ # Converts the document distribution to an array suitable for CSV export.
116
+ #
117
+ # @return [Array<String>] the CSV row data
118
+ def to_csv
119
+ [
120
+ friendlier_id,
121
+ reference_type.name,
122
+ url,
123
+ label
124
+ ]
125
+ end
126
+
127
+ # To Aardvark Reference
128
+ #
129
+ # Converts the document distribution to an aardvark distribution format.
130
+ #
131
+ # @return [Hash] the aardvark reference
132
+ def to_aardvark_distribution
133
+ hash = {}
134
+ hash[reference_type.reference_uri.to_s] = url
135
+ hash[:label] = label if reference_type.reference_uri.to_s == "http://schema.org/downloadUrl"
136
+ hash
137
+ end
138
+
139
+ # Reindex Document
140
+ #
141
+ # Reindexes the associated document.
142
+ def reindex_document
143
+ document.save
144
+ end
145
+ end
@@ -22,6 +22,8 @@ class Element < ApplicationRecord
22
22
 
23
23
  # Validations
24
24
  validates :label, :solr_field, :field_type, presence: true
25
+ validates :label, uniqueness: true
26
+ validates :solr_field, uniqueness: true
25
27
 
26
28
  FIELD_TYPES = %w[
27
29
  string
@@ -31,7 +31,11 @@ module GeoblacklightAdmin
31
31
  }
32
32
  end
33
33
 
34
- @fields = @fields.merge(dct_references_import_mappings)
34
+ unless ENV["GBL_ADMIN_REFERENCES_MIGRATED"] == "true"
35
+ Rails.logger.warn("Deprecation warning: AttrJSON-based dct_references_s will not be supported soon.")
36
+ @fields = @fields.merge(dct_references_import_mappings)
37
+ end
38
+
35
39
  @fields
36
40
  end
37
41
 
@@ -63,7 +67,11 @@ module GeoblacklightAdmin
63
67
  }
64
68
  }
65
69
 
66
- @fields = @fields.merge(dct_references_import_mappings)
70
+ unless ENV["GBL_ADMIN_REFERENCES_MIGRATED"] == "true"
71
+ Rails.logger.warn("Deprecation warning: AttrJSON-based dct_references_s will not be supported soon.")
72
+ @fields = @fields.merge(dct_references_import_mappings)
73
+ end
74
+
67
75
  @fields = @fields.merge(object_metadata)
68
76
  @fields
69
77
  end
@@ -32,6 +32,8 @@ class Import
32
32
  end
33
33
 
34
34
  duplicate_id
35
+ rescue ArgumentError, ActiveStorage::FileNotFoundError
36
+ false
35
37
  end
36
38
  end
37
39
  end
@@ -8,7 +8,7 @@ class Import
8
8
  class CsvHeaderValidator < ActiveModel::Validator
9
9
  def validate(record)
10
10
  valid_csv_header = true
11
- unless valid_csv_headers?(record.csv_file)
11
+ unless valid_csv_headers?(record&.csv_file)
12
12
  valid_csv_header = false
13
13
  record.errors.add(:csv_file,
14
14
  "Missing a required CSV header. Title, Resource Class, Access Rights, and ID are required.")
@@ -20,6 +20,8 @@ class Import
20
20
  def valid_csv_headers?(csv_file)
21
21
  headers = CSV.parse(csv_file.download)[0]
22
22
  (["Title", "Resource Class", "Access Rights", "ID"] - headers).empty?
23
+ rescue ArgumentError, ActiveStorage::FileNotFoundError
24
+ false
23
25
  end
24
26
  end
25
27
  end
@@ -5,7 +5,7 @@ class ImportBtaaAardvark < Import
5
5
  # validations, constants and methods
6
6
 
7
7
  def mapping_configuration
8
- Geomg.field_mappings_btaa_aardvark
8
+ GeoblacklightAdmin::Schema.instance.importable_fields
9
9
  end
10
10
 
11
11
  def klass_delimiter
@@ -98,7 +98,7 @@ class ImportBtaaAardvark < Import
98
98
  json_data.each do |key, value|
99
99
  reference = {
100
100
  value: value,
101
- category: Geomg.uri_2_category_references_mappings[key]
101
+ category: GeoblacklightAdmin::FieldMappingsBtaaAardvark.uri_2_category_references_mappings[key]
102
102
  }
103
103
  references << reference
104
104
  end
@@ -9,4 +9,5 @@ class ImportDocumentStateMachine
9
9
  state :failed
10
10
 
11
11
  transition from: :queued, to: %i[success failed]
12
+ transition from: :failed, to: :success
12
13
  end
@@ -5,7 +5,7 @@ class ImportGblv1 < Import
5
5
  # validations, constants and methods
6
6
 
7
7
  def mapping_configuration
8
- Geomg.field_mappings_gblv1
8
+ GeoblacklightAdmin::Schema.instance.importable_fields
9
9
  end
10
10
 
11
11
  def klass_delimiter
@@ -81,7 +81,7 @@ class ImportGblv1 < Import
81
81
  json_data.each do |key, value|
82
82
  reference = {
83
83
  value: value,
84
- category: Geomg::FieldMappingsGblv1.uri_2_category_references_mappings[key]
84
+ category: GeoblacklightAdmin::FieldMappingsGblv1.uri_2_category_references_mappings[key]
85
85
  }
86
86
  references << reference
87
87
  end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ # The ReferenceType class represents a reference entity in the application.
4
+ # It includes validations for presence and uniqueness of reference attributes,
5
+ # and manages the position of references within the system.
6
+ class ReferenceType < ApplicationRecord
7
+ has_many :document_distributions, dependent: :destroy
8
+
9
+ # Validations
10
+ # Ensures that both reference_type and reference_uri are present and unique.
11
+ validates :name, :reference_type, :reference_uri, presence: true
12
+ validates :name, :reference_type, uniqueness: true
13
+
14
+ # Callbacks
15
+ # Sets the position of the reference before it is created.
16
+ before_create :set_last_position
17
+
18
+ # Sets the position of the reference to the next available position.
19
+ # If no references exist, it sets the position to 1.
20
+ #
21
+ # @return [void]
22
+ def set_last_position
23
+ position = ReferenceType.all.order(position: :desc)&.first&.position
24
+ self.position = position.blank? ? 1 : position + 1
25
+ end
26
+
27
+ # Class method to sort elements based on an array of IDs.
28
+ # Updates the position of each ReferenceType according to the order in the array.
29
+ #
30
+ # @param id_array [Array<Integer>] An array of element IDs to be sorted.
31
+ # @return [void]
32
+ def self.sort_elements(id_array)
33
+ transaction do
34
+ logger.debug { id_array.inspect }
35
+ id_array.each_with_index do |elm_id, i|
36
+ ReferenceType.update(elm_id, position: i)
37
+ end
38
+ end
39
+ end
40
+ end
data/app/models/user.rb CHANGED
@@ -1,13 +1,15 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "devise"
4
+
3
5
  # User
4
6
  class User < ApplicationRecord
7
+ extend Devise::Models
5
8
  include Blacklight::User
6
9
 
7
10
  # Include default devise modules. Others available are:
8
11
  # :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
9
- devise :invitable, :database_authenticatable,
10
- :recoverable, :rememberable, :validatable
12
+ devise :database_authenticatable, :recoverable, :rememberable, :validatable
11
13
 
12
14
  has_many :bookmarks, dependent: :destroy, as: :user
13
15
  has_many :notifications, dependent: :destroy, as: :recipient