geoblacklight_admin 0.4.1 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (109) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +10 -55
  3. data/Rakefile +3 -0
  4. data/app/assets/javascripts/geoblacklight_admin/chosen.js +7 -3
  5. data/app/assets/javascripts/geoblacklight_admin/datepicker.js +2 -2
  6. data/app/assets/javascripts/geoblacklight_admin/inputmask.js +2 -2
  7. data/app/assets/javascripts/geoblacklight_admin/truncate.js +2 -2
  8. data/app/assets/javascripts/geoblacklight_admin.js +1 -4
  9. data/app/assets/stylesheets/geoblacklight_admin/_core.scss +3 -1
  10. data/app/assets/stylesheets/geoblacklight_admin/modules/_images.scss +4 -0
  11. data/app/assets/stylesheets/geoblacklight_admin/modules/_nav.scss +4 -0
  12. data/app/assets/stylesheets/geoblacklight_admin/modules/_results.scss +5 -0
  13. data/app/controllers/admin/admin_controller.rb +1 -1
  14. data/app/controllers/admin/advanced_search_controller.rb +0 -1
  15. data/app/controllers/admin/assets_controller.rb +142 -0
  16. data/app/controllers/admin/bulk_actions_controller.rb +1 -1
  17. data/app/controllers/admin/document_accesses_controller.rb +3 -3
  18. data/app/controllers/admin/document_assets_controller.rb +33 -23
  19. data/app/controllers/admin/documents_controller.rb +6 -2
  20. data/app/controllers/admin/ids_controller.rb +0 -1
  21. data/app/controllers/admin/imports_controller.rb +2 -2
  22. data/app/helpers/asset_helper.rb +8 -0
  23. data/app/helpers/document_helper.rb +4 -0
  24. data/app/helpers/geoblacklight_admin_helper.rb +11 -1
  25. data/{lib/generators/geoblacklight_admin/templates → app}/javascript/controllers/results_controller.js +38 -0
  26. data/app/javascript/entrypoints/engine.js +8 -0
  27. data/app/javascript/index.js +8 -0
  28. data/app/jobs/bulk_action_revert_document_job.rb +2 -2
  29. data/app/jobs/bulk_action_run_document_job.rb +5 -1
  30. data/app/jobs/bulk_action_run_job.rb +11 -1
  31. data/app/jobs/geoblacklight_admin/delete_thumbnail_job.rb +17 -0
  32. data/app/jobs/geoblacklight_admin/remove_parent_dct_references_uri_job.rb +16 -0
  33. data/app/jobs/geoblacklight_admin/set_parent_dct_references_uri_job.rb +19 -0
  34. data/app/jobs/geoblacklight_admin/store_image_job.rb +21 -2
  35. data/app/models/asset.rb +38 -0
  36. data/app/models/blacklight_api.rb +2 -2
  37. data/app/models/blacklight_api_facets.rb +1 -1
  38. data/app/models/blacklight_api_ids.rb +2 -2
  39. data/app/models/bulk_action_document_state_machine.rb +2 -4
  40. data/app/models/bulk_action_state_machine.rb +3 -3
  41. data/app/models/bulk_actions/change_publication_state.rb +10 -0
  42. data/app/models/document/reference.rb +24 -0
  43. data/app/models/document.rb +122 -11
  44. data/app/models/document_thumbnail_state_machine.rb +22 -0
  45. data/app/models/document_thumbnail_transition.rb +26 -0
  46. data/app/models/element.rb +1 -1
  47. data/app/models/geoblacklight_admin/field_mappings_btaa_aardvark.rb +7 -1
  48. data/app/models/geoblacklight_admin/schema.rb +37 -1
  49. data/app/models/geoblacklight_admin/solr_utils.rb +87 -0
  50. data/app/models/kithe/vips_cli_image_to_png.rb +114 -0
  51. data/app/services/geoblacklight_admin/image_service/iiif.rb +2 -2
  52. data/app/services/geoblacklight_admin/image_service/iiif_manifest.rb +111 -0
  53. data/app/services/geoblacklight_admin/image_service/tms.rb +50 -0
  54. data/app/services/geoblacklight_admin/image_service/wms.rb +1 -4
  55. data/app/services/geoblacklight_admin/image_service.rb +16 -40
  56. data/app/services/geoblacklight_admin/item_viewer.rb +1 -1
  57. data/app/uploaders/asset_uploader.rb +6 -11
  58. data/app/views/admin/assets/_form.html.erb +19 -0
  59. data/app/views/admin/assets/display_attach_form.html.erb +39 -0
  60. data/app/views/admin/assets/edit.html.erb +9 -0
  61. data/app/views/admin/assets/index.html.erb +75 -0
  62. data/app/views/admin/assets/show.html.erb +100 -0
  63. data/app/views/admin/bulk_actions/index.html.erb +50 -48
  64. data/app/views/admin/bulk_actions/show.html.erb +3 -2
  65. data/app/views/admin/document_accesses/index.html.erb +68 -64
  66. data/app/views/admin/document_assets/_form.html.erb +17 -0
  67. data/app/views/admin/document_assets/display_attach_form.html.erb +4 -9
  68. data/app/views/admin/document_assets/edit.html.erb +5 -0
  69. data/app/views/admin/document_assets/index.html.erb +88 -72
  70. data/app/views/admin/document_downloads/index.html.erb +64 -62
  71. data/app/views/admin/documents/_document.html.erb +37 -16
  72. data/app/views/admin/documents/_form.html.erb +21 -6
  73. data/app/views/admin/documents/_form_nav.html.erb +12 -3
  74. data/app/views/admin/documents/_result_selected_options.html.erb +6 -1
  75. data/app/views/admin/documents/admin.html.erb +210 -0
  76. data/app/views/admin/documents/index.html.erb +10 -1
  77. data/app/views/admin/documents/versions.html.erb +3 -3
  78. data/app/views/admin/elements/index.html.erb +55 -54
  79. data/app/views/admin/form_elements/index.html.erb +38 -35
  80. data/app/views/admin/imports/index.html.erb +52 -50
  81. data/app/views/admin/layouts/application.html.erb +7 -4
  82. data/app/views/admin/shared/_js_behaviors.html.erb +6 -3
  83. data/app/views/admin/shared/_navbar.html.erb +11 -8
  84. data/config/locales/documents.en.yml +6 -0
  85. data/config/routes.rb +1 -0
  86. data/config/vite.json +14 -0
  87. data/db/migrate/20240619171628_create_document_thumbnail_statesman.rb +18 -0
  88. data/lib/generators/geoblacklight_admin/config_generator.rb +63 -15
  89. data/lib/generators/geoblacklight_admin/install_generator.rb +1 -0
  90. data/lib/generators/geoblacklight_admin/templates/api_controller.rb +0 -2
  91. data/lib/generators/geoblacklight_admin/templates/base.html.erb +53 -0
  92. data/lib/generators/geoblacklight_admin/templates/config/initializers/kithe.rb +1 -0
  93. data/lib/generators/geoblacklight_admin/templates/config/settings.yml +15 -1
  94. data/lib/generators/geoblacklight_admin/templates/config/vite.json +16 -0
  95. data/lib/generators/geoblacklight_admin/templates/frontend/entrypoints/application.js +30 -0
  96. data/lib/generators/geoblacklight_admin/templates/package-test.json +10 -0
  97. data/lib/generators/geoblacklight_admin/templates/package.json +5 -29
  98. data/lib/generators/geoblacklight_admin/templates/vite.config.ts +8 -0
  99. data/lib/geoblacklight_admin/engine.rb +1 -0
  100. data/lib/geoblacklight_admin/rake_task.rb +5 -0
  101. data/lib/geoblacklight_admin/tasks/images.rake +33 -0
  102. data/lib/geoblacklight_admin/tasks/solr.rake +11 -0
  103. data/lib/geoblacklight_admin/version.rb +1 -1
  104. metadata +75 -19
  105. data/lib/generators/geoblacklight_admin/templates/javascript/controllers/application_controller.js +0 -17
  106. data/lib/generators/geoblacklight_admin/templates/javascript/controllers/document_controller.js +0 -26
  107. data/lib/generators/geoblacklight_admin/templates/javascript/controllers/index.js +0 -10
  108. data/lib/tasks/geoblacklight_admin/images.rake +0 -30
  109. data/lib/tasks/geoblacklight_admin.rake +0 -213
