Skip to content
Open
5 changes: 5 additions & 0 deletions app/config/routes.php
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,11 @@ function (RouteBuilder $builder) {
['controller' => 'ApiV2', 'action' => 'generateApiKey', 'model' => 'api_users'])
->setPass(['id'])
->setPatterns(['id' => '[0-9]+']);
$builder->post(
'/terms_and_conditions/record/{id}',
['controller' => 'ApiV2', 'action' => 'recordTAndC', 'model' => 'terms_and_conditions'])
->setPass(['id'])
->setPatterns(['id' => '[0-9]+']);
$builder->get(
'/people/pick',
['controller' => 'ApiV2', 'action' => 'pick', 'model' => 'people']);
Expand Down
12 changes: 0 additions & 12 deletions app/plugins/TermsAgreer/resources/locales/en_US/terms_agreer.po
Original file line number Diff line number Diff line change
Expand Up @@ -37,21 +37,9 @@ msgstr "Ignore"
msgid "enumeration.TAndCEnrollmentModeEnum.IC"
msgstr "Implied Consent"

msgid "error.TAndCAgreement.missing"
msgstr "Did not receive agreement for \"{0}\" (T&C {1})"

msgid "field.AgreementCollectors.t_and_c_mode"
msgstr "Terms and Conditions Mode"

msgid "information.AgreementCollectors.external"
msgstr "These Terms and Conditions will be loaded in an external browser window. After review, you must return to this window and click \"Agree\" to continue."

msgid "information.AgreementCollectors.review"
msgstr "You must review and agree to these Terms and Conditions before continuing."

msgid "information.AgreementCollectors.review.tc"
msgstr "Review Terms & Conditions"

msgid "result.AgreementCollectors.ignored"
msgstr "Terms and Conditions collection disabled"

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ public function dispatch(string $id) {
if(!isset($data[$key]) || $data[$key] != "1") {
$ok = false;

$this->Flash->error(__d('terms_agreer','error.TAndCAgreement.missing', [$tc->description, $tc->id]));
$this->Flash->error(__d('error','TermsAndConditions.agreement.missing', [$tc->description, $tc->id]));
}
}

Expand Down
170 changes: 3 additions & 167 deletions app/plugins/TermsAgreer/templates/AgreementCollectors/dispatch.inc
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?php
/**
* COmanage Registry T&C Agreement Colectors Petition Fields
* COmanage Registry T&C Agreement Collectors Petition Fields
*
* Portions licensed to the University Corporation for Advanced Internet
* Development, Inc. ("UCAID") under one or more contributor license agreements.
Expand All @@ -25,169 +25,5 @@
* @license Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
*/

declare(strict_types = 1);

use \TermsAgreer\Lib\Enum\TAndCEnrollmentModeEnum;

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

// Make the Form fields editable
$this->Field->enableFormEditMode();
?>

<p><?= __d('terms_agreer', 'information.AgreementCollectors.review') ?></p>

<?php

// Begin the form
print $this->Form->create(null, [
'id' => 'agreement-form',
'type' => 'post'
]);


?>

<table>
<tr>
<th><?= __d('controller','TermsAndConditions',[99]) ?></th>
<th class="center"><?= __d('operation','review') ?></th>
<th><?= __d('operation','agreement') ?></th>
</tr>

<?php foreach($vv_tandc as $tc): ?>
<tr class="<?= !empty($tc->url) ? 'tc-url' : 'tc-msp' ?>">
<td>
<?= $tc['description'] ?>
</td>
<td class="center">
<button
data-tc-id="tc<?= $tc['id'] ?>"
class="btn btn-primary btn-sm tc-review-button"
type="button">
<?= __d('operation','review') ?>
</button>
<?php if(!empty($tc->url)): // We have a URL based T&C ?>
<button
data-tc-id="tc<?= $tc['id'] ?>"
id="tc<?= $tc['id'] ?>-window-launcher"
class="tc-window-launcher invisible"
data-tc-url="<?= $tc->url ?>"
type="button">
<?= __d('operation','review') . ' tc' . $tc['id'] ?>
</button>
<?php else: // We have a Mostly Static Page based T&C ?>
<?= $this->element('TermsAgreer.agreeDialog', ['vv_tc' => $tc]); ?>
<?php endif; ?>
</td>
<td>
<div class="form-check tc-agree">
<?=
$this->Form->checkbox(
'tc'.$tc['id'],
['id' => 'tc'.$tc['id'], 'class' => 'form-check-input tc-agree-checkbox']
) .
$this->Form->label(
'tc'.$tc['id'],
__d('operation','agree'),
['class' => 'form-check-label']
)
?>
</div>
</td>
</tr>
<?php endforeach; ?>

