From 20db9f1569ad4d09f7dad518916a0802c1eb3e24 Mon Sep 17 00:00:00 2001 From: Joshua Nicholson <94021017+jnicholCU@users.noreply.github.com> Date: Mon, 15 Dec 2025 16:13:17 -0700 Subject: [PATCH] TOS fields and form Creation of new TOS fields for acceptance and date. User fields are created but not shown in the user page initially. --- ...ld.field.user.user.field_accepted_date.yml | 21 ++++ ...d.field.user.user.field_tos_acceptance.yml | 21 ++++ ...field.storage.user.field_accepted_date.yml | 19 +++ ...ield.storage.user.field_tos_acceptance.yml | 19 +++ src/Controller/TosAcceptanceController.php | 102 ++++++++++++++++ ucb_user_invite.install | 112 ++++++++++++++++++ ucb_user_invite.module | 50 ++++++++ ucb_user_invite.routing.yml | 10 ++ 8 files changed, 354 insertions(+) create mode 100644 config/install/field.field.user.user.field_accepted_date.yml create mode 100644 config/install/field.field.user.user.field_tos_acceptance.yml create mode 100644 config/install/field.storage.user.field_accepted_date.yml create mode 100644 config/install/field.storage.user.field_tos_acceptance.yml create mode 100644 src/Controller/TosAcceptanceController.php diff --git a/config/install/field.field.user.user.field_accepted_date.yml b/config/install/field.field.user.user.field_accepted_date.yml new file mode 100644 index 0000000..92f0f94 --- /dev/null +++ b/config/install/field.field.user.user.field_accepted_date.yml @@ -0,0 +1,21 @@ +langcode: en +status: true +dependencies: + config: + - field.storage.user.field_accepted_date + module: + - user + - datetime +id: user.user.field_accepted_date +field_name: field_accepted_date +entity_type: user +bundle: user +label: 'TOS Accepted Date' +description: 'The date and time when the user accepted the Terms of Service.' +required: false +translatable: true +default_value: { } +default_value_callback: '' +settings: + timezone_override: '' +field_type: datetime diff --git a/config/install/field.field.user.user.field_tos_acceptance.yml b/config/install/field.field.user.user.field_tos_acceptance.yml new file mode 100644 index 0000000..9fda1ee --- /dev/null +++ b/config/install/field.field.user.user.field_tos_acceptance.yml @@ -0,0 +1,21 @@ +langcode: en +status: true +dependencies: + config: + - field.storage.user.field_tos_acceptance + module: + - user +id: user.user.field_tos_acceptance +field_name: field_tos_acceptance +entity_type: user +bundle: user +label: 'Terms of Service Accepted' +description: 'Indicates whether the user has accepted the Terms of Service.' +required: false +translatable: true +default_value: + - + value: 0 +default_value_callback: '' +settings: { } +field_type: boolean diff --git a/config/install/field.storage.user.field_accepted_date.yml b/config/install/field.storage.user.field_accepted_date.yml new file mode 100644 index 0000000..fcd7d87 --- /dev/null +++ b/config/install/field.storage.user.field_accepted_date.yml @@ -0,0 +1,19 @@ +langcode: en +status: true +dependencies: + module: + - user + - datetime +id: user.field_accepted_date +field_name: field_accepted_date +entity_type: user +type: datetime +settings: + datetime_type: datetime +module: core +locked: false +cardinality: 1 +translatable: true +indexes: { } +persist_with_no_fields: false +custom_storage: false diff --git a/config/install/field.storage.user.field_tos_acceptance.yml b/config/install/field.storage.user.field_tos_acceptance.yml new file mode 100644 index 0000000..1804bff --- /dev/null +++ b/config/install/field.storage.user.field_tos_acceptance.yml @@ -0,0 +1,19 @@ +langcode: en +status: true +dependencies: + module: + - user +id: user.field_tos_acceptance +field_name: field_tos_acceptance +entity_type: user +type: boolean +settings: + on_label: 'Accepted' + off_label: 'Not Accepted' +module: core +locked: false +cardinality: 1 +translatable: true +indexes: { } +persist_with_no_fields: false +custom_storage: false diff --git a/src/Controller/TosAcceptanceController.php b/src/Controller/TosAcceptanceController.php new file mode 100644 index 0000000..59f1b77 --- /dev/null +++ b/src/Controller/TosAcceptanceController.php @@ -0,0 +1,102 @@ +currentUser = $current_user; + $this->userStorage = $user_storage; + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container) { + return new static( + $container->get('current_user'), + $container->get('entity_type.manager')->getStorage('user') + ); + } + + /** + * Handles TOS acceptance. + * + * @param \Symfony\Component\HttpFoundation\Request $request + * The request object. + * + * @return \Symfony\Component\HttpFoundation\JsonResponse + * A JSON response. + */ + public function accept(Request $request) { + $user = $this->userStorage->load($this->currentUser->id()); + + if (!$user) { + return new JsonResponse([ + 'success' => FALSE, + 'message' => 'User not found.', + ], 404); + } + + if (!$user->hasField('field_tos_acceptance')) { + return new JsonResponse([ + 'success' => FALSE, + 'message' => 'TOS acceptance field not found.', + ], 500); + } + + // Set TOS accepted to TRUE. + $user->set('field_tos_acceptance', 1); + + // Update the accepted_date field if it exists. + if ($user->hasField('field_accepted_date')) { + // Create a DrupalDateTime object in UTC timezone. + $date = new DrupalDateTime('now', DateTimeItemInterface::STORAGE_TIMEZONE); + // Format according to datetime storage format. + $formatted_date = $date->format(DateTimeItemInterface::DATETIME_STORAGE_FORMAT); + $user->set('field_accepted_date', $formatted_date); + } + + $user->save(); + + return new JsonResponse([ + 'success' => TRUE, + 'message' => 'Terms of Service accepted successfully.', + ]); + } + +} diff --git a/ucb_user_invite.install b/ucb_user_invite.install index 46f8c16..995d52e 100644 --- a/ucb_user_invite.install +++ b/ucb_user_invite.install @@ -88,3 +88,115 @@ function ucb_user_invite_update_9502() { $field_config->save(); } } + +/** + * Adds the 'field_tos_acceptance' and 'field_accepted_date' fields to the User entity. + * + * Introduced to support Terms of Service acceptance tracking. + */ +function ucb_user_invite_update_9503() { + $entity_type = 'user'; + $bundle = 'user'; + + // Create field_tos_acceptance (boolean). + $tos_field_name = 'field_tos_acceptance'; + $tos_field_storage = \Drupal::entityTypeManager() + ->getStorage('field_storage_config') + ->load($entity_type . '.' . $tos_field_name); + + if (!$tos_field_storage) { + $tos_field_storage = \Drupal::entityTypeManager() + ->getStorage('field_storage_config') + ->create([ + 'field_name' => $tos_field_name, + 'entity_type' => $entity_type, + 'type' => 'boolean', + 'settings' => [ + 'on_label' => 'Accepted', + 'off_label' => 'Not Accepted', + ], + ]); + $tos_field_storage->save(); + } + + $tos_field_config = \Drupal::entityTypeManager() + ->getStorage('field_config') + ->load($entity_type . '.' . $bundle . '.' . $tos_field_name); + + if (!$tos_field_config) { + $tos_field_config = \Drupal::entityTypeManager() + ->getStorage('field_config') + ->create([ + 'field_name' => $tos_field_name, + 'entity_type' => $entity_type, + 'bundle' => $bundle, + 'label' => t('Terms of Service Accepted'), + 'description' => t('Indicates whether the user has accepted the Terms of Service.'), + 'required' => FALSE, + 'default_value' => [ + [ + 'value' => 0, + ], + ], + ]); + $tos_field_config->save(); + } + + // Create field_accepted_date (datetime). + $date_field_name = 'field_accepted_date'; + $date_field_storage = \Drupal::entityTypeManager() + ->getStorage('field_storage_config') + ->load($entity_type . '.' . $date_field_name); + + if (!$date_field_storage) { + $date_field_storage = \Drupal::entityTypeManager() + ->getStorage('field_storage_config') + ->create([ + 'field_name' => $date_field_name, + 'entity_type' => $entity_type, + 'type' => 'datetime', + 'settings' => [ + 'datetime_type' => 'datetime', + ], + ]); + $date_field_storage->save(); + } + + $date_field_config = \Drupal::entityTypeManager() + ->getStorage('field_config') + ->load($entity_type . '.' . $bundle . '.' . $date_field_name); + + if (!$date_field_config) { + $date_field_config = \Drupal::entityTypeManager() + ->getStorage('field_config') + ->create([ + 'field_name' => $date_field_name, + 'entity_type' => $entity_type, + 'bundle' => $bundle, + 'label' => t('TOS Accepted Date'), + 'description' => t('The date and time when the user accepted the Terms of Service.'), + 'required' => FALSE, + ]); + $date_field_config->save(); + } + + // Set default value for all existing users to FALSE (0) for tos_acceptance. + $user_storage = \Drupal::entityTypeManager()->getStorage('user'); + $query = $user_storage->getQuery() + ->accessCheck(FALSE) + ->condition('uid', 0, '>'); + $uids = $query->execute(); + + if (!empty($uids)) { + $users = $user_storage->loadMultiple($uids); + foreach ($users as $user) { + if ($user->hasField('field_tos_acceptance')) { + $current_value = $user->get('field_tos_acceptance')->value; + if ($current_value === NULL) { + $user->set('field_tos_acceptance', 0); + $user->save(); + } + } + } + } +} diff --git a/ucb_user_invite.module b/ucb_user_invite.module index c3611c6..d0f691f 100644 --- a/ucb_user_invite.module +++ b/ucb_user_invite.module @@ -31,3 +31,53 @@ function ucb_user_invite_mail($key, &$message, $params) { $message['body'] = ['#markup' => $tokenService->replace(strtr($template, $variables), $tokens, $options)]; } } + +/** + * Implements hook_preprocess_page(). + */ +function ucb_user_invite_preprocess_page(&$variables) { + // Check if TOS acceptance is enabled in site configuration. + $site_config = \Drupal::config('ucb_site_configuration.settings'); + $tos_enabled = $site_config->get('tos_acceptance_enabled'); + + // If TOS acceptance is not enabled (default is FALSE), don't show the modal. + if (empty($tos_enabled)) { + return; + } + + // Only check for logged-in users on user pages. + $route_match = \Drupal::routeMatch(); + $route_name = $route_match->getRouteName(); + + // Check if this is a user page route. + if (in_array($route_name, ['entity.user.canonical', 'user.page'])) { + $current_user = \Drupal::currentUser(); + + // Only show modal to authenticated users. + if ($current_user->isAuthenticated()) { + $user = \Drupal::entityTypeManager() + ->getStorage('user') + ->load($current_user->id()); + + // Check if user has accepted TOS. + if ($user && $user->hasField('field_tos_acceptance')) { + $tos_accepted = $user->get('field_tos_acceptance')->value; + + // If TOS not accepted, attach the library to show modal. + if (empty($tos_accepted)) { + $variables['#attached']['library'][] = 'boulder_base/ucb-tos-acceptance'; + + // TOS URL from Web Central Website. + $tos_url = 'https://www.colorado.edu/webcentral'; + + // Pass settings to JavaScript. + $variables['#attached']['drupalSettings']['ucb_tos_acceptance'] = [ + 'show_modal' => TRUE, + 'tos_url' => $tos_url, + 'accept_url' => Url::fromRoute('ucb_user_invite.tos_acceptance')->toString(), + ]; + } + } + } + } +} diff --git a/ucb_user_invite.routing.yml b/ucb_user_invite.routing.yml index 94573c2..b698964 100644 --- a/ucb_user_invite.routing.yml +++ b/ucb_user_invite.routing.yml @@ -19,3 +19,13 @@ ucb_user_invite.settings_form: _permission: administer user invite options: _admin_route: true + +ucb_user_invite.tos_acceptance: + path: '/ucb-user-invite/tos-acceptance/accept' + defaults: + _controller: '\Drupal\ucb_user_invite\Controller\TosAcceptanceController::accept' + requirements: + _user_is_logged_in: 'TRUE' + _format: 'json' + _csrf_request_header_token: 'TRUE' + methods: [POST]