@@ -293,6 +293,44 @@ export default class extends Controller {
293
293
  form.submit();
294
294
  }
295
295
 
296
+ harvestThumbnails(event) {
297
+ var el = document.querySelector('#bulk_action_field_name');
298
+ el.setAttribute('value', "Harvest Thumbnails");
299
+
300
+ // Set field value
301
+ var el = document.querySelector('#bulk_action_field_value');
302
+ el.setAttribute(
303
+ 'value',
304
+ event.currentTarget.innerHTML.toLowerCase().trim()
305
+ );
306
+
307
+ // Set scope value
308
+ this.setPubState(event);
309
+
310
+ // Submit form
311
+ var form = document.querySelector('#result-action-form');
312
+ form.submit();
313
+ }
314
+
315
+ deleteThumbnails(event) {
316
+ var el = document.querySelector('#bulk_action_field_name');
317
+ el.setAttribute('value', "Delete Thumbnails");
318
+
319
+ // Set field value
320
+ var el = document.querySelector('#bulk_action_field_value');
321
+ el.setAttribute(
322
+ 'value',
323
+ event.currentTarget.innerHTML.toLowerCase().trim()
324
+ );
325
+
326
+ // Set scope value
327
+ this.setPubState(event);
328
+
329
+ // Submit form
330
+ var form = document.querySelector('#result-action-form');
331
+ form.submit();
332
+ }
333
+
296
334
  bulkActionDelete(event) {
297
335
  event.preventDefault();
298
336
 
@@ -0,0 +1,8 @@
1
+ console.log('Vite ⚡️ Rails - GBL Admin')
2
+
3
+ // Stimulus
4
+ import { Application } from '@hotwired/stimulus'
5
+ import ResultsController from "../controllers/results_controller"
6
+
7
+ window.Stimulus = Application.start()
8
+ Stimulus.register("results", ResultsController)
@@ -0,0 +1,8 @@
1
+ console.log('Vite ⚡️ Rails - GBL Admin')
2
+
3
+ // Stimulus
4
+ import { Application } from '@hotwired/stimulus'
5
+ import ResultsController from "./controllers/results_controller"
6
+
7
+ window.Stimulus = Application.start()
8
+ Stimulus.register("results", ResultsController)
@@ -22,9 +22,9 @@ class BulkActionRevertDocumentJob < ApplicationJob
22
22
 
23
23
  versions = document.versions
24
24
  document = versions[doc.version].reify
25
- document.skip_callbacks = true
25
+ document&.skip_callbacks = true
26
26
 
27
- if document.save
27
+ if document&.save
28
28
  doc.state_machine.transition_to!(:success)
29
29
  else
30
30
  doc.state_machine.transition_to!(:failed)
@@ -10,8 +10,12 @@ class BulkActionRunDocumentJob < ApplicationJob
10
10
  update_publication_status(doc, field_value)
11
11
  when :update_delete
12
12
  update_delete(doc, field_value)
13
- # @TODO: Field Level changes
13
+ when :harvest_thumbnails
14
+ GeoblacklightAdmin::StoreImageJob.perform_later(doc.friendlier_id, doc.id, :priority)
15
+ when :delete_thumbnails
16
+ GeoblacklightAdmin::DeleteThumbnailJob.perform_later(doc.friendlier_id, doc.id, :priority)
14
17
  else
18
+ # @TODO: Field Level changes
15
19
  logger.debug("@TODO - #{field_name} => #{field_value}")
16
20
  end
17
21
  end
@@ -4,6 +4,11 @@
4
4
  class BulkActionRunJob < ApplicationJob
5
5
  queue_as :priority
6
6
 
7
+ after_perform do |job|
8
+ logger.debug("BulkActionRunJob - After Perform - #{job.arguments.first.id}")
9
+ job.arguments.first.state_machine.transition_to!(:complete)
10
+ end
11
+
7
12
  def perform(bulk_action)
8
13
  action = case bulk_action.field_name
9
14
  when "Publication State"
@@ -12,13 +17,18 @@ class BulkActionRunJob < ApplicationJob
12
17
  when "Delete"
13
18
  logger.debug("BulkAction: Delete")
14
19
  :update_delete
20
+ when "Harvest Thumbnails"
21
+ logger.debug("BulkAction: Harvest Thumbnails")
22
+ :harvest_thumbnails
23
+ when "Delete Thumbnails"
24
+ logger.debug("BulkAction: Delete Thumbnails")
25
+ :delete_thumbnails
15
26
  else
16
27
  :update_field_value
17
28
  end
18
29
 
19
30
  bulk_action.documents.each do |doc|
20
31
  BulkActionRunDocumentJob.perform_later(action, doc, bulk_action.field_name, bulk_action.field_value)
21
- doc.state_machine.transition_to!(:queued)
22
32
  end
23
33
 
24
34
  # Capture State
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GeoblacklightAdmin
4
+ class DeleteThumbnailJob < ApplicationJob
5
+ queue_as do
6
+ arguments.last
7
+ end
8
+
9
+ def perform(solr_document_id, bad_id = nil, queue = :priority)
10
+ document = Document.find_by_friendlier_id(solr_document_id)
11
+ if document.thumbnail.present?
12
+ document.thumbnail.destroy!
13
+ end
14
+ BulkActionDocument.find(bad_id).state_machine.transition_to!(:success) if bad_id.present?
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GeoblacklightAdmin
4
+ class RemoveParentDctReferencesUriJob < ApplicationJob
5
+ queue_as :priority
6
+
7
+ def perform(asset)
8
+ if asset.dct_references_uri_key.present?
9
+ asset.parent.dct_references_s.delete_if { |i| i.value == asset.full_file_url }
10
+ asset.parent.save!
11
+ end
12
+ rescue => e
13
+ Rails.logger.error "\nError - Removing parent dct_references URI: #{e.message} \n"
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GeoblacklightAdmin
4
+ class SetParentDctReferencesUriJob < ApplicationJob
5
+ queue_as :priority
6
+
7
+ def perform(asset)
8
+ if asset.dct_references_uri_key.present?
9
+ reference = Document::Reference.new
10
+ reference.category = asset.dct_references_uri_key
11
+ reference.value = asset.full_file_url
12
+ asset.parent.dct_references_s << reference
13
+ asset.parent.save!
14
+ end
15
+ rescue => e
16
+ Rails.logger.error "\nError - Setting parent DCT references URI: #{e.message}\n"
17
+ end
18
+ end
19
+ end
@@ -2,11 +2,30 @@
2
2
 
3
3
  module GeoblacklightAdmin
4
4
  class StoreImageJob < ApplicationJob
5
- queue_as :default
5
+ queue_as do
6
+ arguments.last
7
+ end
6
8
 
7
- def perform(solr_document_id)
9
+ def perform(solr_document_id, bad_id = nil, queue = :default)
10
+ # Find the document
8
11
  document = Document.find_by_friendlier_id(solr_document_id)
12
+
13
+ # Delete thumbnail if already present
14
+ if document&.thumbnail&.present?
15
+ document.thumbnail.destroy!
16
+ end
17
+
18
+ # Statesman
19
+ metadata = {}
20
+ metadata["solr_doc_id"] = solr_document_id
21
+ document.thumbnail_state_machine.transition_to!(:queued, metadata)
22
+
23
+ # Crawl politely
24
+ sleep(rand(1..5))
25
+
26
+ # Store the image
9
27
  GeoblacklightAdmin::ImageService.new(document).store
28
+ BulkActionDocument.find(bad_id).state_machine.transition_to!(:success) if bad_id.present?
10
29
  end
11
30
  end
12
31
  end
data/app/models/asset.rb CHANGED
@@ -1,13 +1,51 @@
1
1
  class Asset < Kithe::Asset
2
2
  include AttrJson::Record::QueryScopes
3
+ include Rails.application.routes.url_helpers
4
+
5
+ # Default Sort Order
6
+ default_scope { order(parent_id: :desc, created_at: :asc) }
3
7
 
4
8
  set_shrine_uploader(AssetUploader)
5
9
 
6
10
  # AttrJSON
7
11
  attr_json :thumbnail, :boolean, default: "false"
8
12
  attr_json :derivative_storage_type, :string, default: "public"
13
+ attr_json :dct_references_uri_key, :string
14
+ attr_json :label, :string
9
15
 
10
16
  DERIVATIVE_STORAGE_TYPE_LOCATIONS = {
11
17
  "public" => :kithe_derivatives
12
18
  }.freeze
19
+
20
+ def full_file_url
21
+ if Rails.env.development?
22
+ "http://localhost:3000" + file.url
23
+ else
24
+ file.url
25
+ end
26
+ end
27
+
28
+ # After Promotion Callbacks
29
+ after_promotion :set_parent_dct_references_uri
30
+
31
+ def set_parent_dct_references_uri
32
+ GeoblacklightAdmin::SetParentDctReferencesUriJob.perform_later(self) if parent_id.present?
33
+ end
34
+
35
+ # Before Destroy Callbacks
36
+ before_destroy :remove_parent_dct_references_uri
37
+
38
+ def remove_parent_dct_references_uri
39
+ GeoblacklightAdmin::RemoveParentDctReferencesUriJob.perform_later(self) if parent_id.present?
40
+ end
41
+
42
+ # After Save Callbacks
43
+ after_save :reindex_parent
44
+
45
+ def reindex_parent
46
+ parent.save if parent.present?
47
+ end
13
48
  end
49
+
50
+ # Allow DocumentAsset to be used as a synonym for Asset
51
+ DocumentAsset = Asset
@@ -30,11 +30,11 @@ class BlacklightApi
30
30
  end
31
31
 
32
32
  def facets
33
- fetch["included"]&.filter_map { |s| s if s["type"] == "facet" }
33
+ fetch["included"]&.select { |s| s["type"] == "facet" }
34
34
  end
35
35
 
36
36
  def sorts
37
- fetch["included"].filter_map { |s| s if s["type"] == "sort" }
37
+ fetch["included"].select { |s| s["type"] == "sort" }
38
38
  end
39
39
 
40
40
  def meta
@@ -15,6 +15,6 @@ class BlacklightApiFacets
15
15
  end
16
16
 
17
17
  def facets
18
- fetch["included"].filter_map { |s| s if s["type"] == "facet" } if fetch["included"].present?
18
+ fetch["included"].select { |s| s["type"] == "facet" } if fetch["included"].present?
19
19
  end
20
20
  end
@@ -33,11 +33,11 @@ class BlacklightApiIds
33
33
  end
34
34
 
35
35
  def facets
36
- fetch["included"]&.filter_map { |s| s if s["type"] == "facet" }
36
+ fetch["included"]&.select { |s| s["type"] == "facet" }
37
37
  end
38
38
 
39
39
  def sorts
40
- fetch["included"].filter_map { |s| s if s["type"] == "sort" }
40
+ fetch["included"].select { |s| s["type"] == "sort" }
41
41
  end
42
42
 
43
43
  def meta
@@ -9,8 +9,6 @@ class BulkActionDocumentStateMachine
9
9
  state :success
10
10
  state :failed
11
11
 
12
- transition from: :created, to: %i[queued success]
13
- transition from: :queued, to: %i[queued success failed]
14
- transition from: :success, to: %i[queued success failed]
15
- transition from: :failed, to: %i[queued success failed]
12
+ transition from: :created, to: %i[queued success failed]
13
+ transition from: :queued, to: %i[success failed]
16
14
  end
@@ -10,7 +10,7 @@ class BulkActionStateMachine
10
10
  state :failed
11
11
  state :reverted
12
12
 
13
- transition from: :created, to: %i[queued complete failed]
14
- transition from: :queued, to: %i[created queued complete failed]
15
- transition from: :complete, to: %i[queued reverted]
13
+ transition from: :created, to: %i[queued]
14
+ transition from: :queued, to: %i[created complete failed]
15
+ transition from: :complete, to: %i[reverted]
16
16
  end
@@ -18,4 +18,14 @@ module BulkActions
18
18
  class DraftDocument < BulkAction
19
19
  # Add specific methods and validations for DraftDocument here
20
20
  end
21
+
22
+ # Subclass for HarvestThumbnails
23
+ class HarvestThumbnails < BulkAction
24
+ # Add specific methods and validations for HarvestThumbnails here
25
+ end
26
+
27
+ # Subclass for DeleteThumbnails
28
+ class DeleteThumbnails < BulkAction
29
+ # Add specific methods and validations for DeleteThumbnails here
30
+ end
21
31
  end
@@ -90,6 +90,30 @@ class Document
90
90
  oembed: {
91
91
  label: "oEmbed",
92
92
  uri: "https://oembed.com"
93
+ },
94
+ cog: {
95
+ label: "COG",
96
+ uri: "https://github.com/cogeotiff/cog-spec"
97
+ },
98
+ pmtiles: {
99
+ label: "PMTiles",
100
+ uri: "https://github.com/protomaps/PMTiles"
101
+ },
102
+ xyz_tiles: {
103
+ label: "XYZ Tiles",
104
+ uri: "https://wiki.openstreetmap.org/wiki/Slippy_map_tilenames"
105
+ },
106
+ wmts: {
107
+ label: "WMTS",
108
+ uri: "http://www.opengis.net/def/serviceType/ogc/wmts"
109
+ },
110
+ tile_json: {
111
+ label: "TileJSON",
112
+ uri: "https://github.com/mapbox/tilejson-spec"
113
+ },
114
+ tile_map_service: {
115
+ label: "Tile Map Service",
116
+ uri: "https://wiki.osgeo.org/wiki/Tile_Map_Service_Specification"
93
117
  }
94
118
  }.freeze