</table>


<script nonce="<?= $vv_js_nonce ?>">
// Iterate over our T&Cs to see if they're all checked.
function checkAgreements(mode) {
let allAgreed = true;
$('.tc-agree-checkbox').each(function() {
if(!$(this).prop('checked')) {
allAgreed = false;
if(mode == 'EC') {
// We're in Explicit Consent mode, so disable
// the current checkbox; its T&C must be reviewed.
$(this).prop('disabled', true);
}
}
});

// Enable / disable the "Continue" button.
if(allAgreed) {
$('.submit input[type="submit"]').prop('disabled', false);
} else {
$('.submit input[type="submit"]').prop('disabled', true);
}
}

// On page load: check agreement states and attach event handlers.
$(function() {
// Explicit Consent mode ("EC") will disable "I Agree" checkboxes until review.
const tcmode = '<?= $vv_tandc_mode ?>';

// Run on first load.
checkAgreements(tcmode);

// Handle "Review" button for URL-based terms and conditions.
$('tr.tc-url button.tc-review-button').click(function(e) {
e.preventDefault();
jsConfirmGeneric(
'<?= __d('terms_agreer', 'information.AgreementCollectors.external') ?>',
'',
$(this).attr('data-tc-id')+'-window-launcher',
'<?= __d('operation','continue') ?>',
'<?= __d('operation','cancel') ?>',
'<?= __d('terms_agreer','information.AgreementCollectors.review.tc') ?>'
);
});

// Launch the external window for URL-based terms and conditions
$('tr.tc-url button.tc-window-launcher').click(function(e) {
e.preventDefault();

// Calculate window size based on screen size so that we
// can handle mobile and to center the window.
const winWidth = window.outerWidth / 1.5;
const winHeight = window.outerHeight / 1.5;
const left = window.screenLeft + ((window.outerWidth - winWidth) / 2);
const top = window.screenTop + ((window.outerHeight - winHeight) / 2);

// Launch the T&C window with the external URL
let tandcExternalWindow = window.open(
$(this).attr('data-tc-url'),
'tandcExternalWindow',
'width=' + winWidth + ',height=' + winHeight + ',left=' + left + ',top=' + top +
',resizable=yes,scrollbars=yes,toolbar=no,menubar=no,location=no,status=no'
);

// Enable the T&C checkbox
$('#'+$(this).attr('data-tc-id')).prop('disabled', false);
});

// Handle "Review" button for Mostly Static Page-based terms and conditions.
// Clicking this will reveal the T&C agree dialog.
$('tr.tc-msp button.tc-review-button').click(function(e) {
e.preventDefault();
$('#'+$(this).attr('data-tc-id')+'-agree-dialog').modal('show');
});

// Handle "Agree" button click from MSP Dialog
$('.tc-agree-button').click(function(e) {
// Enable and check the T&C checkbox
$('#'+$(this).attr('data-tc-id'))
.prop('checked', true)
.prop('disabled', false)
.trigger('change');
});

// Check agreements when a checkbox is clicked.
// If all are checked, we'll enable "Continue".
$('.tc-agree-checkbox').change(function() {
checkAgreements(tcmode);
});
});
</script>
// This dispatch uses the same user interface as the core TermsAndConditions/tandcs.inc template.
include ROOT . DS . 'templates' . DS . 'TermsAndConditions' . DS . 'tandcs.inc';
6 changes: 6 additions & 0 deletions app/resources/locales/en_US/error.po
Original file line number Diff line number Diff line change
Expand Up @@ -451,6 +451,12 @@ msgstr "No outgoing SMTP Server configuration found"
msgid "TermsAndConditions.document.one"
msgstr "Exactly one of Mostly Static Page or URL must be specified"

msgid "TermsAndConditions.agreement.missing"
msgstr "Did not receive agreement for \"{0}\" (T&C {1})"

msgid "TermsAndConditions.agreement.failed"
msgstr "Failed to record agreement. Please try again or check with your administrator."

msgid "TermsAndConditions.revoke.none"
msgstr "No Agreements available to revoke"

Expand Down
9 changes: 9 additions & 0 deletions app/resources/locales/en_US/information.po
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,15 @@ msgstr "{0} List"
msgid "TermsAndConditions.desc.old"
msgstr "{0} (Agreement is to an older definition)"

msgid "tandc.review"
msgstr "You must review and agree to these Terms and Conditions before continuing."

msgid "tandc.review.dialog.title"
msgstr "Review Terms & Conditions"

msgid "tandc.review.external"
msgstr "These Terms and Conditions will be loaded in an external browser window. After review, you must return to this window and click \"Agree\" to continue."

