diff --git a/app/controllers/ui/devices_controller.rb b/app/controllers/ui/devices_controller.rb index 0731c0a8..64f0b948 100644 --- a/app/controllers/ui/devices_controller.rb +++ b/app/controllers/ui/devices_controller.rb @@ -34,6 +34,31 @@ def update end end + def delete + find_device! + return unless authorize_device! :destroy?, :delete_device_forbidden + @title = I18n.t(:delete_device_title, name: @device.name) + add_breadcrumbs( + [I18n.t(:show_user_title, owner: helpers.possessive(@device.owner, current_user)), ui_user_path(@device.owner.username)], + [I18n.t(:show_device_title, name: @device.name), ui_device_path(@device.id)], + [I18n.t(:edit_device_title, name: @device.name), edit_ui_device_path(@device.id)], + [I18n.t(:delete_device_title, name: @device.name), delete_ui_device_path(@device.id)] + ) + end + + def destroy + find_device! + return unless authorize_device! :destroy?, :delete_device_forbidden + if @device.name != params[:name] + flash[:alert] = I18n.t(:delete_device_wrong_name) + redirect_to delete_ui_device_path(@device.id) + return + end + @device.archive! + flash[:success] = I18n.t(:delete_device_success) + redirect_to ui_user_path(current_user.username) + end + private def device_params diff --git a/app/views/ui/devices/delete.html.erb b/app/views/ui/devices/delete.html.erb new file mode 100644 index 00000000..1bd7afdb --- /dev/null +++ b/app/views/ui/devices/delete.html.erb @@ -0,0 +1,7 @@ +<%= bootstrap_form_tag url: ui_device_path(@device.id), method: :delete do |f| %> +

<%= t(:delete_device_warning_html, name: @device.name) %>

+ <%= f.text_field :name, label: t(:delete_device_name_label) %> +
+ <%= f.primary t(:delete_device_submit), class: "btn btn-danger w-100 w-md-auto" %> +
+<% end %> diff --git a/app/views/ui/devices/edit.html.erb b/app/views/ui/devices/edit.html.erb index b435d776..9d5a3312 100644 --- a/app/views/ui/devices/edit.html.erb +++ b/app/views/ui/devices/edit.html.erb @@ -42,4 +42,8 @@
<%= f.primary t(:edit_user_submit), class: "btn btn-primary w-100 w-md-auto" %>
+ <% if authorize? @device, :destroy? %> +

<%= t(:edit_device_other_actions_subhead) %>

+
<%= link_to t(:edit_device_delete_device_submit), delete_ui_device_path(@device.id), class: "btn btn-danger w-100 w-md-auto" %>
+ <% end %> <% end %> diff --git a/app/views/ui/users/delete.html.erb b/app/views/ui/users/delete.html.erb index eb585c49..03187258 100644 --- a/app/views/ui/users/delete.html.erb +++ b/app/views/ui/users/delete.html.erb @@ -1,5 +1,4 @@ <%= bootstrap_form_tag url: ui_user_path(@user.id), method: :delete do |f| %> - <%= f.hidden_field :token, value: @token %>

<%= t(:delete_user_warning_html, username: current_user.username) %>