95
119
 
@@ -18,26 +18,59 @@ class Document < Kithe::Work
18
18
  belongs_to :import, optional: true
19
19
 
20
20
  # Statesman
21
+ # - Publication State
21
22
  has_many :document_transitions, foreign_key: "kithe_model_id", autosave: false, dependent: :destroy,
22
23
  inverse_of: :document
24
+ # - Thumbnail State
25
+ has_many :document_thumbnail_transitions, foreign_key: "kithe_model_id", autosave: false, dependent: :destroy,
26
+ inverse_of: :document
23
27
 
24
- # DocumentAccesses
28
+ # Document Collections
29
+ # - DocumentAccesses
25
30
  has_many :document_accesses, primary_key: "friendlier_id", foreign_key: "friendlier_id", autosave: false, dependent: :destroy,
26
31
  inverse_of: :document
27
32
 
28
- # DocumentDownloads
33
+ # - DocumentDownloads
29
34
  has_many :document_downloads, primary_key: "friendlier_id", foreign_key: "friendlier_id", autosave: false, dependent: :destroy,
30
35
  inverse_of: :document
31
36
 
37
+ # DocumentAssets - Thumbnails, Attachments, etc
38
+ # @TODO: Redundant? Kithe also includes a members association
39
+ def document_assets
40
+ scope = Kithe::Asset
41
+ scope = scope.where(parent_id: id)
42
+
43
+ # scope = scope.page(params[:page]).per(20).order(created_at: :desc)
44
+ scope.includes(:parent)
45
+ end
46
+
47
+ def downloadable_assets
48
+ document_assets.select { |a| a.dct_references_uri_key == "download" }
49
+ end
50
+
32
51
  include Statesman::Adapters::ActiveRecordQueries[
33
52
  transition_class: DocumentTransition,
34
53
  initial_state: :draft
35
54
  ]
