Skip to content

Commit

Permalink
Resend Confirmation Code
Browse files Browse the repository at this point in the history
  • Loading branch information
Ioannis committed Mar 3, 2025
1 parent 435fd31 commit 1ee1e2c
Show file tree
Hide file tree
Showing 14 changed files with 609 additions and 139 deletions.
53 changes: 53 additions & 0 deletions app/plugins/CoreEnroller/config/routes.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
<?php
/**
* ApiSource plugin specific routes.
*
* Portions licensed to the University Corporation for Advanced Internet
* Development, Inc. ("UCAID") under one or more contributor license agreements.
* See the NOTICE file distributed with this work for additional information
* regarding copyright ownership.
*
* UCAID licenses this file to you under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* @link https://www.internet2.edu/comanage COmanage Project
* @package registry-plugins
* @since COmanage Registry v5.0.0
* @license Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
*/

use Cake\Routing\Route\DashedRoute;

// In general, we're probably trying to set up API routes if we're doing
// something within a plugin, but not necessarily. API routes are a subset
// of Cake routes, so either can be specified here.

// Core Enroller


$routes->plugin(
'CoreEnroller',
['path' => '/core-enroller/'],
function ($routes) {
$routes->setRouteClass(DashedRoute::class);

$routes->get(
'email-verifiers/resend',
[
'plugin' => 'CoreEnroller',
'controller' => 'EmailVerifiers',
'action' => 'resend',
])
->setPass(['id'])
->setPatterns(['id' => '[0-9]+']);
}
);
22 changes: 20 additions & 2 deletions app/plugins/CoreEnroller/resources/locales/en_US/core_enroller.po
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,22 @@ msgid "information.EmailVerifiers.A"
msgstr "The following email addresses have been found in this Petition. You must verify all of them in order to proceed to the next Enrollment Step."

msgid "information.EmailVerifiers.code_sent"
msgstr "A code has been sent to {0}. Please enter it below. You may also request a new code if you haven't received it after a few minutes, or cancel verification and return to the list of available Email Addresses."
msgstr "A code has been sent to <span class="accessible-underline"><strong>{0}</strong></span>. Please enter it below. You may also request a new code if you haven't received it after a few minutes, or cancel verification and return to the list of available Email Addresses."

msgid "information.EmailVerifiers.resend-pre-text"
msgstr "Didn't receive the code?"

msgid "information.EmailVerifiers.resend"
msgstr "Resend"

msgid "information.EmailVerifiers.sending"
msgstr "Sending"

msgid "information.EmailVerifiers.success"
msgstr "New Code Submitted!"

msgid "information.EmailVerifiers.abort"
msgstr "Abort"

msgid "field.AttributeCollectors.valid_through.default.after.desc"
msgstr "Days After Finalization"
Expand Down Expand Up @@ -257,4 +272,7 @@ msgid "result.InvitationAccepters.declined"
msgstr "Invitation Declined at {0}"

msgid "result.InvitationAccepters.none"
msgstr "No response to invitation yet"
msgstr "No response to invitation yet"

msgid "op.EmailVerifiers.verify"
msgstr "Verify Email"
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@

namespace CoreEnroller\Controller;

use Cake\Http\Exception\BadRequestException;
use Cake\ORM\TableRegistry;
use App\Controller\StandardEnrollerController;
use App\Lib\Enum\PetitionStatusEnum;
Expand Down Expand Up @@ -60,9 +61,58 @@ public function beforeRender(\Cake\Event\EventInterface $event) {
$this->set('vv_bc_parent_primarykey', $this->EmailVerifiers->EnrollmentFlowSteps->getPrimaryKey());
}

if ($this->getRequest()->getQuery("op") && $this->getRequest()->getQuery("op")) {
// This will suppress the default behavior. By default, we print the submit button in the
// unorderedList.php element. But for the verify view we want to override and customize
$this->set('suppress_submit', true);
}

return parent::beforeRender($event);
}

