Skip to content

Commit 413d2e4

Browse files
authored
Merge pull request #3430 from fhp/real-bulk-edit
Add "Update All Models" functionality to bulk edit models
2 parents 6754ec8 + 10b3db7 commit 413d2e4

File tree

12 files changed

+123
-12
lines changed

12 files changed

+123
-12
lines changed

Diff for: app/controllers/models_controller.rb

+11-2
Original file line numberDiff line numberDiff line change
@@ -139,8 +139,17 @@ def bulk_update
139139
organize = hash.delete(:organize) == "1"
140140
add_tags = Set.new(hash.delete(:add_tags))
141141
remove_tags = Set.new(hash.delete(:remove_tags))
142-
ids = params[:models].select { |k, v| v == "1" }.keys
143-
policy_scope(Model).where(public_id: ids).find_each do |model|
142+
143+
models_to_update = if params[:commit] == t("models.bulk_edit.update_all")
144+
# If "Update All Models" was clicked, update all models in the filtered set
145+
filtered_models(@filters)
146+
else
147+
# If "Update Selected Models" was clicked, only update checked models
148+
ids = params[:models].select { |k, v| v == "1" }.keys
149+
policy_scope(Model).where(public_id: ids)
150+
end
151+
152+
models_to_update.find_each do |model|
144153
if model&.update(hash)
145154
existing_tags = Set.new(model.tag_list)
146155
model.tag_list = existing_tags + add_tags - remove_tags

Diff for: app/views/models/bulk_edit.html.erb

+1
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@
5858
<%= form.hidden_field :library, value: @filters[:library] if @filters[:library] %>
5959
<%= form.hidden_field :creator, value: @filters[:creator] if @filters[:creator] %>
6060
<%= form.submit translate(".submit"), class: "btn btn-primary" %>
61+
<%= form.submit translate(".update_all"), class: "btn btn-secondary" %>
6162

6263
</div>
6364
<% if !@filters.empty? %>

Diff for: config/locales/models/de.yml

+1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ de:
1010
select_all: Alle Modelle auswählen
1111
submit: Ausgewählte Modelle aktualisieren
1212
title: Modelle massenweise bearbeiten
13+
update_all: Alle Modelle aktualisieren
1314
bulk_fields:
1415
add_tags: Tags hinzufügen
1516
bulk_update:

Diff for: config/locales/models/en.yml

+1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ en:
1010
select_all: Select all models
1111
submit: Update Selected Models
1212
title: Bulk Edit Models
13+
update_all: Update All Models
1314
bulk_fields:
1415
add_tags: Add tags
1516
bulk_update:

Diff for: config/locales/models/es.yml

+1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ es:
1010
select_all: Seleccione todos los modelos
1111
submit: Actualizar modelos seleccionados
1212
title: Edición masiva de modelos
13+
update_all: Actualizar todos los modelos
1314
bulk_fields:
1415
add_tags: Añadir etiquetas
1516
bulk_update:

Diff for: config/locales/models/fr.yml

+1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ fr:
1010
select_all: Sélectionner tous les modèles
1111
submit: Mettre à jour les modèles sélectionnés
1212
title: Modifier les modèles en lot
13+
update_all: Mettre à jour tous les modèles
1314
bulk_fields:
1415
add_tags: Ajouter des étiquettes
1516
bulk_update:

Diff for: config/locales/models/pl.yml

+1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ pl:
1010
select_all: Zaznacz wszystkie modele
1111
submit: Aktualizuj zaznaczone modele
1212
title: Edytuj zbiorczo modele
13+
update_all: Aktualizuj wszystkie modele
1314
bulk_fields:
1415
add_tags: Dodaj tagi
1516
bulk_update:

Diff for: spec/factories/creator.rb

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
FactoryBot.define do
22
factory :creator do
3-
name { Faker::Name.name }
3+
sequence(:name) { |n| "Creator #{n}" }
4+
sequence(:public_id) { |n| "creator_#{n}" }
45
end
56
end

Diff for: spec/factories/library.rb