msgid "ug.tasks.post"
msgstr "Executing post-database tasks for version {0}"

Expand Down
28 changes: 10 additions & 18 deletions app/src/Controller/ApiV2Controller.php
Original file line number Diff line number Diff line change
Expand Up @@ -131,21 +131,6 @@ public function add() {
$this->render('/Standard/api/v2/json/add-edit');
}

/**
* beforeFilter callback.
*
* @param \Cake\Event\EventInterface $event Event.
* @return \Cake\Http\Response|null|void
*/
public function beforeFilter(\Cake\Event\EventInterface $event)
{
parent::beforeFilter($event);

if ($this->request->is('ajax') && $this->request->is(['post', 'put'])) {
$this->FormProtection->setConfig('validate', false);
}
}

/**
* Callback run prior to the request rendering.
*
Expand Down Expand Up @@ -446,20 +431,27 @@ public function recordTAndC(string $id) {
}

$TAndCAgreements = TableRegistry::getTableLocator()->get('TAndCAgreements');
$TAndCs = TableRegistry::getTableLocator()->get('TermsAndConditions');

$agreement = $TAndCAgreements->record(
termsAndConditionsId: (int)$id,
personId: $json['personId'],
actorPersonId: $json['actorPersonId'],
personId: (int)$json['personId'],
actorPersonId: (int)$json['actorPersonId'],
identifier: $json['identifier']
);

$TAndCAgreements->People->requestProvisioning(
id: $json['personId'],
id: (int)$json['personId'],
context: ProvisioningContextEnum::Automatic
);

$this->set('vv_results', ['id' => $agreement->id]);

// Only this action uses a Flash message. If more restful actions need it in the future,
// move the loadComponent('Flash') out of the !restful test in AppController.
$this->loadComponent('Flash');
$tandc = $TAndCs->get($agreement->terms_and_conditions_id);
$this->Flash->success(__d('result', 'TermsAndConditions.agreed', [$tandc->description]));

// Let the view render
$this->viewBuilder()->setLayout('rest');
Expand Down
1 change: 1 addition & 0 deletions app/src/Controller/AppController.php
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,7 @@ public function beforeFilter(\Cake\Event\EventInterface $event) {
$this->RegistryAuth->isAuthenticatedUser()
&& !$this->RegistryAuth->isApiUser()
&& $this->getCOID() !== null
&& !$this->request->is('ajax')
) {
$this->set('vv_person_id', $this->RegistryAuth->getPersonId($this->getCOID()));

Expand Down
45 changes: 4 additions & 41 deletions app/src/Controller/TermsAndConditionsController.php
Original file line number Diff line number Diff line change
Expand Up @@ -161,34 +161,7 @@ public function proxy(string $id) {
*/

public function review() {
// Before we get started, see if a return parameter was requested, and if so if it is permitted.
// If so, we'll override any current return URL. We check the return URL here rather than when
// we're done because URLs stored by AppController are not subject to the allow list check.

$returnUrl = $this->request->getQuery('return');

if(!empty($returnUrl)) {
$returnUrl = base64_decode($returnUrl);

$CoSettings = TableRegistry::getTableLocator()->get('CoSettings');
$settings = $CoSettings->find()->where(['co_id' => $this->getCOID()])->firstOrFail();

if(!empty($settings->tc_return_url_allowlist)) {
foreach(preg_split('/\R/', $settings->tc_return_url_allowlist) as $u) {
if(preg_match($u, $returnUrl)) {
// The requested URL is permitted, so store it in the session, potentially overriding
// the original return URL
$this->request->getSession()->write('TAndC.return', $returnUrl);
break;
}
}
} else {
// No allowed URLs, so ignore redirect
}
}

// We get the current user from RegistryAuthComponent and then pass their T&C status to the view.

$personId = $this->RegistryAuth->getPersonID($this->getCOID());

if(empty($personId)) {
Expand All @@ -199,23 +172,13 @@ public function review() {

$status = $this->TermsAndConditions->status((int)$personId);

// If there is nothing left to do, redirect to the original request
$done = true;

foreach($status as $s) {
if($s['status'] != TAndCStatusEnum::Agreed) {
$done = false;
break;
}
}

if($done) {
return $this->redirect($this->request->getSession()->read('TAndC.return'));
}

$this->set('vv_tandc_statuses', $status);
$this->set('vv_person_id', (int)$personId);

// The default mode for CO and platform wide T&Cs is "Explicit Consent".
// There is no enumeration for this in core, so set the value explicitly to 'EC'.
$this->set('vv_tandc_mode', 'EC');

$this->set('vv_title', __d('controller', 'TermsAndConditions', 99));
}

Expand Down
Loading