geoblacklight_admin 0.3.2 → 0.4.1

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 (44) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -0
  3. data/app/controllers/admin/document_downloads_controller.rb +1 -1
  4. data/app/controllers/admin/ids_controller.rb +3 -0
  5. data/app/controllers/admin/imports_controller.rb +2 -2
  6. data/app/helpers/geoblacklight_admin_helper.rb +30 -0
  7. data/app/jobs/bulk_action_collect_documents.rb +11 -0
  8. data/app/jobs/geoblacklight_admin/store_image_job.rb +12 -0
  9. data/app/models/asset.rb +13 -0
  10. data/app/models/bulk_action.rb +8 -5
  11. data/app/models/bulk_actions/change_publication_state.rb +21 -0
  12. data/app/models/concerns/geoblacklight_admin/publication_state_search_behavior.rb +26 -0
  13. data/app/models/document/date_validator.rb +46 -0
  14. data/app/models/document.rb +114 -2
  15. data/app/models/document_access.rb +3 -1
  16. data/app/models/document_download.rb +3 -1
  17. data/app/models/element.rb +1 -0
  18. data/app/services/geoblacklight_admin/image_service/dynamic_map_layer.rb +15 -0
  19. data/app/services/geoblacklight_admin/image_service/iiif.rb +17 -0
  20. data/app/services/geoblacklight_admin/image_service/image_map_layer.rb +17 -0
  21. data/app/services/geoblacklight_admin/image_service/tiled_map_layer.rb +15 -0
  22. data/app/services/geoblacklight_admin/image_service/wms.rb +28 -0
  23. data/app/services/geoblacklight_admin/image_service.rb +238 -0
  24. data/app/services/geoblacklight_admin/item_viewer.rb +31 -0
  25. data/app/uploaders/asset_uploader.rb +31 -0
  26. data/app/views/admin/document_assets/index.html.erb +13 -1
  27. data/app/views/admin/documents/_document.html.erb +1 -1
  28. data/app/views/admin/documents/_document_kithe.html.erb +47 -0
  29. data/app/views/admin/documents/_form_nav.html.erb +1 -1
  30. data/app/views/admin/documents/_form_nav_kithe.html.erb +30 -0
  31. data/app/views/admin/documents/_result_facets.html.erb +26 -2
  32. data/app/views/admin/documents/index.html.erb +4 -8
  33. data/app/views/admin/documents/versions.html.erb +1 -1
  34. data/app/views/admin/imports/show.html.erb +35 -9
  35. data/app/views/admin/shared/_navbar.html.erb +6 -6
  36. data/db/migrate/20231106215104_bulk_action_sti.rb +10 -0
  37. data/lib/generators/geoblacklight_admin/config_generator.rb +33 -3
  38. data/{app/controllers/admin → lib/generators/geoblacklight_admin/templates}/api_controller.rb +4 -1
  39. data/lib/generators/geoblacklight_admin/templates/config/initializers/shrine.rb +3 -2
  40. data/lib/generators/geoblacklight_admin/templates/config/settings.yml +4 -1
  41. data/lib/generators/geoblacklight_admin/templates/views/_index_split_default.html.erb +27 -0
  42. data/lib/geoblacklight_admin/version.rb +1 -1
  43. data/lib/tasks/geoblacklight_admin/images.rake +30 -0
  44. metadata +24 -5