+2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
FactoryBot.define do
22
factory :library do
3+
sequence(:name) { |n| "Library #{n}" }
4+
sequence(:public_id) { |n| "library_#{n}" }
35
path {
46
dir = Dir.mktmpdir(Faker::File.file_name, "/tmp")
57
at_exit { FileUtils.remove_entry(dir) }

Diff for: spec/factories/model.rb

+33-1
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,40 @@
11
FactoryBot.define do
22
factory :model do
3+
sequence(:name) { |n| "Model #{n}" }
34
library
4-
name { Faker::Creature::Animal.name }
5+
sequence(:public_id) { |n| "model_#{n}" }
56
path { Faker::File.dir }
67
license { "MIT" }
8+
9+
trait :with_tags do
10+
transient do
11+
tags_count { 2 }
12+
end
13+
14+
after(:create) do |model, evaluator|
15+
evaluator.tags_count.times do |i|
16+
model.tag_list.add("tag_#{i}")
17+
end
18+
model.save
19+
end
20+
end
21+
22+
trait :needs_organizing do
23+
after(:create) do |model|
24+
model.update!(needs_organizing: true)
25+
end
26+
end
27+
28+
trait :sensitive do
29+
sensitive { true }
30+
end
31+
32+
trait :with_creator do
33+
creator
34+
end
35+
36+
trait :with_collection do
37+
collection
38+
end
739
end
840
end

Diff for: spec/factories/tag.rb

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
FactoryBot.define do
2+
factory :tag, class: "ActsAsTaggableOn::Tag" do
3+
sequence(:name) { |n| "tag_#{n}" }
4+
end
5+
end

Diff for: spec/requests/models_spec.rb

+64-8
Original file line numberDiff line numberDiff line change
@@ -116,17 +116,35 @@
116116

117117
describe "GET /models/edit" do # rubocop:todo RSpec/RepeatedExampleGroupBody
118118
it "shows bulk edit page", :as_moderator do
119-
get "/models/edit"
119+
get edit_models_path
120120
expect(response).to have_http_status(:success)
121121
end
122122

123123
it "sets returnable session param", :as_moderator do
124-
get "/models/edit"
124+
get edit_models_path
125125
expect(session[:return_after_new]).to eq "/models/edit"
126126
end
127127

128128
it "is denied to non-moderators", :as_contributor do
129-
expect { get "/models/edit" }.to raise_error(Pundit::NotAuthorizedError)
129+
expect { get edit_models_path }.to raise_error(Pundit::NotAuthorizedError)
130+
end
131+
132+
context "with filters", :as_moderator do
133+
let(:tag) { create(:tag) }
134+
let!(:tagged_model) { create(:model, library: library, tag_list: [tag.name]) }
135+
136+
it "shows filtered models" do
137+
get edit_models_path(tag: [tag.name])
138+
expect(response.body).to include(tagged_model.name)
139+
end
140+
141+
it "doesn't show other models" do
142+
get edit_models_path(tag: [tag.name])
143+
library.models.each do |model|
144+
next if model == tagged_model
145+
expect(response.body).not_to include(model.name)
146+
end
147+
end
130148
end
131149
end
132150

@@ -145,7 +163,7 @@
145163
update[models[0].to_param] = 1
146164
update[models[1].to_param] = 1
147165

148-
patch "/models/update", params: {models: update, creator_id: creator.id}
166+
patch update_models_path, params: {models: update, creator_id: creator.id}
149167

150168
expect(response).to have_http_status(:redirect)
151169
models.each { |model| model.reload }
@@ -154,7 +172,7 @@
154172
end
155173

156174
it "adds tags to models", :as_moderator do # rubocop:todo RSpec/ExampleLength, RSpec/MultipleExpectations
157-
patch "/models/update", params: {models: model_params, add_tags: ["a", "b", "c"]}
175+
patch update_models_path, params: {models: model_params, add_tags: ["a", "b", "c"]}
158176

159177
expect(response).to have_http_status(:redirect)
160178
library.models.take(2).each do |model|
@@ -168,7 +186,7 @@
168186
model.save
169187
end
170188

171-
patch "/models/update", params: {models: model_params, remove_tags: ["a", "b"]}
189+
patch update_models_path, params: {models: model_params, remove_tags: ["a", "b"]}
172190

173191
expect(response).to have_http_status(:redirect)
174192
library.models.take(2).each do |model|
@@ -178,7 +196,7 @@
178196
end
179197

180198
it "clears returnable session param", :as_moderator do
181-
patch "/models/update", params: {models: model_params, remove_tags: ["a", "b"]}
199+
patch update_models_path, params: {models: model_params, remove_tags: ["a", "b"]}
182200
expect(session[:return_after_new]).to be_nil
183201
end
184202

@@ -187,7 +205,45 @@
187205
library.models.take(2).each do |model|
188206
update[model.to_param] = 1
189207
end
190-
expect { patch "/models/update", params: {models: model_params, remove_tags: ["a", "b"]} }.to raise_error(Pundit::NotAuthorizedError)
208+
expect { patch update_models_path, params: {models: model_params, remove_tags: ["a", "b"]} }.to raise_error(Pundit::NotAuthorizedError)
209+
end
210+
211+
context "when updating all filtered models", :as_moderator do # rubocop:todo RSpec/MultipleMemoizedHelpers
212+
let(:tag) { create(:tag) }
213+
let!(:tagged_model) { create(:model, library: library, tag_list: [tag.name]) }
214+
let(:new_library) { create(:library) }
215+
216+
let(:params) do
217+
{
218+
commit: I18n.t("models.bulk_edit.update_all"),
219+
new_library_id: new_library.id,
220+
tag: [tag.name]
221+
}
222+
end
223+
224+
it "updates all models matching the filter" do
225+
patch update_models_path, params: params
226+
227+
library.models.each do |model|
228+
next if model == tagged_model
229+
expect(model.reload.library_id).to eq(library.id)
230+
end
231+
end
232+
end
233+
234+
context "with organization", :as_moderator do
235+
let(:params) do
236+
{
237+
models: library.models.take(2).map { |m| [m.to_param, "1"] }.to_h,
238+
organize: "1"
239+
}
240+
end
241+
242+
it "enqueues organize jobs for selected models" do
243+
expect {
244+
patch update_models_path, params: params
245+
}.to have_enqueued_job(OrganizeModelJob).exactly(2).times
246+
end
191247
end
192248
end
193249

0 commit comments

Comments
 (0)