bulkrax 9.5.0 → 10.0.0

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 (41) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +49 -0
  3. data/app/controllers/bulkrax/application_controller.rb +83 -0
  4. data/app/controllers/bulkrax/entries_controller.rb +16 -13
  5. data/app/controllers/bulkrax/exporters_controller.rb +21 -25
  6. data/app/controllers/bulkrax/guided_imports_controller.rb +4 -8
  7. data/app/controllers/bulkrax/importers_controller.rb +26 -29
  8. data/app/controllers/concerns/bulkrax/datatables_behavior.rb +18 -12
  9. data/app/controllers/concerns/bulkrax/importer_file_handler.rb +1 -1
  10. data/app/factories/bulkrax/object_factory.rb +2 -2
  11. data/app/factories/bulkrax/valkyrie_object_factory.rb +29 -17
  12. data/app/helpers/bulkrax/validation_helper.rb +1 -1
  13. data/app/jobs/bulkrax/create_relationships_job.rb +1 -1
  14. data/app/jobs/bulkrax/import_collection_job.rb +9 -2
  15. data/app/matchers/bulkrax/application_matcher.rb +5 -2
  16. data/app/models/bulkrax/csv_entry.rb +1 -1
  17. data/app/models/bulkrax/current.rb +7 -0
  18. data/app/models/bulkrax/export_scope.rb +21 -0
  19. data/app/models/bulkrax/exporter.rb +2 -2
  20. data/app/models/bulkrax/importer.rb +1 -1
  21. data/app/models/concerns/bulkrax/ability_behavior.rb +139 -0
  22. data/app/models/concerns/bulkrax/has_matchers.rb +15 -2
  23. data/app/models/concerns/bulkrax/import_behavior.rb +3 -3
  24. data/app/parsers/bulkrax/csv_parser.rb +13 -12
  25. data/app/parsers/bulkrax/oai_dc_parser.rb +1 -1
  26. data/app/parsers/bulkrax/parser_export_record_set.rb +26 -8
  27. data/app/parsers/concerns/bulkrax/csv_parser/csv_template_generation.rb +1 -1
  28. data/app/parsers/concerns/bulkrax/csv_parser/csv_validation.rb +1 -2
  29. data/app/parsers/concerns/bulkrax/csv_parser/csv_validation_helpers.rb +18 -1
  30. data/app/parsers/concerns/bulkrax/csv_parser/csv_validation_hierarchy.rb +2 -2
  31. data/app/search_builders/bulkrax/export_search_builder.rb +9 -0
  32. data/app/services/bulkrax/csv_template/column_builder.rb +2 -2
  33. data/app/services/bulkrax/csv_template/mapping_manager.rb +9 -7
  34. data/app/services/bulkrax/csv_template/schema_analyzer.rb +1 -5
  35. data/lib/bulkrax/engine.rb +1 -1
  36. data/lib/bulkrax/version.rb +1 -1
  37. data/lib/bulkrax.rb +76 -10
  38. data/lib/generators/bulkrax/install_generator.rb +22 -10
  39. data/lib/generators/bulkrax/templates/config/initializers/bulkrax.rb +1 -1
  40. metadata +33 -13
  41. data/lib/wings/custom_queries/find_by_source_identifier.rb +0 -41
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: dd9cb038733c14b67261147e37dfe9087776846d22f327b3985761e58267c129
4
- data.tar.gz: 46f82669413a304c2c8f511368f7a51e088908bf387fa617234597b26bea07e7
3
+ metadata.gz: ae568e80c3ac44aeeefdcbea1810035b250ab070cd7d72b82481f8ee878cda54
4
+ data.tar.gz: 2b927f012dce82c3fc5c60dc23877bbae24ef986efec97ae6ff6a9acc89f3d3d
5
5
  SHA512:
6
- metadata.gz: fe9ef3f4391865d4f95f74fd2eb4f8e95f529963fbbd2d6439f907ace1908a5c70249d59f73c15593f2d237f3f653d945a458e50e3beaf3727928f6529614d76
7
- data.tar.gz: 4413721e9203032f1bb03add7072ece3c58ec7ec8afcd51b103180b53ca8808df87de0c2793c6101646a8e7285058234cc76628e46c213eda4a62adbacb97b0f
6
+ metadata.gz: c198d55e37b8f4338a9ebe6a0042cada18f749f9dc0fead3fa9fd26b91f75a37a68f44a88e6a65e011c7c3a87183c77cf432a1e94d30027130ba6e2b090467f3
7
+ data.tar.gz: 9b72aaa5e796a76b804b011a532a91589486deee690488b9c514dc8e2ba5054e6a5a896387b30d2c3a6fbc8313ff4581ad4c473cc7c604166462e3cbfa4b1f8f
data/README.md CHANGED
@@ -5,6 +5,25 @@ Bulkrax is a batteries included importer for Samvera applications. It currently
5
5
 