@@ -0,0 +1,238 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "addressable/uri"
4
+
5
+ module GeoblacklightAdmin
6
+ class ImageService
7
+ attr_reader :document
8
+ attr_writer :metadata, :logger
9
+
10
+ def initialize(document)
11
+ @document = document
12
+
13
+ @metadata = {}
14
+ @metadata["solr_doc_id"] = document.id
15
+ @metadata["placeheld"] = false
16
+
17
+ @logger ||= ActiveSupport::TaggedLogging.new(
18
+ Logger.new(
19
+ Rails.root.join("log", "image_service_#{Rails.env}.log")
20
+ )
21
+ )
22
+ end
23
+
24
+ # Stores the document's image in ActiveStorage
25
+ # @return [Boolean]
26
+ #
27
+ def store
28
+ # Gentle hands
29
+ sleep(1)
30
+
31
+ puts "Storing ImageService..."
32
+ puts "Document ID: #{@document.id}"
33
+
34
+ io_file = image_tempfile(@document.id)
35
+
36
+ if io_file.nil?
37
+ puts "IO is NIL"
38
+ else
39
+ puts "Attaching IO"
40
+ attach_io(io_file)
41
+ end
42
+
43
+ log_output
44
+ rescue => e
45
+ @metadata["exception"] = e.inspect
46
+ log_output
47
+ end
48
+
49
+ private
50
+
51
+ def image_tempfile(document_id)
52
+ # puts "IMAGE TEMPFILE..."
53
+ puts "Document Viewer Protocol: #{@document.viewer_protocol}"
54
+ # puts "Image URL: #{image_url}"
55
+ # puts "IMAGE DATA: #{image_data}"
56
+
57
+ @metadata["viewer_protocol"] = @document.viewer_protocol
58
+ @metadata["image_url"] = image_url
59
+ @metadata["gblsi_thumbnail_uri"] = gblsi_thumbnail_uri
60
+
61
+ # puts "IMAGE DATA: #{image_data.inspect}"
62
+
63
+ return nil unless image_data && @metadata["placeheld"] == false
64
+
65
+ temp_file = Tempfile.new("#{document_id}.tmp")
66
+ temp_file.binmode
67
+ temp_file.write(image_data)
68
+ temp_file.rewind
69
+
70
+ # puts "TEMPFILE: #{temp_file.inspect}"
71
+
72
+ @metadata["image_tempfile"] = temp_file.inspect
73
+ temp_file
74
+ end
75
+
76
+ def attach_io(io)
77
+ # Remote content-type headers are untrustworthy
78
+ # Pull the mimetype and file extension via MimeMagic
79
+ content_type = Marcel::MimeType.for(File.open(io))
80
+ mime_type = Marcel::TYPES[content_type][0][0]
81
+
82
+ puts "Content Type: #{content_type.inspect}"
83
+ puts "MIME Type: #{mime_type.inspect}"
84
+
85
+ if content_type.start_with?("image")
86
+
87
+ puts "\n\nStoring an image!\n\n"
88
+
89
+ asset = Asset.new
90
+ asset.parent_id = @document.id
91
+ asset.file = io
92
+ asset.title = (asset.file&.original_filename || "Untitled")
93
+ asset.thumbnail = true
94
+ asset.save
95
+ else
96
+ # @TODO: If no thumb, what to do?
97
+ end
98
+ end
99
+
100
+ # Returns geoserver auth credentials if the document is a restriced Local WMS layer.
101
+ def geoserver_credentials
102
+ return unless restricted_wms_layer?
103
+
104
+ Settings.PROXY_GEOSERVER_AUTH&.gsub("Basic ", "")
105
+ end
106
+
107
+ def restricted_wms_layer?
108
+ @document.local_restricted? && @document.viewer_protocol == "wms"
109
+ end
110
+
111
+ # Tests if geoserver credentials are set beyond the default.
112
+ def geoserver_credentials_valid?
113
+ Settings.PROXY_GEOSERVER_AUTH != "Basic base64encodedusername:password"
114
+ end
115
+
116
+ # Tests if local thumbnail method is configured
117
+ def gblsi_thumbnail_field?
118
+ Settings.GBLSI_THUMBNAIL_FIELD
119
+ end
120
+
121
+ def gblsi_thumbnail_uri
122
+ if gblsi_thumbnail_field? && @document.send(Settings.GBLSI_THUMBNAIL_FIELD)
123
+ @document.send(Settings.GBLSI_THUMBNAIL_FIELD)
124
+ else
125
+ false
126
+ end
127
+ end
128
+
129
+ # Generates hash containing thumbnail mime_type and image.
130
+ def image_data
131
+ # puts "IMAGE DATA..."
132
+ return nil unless image_url
133
+
134
+ remote_image
135
+ end
136
+
137
+ # Gets thumbnail image from URL. On error, placehold image.
138
+ def remote_image
139
+ # puts "remote_image..."
140
+ auth = geoserver_credentials
141
+
142
+ uri = Addressable::URI.parse(image_url)
143
+
144
+ return nil unless uri.scheme.include?("http")
145
+
146
+ conn = Faraday.new(url: uri.normalize.to_s) do |b|
147
+ b.use Geoblacklight::FaradayMiddleware::FollowRedirects
148
+ b.adapter :net_http
149
+ end
150
+
151
+ conn.options.timeout = timeout
152
+ conn.authorization :Basic, auth if auth
153
+ conn.get.body
154
+ rescue Faraday::ConnectionFailed
155
+ @metadata["error"] = "Faraday::ConnectionFailed"
156
+ @metadata["placeheld"] = true
157
+ nil
158
+ rescue Faraday::TimeoutError
159
+ @metadata["error"] = "Faraday::TimeoutError"
160
+ @metadata["placeheld"] = true
161
+ nil
162
+ end
163
+
164
+ # Returns the thumbnail url.
165
+ # If the layer is restriced Local WMS, and the geoserver credentials
166
+ # have not been set beyond the default, then a thumbnail url from
167
+ # dct references is used instead.
168
+ def image_url
169
+ # puts "IMAGE URL..."
170
+ # puts "gblsi_thumbnail_uri: #{gblsi_thumbnail_uri.inspect}"
171
+ # puts "restricted_scanned_map?: #{restricted_scanned_map?}"
172
+ # puts "service_url: #{service_url.inspect}"
173
+ # puts "image_reference: #{image_reference.inspect}"
174
+
175
+ @image_url ||= gblsi_thumbnail_uri || service_url || image_reference
176
+ end
177
+
178
+ # Checks if the document is Local restriced access and is a scanned map.
179
+ def restricted_scanned_map?
180
+ @document.local_restricted?
181
+ end
182
+
183
+ # Gets the url for a specific service endpoint if the item is
184
+ # public, has the same institution as the GBL instance, and the viewer
185
+ # protocol is not 'map' or nil. A module name is then dynamically generated
186
+ # from the viewer protocol, and if it's loaded, the image_url
187
+ # method is called.
188
+ def service_url
189
+ # puts "SERVICE URL..."
190
+ # Follow image_url instead
191
+ return nil if gblsi_thumbnail_uri
192
+
193
+ @service_url ||=
194
+ begin
195
+ return unless @document.available?
196
+
197
+ protocol = @document.viewer_protocol
198
+
199
+ if protocol == "map" || protocol.nil?
200
+ @metadata["error"] = "Unsupported viewer protocol"
201
+ @metadata["placeheld"] = true
202
+ return nil
203
+ end
204
+
205
+ puts "Image Service: #{protocol.to_s.camelcase}"
206
+
207
+ "GeoblacklightAdmin::ImageService::#{protocol.to_s.camelcase}".constantize.image_url(@document, image_size)
208
+ rescue NameError
209
+ @metadata["error"] = "service_url NameError"
210
+ @metadata["placeheld"] = true
211
+ return nil
212
+ end
213
+ end
214
+
215
+ # Retreives a url to a static thumbnail from the document's dct_references field, if it exists.
216
+ def image_reference
217
+ @document.references["http://schema.org/thumbnailUrl"]
218
+ end
219
+
220
+ # Default image size.
221
+ def image_size
222
+ 1500
223
+ end
224
+
225
+ # Faraday timeout value.
226
+ def timeout
227
+ 30
228
+ end
229
+
230
+ # Capture metadata within image harvest log
231
+ def log_output
232
+ # @metadata["state"] = @document.sidecar.image_state.current_state
233
+ @metadata.each do |key, value|
234
+ @logger.tagged(@document.id, key.to_s) { @logger.info value }
235
+ end
236
+ end
237
+ end
238
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GeoblacklightAdmin
4
+ class ItemViewer
5
+ def initialize(references)
6
+ @references = references
7
+ @keys = references.keys.collect { |k| reference_uri_2_key(k) }
8
+ end
9
+
10
+ def viewer_protocol
11
+ viewer_preference.find { |vp| @keys.include?(vp) } || :map
12
+ end
13
+
14
+ def viewer_endpoint
15
+ @references[viewer_protocol_2_endpoint]
16
+ end
17
+
18
+ def reference_uri_2_key(value)
19
+ Geoblacklight::Constants::URI.key(value)
20
+ end
21
+
22
+ def viewer_protocol_2_endpoint
23
+ Geoblacklight::Constants::URI[viewer_protocol]
24
+ end
25
+
26
+ def viewer_preference
27
+ [:oembed, :index_map, :tilejson, :xyz, :wmts, :tms, :wms, :iiif, :tiled_map_layer, :dynamic_map_layer,
28
+ :image_map_layer, :feature_layer]
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,31 @@
1
+ class AssetUploader < Kithe::AssetUploader
2
+ # gives us md5, sha1, sha512
3
+ plugin :kithe_checksum_signatures
4
+
5
+ THUMB_WIDTHS = {
6
+ mini: 54,
7
+ large: 525,
8
+ standard: 208
9
+ }
10
+
11
+ # define thumb derivatives for TIFF, PDF, and other image input: :thumb_mini, :thumb_mini_2X, etc.
12
+ THUMB_WIDTHS.each_pair do |key, width|
13
+ # Single-width thumbnails
14
+ Attacher.define_derivative("thumb_#{key}", content_type: "image") do |original_file|
15
+ Kithe::VipsCliImageToJpeg.new(max_width: width, thumbnail_mode: true).call(original_file)
16
+ end
17
+
18
+ # Double-width thumbnails
19
+ Attacher.define_derivative("thumb_#{key}_2X", content_type: "image") do |original_file|
20
+ Kithe::VipsCliImageToJpeg.new(max_width: width * 2, thumbnail_mode: true).call(original_file)
21
+ end
22
+ end
23
+
24
+ # and a full size jpg
25
+ Attacher.define_derivative("download_full", content_type: "image") do |original_file, attacher:|
26
+ # No need to do this if our original is a JPG
27
+ unless attacher.file.content_type == "image/jpeg"
28
+ Kithe::VipsCliImageToJpeg.new.call(original_file)
29
+ end
30
+ end
31
+ end
@@ -30,16 +30,28 @@
30
30
  <table class="table table-striped table-bordered sortable">
