Skip to content

Improve Terms and Conditions UI/UX (CFM-501) #368

Open
wants to merge 7 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -37,12 +37,21 @@ msgstr "Ignore"
msgid "enumeration.TAndCEnrollmentModeEnum.IC"
msgstr "Implied Consent"

msgid "error.TAndCAgreementMissing"
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 \"I 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 @@ -75,8 +75,8 @@ public function dispatch(string $id) {
$TermsAndConditions = TableRegistry::getTableLocator()->get('TermsAndConditions');

$whereClause = [
'co_id' => $coId,
'status' => SuspendableStatusEnum::Active
'TermsAndConditions.co_id' => $coId,
'TermsAndConditions.status' => SuspendableStatusEnum::Active
];

if(!empty($petition->cou_id)) {
Expand All @@ -89,6 +89,7 @@ public function dispatch(string $id) {
}

$tandc = $TermsAndConditions->find()
->contain(['MostlyStaticPages'])
->where($whereClause)
->order('ordr ASC')
->all();
Expand Down Expand Up @@ -118,7 +119,7 @@ public function dispatch(string $id) {
if(!isset($data[$key]) || $data[$key] != "1") {
$ok = false;

$this->Flash->error("Did not find agreement for T&C " . $tc->id); // XXX I18n
$this->Flash->error(__d('terms_agreer','error.TAndCAgreementMissing', [$tc->description, $tc->id]));
}
}

Expand Down
169 changes: 144 additions & 25 deletions app/plugins/TermsAgreer/templates/AgreementCollectors/dispatch.inc
Original file line number Diff line number Diff line change
Expand Up @@ -35,40 +35,159 @@ print $this->element('flash', []);
$this->Field->enableFormEditMode();
?>

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

<?php

// Disable submit (Continue) button until all T&C are agreed to
if($vv_tandc_mode == TAndCEnrollmentModeEnum::ExplicitConsent) {
// XXX T&C must be opened and scrolled before checkbox is enabled
} else {
// XXX Checkboxes are all enabled, but must be clicked before Continue button is enabled
}

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

print "
<table>
<tr>
<th>Terms and Conditions</th>
<th>I Agree</th>
</tr>
";

foreach($vv_tandc as $tc) {
print "
<tr>
<td>" . $tc['description'] . "</td>
<td>" . $this->Form->checkbox("tc".$tc['id']) . "</td>
?>

<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>
// 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');
});

print "
</table>
";
// Check agreements when a checkbox is clicked.
// If all are checked, we'll enable "Continue".
$('.tc-agree-checkbox').change(function() {
checkAgreements(tcmode);
});
});
</script>
76 changes: 76 additions & 0 deletions app/plugins/TermsAgreer/templates/element/agreeDialog.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
<?php
/**
* COmanage Registry T&C Agreement Confirmation Dialog Element
*
* 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
* @since COmanage Registry v5.2.0
* @license Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
*
* This is the Terms and Conditions Agree dialog - a more specialized dialog intended to be
* rendered for each T&C that uses a Mostly Static Page.
*
*/

declare(strict_types = 1);

use Symfony\Component\HtmlSanitizer\HtmlSanitizer;
use Symfony\Component\HtmlSanitizer\HtmlSanitizerConfig;

// Mostly Static Pages allow HTML input in the page body.
// Set up the Symfony HTML Sanitizer to disallow dom elements like <script> and <style>,
// and run the field through the sanitizer before display.
$htmlSanitizer = new HtmlSanitizer(
(new HtmlSanitizerConfig())->allowStaticElements()
);

?>

<?php if(!empty($vv_tc)): ?>
<div class="modal fade co-dialog tc-agree-dialog"
id="tc<?= $vv_tc->id ?>-agree-dialog"
tabindex="-1"
aria-labelledby="tc<?= $vv_tc->id ?>-dialog-title" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered modal-dialog-scrollable modal-xl">
<div class="modal-content">
<div class="modal-header">
<h2 class="modal-title" id="tc<?= $vv_tc->id ?>-dialog-title">
<?= __d('terms_agreer','information.AgreementCollectors.review.tc') ?>
</h2>
<button type="button" class="btn-close nospin" data-bs-dismiss="modal" aria-label="<?= __d('operation', 'close'); ?>"></button>
</div>
<div class="modal-body">
<h1 class="tc-agree-dialog-title"><?= $vv_tc->mostly_static_page->title ?></h1>
<div class="tc-content">
<?= $htmlSanitizer->sanitize($vv_tc->mostly_static_page->body) ?>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn nospin" data-bs-dismiss="modal">
<?= __d('operation', 'cancel'); ?>
</button>
<button data-tc-id="tc<?= $vv_tc['id'] ?>" type="button" class="btn btn-primary tc-agree-button" data-bs-dismiss="modal">
<?= __d('operation', 'agree'); ?>
</button>
</div>
</div>
</div>
</div>
<?php endif; ?>
4 changes: 2 additions & 2 deletions app/resources/locales/en_US/field.po
Original file line number Diff line number Diff line change
Expand Up @@ -1003,7 +1003,7 @@ msgid "TermsAndConditions.mostly_static_page_id"
msgstr "T&C via Mostly Static Page"

msgid "TermsAndConditions.mostly_static_page_id.desc"
msgstr "Content of Terms and Conditions, as defined in a Mostly Static Page (this or URL must be specified)"
msgstr "Content of Terms and Conditions, as defined in a Mostly Static Page<br/>(this or URL must be specified)"

msgid "TermsAndConditions.cou_id.desc"
msgstr "If set, these Terms and Conditions only apply to People with a Person Role in the specified COU"
Expand All @@ -1012,7 +1012,7 @@ msgid "TermsAndConditions.url"
msgstr "T&C via URL"

msgid "TermsAndConditions.url.desc"
msgstr "URL to web page holding Terms and Conditions (this or Mostly Static Page must be specified)"
msgstr "URL to web page holding Terms and Conditions<br/>(this or Mostly Static Page must be specified)"

msgid "Types.case_insensitive"
msgstr "Case Insensitive"
Expand Down
18 changes: 18 additions & 0 deletions app/resources/locales/en_US/operation.po
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,12 @@ msgstr "Add member: "
msgid "adopt"
msgstr "Adopt"

msgid "agree"
msgstr "I Agree"

msgid "agreement"
msgstr "Agreement"

msgid "apply"
msgstr "Apply"

Expand Down Expand Up @@ -258,6 +264,9 @@ msgstr "Login"
msgid "logout"
msgstr "Logout"

msgid "manage.a"
msgstr "Manage {0}"

msgid "next"
msgstr "Next"

Expand Down Expand Up @@ -330,6 +339,9 @@ msgstr "Resend"
msgid "resume"
msgstr "Resume"

msgid "review"
msgstr "Review"

msgid "save"
msgstr "Save"

Expand Down Expand Up @@ -384,9 +396,15 @@ msgstr "View"
msgid "view.a"
msgstr "View {0}"

msgid "visit.msp"
msgstr "Visit Page"

msgid "visit.link"
msgstr "Visit link"

msgid "visit.tandc"
msgstr "Visit Terms and Conditions URL"

msgid "view.PersonRoles.a"
msgstr "View Role {0}"

Expand Down
Loading