6
6
  ## Installation
7
7
 
8
+ ### Rails compatibility
9
+
10
+ Existing Hyrax integration tests retain Rails 6.1 compatibility. Rails 8.1 is
11
+ tested independently with a minimal Rails application: engine boot, routes,
12
+ engine migrations, and persisted JSON/YAML serialization. This smoke coverage
13
+ unblocks a subsequent Hyrax upgrade; it does **not** claim that current Hyrax
14
+ releases support Rails 8.1, or exercise repository-backed import/export workflows.
15
+
16
+ Run the standalone smoke suite from the Bulkrax checkout with a separate bundle:
17
+
18
+ ```bash
19
+ BULKRAX_WITH_HYRAX=false RAILS_GEM_VERSION='~> 8.1.0' BUNDLE_PATH=vendor/rails81 bundle install
20
+ BULKRAX_WITH_HYRAX=false RAILS_GEM_VERSION='~> 8.1.0' BUNDLE_PATH=vendor/rails81 bundle exec rspec spec/rails_compat
21
+ ```
22
+
23
+ Hyrax remains enabled by default for the existing integration suite. Resolve the
24
+ bundle again when switching modes; do not reuse a frozen lockfile from the other
25
+ mode. Rails 8.1 requires Ruby 3.2 or newer and SQLite 2.1 or newer for this smoke app.
26
+
8
27
  ### Install Generator
9
28
 
10
29
  Add this line to your application's Gemfile:
@@ -24,6 +43,36 @@ $ rails db:migrate
24
43
 
25
44
  If using Sidekiq, set up queues for `import` and `export`.
26
45
 
46
+ ### Permissions
47
+
48
+ Add Bulkrax's rules to your application's `app/models/ability.rb`:
49
+
50
+ ```ruby
51
+ include Bulkrax::AbilityBehavior
52
+ self.ability_logic += [:bulkrax_default_abilities]
53
+
54
+ def can_import_works?
55
+ can_create_any_work?
56
+ end
57
+
58
+ def can_export_works?
59
+ can_create_any_work?
60
+ end
61
+
62
+ def can_admin_importers?
63
+ current_user.admin?
64
+ end
65
+
66
+ def can_admin_exporters?
67
+ current_user.admin?
68
+ end
69
+ ```
70
+
71
+ Replace the example predicates with your application's policy. By default, all
72
+ four return `false`; permitted users manage only their own importers and
73
+ exporters, while the admin predicates grant access to all records. Ensure
74
+ existing importers and exporters have an owner (`user_id`).
75
+
27
76
  ### Bundle errors on ARM
28
77
 
29
78
  If posix-spawn is failing to bundle on an ARM based processor, try the following
@@ -4,5 +4,88 @@ module Bulkrax
4
4
  class ApplicationController < ::ApplicationController
5
5
  helper Rails.application.class.helpers
6
6
  protect_from_forgery with: :exception