31
31
  <thead class="thead-dark">
32
32
  <tr>
33
+ <th>Thumbnail</th>
34
+ <th>Preview</th>
33
35
  <th>Title</th>
34
36
  <th>MimeType</th>
35
37
  <th>Actions</th>
36
38
  </tr>
37
39
  </thead>
38
-
39
40
  <tbody>
40
41
  <% @document_assets.each do |document_asset| %>
41
42
  <tr>
43
+ <td>
44
+ <% if document_asset.respond_to?(:thumbnail) %>
45
+ <%= document_asset.respond_to?(:thumbnail) %>
46
+ <% end %>
47
+ </td>
48
+ <td>
49
+ <% if document_asset.respond_to?(:thumbnail) %>
50
+ <%= image_tag(document_asset.file_url(:thumb_mini_2X), { height: 100 }) %>
51
+ <% end %>
52
+ </td>
42
53
  <td><%= link_to(document_asset.title, document_asset.file.url) %></td>
54
+
43
55
  <td>
44
56
  <%= document_asset.file_data["metadata"]["mime_type"] %>
45
57
  <span class="sr-only"><%= document_asset.inspect %></span>
@@ -26,4 +26,4 @@
26
26
  <li>
27
27
  <p><strong>Missing from database: </strong><%= document.inspect %></p>