36
55
 
56
+ # @TODO: Rename this to publication_state_machine
37
57
  def state_machine
38
58
  @state_machine ||= DocumentStateMachine.new(self, transition_class: DocumentTransition)
39
59
  end
40
60
 
61
+ include Statesman::Adapters::ActiveRecordQueries[
62
+ transition_class: DocumentThumbnailTransition,
63
+ initial_state: :initialized
64
+ ]
65
+
66
+ def thumbnail_state_machine
67
+ @thumbnail_state_machine ||= DocumentThumbnailStateMachine.new(self, transition_class: DocumentThumbnailTransition)
68
+ end
69
+
70
+ def raw_solr_document
71
+ Blacklight.default_index.connection.get("select", {params: {q: "id:\"#{geomg_id_s}\""}})["response"]["docs"][0]
72
+ end
73
+
41
74
  delegate :current_state, to: :state_machine
42
75
 
43
76
  before_save :transition_publication_state, unless: :skip_callbacks
@@ -78,27 +111,101 @@ class Document < Kithe::Work
78
111
  # Index Transformations - *_json functions
79
112
  def references
80
113
  references = ActiveSupport::HashWithIndifferentAccess.new
114
+
115
+ # Prep value arrays
116
+ send(GeoblacklightAdmin::Schema.instance.solr_fields[:reference]).each do |ref|
117
+ references[Document::Reference::REFERENCE_VALUES[ref.category.to_sym][:uri]] = []
118
+ end
119
+
120
+ # Seed value arrays
81
121
  send(GeoblacklightAdmin::Schema.instance.solr_fields[:reference]).each do |ref|