7
+
8
+ # Rescue CanCan::AccessDenied in all Bulkrax controllers. HTML requests are
9
+ # redirected to the host-app root with an alert; JSON requests receive a 403
10
+ # response. Defining the handler here (rather than in individual controllers)
11
+ # keeps error handling in one place and consistent across all resources.
12
+ rescue_from CanCan::AccessDenied do |exception|
13
+ respond_to do |format|
14
+ format.html do
15
+ flash[:alert] = exception.message
16
+ redirect_to main_app.root_path
17
+ end
18
+ format.json { render json: { error: exception.message }, status: :forbidden }
19
+ end
20
+ end
21
+
22
+ protected
23
+
24
+ # Single auth before_action for ImportersController and ExportersController.
25
+ # Authenticates the request, then loads and authorizes the record targeted
26
+ # by the current action, assigning it to @importer / @exporter (derived
27
+ # from +klass+).
28
+ #
29
+ # * Authentication: API requests (see Bulkrax::API) use the token;
30
+ # everything else uses authenticate_user!.
31
+ # * new/create: build a new record (from +<name>_params+ on create) and
32
+ # authorize it. Skipped for API requests, which validate params and build
33
+ # the record inside the action.
34
+ # * Member actions: find by :id or :<name>_id (e.g. continue, download,
35
+ # entry_table) and authorize action_name. Custom actions resolve through
36
+ # the alias_action mappings in Bulkrax::AbilityBehavior (e.g. :export_errors ->
37
+ # :read, :continue -> :update).
38
+ # * Browser collection actions (no id param, e.g. index, *_table): check
39
+ # the controller-supplied capability predicate. The action scopes records
40
+ # with accessible_by after authorization. API collection actions remain
41
+ # compatible until token-to-user wiring is available.
42
+ #
43
+ # @param klass [Class] Bulkrax::Importer or Bulkrax::Exporter
44
+ # @param collection_ability [Symbol] ability predicate for collection actions
45
+ def authenticate_and_authorize!(klass, collection_ability)
46
+ return unless authenticate_request!
47
+
48
+ name = klass.model_name.element
49
+ record = case action_name
50
+ when 'new', 'create'
51
+ return if api_request?
52
+ klass.new(action_name == 'create' ? send(:"#{name}_params") : {})
53
+ else
54
+ id = params[:id] || params[:"#{name}_id"]
55
+ return if id.blank? && api_request?
56
+ return authorize_collection!(collection_ability) if id.blank?
57
+ klass.find(id)
58
+ end
59
+ instance_variable_set(:"@#{name}", record)
60
+
61
+ # TODO(#1237): API requests have no current_user until the token is
62
+ # resolved to a user, so API actions addressed by :id (and entry_table)
63
+ # skip authorization to keep existing consumers working. Other
64
+ # :<name>_id actions are authorized.
65
+ return if api_request? && (params[:id].present? || action_name == 'entry_table')
66
+
67
+ authorize! action_name.to_sym, record
68
+ end
69
+
70
+ private
71
+
72
+ def authorize_collection!(ability_method)
73
+ return if current_ability.public_send(ability_method)
74
+
75
+ raise CanCan::AccessDenied, 'Not authorized'
76
+ end
77
+
78
+ # Overridden by Bulkrax::API in controllers that accept token-authenticated
79
+ # JSON requests.
80
+ def api_request?
81
+ false
82
+ end
83
+
84
+ # Returns false when authentication rendered or redirected, so the caller
85
+ # can halt.
86
+ def authenticate_request!
87
+ api_request? ? token_authenticate! : authenticate_user!
88
+ !performed?
89
+ end
7
90
  end
8
91
  end
@@ -2,10 +2,9 @@
2
2
 
3
3
  module Bulkrax
4
4
  class EntriesController < ApplicationController
5
- include Hyrax::ThemedLayoutController if defined?(::Hyrax)
5
+ include Hyrax::ThemedLayoutController if Bulkrax.hyrax_loaded?
6
6
  before_action :authenticate_user!
7
- before_action :check_permissions
8
- with_themed_layout 'dashboard' if defined?(::Hyrax)
7
+ with_themed_layout 'dashboard' if Bulkrax.hyrax_loaded?
9
8
 
10
9
  def show
11
10
  if params[:importer_id].present?
@@ -17,6 +16,8 @@ module Bulkrax
17
16
 
18
17
  def update
19
18
  @entry = Entry.find(params[:id])
19
+ authorize! :update, @entry
20
+
20
21
  type = case @entry.type.downcase
21
22
  when /fileset/
22
23
  'file_set'
@@ -25,8 +26,8 @@ module Bulkrax
25
26
  else
26
27
  'work'
27
28
  end
28
- item = @entry.importerexporter
29
29
  # do not run counters as it loads the whole parser
30
+ item = @entry.importerexporter
30
31
  current_run = item.current_run(skip_counts: true)
31
32
  @entry.set_status_info('Pending', current_run)
32
33
  ScheduleRelationshipsJob.set(wait: 5.minutes).perform_later(importer_id: @entry.importer.id)
@@ -44,6 +45,9 @@ module Bulkrax
44
45
 
45
46
  def destroy
46
47
  @entry = Entry.find(params[:id])
48
+ authorize! :destroy, @entry
49
+
50
+ item = @entry.importerexporter
47
51
  @status = ""
48
52
  begin
49
53
  work = @entry.factory&.find
@@ -59,7 +63,6 @@ module Bulkrax
59
63
  @status = "Error: #{e.message}"
60
64
  end
61
65
 
62
- item = @entry.importerexporter
63
66
  entry_path = item.class.to_s.include?('Importer') ? bulkrax.importer_entry_path(item.id, @entry.id) : bulkrax.exporter_entry_path(item.id, @entry.id)
64
67
 
65
68
  redirect_back fallback_location: entry_path, notice: @status