28
28
  </li>
29
- <% end %>
29
+ <% end %>
@@ -0,0 +1,47 @@
1
+ <% bookmarked = current_user.document_is_bookmarked?(document) ? 'bookmarked' : '' %>
2
+
3
+ <% if document.respond_to?('friendlier_id') %>
4
+ <%= content_tag :li, id: document.friendlier_id, class: "#{bookmarked} mb-3" do %>
5
+ <div class="row">
6
+ <div class="doc-actions ml-3">
7
+ <%= check_box_tag nil, nil, false, data: { id: document.friendlier_id, action: 'click->results#checkChecked'}%>
8
+ <%= render 'admin/documents/document_bookmark', document: document %>
9
+ </div>
10
+ <div class="doc-hit col ml-2 mb-2">
11
+ <div class="row" style="font-size:1.1rem;">
12
+ <%= "#{@documents.meta['pages']['offset_value'] + index + 1}." if @documents %> &nbsp;
13
+ <%= link_to document.title, edit_admin_document_path(document.friendlier_id), { class: "js-truncate mb-2" } %>
14
+ </div>
15
+ <div class="row">
16
+ <div class="media">
17
+ <% if document&.members&.first&.file&.exists? %>
18
+ <%= link_to edit_admin_document_path(document.friendlier_id) do %>
19
+ <%= image_tag(document.members.first.file_url(:thumb_mini_2X), { height: 64, width:64 }) %>
20
+ <% end %>
21
+ <% else %>
22
+ <img class="" data-src="holder.js/64x64" alt="placeholder" src="data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D%2264%22%20height%3D%2264%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2064%2064%22%20preserveAspectRatio%3D%22none%22%3E%3Cdefs%3E%3Cstyle%20type%3D%22text%2Fcss%22%3E%23holder_18bb5833508%20text%20%7B%20fill%3Argba(255%2C255%2C255%2C.75)%3Bfont-weight%3Anormal%3Bfont-family%3AHelvetica%2C%20monospace%3Bfont-size%3A10pt%20%7D%20%3C%2Fstyle%3E%3C%2Fdefs%3E%3Cg%20id%3D%22holder_18bb5833508%22%3E%3Crect%20width%3D%2264%22%20height%3D%2264%22%20fill%3D%22%23777%22%3E%3C%2Frect%3E%3Cg%3E%3Ctext%20x%3D%2213.83984375%22%20y%3D%2236.65%22%3E64x64%3C%2Ftext%3E%3C%2Fg%3E%3C%2Fg%3E%3C%2Fsvg%3E" data-holder-rendered="true" style="width: 64px; height: 64px;">
23
+ <% end %>
24
+ <div class="media-body ml-3">
25
+ <%- if document.send(Settings.FIELDS.RESOURCE_CLASS) %>
26
+ <%= geoblacklight_icon(document.send(Settings.FIELDS.RESOURCE_CLASS)&.first) %>
27
+ <% end %>
28
+ <%= publication_state_badge(document) %>
29
+ <% if document.send("schema_provider_s") %>
30
+ &middot; <%= document.send("schema_provider_s") %>
31
+ <% end %>
32
+ <% if document.send("dct_temporal_sm") %>
33
+ &middot; <%= document.send("dct_temporal_sm").join("-") %>
34
+ <% end %>
35
+ <% if document.send("dct_description_sm").present? %>
36
+ <p class="js-truncate"><%= document.send("dct_description_sm").join %></p>
37
+ <% end %>
38
+ </div>
39
+ </div>
40
+ </div>
41
+ </div>
42
+ <% end %>
43
+ <% else %>
44
+ <li>
45
+ <p><strong>Missing from database: </strong><%= document.inspect %></p>
46
+ </li>
47
+ <% end %>
@@ -1,7 +1,7 @@
1
1
  <div class="make-me-sticky mt-4 ml-4">
