diff --git a/CHANGELOG.md b/CHANGELOG.md
index fe1f3f3280..022ca96f02 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,6 @@
# Changelog
+- Add Functionality Allowing Super Admins to Confirm/Unconfirm User Emails [#3535](https://github.com/DMPRoadmap/roadmap/pull/3535)
- Updated seeds.rb file for identifier_schemes to include context value and removed logo_url and idenitifier_prefix for Shibboleth (as it was causing issues with SSO). [#3525](https://github.com/DMPRoadmap/roadmap/pull/3525)
- Adjustments to style of select tags and plan download layout [#3509](https://github.com/DMPRoadmap/roadmap/pull/3509)
- Fix failing eslint workflow / upgrade `actions/checkout` & `actions/setup-node` to v3 [#3503](https://github.com/DMPRoadmap/roadmap/pull/3503)
diff --git a/app/controllers/super_admin/users_controller.rb b/app/controllers/super_admin/users_controller.rb
index c4b96141b7..3b98ce978c 100644
--- a/app/controllers/super_admin/users_controller.rb
+++ b/app/controllers/super_admin/users_controller.rb
@@ -38,6 +38,7 @@ def update
# Remove the extraneous Org Selector hidden fields
attrs = remove_org_selection_params(params_in: attrs)
+ attrs = handle_confirmed_at_param(attrs)
if @user.update(attrs)
# If its a new Org create it
@@ -125,7 +126,8 @@ def user_params
:org_id, :org_name, :org_crosswalk,
:department_id,
:language_id,
- :other_organisation)
+ :other_organisation,
+ :confirmed_at)
end
def merge_accounts
@@ -136,5 +138,25 @@ def merge_accounts
flash.now[:alert] = failure_message(@user, _('merge'))
end
end
+
+ def handle_confirmed_at_param(attrs)
+ # NOTE: The :confirmed_at param is controlled by a check_box in the form
+ # `app/views/super_admin/users/_email_confirmation_status.html.erb`.
+ # When the checkbox is checked, Rails submits the string '1' (indicating "confirmed").
+ # When unchecked, it submits the string '0' (indicating "unconfirmed").
+
+ # if an unconfirmed email is now being confirmed
+ if !@user.confirmed? && attrs[:confirmed_at] == '1'
+ attrs[:confirmed_at] = Time.current
+ # elsif a confirmed email is now being unconfirmed and the user is not a super admin
+ elsif @user.confirmed? && attrs[:confirmed_at] == '0' && !@user.can_super_admin?
+ attrs[:confirmed_at] = nil
+ else
+ # else delete the param
+ # (keeps value nil for unconfirmed user and maintains previous Time value for confirmed user)
+ attrs.delete(:confirmed_at)
+ end
+ attrs
+ end
end
end
diff --git a/app/views/super_admin/users/_email_confirmation_status.html.erb b/app/views/super_admin/users/_email_confirmation_status.html.erb
new file mode 100644
index 0000000000..2641962229
--- /dev/null
+++ b/app/views/super_admin/users/_email_confirmation_status.html.erb
@@ -0,0 +1,12 @@
+
+ <%= f.label(:confirmed_at, _('Email status: '), class: 'control-label') %>
+ <%= @user.confirmed? ? _("Confirmed.") : _("Unconfirmed.") %>
+
+ <% is_checkbox_disabled = @user.can_super_admin? && @user.confirmed_at.present? %>
+ <%= f.check_box(:confirmed_at, { checked: @user.confirmed?,
+ disabled: is_checkbox_disabled,
+ style: 'vertical-align: middle;
+ margin-top: -2px;' }) %>
+ <% checkbox_helper_text = @user.confirmed? ? _('(Uncheck to unconfirm email)') : _('(Check to confirm email)') %>
+ <%= content_tag(:small, checkbox_helper_text) unless is_checkbox_disabled %>
+
diff --git a/app/views/super_admin/users/edit.html.erb b/app/views/super_admin/users/edit.html.erb
index d4dc76902b..e58da75449 100644
--- a/app/views/super_admin/users/edit.html.erb
+++ b/app/views/super_admin/users/edit.html.erb
@@ -57,6 +57,8 @@
<% end %>
+ <%= render 'email_confirmation_status', f: f %>
+
<%= f.button(_('Save'), class: 'btn btn-secondary', type: "submit", id: "personal_details_registration_form_submit") %>
diff --git a/spec/controllers/super_admin/users_controller_spec.rb b/spec/controllers/super_admin/users_controller_spec.rb
new file mode 100644
index 0000000000..5d3232eb76
--- /dev/null
+++ b/spec/controllers/super_admin/users_controller_spec.rb
@@ -0,0 +1,53 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+RSpec.describe SuperAdmin::UsersController, type: :controller do
+ let(:super_admin) { create(:user, :super_admin) }
+ let(:user) { create(:user, confirmed_at: nil) }
+
+ before do
+ sign_in super_admin
+ end
+
+ describe 'PUT #update' do
+ context 'when confirming an unconfirmed user' do
+ it 'sets confirmed_at to the current time' do
+ put :update, params: { id: user.id, user: { confirmed_at: '1' } }
+ user.reload
+ expect(user.confirmed_at).to be_a(Time)
+ end
+ end
+
+ context 'when unconfirming a confirmed user' do
+ before do
+ user.update(confirmed_at: Time.current)
+ end
+
+ it 'sets confirmed_at to nil' do
+ put :update, params: { id: user.id, user: { confirmed_at: '0' } }
+ user.reload
+ expect(user.confirmed_at).to be_nil
+ end
+ end
+
+ context 'when update will not affect confirmation status' do
+ it 'does not update confirmed_at value for an already confirmed user' do
+ # (usec: 0) removes mircoseconds to better enable comparison
+ user.update(confirmed_at: Time.current.change(usec: 0))
+ original_confirmed_at = user.confirmed_at
+ put :update, params: { id: user.id, user: { firstname: 'NewName', confirmed_at: '1' } }
+ user.reload
+ expect(user.confirmed_at).to eq(original_confirmed_at)
+ end
+ end
+
+ context 'when attempting to set a super_admin to unconfirmed' do
+ it 'does not update confirmed_at value to nil' do
+ put :update, params: { id: super_admin.id, user: { confirmed_at: '0' } }
+ super_admin.reload
+ expect(super_admin.confirmed_at).not_to be_nil
+ end
+ end
+ end
+end