82
- references[Document::Reference::REFERENCE_VALUES[ref.category.to_sym][:uri]] = ref.value
122
+ # @TODO: Need to support multiple entries per key here
123
+ references[Document::Reference::REFERENCE_VALUES[ref.category.to_sym][:uri]] << ref.value
124
+ end
125
+
126
+ logger.debug("\n\nDocument#references > seeded: #{references}")
127
+
128
+ # Apply Downloads
129
+ references = apply_downloads(references)
130
+
131
+ logger.debug("Document#references > downloads: #{references}\n\n")
132
+
133
+ # Need to flatten the arrays here to avoid the following potential error:
134
+ # - ArgumentError: Please use symbols for polymorphic route arguments.
135
+ # - Via: app/helpers/geoblacklight_helper.rb:224:in `render_references_url'
136
+ references.each do |key, value|
137
+ next if key == "http://schema.org/downloadUrl"
138
+ if value.is_a?(Array) && value.length == 1
139
+ references[key] = value.first
140
+ end
83
141
  end
84
- apply_downloads(references)
142
+
143
+ references
85
144
  end
86
145
 
87
146
  def references_json
88
147
  references.to_json
89
148
  end
90
149
 
150
+ def asset_label(asset)
151
+ if asset.label.present?
152
+ asset.label
153
+ else
154
+ asset.title
155
+ end
156
+ end
157
+
158
+ # Apply Downloads
159
+ # 1. Native Aardvark Downloads
160
+ # 2. Multiple Document Download Links
161
+ # 3. Downloadable Document Assets
91
162
  def apply_downloads(references)