2
2
  <nav class="nav flex-column ml-4 mt-6">
3
3
  <% if @document.persisted? %>
4
- <strong class="mt-4"><%= link_to "GBL♦Admin | View in GeoBlacklight", solr_document_url(@document), { class: 'btn btn-warning' } %></strong>
4
+ <strong class="mb-4"><%= link_to "GBL♦Admin | View in GeoBlacklight", solr_document_url(@document), { class: 'btn btn-warning' } %></strong>
5
5
  <% end %>
6
6
 
7
7
  <%- form_elements = FormElement.all.order(position: :asc) %>
@@ -0,0 +1,30 @@
1
+ <div class="make-me-sticky mt-4 ml-4">
2
+ <nav class="nav flex-column ml-4 mt-6">
3
+ <% if @document.persisted? %>
4
+ <strong class="mb-4"><%= link_to "GBL♦Admin | View in GeoBlacklight", solr_document_url(@document), { class: 'btn btn-warning' } %></strong>
5
+
6
+ <% if @document&.members&.first&.file&.exists? %>
7
+ <%= link_to admin_document_document_assets_url(@document) do %>
8
+ <strong class="mt-2">Thumbnail</strong><br>
9
+ <%= image_tag(@document.members.first.file_url(:thumb_standard_2X), { class: "p-2", width: 150 }) %>
10
+ <% end %>
11
+ <% end %>
12
+ <% end %>
13
+
14
+ <%- form_elements = FormElement.all.order(position: :asc) %>
15
+ <%- form_elements.each do |form_element| %>
16
+ <% if form_element.kind_of? FormHeader %>
17
+ <strong class="mt-2"><a data-controller="scroll-to" class="mt-4" href="#<%= form_element.label.parameterize(separator: '-') %>"><%= form_element.label %></a></strong>
18
+ <% end %>
19
+ <% if form_element.kind_of? FormGroup %>
20
+ <a data-controller="scroll-to" class="ml-2" href="#<%= form_element.label.parameterize(separator: '-') %>"><%= form_element.label %></a>
21
+ <% end %>
22
+ <% end %>
23
+
24
+ <% if @document.persisted? %>
25
+ <%= link_to "Institutional Access Links", admin_document_document_accesses_url(@document), class: "ml-2" %>
26
+ <%= link_to "Multiple Download Links", admin_document_document_downloads_url(@document), class: "ml-2" %>
27
+ <%= link_to "Additional Assets", admin_document_document_assets_url(@document), class: "ml-2" %>
28
+ <% end %>
29
+ </nav>
30
+ </div>
@@ -1,4 +1,28 @@
1
- <%= form_with url: '/documents', method: :get, local: true do |form| %>
1
+ <a class="h5" data-toggle="collapse" href="#collapseExample" role="button" aria-expanded="false" aria-controls="collapseExample">
2
+ Imports
3
+ </a>
4
+
5
+ <% if params["f"]&.keys&.include?("b1g_geom_import_id_ssi") %>
6
+ <% import = Import.find_by_id(params["f"]["b1g_geom_import_id_ssi"]) %>
7
+ <ul class="list-unstyled mt-3">
8
+ <li><%= link_to("- #{import.name} &#10060;".html_safe) %></li>
9
+ </ul>
10
+ <% else %>
11
+ <div class="collapse" id="collapseExample">
12
+ <ul class="list-unstyled mt-3">
13
+ <% Import.last(20).sort_by(&:created_at).reverse.each do |import| %>
14
+ <li>
15
+ <%= link_to_admin_import(import) %> -
16
+ <small><%= time_ago_in_words(import.created_at) %> ago</small>
17
+ </li>
18
+ <% end %>
19
+ </ul>
20
+ </div>
21
+ <% end %>
22
+
23
+ <hr/>
24
+
25
+ <%= form_with url: '/admin/documents', method: :get, local: true do |form| %>
2
26
  <div class="input-group mb-3" style="width:300px">
