geoblacklight_admin 0.3.2 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (41) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -0
  3. data/app/controllers/admin/ids_controller.rb +3 -0
  4. data/app/controllers/admin/imports_controller.rb +2 -2
  5. data/app/helpers/geoblacklight_admin_helper.rb +30 -0
  6. data/app/jobs/bulk_action_collect_documents.rb +11 -0
  7. data/app/jobs/geoblacklight_admin/store_image_job.rb +12 -0
  8. data/app/models/asset.rb +13 -0
  9. data/app/models/bulk_action.rb +8 -5
  10. data/app/models/bulk_actions/change_publication_state.rb +21 -0
  11. data/app/models/concerns/geoblacklight_admin/publication_state_search_behavior.rb +26 -0
  12. data/app/models/document/date_validator.rb +46 -0
  13. data/app/models/document.rb +114 -2
  14. data/app/models/element.rb +1 -0
  15. data/app/services/geoblacklight_admin/image_service/dynamic_map_layer.rb +15 -0
  16. data/app/services/geoblacklight_admin/image_service/iiif.rb +17 -0
  17. data/app/services/geoblacklight_admin/image_service/image_map_layer.rb +17 -0
  18. data/app/services/geoblacklight_admin/image_service/tiled_map_layer.rb +15 -0
  19. data/app/services/geoblacklight_admin/image_service/wms.rb +28 -0
  20. data/app/services/geoblacklight_admin/image_service.rb +238 -0
  21. data/app/services/geoblacklight_admin/item_viewer.rb +31 -0
  22. data/app/uploaders/asset_uploader.rb +31 -0
  23. data/app/views/admin/document_assets/index.html.erb +13 -1
  24. data/app/views/admin/documents/_document.html.erb +1 -1
  25. data/app/views/admin/documents/_document_kithe.html.erb +47 -0
  26. data/app/views/admin/documents/_form_nav.html.erb +1 -1
  27. data/app/views/admin/documents/_form_nav_kithe.html.erb +30 -0
  28. data/app/views/admin/documents/_result_facets.html.erb +26 -2
  29. data/app/views/admin/documents/index.html.erb +4 -8
  30. data/app/views/admin/documents/versions.html.erb +1 -1
  31. data/app/views/admin/imports/show.html.erb +35 -9
  32. data/app/views/admin/shared/_navbar.html.erb +6 -6
  33. data/db/migrate/20231106215104_bulk_action_sti.rb +10 -0
  34. data/lib/generators/geoblacklight_admin/config_generator.rb +33 -3
  35. data/{app/controllers/admin → lib/generators/geoblacklight_admin/templates}/api_controller.rb +4 -1
  36. data/lib/generators/geoblacklight_admin/templates/config/initializers/shrine.rb +3 -2
  37. data/lib/generators/geoblacklight_admin/templates/config/settings.yml +4 -1
  38. data/lib/generators/geoblacklight_admin/templates/views/_index_split_default.html.erb +27 -0
  39. data/lib/geoblacklight_admin/version.rb +1 -1
  40. data/lib/tasks/geoblacklight_admin/images.rake +30 -0
  41. metadata +22 -3
@@ -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
+ inject_into_file "app/controllers/catalog_controller.rb", after: 'require "blacklight/catalog"' do
318
+ "\nrequire 'kithe/blacklight_tools/bulk_loading_search_service'"
319
+ end
320
+
321
+ inject_into_file "app/controllers/catalog_controller.rb", after: "include Blacklight::Catalog" do
322
+ "\nself.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