163
+ multiple_downloads = []
164
+
92
165
  dct_downloads = references["http://schema.org/downloadUrl"]
93
- # Make sure downloads exist!
94
- if document_downloads.present?
95
- multiple_downloads = multiple_downloads_array
96
- if dct_downloads.present?
166
+
167
+ logger.debug("Document#dct_downloads > init: #{dct_downloads}\n\n")
168
+
169
+ # Native Aardvark Downloads
170
+ # - Via CSV Import or via the webform
171
+ if dct_downloads.present?
172
+ dct_downloads.each do |download|
97
173
  multiple_downloads << {label: download_text(send(GeoblacklightAdmin::Schema.instance.solr_fields[:format])),
98
- url: dct_downloads}
174
+ url: download}
175
+ end
176
+ end
177
+
178
+ logger.debug("Document#multiple_downloads > aardvark: #{multiple_downloads.inspect}\n\n")
179
+
180
+ # Multiple Document Download Links
181
+ # - Via DocumentDownloads
182
+ if document_downloads.present?
183
+ multiple_downloads << multiple_downloads_array
184
+ end
185
+
186
+ logger.debug("Document#dct_downloads > document_downloads: #{multiple_downloads.inspect}\n\n")
187
+
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
99
203
  end
100
- references[:"http://schema.org/downloadUrl"] = multiple_downloads
101
204
  end