/**
* Resend the email verification request.
*
* @param string $id Email Verifier ID
* @throws BadRequestException If the request is not AJAX
* @throws \InvalidArgumentException If required query parameters are missing
* @return void
* @since COmanage Registry v5.1.0
*/
public function resend($id)
{

$this->viewBuilder()->setClassName('Json');

if (!$this->getRequest()->is('ajax')) {
throw new BadRequestException(__('Bad Request'));
}

if (!$this->getRequest()->getQuery('petition_id') || !$this->getRequest()->getQuery('m')) {
throw new \InvalidArgumentException(__('error', 'invalid.request'));
}

// Generate a Verification request and send it
$Petitions = TableRegistry::getTableLocator()->get('Petitions');
$petition = $Petitions->get($this->getRequest()->getQuery('petition_id'));
$cfg = $this->EmailVerifiers->get($id);
$mail = StringUtilities::urlbase64decode($this->requestParam('m'));
$status = $this->EmailVerifiers->sendVerificationRequest($cfg, $petition, $mail, true);

if ($status) {
return $this->response
->withType('application/json')
->withStatus(200)
->withStringBody(json_encode(['status' => 'ok']));
}


return $this->response
->withType('application/json')
->withStatus(501)
->withStringBody(json_encode(['status' => 'failed']));
}