<%= f.text_field :username, label: t(:delete_user_username_label, owner: possessive(@user, current_user)) %>
diff --git a/config/locales/controllers/en.yml b/config/locales/controllers/en.yml index 44eda746..72993001 100644 --- a/config/locales/controllers/en.yml +++ b/config/locales/controllers/en.yml @@ -29,3 +29,7 @@ en: edit_device_forbidden: "You are not allowed to edit settings for that kit!" update_device_success: "The kit's settings have been updated!" update_device_failure: "Some errors prevented us from updating the kit's settings. Please check below and try again!" + delete_device_title: "Delete kit: %{name}" + delete_device_forbidden: "You are not allowed to delete that kit!" + delete_device_success: "The kit has been deleted!" + delete_device_wrong_name: "That kit name did not match! Please try again." diff --git a/config/locales/views/devices/en.yml b/config/locales/views/devices/en.yml index f6e426b9..882e2fd0 100644 --- a/config/locales/views/devices/en.yml +++ b/config/locales/views/devices/en.yml @@ -19,5 +19,10 @@ en: edit_device_tags_blurb: "Kits can be grouped by tags. Choose from the available tags or submit a tag request on the Forum." edit_device_tags_label: "Tags" edit_device_postprocessing_subhead: "Postprocessing information" - edit_device_postprocessing_blurb: "Follow the instructions here to generate a valid JSON containing the postprocessing information for your device. This is an advanced feature and it's not required for standard Smart Citizen Kits!" + edit_device_postprocessing_blurb: "Follow the instructions here to generate a valid JSON containing the postprocessing information for your kit. This is an advanced feature and it's not required for standard Smart Citizen Kits!" edit_device_hardware_url_label: "Hardware URL" + edit_device_other_actions_subhead: "Other actions" + edit_device_delete_device_submit: "Delete this kit" + delete_device_warning_html: "🚨Warning! This will permanently delete the kit %{name}.🚨" + delete_device_name_label: "To confirm, type the kit name below:" + delete_device_submit: "I understand, delete the kit" diff --git a/config/locales/views/users/en.yml b/config/locales/views/users/en.yml index d9c457ab..7d2a7209 100644 --- a/config/locales/views/users/en.yml +++ b/config/locales/views/users/en.yml @@ -3,7 +3,7 @@ en: show_user_edit_cta: "Edit %{owner} profile" show_user_secrets_cta: "Show %{owner} API keys" show_user_log_out_cta: "Sign out" - show_user_devices_heading: "Devices" + show_user_devices_heading: "Kits" show_user_no_devices_message: "%{username} has no kits yet!" secrets_user_blurb_html: This key gives you access to your data in the SmartCitizen platform using the REST API. Please keep it safe and do not share with anyone, just as you would with a password. secrets_user_api_key_label: API key @@ -24,7 +24,7 @@ en: edit_user_public_profile_subhead: "Public profile" edit_user_other_actions_subhead: "Other Actions" edit_user_delete_account_submit: "Permanently delete %{owner} account" - delete_user_warning_html: "🚨Warning! This will permanently delete the account %{username} and all of its devices.🚨" + delete_user_warning_html: "🚨Warning! This will permanently delete the account %{username} and all of its kits.🚨" delete_user_username_label: "To confirm, type %{owner} username below:" delete_user_submit: "I understand, delete %{owner} account" users_password_reset_landing_confirmation_label: "Confirm new password" diff --git a/config/routes.rb b/config/routes.rb index f7fc6119..9569ebe9 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -30,6 +30,7 @@ resources :devices, as: "devices" do member do get :edit + get :delete end end diff --git a/spec/controllers/ui/devices_controller_spec.rb b/spec/controllers/ui/devices_controller_spec.rb index c081b285..bfa8583f 100644 --- a/spec/controllers/ui/devices_controller_spec.rb +++ b/spec/controllers/ui/devices_controller_spec.rb @@ -153,5 +153,85 @@ end end end + + describe "delete" do + context "when the device's owner is logged in" do + it "displays the delete device form" do + get :delete, params: { id: device.id }, session: { user_id: user.try(:id) } + expect(response).to have_http_status(:success) + expect(response).to render_template(:delete) + end + end + + context "when a different user is logged in" do + let(:owner) { create(:user) } + it "redirects to the ui users page" do + get :delete, params: { id: device.id }, session: { user_id: user.try(:id) } + expect(response).to redirect_to(ui_user_path(user.username)) + expect(flash[:alert]).to be_present + end + end + + context "when no user is logged in" do + let(:user) { nil } + it "redirects to the login page" do + get :delete, params: { id: device.id }, session: { user_id: user.try(:id) } + expect(response).to redirect_to(login_path) + expect(flash[:alert]).to be_present + end + end + end + + describe "destroy" do + context "when the device's owner is logged in" do + context "when the correct device name is provided" do + it "archives the devicer, and redirects to the user's profile" do + expect_any_instance_of(Device).to receive(:archive!) + delete :destroy, + params: { id: device.id, name: device.name }, + session: { user_id: user.try(:id) } + expect(response).to redirect_to(ui_user_path(user.username)) + expect(flash[:success]).to be_present + end + end + + context "when an incorrect device name is provided" do + it "does not archive the device and redirects to the delete page" do + expect_any_instance_of(Device).not_to receive(:archive!) + delete :destroy, + params: { id: device.id, name: "a wrong device name" }, + session: { user_id: user.try(:id) } + expect(response).to redirect_to(delete_ui_device_path(device.id)) + expect(flash[:alert]).to be_present + end + end + end + + context "when a different user is logged in" do + let(:owner) { create(:user) } + + it "does not archive the device and redirects to the ui users page" do + expect_any_instance_of(Device).not_to receive(:archive!) + delete :destroy, + params: { id: device.id, name: device.name }, + session: { user_id: user.try(:id) } + expect(response).to redirect_to(ui_user_path(user.username)) + expect(flash[:alert]).to be_present + end + end + + context "when no user is logged in" do + let(:user) { nil } + + it "does not archive the user and redirets to the login page" do + expect_any_instance_of(Device).not_to receive(:archive!) + delete :destroy, + params: { id: device.id, name: device.name }, + session: { user_id: user.try(:id) } + expect(response).to redirect_to(login_path) + expect(flash[:alert]).to be_present + end + end + end end diff --git a/spec/features/device_management_spec.rb b/spec/features/device_management_spec.rb index c36eec6c..a8dfabf9 100644 --- a/spec/features/device_management_spec.rb +++ b/spec/features/device_management_spec.rb @@ -38,4 +38,27 @@ expect(page).to have_current_path(ui_device_path(device.id)) expect(page).to have_content("new device name") end + + + scenario "User deletes a device" do + password = "password123" + username = "username" + device_name = "devicename" + user = create(:user, username: username, password: password, password_confirmation: password) + device = create(:device, name: device_name, owner: user) + visit "/login" + fill_in "Username or email", with: user.email + fill_in "Password", with: password + click_on "Sign into your account" + click_on device_name + click_on "Edit kit settings" + click_on "Delete this kit" + expect(page).to have_current_path(delete_ui_device_path(device.id)) + fill_in "To confirm, type the kit name below:", with: device_name + click_on "I understand, delete the kit" + expect(page).to have_current_path(ui_user_path(username)) + expect(page).to have_content("The kit has been deleted!") + expect(page).not_to have_content(device_name) + expect(device.reload).to be_archived + end end