3
27
  <%= form.text_field("daterange", { class: 'form-control', placeholder: 'Date range filter', size: 24, 'aria-label': "Date range filter", value: params['daterange'] }) %>
4
28
  <%= params_as_hidden_fields(
@@ -16,7 +40,7 @@
16
40
  <% if @documents.facets.present? %>
17
41
  <% @documents.facets.each do |facet| %>
18
42
  <% if facet['type'] == 'facet' %>
19
- <%- next if ['b1g_code_s', 'dct_isPartOf_sm'].include?(facet['id']) %>
43
+ <%- next if ['b1g_geom_import_id_ssi', 'b1g_code_s', 'dct_isPartOf_sm'].include?(facet['id']) %>
20
44
  <h3 class="h5"><%= facet['attributes']['label'] %></h3>
21
45
  <ul class="list-unstyled">
22
46
  <%- facet['attributes']['items'].each do |item| %>
@@ -11,18 +11,14 @@
11
11
  </div>
12
12
 
13
13
  <div id="resultset" class="col-8">
14
- <p>
15
- <%= render "result_toggle" %>
16
- <%= render "result_selected_options" %>
17
- <%= render "result_pagination" %>
18
- </p>
19
-
20
- <hr/>
21
14
 
15
+ <%= render "result_toggle" %>
16
+ <%= render "result_selected_options" %>
17
+ <%= render "result_pagination" %>
22
18
  <%= render "result_selection_options" %>
23
19
 
24
20
  <% if @documents.results.present? && @documents.results.kind_of?(Array) %>
25
- <ol id='results' class='list-unstyled' data-controller='results'>
21
+ <ol id='results' class='list-unstyled mt-4' data-controller='results'>
26
22
  <% @documents.results.each_with_index do |document, index| %>
27
23
  <% doc = Document.find_by(friendlier_id: document['id']) %>
28
24
  <% if doc.present? %>
@@ -33,7 +33,7 @@
33
33
  <%- # Current Document %>
34
34
  <%- if index == 0 %>
35
35
  <%- current = @document.json_attributes %>
36
- <%- previous = version.previous.reify&.json_attributes %>
36
+ <%- previous = version&.previous&.reify&.json_attributes %>
37
37
  <%- diff = Hashdiff.diff(previous,current) %>
38
38
  <%- changeset = version.changeset.except(:json_attributes).collect{ |change| ["+", change[0], change[1][1]] }.flatten %>
39
39
  <%- diff << changeset %>
@@ -50,7 +50,7 @@
50
50
  <h3 class="h5">Imported Documents</h3>
51
51
  </div>
52
52
  <ul class="list-group list-group-flush">
53
- <li class="list-group-item text-center">
53
+ <li class="list-group-item">
54
54
  <% if @import.state_machine.current_state == 'mapped' %>
55
55
  <%= form_tag run_admin_import_path(@import), method: :patch do -%>
56
56
  <%= hidden_field_tag :run, true -%>
@@ -58,7 +58,34 @@
58
58
  <%- end -%>
59
59
  <% end %>
60
60
  <% if @import.state_machine.current_state == 'imported' %>
61
- <%= @import.documents.size %>
61
+ <table class="table table-sm table-bordered import-documents">
62
+ <thead>
63
+ <tr>
64
+ <th>Publication State</th>
65
+ <th>Document Count</th>
66
+ </tr>
67
+ </thead>
68
+ <tbody>
69
+ <tr>
70
+ <td><%= link_to_gbl_import('Published', @import, 'published') %></td>
71
+ <td><%= @import.documents.where(publication_state: 'published').size %></td>
72
+ </tr>
73
+ <tr>
74
+ <td><%= link_to_gbl_import('Unpublished', @import, 'unpublished') %></td>
75
+ <td><%= @import.documents.where(publication_state: 'unpublished').size %></td>
76
+ </tr>
77
+ <tr>
78
+ <td><%= link_to_gbl_import('Draft', @import, 'draft') %></td>
79
+ <td><%= @import.documents.where(publication_state: 'draft').size %></td>
80
+ </tr>
81
+ </tbody>
82
+ <tfoot>
83
+ <tr>
84
+ <th><%= link_to_gbl_import('Total', @import) %></th>
85
+ <td><%= @import.documents.size %></td>
86
+ </tr>
87
+ </tfoot>
88
+ </table>
62
89
  <% end %>
63
90
  </li>
64
91
  </ul>
@@ -104,19 +131,18 @@
104
131
 
105
132
  <nav>
106
133
  <div class="nav nav-tabs" id="import-state-tabs" role="tablist">
107
- <a class="nav-item nav-link active" id="import-failed-tab" data-toggle="tab" href="#import-state-failed" role="tab" aria-controls="import-state-failed" aria-selected="true">Failed</a>
108
- <a class="nav-item nav-link" id="import-success-tab" data-toggle="tab" href="#import-state-success" role="tab" aria-controls="import-state-success"
109
- aria-selected="false">Success</a>
134
+ <a class="nav-item nav-link active" id="import-success-tab" data-toggle="tab" href="#import-state-success" role="tab" aria-controls="import-state-success" aria-selected="true">Success</a>
135
+ <a class="nav-item nav-link" id="import-failed-tab" data-toggle="tab" href="#import-state-failed" role="tab" aria-controls="import-state-failed" aria-selected="false">Failed</a>
110
136
  </div>
111
137
  </nav>
112
138
 
113
139
  <div class="tab-content" id="import-states">
114
- <div class="tab-pane fade show active" id="import-state-failed" role="tabpanel" aria-labelledby="import-state-failed-tab">
115
- <%= render partial: 'show_failed_tab' %>
140
+ <div class="tab-pane fade show active" id="import-state-success" role="tabpanel" aria-labelledby="import-state-success-tab">
141
+ <%= render partial: 'show_success_tab' %>
116
142
  </div>
117
143
 
118
- <div class="tab-pane fade" id="import-state-success" role="tabpanel" aria-labelledby="import-state-success-tab">
119
- <%= render partial: 'show_success_tab' %>
144
+ <div class="tab-pane fade" id="import-state-failed" role="tabpanel" aria-labelledby="import-state-failed-tab">
145
+ <%= render partial: 'show_failed_tab' %>
120
146
  </div>
121
147
  </div>
122
148
  </div>
@@ -19,15 +19,15 @@
19
19
  <% end %>
20
20
 
21
21
  <div class="input-group-append">
22
- <button class="btn btn-outline-secondary my-2 my-sm-0" type="submit">Search</button>
23
- <button type="button" class="btn btn-outline-secondary dropdown-toggle dropdown-toggle-split" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
24
- <span class="sr-only">Toggle Dropdown</span>
22
+ <button class="btn btn-outline-secondary my-2 my-sm-0" type="submit">
23
+ <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-search" viewBox="0 0 16 16">
24
+ <path d="M11.742 10.344a6.5 6.5 0 1 0-1.397 1.398h-.001c.03.04.062.078.098.115l3.85 3.85a1 1 0 0 0 1.415-1.414l-3.85-3.85a1.007 1.007 0 0 0-.115-.1zM12 6.5a5.5 5.5 0 1 1-11 0 5.5 5.5 0 0 1 11 0"/>
25
+ </svg>
26
+ <span class="sr-only">Search</span>
25
27
  </button>
26
- <div class="dropdown-menu">
27
- <%= link_to "Advanced", admin_search_path, { class: 'dropdown-item' } %>
28
- </div>
29
28
  </div>
30
29
  </div>
30
+ <%= link_to "Advanced", admin_search_path, { class: 'btn my-2 my-sm-0' } %>
31
31
  <% end %>
32
32
 
33
33
  <div class="collapse navbar-collapse" id="navbarSupportedContent">
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ class BulkActionSti < ActiveRecord::Migration[6.1]
4
+ def change
5
+ add_column :bulk_actions, :bulk_action_type, :string, default: "ChangePublicationState", null: false
6
+ add_column :bulk_actions, :action, :string, null: true
7
+ change_column :bulk_actions, :field_name, :string, null: true
8
+ change_column :bulk_actions, :field_value, :string, null: true
9
+ end
10
+ end
@@ -270,10 +270,18 @@ module GeoblacklightAdmin
270
270
  end
271
271
  end
272
272
 
273
+ def add_api_controller
274
+ copy_file "api_controller.rb", "app/controllers/admin/api_controller.rb"
275
+ end
276
+
273
277
  def add_user_util_links
274
278
  copy_file "_user_util_links.html.erb", "app/views/shared/_user_util_links.html.erb"
275
279
  end
276
280
 
281
+ def copy_catalog_index_view
282
+ copy_file "views/_index_split_default.html.erb", "app/views/catalog/_index_split_default.html.erb"
283
+ end
284
+
277
285
  def add_show_sidebar
278
286
  copy_file "_show_sidebar.html.erb", "app/views/catalog/_show_sidebar.html.erb"
279
287
  end
@@ -305,9 +313,31 @@ module GeoblacklightAdmin
305
313
  end
306
314
  end
307
315
 
308
- def add_catalog_controller_default_params
309
- inject_into_file "app/controllers/catalog_controller.rb", after: '"q.alt" => "*:*"' do
310
- ",\n 'fq' => ['b1g_publication_state_s:published']"
316
+ def add_kithe_bulk_loading_service
317
+ prepend_to_file "app/controllers/catalog_controller.rb" do
318
+ "require 'kithe/blacklight_tools/bulk_loading_search_service'\n\n"
319
+ end
320
+
321
+ inject_into_file "app/controllers/catalog_controller.rb", after: "include Blacklight::Catalog" do
322
+ "\n self.search_service_class = Kithe::BlacklightTools::BulkLoadingSearchService"
323
+ end
324
+ end
325
+
326
+ def add_kithe_model_to_solr_document
327
+ inject_into_file "app/models/solr_document.rb", after: "include Geoblacklight::SolrDocument" do
328
+ "\n\nattr_accessor :model"
329
+ end
330
+ end
331
+
332
+ def add_search_builder_publication_state_concern
333
+ inject_into_file "app/models/search_builder.rb", after: "include Geoblacklight::SuppressedRecordsSearchBehavior" do
334
+ "\n include GeoblacklightAdmin::PublicationStateSearchBehavior"
335
+ end
336
+ end
337
+
338
+ def add_import_id_facet
339
+ inject_into_file "app/controllers/catalog_controller.rb", before: "# Item Relationship Facets" do
340
+ "\nconfig.add_facet_field Settings.FIELDS.B1G_IMPORT_ID, label: 'Import ID', show: false\n"
311
341
  end
312
342
  end
313
343
  end