@@ -70,9 +73,11 @@ module Bulkrax
70
73
  # GET /importers/1/entries/1
71
74
  def show_importer
72
75
  @importer = Importer.find(params[:importer_id])
73
- @entry = Entry.find(params[:id])
76
+ authorize! :read, @importer
77
+ @entry = @importer.entries.find(params[:id])
78
+ authorize! :read, @entry
74
79
 
75
- return unless defined?(::Hyrax)
80
+ return unless Bulkrax.hyrax_loaded?
76
81
  add_breadcrumb t(:'hyrax.controls.home'), main_app.root_path
77
82
  add_breadcrumb t(:'hyrax.dashboard.breadcrumbs.admin'), hyrax.dashboard_path
78
83
  add_breadcrumb t(:'bulkrax.headings.importers'), bulkrax.importers_path
@@ -83,18 +88,16 @@ module Bulkrax
83
88
  # GET /exporters/1/entries/1
84
89
  def show_exporter
85
90
  @exporter = Exporter.find(params[:exporter_id])
86
- @entry = Entry.find(params[:id])
91
+ authorize! :read, @exporter
92
+ @entry = @exporter.entries.find(params[:id])
93
+ authorize! :read, @entry
87
94
 
88
- return unless defined?(::Hyrax)
95
+ return unless Bulkrax.hyrax_loaded?
89
96
  add_breadcrumb t(:'hyrax.controls.home'), main_app.root_path
90
97
  add_breadcrumb t(:'hyrax.dashboard.breadcrumbs.admin'), hyrax.dashboard_path
91
98
  add_breadcrumb t(:'bulkrax.headings.exporters'), bulkrax.exporters_path
92
99
  add_breadcrumb @exporter.name, bulkrax.exporter_path(@exporter.id)
93
100
  add_breadcrumb @entry.id
94
101
  end
95
-
96
- def check_permissions
97
- raise CanCan::AccessDenied unless current_ability.can_import_works? || current_ability.can_export_works?
98
- end
99
102
  end
100
103
  end
@@ -2,33 +2,38 @@
2
2
 
3
3
  module Bulkrax
4
4
  class ExportersController < ApplicationController
5
- include Hyrax::ThemedLayoutController if defined?(::Hyrax)
5
+ include Hyrax::ThemedLayoutController if Bulkrax.hyrax_loaded?
6
6
  include Bulkrax::DownloadBehavior
7
7
  include Bulkrax::DatatablesBehavior
8
- before_action :authenticate_user!
9
- before_action :check_permissions
10
- before_action :set_exporter, only: [:show, :entry_table, :edit, :update, :destroy]
11
- with_themed_layout 'dashboard' if defined?(::Hyrax)
8
+
9
+ # Authentication, record loading and authorization for every action.
10
+ before_action(except: [:download]) { authenticate_and_authorize!(Bulkrax::Exporter, :can_export_works?) }
11
+
12
+ with_themed_layout 'dashboard' if Bulkrax.hyrax_loaded?
12
13
 
13
14
  # GET /exporters
14
15
  def index
15
16
  # NOTE: We're paginating this in the browser.
16
- @exporters = Exporter.order(created_at: :desc).all
17
+ @exporters = Exporter.accessible_by(current_ability).order(created_at: :desc)
17
18
 
18
- add_exporter_breadcrumbs if defined?(::Hyrax)
19
+ add_exporter_breadcrumbs if Bulkrax.hyrax_loaded?
19
20
  end
20
21
 
21
22
  def exporter_table
22
- @exporters = Exporter.order(table_order).page(table_page).per(table_per_page)
23
+ @exporters = Exporter.accessible_by(current_ability)
23
24
  @exporters = @exporters.where(exporter_table_search) if exporter_table_search.present?
25
+ # Count the filtered relation before applying pagination so the UI receives
26
+ # the total number of matching exporters, not just the total number of exporters.
27
+ filtered_count = @exporters.count
28
+ @exporters = @exporters.order(table_order).page(table_page).per(table_per_page)
24
29
  respond_to do |format|
25
- format.json { render json: format_exporters(@exporters) }
30
+ format.json { render json: format_exporters(@exporters, filtered_count) }
26
31
  end
27
32
  end
28
33
 
29
34
  # GET /exporters/1
30
35
  def show
31
- if defined?(::Hyrax)
36
+ if Bulkrax.hyrax_loaded?
32
37
  add_exporter_breadcrumbs
33
38
  add_breadcrumb @exporter.name
34
39
  end
@@ -45,15 +50,14 @@ module Bulkrax
45
50
 