/**
* Dispatch an Enrollment Flow Step.
*
Expand Down
55 changes: 36 additions & 19 deletions app/plugins/CoreEnroller/src/Model/Table/EmailVerifiersTable.php
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ public function initialize(array $config): void {

$this->setPrimaryLink('enrollment_flow_step_id');
$this->setRequiresCO(true);
$this->setAllowLookupPrimaryLink(['dispatch', 'display']);
$this->setAllowLookupPrimaryLink(['dispatch', 'display', 'resend']);

// All the tabs share the same configuration in the ModelTable file
$this->setTabsConfig(
Expand All @@ -112,6 +112,14 @@ public function initialize(array $config): void {
'type' => 'select',
'model' => 'MessageTemplates',
'where' => ['context' => \App\Lib\Enum\MessageTemplateContextEnum::Verification]
],
'cosettings' => [
'type' => 'auxiliary',
'model' => 'CoSettings'
],
'types' => [
'type' => 'auxiliary',
'model' => 'Types'
]
]);

Expand All @@ -122,6 +130,7 @@ public function initialize(array $config): void {
'dispatch' => true,
'display' => true,
'edit' => ['platformAdmin', 'coAdmin'],
'resend' => true,
'view' => ['platformAdmin', 'coAdmin']
],
// Actions that operate over a table (ie: do not require an $id)
Expand Down Expand Up @@ -412,8 +421,10 @@ public function prepare(
public function sendVerificationRequest(
EmailVerifier $emailVerifier,
Petition $petition,
string $mail
) {
string $mail,
bool $resend = false,
): bool
{
// First check if there is already an existing Petition Verification.
// If so, use that to get the existing Verification.

Expand All @@ -427,21 +438,7 @@ public function sendVerificationRequest(
])
->first();

if(!empty($pVerification)) {
// Request a new code

$this->llog('debug', "Sending replacement verification code to $mail for Petition " . $petition->id);

$verificationId = $Verifications->requestCodeForPetition(
$petition->id,
$mail,
$emailVerifier->message_template_id,
$emailVerifier->request_validity,
$pVerification->verification_id
);

// There's nothing to update in the Petition Verification
} else {
if (empty($pVerification)) {
// Request Verification and create an associated Petition Verification

$this->llog('debug', "Sending verification code to $mail for Petition " . $petition->id);
Expand All @@ -458,8 +455,28 @@ public function sendVerificationRequest(
'petition_id' => $petition->id,
'mail' => $mail,
'verification_id' => $verificationId
]));
]));
return true;
}

if ($resend) {
// Request a new code

$this->llog('debug', "Sending replacement verification code to $mail for Petition " . $petition->id);

$verificationId = $Verifications->requestCodeForPetition(
$petition->id,
$mail,
$emailVerifier->message_template_id,
$emailVerifier->request_validity,
$pVerification->verification_id
);
// There's nothing to update in the Petition Verification

return true;
}

return false;
}

/**
Expand Down
130 changes: 15 additions & 115 deletions app/plugins/CoreEnroller/templates/EmailVerifiers/dispatch.inc
Original file line number Diff line number Diff line change
Expand Up @@ -27,121 +27,21 @@

declare(strict_types = 1);

use CoreEnroller\Lib\Enum\VerificationModeEnum;
use App\Lib\Util\StringUtilities;

print $this->element('flash', []);

// This view is intended to work with dispatch
if($vv_action == 'dispatch') {
if($vv_op == 'index') {
// Render the list of known email addresses and their verification statuses.
// The configuration drives how many email addresses are required to complete this step.

print '<p>';

if($vv_all_done) {
print __d('core_enroller', 'information.EmailVerifiers.done');
} else {
switch($vv_config->mode) {
case VerificationModeEnum::All:
print __d('core_enroller', 'information.EmailVerifiers.A');
break;
case VerificationModeEnum::None:
print __d('core_enroller', 'information.EmailVerifiers.0');
break;
case VerificationModeEnum::One:
if($vv_minimum_met) {
print __d('core_enroller', 'information.EmailVerifiers.1.met');
} else {
print __d('core_enroller', 'information.EmailVerifiers.1.none');
}
break;
}
}

print '</p>';

print '
<table id="verifications-table" class="index-table list-mode">
<thead>
<tr>
<th>' . __d('controller', 'EmailAddresses', [1]) . '</th>
<th>' . __d('field', 'status') . '</th>
</tr>
</thead>
</tbody>
';

foreach(array_keys($vv_email_addresses) as $addr) {
$verified = isset($vv_verified_addresses[$addr]) && $vv_verified_addresses[$addr];

$button = "";

if(!$verified) {
// We're already in a form here, so we need to use a GET URL to not mess things up.
// This also means we need to manually insert the token and petition ID, which is
// a bit duplicative with templates/Standard/dispatch.php

$url = [
'plugin' => 'CoreEnroller',
'controller' => 'email_verifiers',
'action' => 'dispatch',
$vv_config->id,
'?' => [
'op' => 'verify',
'petition_id' => $vv_petition->id,
// We base64 encode the address partly to not have bare email addresses in URLs
// and partly to avoid special characters (like dots) messing up the URL
'm' => StringUtilities::urlbase64encode($addr)
]
];

if(isset($vv_token_ok) && $vv_token_ok && !empty($vv_petition->token)) {
$url['?']['token'] = $vv_petition->token;
}

$button = $this->Html->link(__d('operation', 'verify'), $url);
}

print '
<tr>
<td>' . $addr . '</td>
<td>' . __d('result', ($verified ? 'verified' : 'verified.not')) . $button . '</td>
</tr>
';
}

print '
</tbody>
</table>
';

if($vv_minimum_met) {
$this->Field->enableFormEditMode();

print $this->Form->hidden('op', ['default' => 'finish']);
}
} elseif($vv_op == 'verify') {
if(!empty($vv_verify_address)) {
// Render a form prompting for the code that was sent to the Enrollee

print __d('core_enroller', 'information.EmailVerifiers.code_sent', [$vv_verify_address]);

$this->Field->enableFormEditMode();

print $this->Form->hidden('op', ['default' => 'verify']);
print $this->Form->hidden('co_id', ['default' => $vv_cur_co->id]);
print $this->Form->hidden('m', ['default' => StringUtilities::urlbase64encode($vv_verify_address)]);

print $this->element('form/listItem', [
'arguments' => [
'fieldName' => 'code',
'fieldLabel' => "Code", //__d('field', 'mail')
'fieldOptions' => [
'required' => true
]
]]);
}
}
}
if ($vv_action !== 'dispatch') {
return;
}

if ($vv_op == 'index') {
$this->set('vv_include_cancel', false);
$this->set('vv_submit_button_label', __d('operation', 'finish'));
print $this->element('CoreEnroller.emailVerifiers/list');
} elseif ($vv_op == 'verify') {
$this->set('vv_submit_button_label', __d('core_enroller', 'op.EmailVerifiers.verify'));
$this->set('vv_include_cancel', true);
print $this->element('CoreEnroller.emailVerifiers/verify');
} else {
print __d('error', 'something.went.wrong');
}
Loading

0 comments on commit 1ee1e2c

Please sign in to comment.