205
+
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?
102
209
  references
103
210
  end
104
211
 
@@ -231,8 +338,9 @@ class Document < Kithe::Work
231
338
 
232
339
  ### End / From GBL
233
340
 
341
+ # Thumbnail is a special case of document_assets
234
342
  def thumbnail
235
- members.find { |m| m.respond_to?(:thumbnail) }
343
+ members.find { |m| m.respond_to?(:thumbnail) && m.thumbnail? }
236
344
  end
237
345
 
238
346
  def access_json
@@ -282,6 +390,9 @@ class Document < Kithe::Work
282
390
  if value[:delimited]
283
391
  send(value[:destination])&.join("|")
284
392
  elsif value[:destination] == "dct_references_s"
393
+ # @TODO: Downloads need to be handled differently
394
+ # - Need to support multiple entries per key here
395
+ # - Need to respect label and url
285
396
  dct_references_s_to_csv(key, value[:destination])
286
397
  elsif value[:destination] == "b1g_publication_state_s"
287
398
  send(:current_state)
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Document Thumbnail State Machine
4
+ class DocumentThumbnailStateMachine
5
+ include Statesman::Machine
6
+
7
+ state :initialized, initial: true
8
+ state :queued
9
+ state :processing
10
+ state :succeeded
11
+ state :failed
12
+ state :placeheld
13
+
14
+ # Queued => Background Job Init
15
+ # Processing => Failed, Placeheld, Succeeded
16
+ transition from: :initialized, to: %i[queued processing]
17
+ transition from: :queued, to: %i[queued processing]
18
+ transition from: :processing, to: %i[queued processing placeheld succeeded failed]
19
+ transition from: :placeheld, to: %i[queued processing failed]
20
+ transition from: :failed, to: %i[queued processing]
21
+ transition from: :succeeded, to: %i[queued processing]
22
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Add Document Thumbnail State Transitions
4
+ class DocumentThumbnailTransition < ApplicationRecord
5
+ include Statesman::Adapters::ActiveRecordTransition
6
+
7
+ # If your transition table doesn't have the default `updated_at` timestamp column,
8
+ # you'll need to configure the `updated_timestamp_column` option, setting it to
9
+ # another column name (e.g. `:updated_on`) or `nil`.
10
+ #
11
+ # self.updated_timestamp_column = :updated_on
12
+ # self.updated_timestamp_column = nil
13
+
14
+ belongs_to :document, inverse_of: :document_thumbnail_transitions, foreign_key: "kithe_model_id"
15
+
16
+ after_destroy :update_most_recent, if: :most_recent?
17
+
18
+ private
19
+
20
+ def update_most_recent
21
+ last_transition = document.document_thumbnail_transitions.order(:sort_key).last
22
+ return if last_transition.blank?
23
+
24
+ last_transition.update_column(:most_recent, true)
25
+ end
26
+ end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class Element < ApplicationRecord
4
- serialize :html_attributes
4
+ serialize :html_attributes, coder: JSON
5
5
 
6
6
  # Scopes
7
7
  scope :formable, -> { where(formable: true) }
@@ -372,7 +372,13 @@ module GeoblacklightAdmin
372
372
  "urn:x-esri:serviceType:ArcGIS#ImageMapLayer": "arcgis_image_map_layer",
373
373
  "http://schema.org/DownloadAction": "harvard",
374
374
  "https://openindexmaps.org": "open_index_map",
375
- "https://oembed.com": "oembed"
375
+ "https://oembed.com": "oembed",
376
+ "https://github.com/cogeotiff/cog-spec": "cog",
377
+ "https://github.com/protomaps/PMTiles": "pmtiles",
378
+ "https://wiki.openstreetmap.org/wiki/Slippy_map_tilenames": "xyz_tiles",
379
+ "http://www.opengis.net/def/serviceType/ogc/wmts": "wmts",
380
+ "https://github.com/mapbox/tilejson-spec": "tile_json",
381
+ "https://wiki.osgeo.org/wiki/Tile_Map_Service_Specification": "tile_map_service"
376
382
  })
377
383
  end
378
384
  end