46
51
  # GET /exporters/new
47
52
  def new
48
- @exporter = Exporter.new
49
- return unless defined?(::Hyrax)
53
+ return unless Bulkrax.hyrax_loaded?
50
54
  add_exporter_breadcrumbs
51
55
  add_breadcrumb t(:'bulkrax.headings.new_exporter')
52
56
  end
53
57
 
54
58
  # GET /exporters/1/edit
55
59
  def edit
56
- if defined?(::Hyrax)
60
+ if Bulkrax.hyrax_loaded?
57
61
  add_exporter_breadcrumbs
58
62
  add_breadcrumb @exporter.name, bulkrax.exporter_path(@exporter.id)
59
63
  add_breadcrumb t(:'bulkrax.headings.edit_exporter')
@@ -65,7 +69,7 @@ module Bulkrax
65
69
 
66
70
  # POST /exporters
67
71
  def create
68
- @exporter = Exporter.new(exporter_params)
72
+ @exporter.user_id = current_user.id
69
73
  field_mapping_params
70
74
 
71
75
  if @exporter.save
@@ -107,25 +111,21 @@ module Bulkrax
107
111
  # GET /exporters/1/download
108
112
  def download
109
113
  @exporter = Exporter.find(params[:exporter_id])
114
+ authorize! :read, @exporter
110
115
  send_content
111
116
  end
112
117
 
113
118
  private
114
119
 
115
- # Use callbacks to share common setup or constraints between actions.
116
- def set_exporter
117
- @exporter = Exporter.find(params[:id] || params[:exporter_id])
118
- end
119
-
120
120
  # Only allow a trusted parameters through.
121
121
  def exporter_params
122
122
  params[:exporter][:export_source] = params[:exporter]["export_source_#{params[:exporter][:export_from]}".to_sym]
123
123
  if params[:exporter][:date_filter] == "1"
124
- params.fetch(:exporter).permit(:name, :user_id, :export_source, :export_from, :export_type, :generated_metadata,
124
+ params.fetch(:exporter).permit(:name, :export_source, :export_from, :export_type, :generated_metadata,
125
125
  :include_thumbnails, :parser_klass, :limit, :start_date, :finish_date, :work_visibility,
126
126
  :workflow_status, field_mapping: {})
127
127
  else
128
- params.fetch(:exporter).permit(:name, :user_id, :export_source, :export_from, :export_type, :generated_metadata,
128
+ params.fetch(:exporter).permit(:name, :export_source, :export_from, :export_type, :generated_metadata,
129
129
  :include_thumbnails, :parser_klass, :limit, :work_visibility, :workflow_status,
130
130
  field_mapping: {}).merge(start_date: nil, finish_date: nil)
131
131
  end
@@ -149,9 +149,5 @@ module Bulkrax
149
149
  def file_path
150
150
  "#{@exporter.exporter_export_zip_path}/#{params['exporter']['exporter_export_zip_files']}"
151
151
  end
152
-
153
- def check_permissions
154
- raise CanCan::AccessDenied unless current_ability.can_export_works?
155
- end
156
152
  end
157
153
  end
@@ -2,19 +2,19 @@
2
2
 
3
3
  module Bulkrax
4
4
  class GuidedImportsController < ::Bulkrax::ApplicationController
5
- include Hyrax::ThemedLayoutController if defined?(::Hyrax)
5
+ include Hyrax::ThemedLayoutController if Bulkrax.hyrax_loaded?
6
6
  include Bulkrax::GuidedImportDemoScenarios if Bulkrax.config.guided_import_demo_scenarios_enabled
7
7
  include Bulkrax::ImporterFileHandler
8
8
  helper Bulkrax::ImportersHelper
9
9
 
10
10
  before_action :authenticate_user!
11
- before_action :check_permissions
12
- with_themed_layout 'dashboard' if defined?(::Hyrax)
11
+ before_action { authorize! :create, Bulkrax::Importer }
12
+ with_themed_layout 'dashboard' if Bulkrax.hyrax_loaded?
13
13
 
14
14
  # trigger form to allow upload
15
15
  def new
16
16
  @importer = Importer.new
17
- return unless defined?(::Hyrax)
17
+ return unless Bulkrax.hyrax_loaded?
18
18
  add_importer_breadcrumbs
19
19
  add_breadcrumb I18n.t('bulkrax.importer.guided_import.breadcrumb')
20
20
  end
@@ -169,9 +169,5 @@ module Bulkrax
169
169
  add_breadcrumb t(:'hyrax.dashboard.breadcrumbs.admin'), hyrax.dashboard_path
170
170
  add_breadcrumb t(:'bulkrax.headings.importers'), bulkrax.importers_path
171
171
  end
172
-
173
- def check_permissions
174
- raise CanCan::AccessDenied unless current_ability.can_import_works?
175
- end
176
172
  end
177
173
  end
@@ -3,7 +3,7 @@
3
3
  module Bulkrax
4
4
  # rubocop:disable Metrics/ClassLength
5
5
  class ImportersController < ::Bulkrax::ApplicationController
6
- include Hyrax::ThemedLayoutController if defined?(::Hyrax)
6
+ include Hyrax::ThemedLayoutController if Bulkrax.hyrax_loaded?
7
7
  include Bulkrax::DownloadBehavior
8
8
  include Bulkrax::API
9
9
  include Bulkrax::DatatablesBehavior
@@ -11,29 +11,39 @@ module Bulkrax
11
11
  include Bulkrax::ImporterFileHandler
12
12
 
13
13
  protect_from_forgery unless: -> { api_request? }
14
- before_action :token_authenticate!, if: -> { api_request? }, only: [:create, :update, :delete]
15
- before_action :authenticate_user!, unless: -> { api_request? }
16
- before_action :check_permissions
17
- before_action :set_importer, only: [:show, :entry_table, :edit, :update, :destroy, :original_file]
18
- with_themed_layout 'dashboard' if defined?(::Hyrax)
14
+ # Authentication, record loading and authorization for every action.
15
+ before_action { authenticate_and_authorize!(Bulkrax::Importer, :can_import_works?) }
16
+ with_themed_layout 'dashboard' if Bulkrax.hyrax_loaded?
19
17
 
20
18
  # GET /importers
21
19
  def index
22
20
  # NOTE: We're paginating this in the browser.
23
21
  if api_request?
22
+ # TODO(#1237): Scope API index by token owner once token-to-user wiring
23
+ # is in place. Until then, API clients see all importers
24
+ # to preserve backward-compatibility with existing API consumers.
24
25
  @importers = Importer.order(created_at: :desc).all
25
26
  json_response('index')
26
- elsif defined?(::Hyrax)
27
+ elsif Bulkrax.hyrax_loaded?
27
28
  add_importer_breadcrumbs
28
29
  end
29
30
  end
30
31
 
31
32
  def importer_table
32
33
  order = table_order.presence || Arel.sql('last_imported_at DESC NULLS LAST')
33
- @importers = Importer.order(order).page(table_page).per(table_per_page)
34
+ # TODO(#1237): API requests bypass ownership scoping here too.
35
+ @importers = if api_request?
36
+ Importer.all
37
+ else
38
+ Importer.accessible_by(current_ability)
39
+ end
34
40
  @importers = @importers.where(importer_table_search) if importer_table_search.present?
41
+ # Count the filtered relation before applying pagination so the UI receives
42
+ # the total number of matching importers, not just the total number of importers.
43
+ filtered_count = @importers.count
44
+ @importers = @importers.order(order).page(table_page).per(table_per_page)
35
45
  respond_to do |format|
36
- format.json { render json: format_importers(@importers) }
46
+ format.json { render json: format_importers(@importers, filtered_count) }
37
47
  end
38
48
  end
39
49
 
@@ -41,7 +51,7 @@ module Bulkrax
41
51
  def show
42
52
  if api_request?
43
53
  json_response('show')
44
- elsif defined?(::Hyrax)
54
+ elsif Bulkrax.hyrax_loaded?
45
55
  add_importer_breadcrumbs
46
56
  add_breadcrumb @importer.name
47
57
  end
@@ -58,10 +68,9 @@ module Bulkrax
58
68
 
59
69
  # GET /importers/new
60
70
  def new
61
- @importer = Importer.new
62
71
  if api_request?
63
72
  json_response('new')
64
- elsif defined?(::Hyrax)
73
+ elsif Bulkrax.hyrax_loaded?
65
74
  add_importer_breadcrumbs
66
75
  add_breadcrumb t(:'bulkrax.headings.new_importer')
67
76
  end
@@ -81,7 +90,7 @@ module Bulkrax
81
90
  def edit
82
91
  if api_request?
83
92
  json_response('edit')
84
- elsif defined?(::Hyrax)
93
+ elsif Bulkrax.hyrax_loaded?
85
94
  add_importer_breadcrumbs
86
95
  add_breadcrumb @importer.name, bulkrax.importer_path(@importer.id)
87
96
  add_breadcrumb t(:'bulkrax.headings.edit_importer')
@@ -95,12 +104,14 @@ module Bulkrax
95
104
  # rubocop:disable Style/IfInsideElse
96
105
  if api_request?
97
106
  return return_json_response unless valid_create_params?
107
+ # authenticate_and_authorize! doesn't build for API requests; build it here
108
+ @importer ||= Importer.new(importer_params)
98
109
  end
110
+ @importer.user_id = current_user.id
99
111
  uploads = uploaded_files_scope
100
112
  file = file_param
101
113
  cloud_files = cloud_params
102
114
 
103
- @importer = Importer.new(importer_params)
104
115
  field_mapping_params
105
116
  @importer.validate_only = true if params[:commit] == 'Create and Validate'
106
117
  # the following line is needed to handle updating remote files of a FileSet
@@ -177,7 +188,6 @@ module Bulkrax
177
188
 
178
189
  # PUT /importers/1
179
190
  def continue
180
- @importer = Importer.find(params[:importer_id])
181
191
  params[:importer] = { name: @importer.name }
182
192
  @importer.validate_only = false
183
193
  update
@@ -185,8 +195,7 @@ module Bulkrax
185
195
 
186
196
  # GET /importer/1/upload_corrected_entries
187
197
  def upload_corrected_entries
188
- @importer = Importer.find(params[:importer_id])
189
- return unless defined?(::Hyrax)
198
+ return unless Bulkrax.hyrax_loaded?
190
199
  add_breadcrumb t(:'hyrax.controls.home'), main_app.root_path
191
200
  add_breadcrumb t(:'hyrax.dashboard.breadcrumbs.admin'), hyrax.dashboard_path
192
201
  add_breadcrumb t(:'bulkrax.headings.importers'), bulkrax.importers_path
@@ -197,7 +206,6 @@ module Bulkrax
197
206
  # POST /importer/1/upload_corrected_entries_file
198
207
  def upload_corrected_entries_file
199
208
  file = params[:importer][:parser_fields].delete(:file)
200
- @importer = Importer.find(params[:importer_id])
201
209
  if file.present?
202
210
  @importer[:parser_fields]['partial_import_file_path'] = @importer.parser.write_partial_import_file(file)
203
211
  @importer.save
@@ -242,18 +250,12 @@ module Bulkrax
242
250
 
243
251
  # GET /importers/1/export_errors
244
252
  def export_errors
245
- @importer = Importer.find(params[:importer_id])
246
253
  @importer.write_errored_entries_file
247
254
  send_content
248
255
  end
249
256
 
250
257
  private
251
258
 
252
- # Use callbacks to share common setup or constraints between actions.
253
- def set_importer
254
- @importer = Importer.find(params[:id] || params[:importer_id])
255
- end
256
-
257
259
  def importable_params
258
260
  params.except(:selected_files)
259
261
  end
@@ -267,7 +269,6 @@ module Bulkrax
267
269
  importable_params.require(:importer).permit(
268
270
  :name,
269
271
  :admin_set_id,
270
- :user_id,
271
272
  :frequency,
272
273
  :parser_klass,
273
274
  :limit,
@@ -365,10 +366,6 @@ module Bulkrax
365
366
  end
366
367
  @importer.save
367
368
  end
368
-
369
- def check_permissions
370
- raise CanCan::AccessDenied unless current_ability.can_import_works?
371
- end
372
369
  end
373
370
  # rubocop:enable Metrics/ClassLength
374
371
  end
@@ -83,7 +83,7 @@ module Bulkrax
83
83
  @exporter_table_search
84
84
  end
85
85
 
86
- def format_importers(importers)
86
+ def format_importers(importers, filtered_count = Bulkrax::Importer.accessible_by(current_ability).count)
87
87
  result = importers.map do |i|
88
88
  {
89
89
  name: view_context.link_to(i.name, view_context.importer_path(i)),
@@ -102,12 +102,12 @@ module Bulkrax
102
102
  end
103
103
  {
104
104
  data: result,
105
- recordsTotal: Bulkrax::Importer.count,
106
- recordsFiltered: Bulkrax::Importer.count
105
+ recordsTotal: api_request? ? Bulkrax::Importer.count : Bulkrax::Importer.accessible_by(current_ability).count,
106
+ recordsFiltered: filtered_count
107
107
  }
108
108
  end
109
109
 
110
- def format_exporters(exporters)
110
+ def format_exporters(exporters, filtered_count = Bulkrax::Exporter.accessible_by(current_ability).count)
111
111
  result = exporters.map do |e|
112
112
  {
113
113
  name: view_context.link_to(e.name, view_context.exporter_path(e)),
@@ -119,8 +119,8 @@ module Bulkrax
119
119
  end
120
120
  {
121
121
  data: result,
122
- recordsTotal: Bulkrax::Exporter.count,
123
- recordsFiltered: Bulkrax::Exporter.count
122
+ recordsTotal: Bulkrax::Exporter.accessible_by(current_ability).count,
123
+ recordsFiltered: filtered_count
124
124
  }
125
125
  end
126
126
 
@@ -146,8 +146,10 @@ module Bulkrax
146
146
  def entry_util_links(e, item)
147
147
  links = []
148
148
  links << view_context.link_to(view_context.raw('<span class="fa fa-info-circle"></span>'), view_context.item_entry_path(item, e))
149
- links << "<a class='fa fa-repeat' data-toggle='modal' data-target='#bulkraxItemModal' data-entry-id='#{e.id}'></a>" if view_context.an_importer?(item)
150
- links << view_context.link_to(view_context.raw('<span class="fa fa-trash"></span>'), view_context.item_entry_path(item, e), method: :delete, data: { confirm: 'This will delete the entry and any work associated with it. Are you sure?' })
149
+ if can?(:update, item)
150
+ links << "<a class='fa fa-repeat' data-toggle='modal' data-target='#bulkraxItemModal' data-entry-id='#{e.id}'></a>" if view_context.an_importer?(item)
151
+ links << view_context.link_to(view_context.raw('<span class="fa fa-trash"></span>'), view_context.item_entry_path(item, e), method: :delete, data: { confirm: 'This will delete the entry and any work associated with it. Are you sure?' })
152
+ end
151
153
  links.join(" ")
152
154
  end
153
155
 
@@ -166,16 +168,20 @@ module Bulkrax
166
168
  def importer_util_links(i)
167
169
  links = []
168
170
  links << view_context.link_to(view_context.raw('<span class="fa fa-info-circle"></span>'), importer_path(i))
169
- links << view_context.link_to(view_context.raw('<span class="fa fa-pencil"></span>'), edit_importer_path(i))
170
- links << view_context.link_to(view_context.raw('<span class="fa fa-remove"></span>'), i, method: :delete, data: { confirm: 'Are you sure?' })
171
+ if can?(:update, i)
172
+ links << view_context.link_to(view_context.raw('<span class="fa fa-pencil"></span>'), edit_importer_path(i))
173
+ links << view_context.link_to(view_context.raw('<span class="fa fa-remove"></span>'), i, method: :delete, data: { confirm: 'Are you sure?' })
174
+ end
171
175
  links.join(" ")
172
176
  end
173
177
 
174
178
  def exporter_util_links(i)
175
179
  links = []
176
180
  links << view_context.link_to(view_context.raw('<span class="fa fa-info-circle"></span>'), exporter_path(i))
177
- links << view_context.link_to(view_context.raw('<span class="fa fa-pencil"></span>'), edit_exporter_path(i), data: { turbolinks: false })
178
- links << view_context.link_to(view_context.raw('<span class="fa fa-remove"></span>'), i, method: :delete, data: { confirm: 'Are you sure?' })
181
+ if can?(:update, i)
182
+ links << view_context.link_to(view_context.raw('<span class="fa fa-pencil"></span>'), edit_exporter_path(i), data: { turbolinks: false })
183
+ links << view_context.link_to(view_context.raw('<span class="fa fa-remove"></span>'), i, method: :delete, data: { confirm: 'Are you sure?' })
184
+ end
179
185
  links.join(" ")
180
186
  end
181
187
 
@@ -43,7 +43,7 @@ module Bulkrax
43
43
  end
44
44
 
45
45
  def uploaded_files_scope
46
- return [] unless defined?(::Hyrax)
46
+ return [] unless Bulkrax.hyrax_loaded?
47
47
 
48
48
  base = Hyrax::UploadedFile.where(id: params[:uploaded_files])
49
49
  if respond_to?(:current_user) && current_user.present?
@@ -168,8 +168,8 @@ module Bulkrax
168
168
  end
169
169
 
170
170
  def self.solr_name(field_name)
171
- if defined?(Hyrax)
172
- Hyrax.index_field_mapper.solr_name(field_name)
171
+ if defined?(Hyrax) && Hyrax.respond_to?(:config) && Hyrax.config.respond_to?(:index_field_mapper)
172
+ Hyrax.config.index_field_mapper.solr_name(field_name)
173
173
  else
174
174
  ActiveFedora.index_field_mapper.solr_name(field_name)
175
175
  end