You are here

legal.module in Legal 2.0.x

Module file for Legal.

File

legal.module
View source
<?php

/**
 * @file
 * Module file for Legal.
 */
use Drupal\Component\Utility\Xss;
use Drupal\Core\Url;
use Drupal\Core\Link;
use Drupal\Component\Serialization\Json;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Routing\RouteMatchInterface;
use Drupal\user\UserInterface;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Drupal\Component\Render\PlainTextOutput;
use Drupal\user\Entity\User;
use Drupal\Component\Utility\Crypt;
use Drupal\Core\Entity\EntityInterface;
use Drupal\legal\Entity\Accepted;
use Drupal\Core\Database\Query\Condition;

/**
 * Implements hook_help().
 */
function legal_help($route_name, RouteMatchInterface $route_match) {
  $output = '';
  switch ($route_name) {
    case 'help.page.legal':
      $output .= t('Display a Terms & Conditions statement on the registration page, require visitor to accept T&C to register. When a user creates an account they are required to accept your Terms & Conditions to complete their registration.');
      break;
    case 'legal.config_legal':
      $output .= t('Display a Terms & Conditions statement on the registration page, require visitor to accept the T&C to register. A <a href="@page">page</a> displaying your T&C will be automatically created, access to this page can be set via the <a href="@access">permissions</a> administration page.', [
        '@page' => \Drupal::urlGenerator()
          ->generate('legal.legal'),
        '@access' => \Drupal::urlGenerator()
          ->generate('user.admin_permissions'),
      ]);
  }
  return $output;
}

/**
 * Form elements for displaying T&Cs to users.
 *
 * @param array $form
 *   An associative array containing the structure of the form.
 * @param array $conditions
 *   Terms & Conditions to be displayed.
 * @param string $action
 *   What user action the form is being used for.
 */
function legal_display_fields(array &$form, array $conditions, $action) {
  $settings = \Drupal::config('legal.settings');
  switch ($action) {
    case 'registration':
      $legal_display = $settings
        ->get('registration_terms_style');
      $legal_display_container = $settings
        ->get('registration_container');
      $modal_terms = $settings
        ->get('registration_modal_terms');
      break;
    case 'login':
      $legal_display = $settings
        ->get('login_terms_style');
      $legal_display_container = $settings
        ->get('login_container');
      $modal_terms = $settings
        ->get('login_modal_terms');
      break;
  }
  $form['current_id'] = [
    '#type' => 'value',
    '#value' => $conditions['version'],
  ];
  $form['language_value'] = [
    '#type' => 'value',
    '#value' => $conditions['language'],
  ];
  $form['revision_id'] = [
    '#type' => 'value',
    '#value' => $conditions['revision'],
  ];
  $form['current_date'] = [
    '#type' => 'value',
    '#value' => $conditions['date'],
  ];
  $form['display'] = [
    '#type' => 'value',
    '#value' => $legal_display,
  ];
  $form['legal'] = [
    '#type' => $legal_display_container ? 'details' : 'markup',
    '#title' => $legal_display_container ? t('Terms and Conditions of Use') : '',
    '#weight' => 29,
    '#open' => TRUE,
  ];
  switch ($legal_display) {

    // Scroll box (CSS).
    case 1:
      $form['#attached']['library'][] = 'legal/css-scroll';
      $form['legal']['content'] = [
        '#type' => 'html_tag',
        '#tag' => 'div',
        '#attributes' => [
          'class' => [
            'legal-terms',
            'legal-terms-scroll',
          ],
        ],
      ];
      $form['legal']['content']['terms'] = [
        '#type' => 'processed_text',
        '#text' => $conditions['conditions'],
        '#format' => isset($conditions['format']) ? $conditions['format'] : filter_default_format(),
      ];
      $accept_label = legal_accept_label();
      break;

    // HTML.
    case 2:
      $form['legal']['legal_accept']['#title'] = t('<strong>Accept</strong> Terms & Conditions of Use');
      $form['legal']['conditions'] = [
        '#type' => 'html_tag',
        '#tag' => 'div',
        '#attributes' => [
          'class' => [
            'legal-terms',
          ],
        ],
      ];
      $form['legal']['conditions']['terms'] = [
        '#type' => 'processed_text',
        '#text' => $conditions['conditions'],
        '#format' => isset($conditions['format']) ? $conditions['format'] : filter_default_format(),
      ];
      $accept_label = legal_accept_label();
      break;

    // Page Link.
    case 3:
      $form['#attached']['library'][] = 'core/drupal.dialog.ajax';
      $form['#attached']['library'][] = 'core/drupal.ajax';
      $form['#attached']['library'][] = 'core/jquery.form';
      $form['legal']['conditions'] = [
        '#markup' => '',
      ];
      $accept_label = legal_accept_label(TRUE, $modal_terms);
      break;

    // Scroll box (HTML).
    default:
      $form['legal']['conditions'] = [
        '#type' => 'textarea',
        '#title' => t('Terms & Conditions'),
        '#default_value' => PlainTextOutput::renderFromHtml($conditions['conditions']),
        '#value' => PlainTextOutput::renderFromHtml($conditions['conditions']),
        '#rows' => 10,
        '#weight' => 0,
        '#attributes' => [
          'readonly' => 'readonly',
        ],
      ];
      $accept_label = legal_accept_label();
  }
  if (!empty($conditions['extras'])) {
    foreach ($conditions['extras'] as $key => $label) {
      if (!empty($label)) {
        $form['legal'][$key] = [
          '#type' => 'checkbox',
          '#title' => Xss::filter($label),
          '#default_value' => 0,
          '#weight' => 2,
          '#required' => TRUE,
        ];
      }
    }
  }
  $form['legal']['legal_accept'] = [
    '#type' => 'checkbox',
    '#title' => $accept_label,
    '#default_value' => 0,
    '#weight' => 50,
    '#required' => TRUE,
  ];
}

/**
 * The accept terms and conditions label.
 *
 * @param bool $link
 *   Should the label contain a link.
 * @param bool $modal
 *   Should target be shown in a modal dialog.
 *
 * @return \Drupal\Core\StringTranslation\TranslatableMarkup
 *   Label with markup.
 */
function legal_accept_label($link = FALSE, $modal = FALSE) {
  if ($link) {
    $url = \Drupal::urlGenerator()
      ->generate('legal.legal');
    if ($modal) {
      return t('<strong>Accept</strong> @terms of Use', [
        '@terms' => Link::fromTextAndUrl(t('Terms & Conditions'), Url::fromRoute('legal.legal', [], [
          'attributes' => [
            'data-dialog-type' => 'modal',
            'data-dialog-options' => Json::encode([
              'width' => 'auto',
            ]),
            'class' => [
              'use-ajax',
            ],
            'rel' => 'nofollow',
          ],
        ]))
          ->toString(),
      ]);
    }
    else {
      return t('<strong>Accept</strong> <a href=":terms"  target="_blank">Terms & Conditions</a> of Use', [
        ':terms' => $url,
      ]);
    }
  }
  else {
    return t('<strong>Accept</strong> Terms & Conditions of Use');
  }
}

/**
 * Implements hook_form_FORM_ID_alter().
 */
function legal_form_user_register_form_alter(&$form, FormStateInterface $form_state, $form_id) {
  $user = \Drupal::currentUser();

  // Users with 'administer users' can access registration on user create page.
  if (!empty($user
    ->id())) {

    // Use legal_form_user_form_alter() to deal with admin created users.
    return;
  }
  $language = \Drupal::languageManager()
    ->getCurrentLanguage();
  $conditions = legal_get_conditions($language
    ->getId());

  // Do nothing if there's no Terms and Conditions text set.
  if (empty($conditions['conditions'])) {
    return;
  }
  legal_display_fields($form, $conditions, 'registration');
  $settings = \Drupal::config('legal.settings');

  // Make sure user is not asked to accept T&C again in post-registration login.
  if ($settings
    ->get('accept_every_login') == 1) {
    $request = \Drupal::request();
    $session = $request
      ->getSession();
    $session
      ->set('legal_login', TRUE);
  }
}

/**
 * Implements hook_form_FORM_ID_alter().
 */
function legal_form_user_form_alter(&$form, FormStateInterface $form_state, $form_id) {

  // Deal with Registration form in legal_form_user_register_form_alter().
  if ($form_id == 'user_register_form') {
    return;
  }
  $accepted = FALSE;
  $settings = \Drupal::config('legal.settings');

  // Do nothing if configuration option is set to not display T&C.
  if ($settings
    ->get('user_profile_display') == 0) {
    return;
  }

  // User being edited.
  $account = $form_state
    ->getFormObject()
    ->getEntity();
  $uid = $account
    ->id();

  // Do nothing for user 1 or user with exempt role.
  $exempt = legal_user_is_exempt($account);
  if ($exempt) {
    return;
  }

  // Do nothing if there's no Terms and Conditions text set.
  $language = \Drupal::languageManager()
    ->getCurrentLanguage();
  $conditions = legal_get_conditions($language
    ->getId());
  if (empty($conditions['conditions'])) {
    return;
  }

  // Set reminder to change password if coming from one time login link.
  if (isset($_REQUEST['pass-reset-token'])) {
    $messages = \Drupal::messenger()
      ->messagesByType('status');
    $status_messages = isset($messages['status']) ? $messages['status'] : [];
    $reminder = t('You have just used your one-time login link. It is no longer necessary to use this link to log in. Please change your password.');
    if (!in_array($reminder, $status_messages)) {
      \Drupal::messenger()
        ->addMessage($reminder);
    }
  }

  // Current logged in user.
  $user = \Drupal::currentUser();
  $uid_active = $user
    ->id();

  // Get last accepted version for this account.
  $legal_account = legal_get_accept($uid);

  // If no version accepted, get version with current language revision.
  if (!isset($legal_account['version']) || empty($legal_account['version'])) {
    $conditions = legal_get_conditions($language
      ->getId());

    // No conditions set yet.
    if (empty($conditions['conditions'])) {
      return;
    }
  }
  else {

    // Get version / revision of last accepted language.
    $conditions = legal_get_conditions($legal_account['language']);

    // No conditions set yet.
    if (empty($conditions['conditions'])) {
      return;
    }

    // Check latest version of T&C has been accepted.
    $accepted = legal_version_check($uid, $conditions['version'], $conditions['revision'], $legal_account);

    // Enable language switching if version accepted and revision up to date.
    if ($accepted && $legal_account['language'] != $language
      ->getId()) {
      $conditions = legal_get_conditions($language
        ->getId());
    }
  }
  legal_display_fields($form, $conditions, 'login');
  if ($accepted === TRUE) {
    $form['legal']['legal_accept']['#value'] = 1;
    if (!empty($conditions['extras'])) {
      foreach ($conditions['extras'] as $key => $label) {
        if (!empty($label)) {
          $form['legal'][$key]['#value'] = 1;
        }
      }
    }
  }

  // Disable checkbox if:
  // - user is not account owner;
  // - latest T&C has already been accepted.
  if ($uid_active != $uid || $accepted == TRUE) {
    $form['legal']['legal_accept']['#attributes'] = [
      'disabled' => 'disabled',
    ];
    if (!empty($conditions['extras'])) {
      reset($conditions['extras']);
      foreach ($conditions['extras'] as $key => $label) {
        if (!empty($label)) {
          $form['legal'][$key]['#attributes'] = [
            'disabled' => 'disabled',
          ];
        }
      }
    }
  }

  // Not required if user is not account owner.
  if ($uid_active != $uid) {
    $form['legal']['legal_accept']['#required'] = FALSE;
    if (!empty($conditions['extras'])) {
      reset($conditions['extras']);
      foreach ($conditions['extras'] as $key => $label) {
        if (!empty($label)) {
          $form['legal'][$key]['#required'] = FALSE;
        }
      }
    }
  }

  // Enable account owner to accept.
  if ($uid_active == $uid && $accepted != TRUE) {
    $form['legal']['legal_accept']['#default_value'] = isset($edit['legal_accept']) ? $edit['legal_accept'] : '';
    $form['legal']['legal_accept']['#required'] = TRUE;
    if (!empty($conditions['extras'])) {
      reset($conditions['extras']);
      foreach ($conditions['extras'] as $key => $label) {
        if (!empty($label)) {
          $form['legal'][$key]['#default_value'] = isset($edit[$key]) ? $edit[$key] : '';
          $form['legal'][$key]['#required'] = TRUE;
        }
      }
    }
  }
}

/**
 * Implements hook_user_login().
 */
function legal_user_login(UserInterface $account) {

  // Skip T&Cs for user 1 or user with exempt role.
  $exempt = legal_user_is_exempt($account);
  if ($exempt) {
    return;
  }
  $settings = \Drupal::config('legal.settings');

  // Get last accepted version for this account.
  $uid = $account
    ->get('uid')
    ->getString();
  $legal_account = legal_get_accept($uid);

  // If no version accepted, get version with current language revision.
  $language = \Drupal::languageManager()
    ->getCurrentLanguage();
  if (empty($legal_account['version'])) {
    $conditions = legal_get_conditions($language
      ->getId());

    // No conditions set yet, skip T&Cs.
    if (empty($conditions['conditions'])) {
      return;
    }
  }
  else {

    // Get version / revision of last accepted language.
    $conditions = legal_get_conditions($legal_account['language']);

    // No conditions set yet, skip T&Cs.
    if (empty($conditions['conditions'])) {
      return;
    }

    // Check latest version of T&C has been accepted.
    $accepted = legal_version_check($uid, $conditions['version'], $conditions['revision'], $legal_account);

    // User has accepted latest T&C.
    if ($accepted) {
      if ($settings
        ->get('accept_every_login') == 0) {
        return;
      }
      else {
        $request = \Drupal::request();
        $session = $request
          ->getSession();
        $newly_accepted = $session
          ->get('legal_login', FALSE);
        if ($newly_accepted) {
          return;
        }
      }
    }
  }

  // Log the user out and regenerate the Drupal session.
  \Drupal::logger('user')
    ->notice('Session closed for %name.', [
    '%name' => $account
      ->getAccountName(),
  ]);
  \Drupal::moduleHandler()
    ->invokeAll('user_logout', [
    $account,
  ]);

  // Destroy the current session, and reset $user to the anonymous user.
  \Drupal::service('session_manager')
    ->destroy();
  $query = NULL;
  $path = \Drupal::request()
    ->getpathInfo();
  $arg = explode('/', $path);

  // One time login link - set user edit page as destination after T&Cs.
  if (isset($arg[1]) && $arg[1] == 'user' && isset($arg[2]) && $arg[2] == 'reset') {
    $query = [
      'destination' => $account
        ->toUrl('edit-form')
        ->toString(),
    ];
  }

  // Preserve custom destination if it's been set.
  if (!empty($_REQUEST['destination'])) {
    $query = [
      'destination' => $_REQUEST['destination'],
    ];
  }
  unset($_GET['destination']);
  $signatory = User::load($uid);
  $login = $signatory
    ->get('login')->value;
  $password = $signatory
    ->get('pass')->value;
  $token = Crypt::randomBytesBase64();
  $data = $login . $uid . $password;
  $hash = Crypt::hmacBase64($data, $token);
  user_cookie_save([
    'legal_hash' => $hash,
    'legal_id' => $uid,
  ]);
  $query['token'] = $token;
  $path = Url::fromUserInput('/legal_accept', [
    'query' => $query,
  ])
    ->toString();
  $response = new RedirectResponse($path);
  $response
    ->sendHeaders();
  exit;
}

/**
 * Implements hook_entity_info().
 */
function legal_entity_info() {
  $info = [];
  $info['legal_conditions'] = [
    'label' => t('Legal Terms & Conditions'),
    'base table' => 'legal_conditions',
    'entity keys' => [
      'id' => 'tc_id',
      'label' => 'name',
    ],
    'module' => 'legal',
  ];
  return $info;
}

/**
 * Implements hook_ENTITY_TYPE_insert().
 */
function legal_user_insert(EntityInterface $entity) {
  $language = \Drupal::languageManager()
    ->getCurrentLanguage();
  if ($entity instanceof User) {
    $conditions = legal_get_conditions($language
      ->getId());
    if (empty($conditions['conditions'])) {
      return;
    }

    // Record the accepted state before removing legal_accept from $edit.
    $accepted = \Drupal::request()->request
      ->get('legal_accept') ? TRUE : FALSE;

    // Don't insert if user is already registered (administrator).
    if (\Drupal::currentUser()
      ->id() != 0) {
      return;
    }
    if ($accepted) {
      legal_save_accept($conditions['version'], $conditions['revision'], $conditions['language'], $entity
        ->get('uid')
        ->getString());
    }
  }
}

/**
 * Implements hook_ENTITY_TYPE_update().
 */
function legal_user_update(EntityInterface $entity) {
  if ($entity instanceof User) {
    $language = \Drupal::languageManager()
      ->getCurrentLanguage();
    $conditions = legal_get_conditions($language
      ->getId());
    if (empty($conditions['conditions'])) {
      return;
    }

    // Record the accepted state before removing legal_accept from $edit.
    $accepted = \Drupal::request()->request
      ->get('legal_accept') ? TRUE : FALSE;
    if (\Drupal::currentUser()
      ->id() != $entity
      ->get('uid')
      ->getString()) {
      return;
    }

    // If already accepted skip data entry.
    $previously_accepted = legal_version_check($entity
      ->get('uid')
      ->getString(), $conditions['version'], $conditions['revision']);
    if ($previously_accepted === TRUE) {
      return;
    }
    if ($accepted) {
      legal_save_accept($conditions['version'], $conditions['revision'], $conditions['language'], $entity
        ->get('uid')
        ->getString());
    }
  }
}

/**
 * Get last version of T&C accepted by a user.
 *
 * @param int $uid
 *   User ID.
 *
 * @return array
 *   Acceptance information.
 */
function legal_get_accept($uid) {
  $keys = [
    'legal_id',
    'version',
    'revision',
    'language',
    'uid',
    'accepted',
  ];
  $result = \Drupal::database()
    ->select('legal_accepted', 'la')
    ->fields('la')
    ->condition('uid', $uid)
    ->orderBy('version', 'DESC')
    ->orderBy('revision', 'DESC')
    ->execute()
    ->fetchAllAssoc('legal_id');
  $result = count($result) ? array_shift($result) : [];
  $accept = [];
  foreach ($keys as $key) {
    if (isset($result->{$key})) {
      $accept[$key] = $result->{$key};
    }
  }
  return $accept;
}

/**
 * Save instance of a user accepting T&C.
 *
 * @param int $version
 *   Version ID of T&C.
 * @param int $revision
 *   Revision ID of T&C.
 * @param string $language
 *   Language code of T&C.
 * @param int $uid
 *   User ID of user.
 *
 * @throws \Drupal\Core\Entity\EntityStorageException
 */
function legal_save_accept($version, $revision, $language, $uid) {
  Accepted::create([
    'version' => $version,
    'revision' => $revision,
    'language' => $language,
    'uid' => $uid,
    'accepted' => time(),
  ])
    ->save();
}

/**
 * Get latest T&C.
 *
 * @param string $language
 *   Language code.
 *
 * @return array
 *   T&C conditions content and metadata.
 */
function legal_get_conditions($language = '') {
  $keys = [
    'tc_id',
    'version',
    'revision',
    'language',
    'conditions',
    'format',
    'date',
    'extras',
    'changes',
  ];
  if (!empty($language)) {
    $result = \Drupal::database()
      ->select('legal_conditions', 'lc')
      ->fields('lc')
      ->condition('language', $language)
      ->orderBy('version', 'DESC')
      ->orderBy('revision', 'DESC')
      ->range(0, 1)
      ->execute()
      ->fetchAllAssoc('tc_id');
    $result = (array) array_shift($result);
  }
  else {
    $result = \Drupal::database()
      ->select('legal_conditions', 'lc')
      ->fields('lc')
      ->orderBy('tc_id', 'DESC')
      ->execute()
      ->fetchAllAssoc('tc_id');
    $result = (array) array_shift($result);
  }
  foreach ($keys as $key) {
    $conditions[$key] = isset($result[$key]) ? $result[$key] : '';
  }
  $conditions['extras'] = empty($conditions['extras']) ? [] : unserialize($conditions['extras']);
  return $conditions;
}

/**
 * Get all changes since user last accepted.
 */
function legal_display_changes($form, $uid) {
  $bullet_points = [];
  $last_accepted = legal_get_accept($uid);
  if (empty($last_accepted)) {
    return $form;
  }
  $result = \Drupal::database()
    ->select('legal_conditions', 'lc')
    ->fields('lc')
    ->condition((new Condition('OR'))
    ->condition('version', $last_accepted['version'], '>')
    ->condition((new Condition('AND'))
    ->condition('version', $last_accepted['version'])
    ->condition('revision', $last_accepted['revision'], '>')))
    ->condition('language', $last_accepted['language'])
    ->orderBy('revision', 'ASC')
    ->orderBy('version', 'ASC')
    ->execute()
    ->fetchAllAssoc('tc_id');
  if (empty($result)) {
    return $form;
  }
  foreach ($result as $term) {
    $changes = Xss::filterAdmin($term->changes);
    if (!empty($changes)) {
      $bullet_points = array_merge($bullet_points, explode("\r\n", $changes));
    }
  }
  if (empty($bullet_points)) {
    return $form;
  }
  $form['changes'] = [
    '#type' => 'details',
    '#title' => t('Changes List'),
    '#description' => t('Changes to the Terms & Conditions since last accepted:'),
    '#tree' => TRUE,
  ];
  $form['changes']['bullet_points'] = [
    '#theme' => 'item_list',
    '#items' => $bullet_points,
  ];
  return $form;
}

/**
 * Check if user has accepted latest version of T&C.
 */
function legal_version_check($uid, $version, $revision, $legal_account = []) {
  $accepted = FALSE;
  if (empty($legal_account)) {
    $legal_account = legal_get_accept($uid);
  }
  if (array_key_exists('version', $legal_account) && array_key_exists('revision', $legal_account)) {
    if ($legal_account['version'] == $version && $legal_account['revision'] == $revision) {
      $accepted = TRUE;
    }
  }
  return $accepted;
}

/**
 * Determine version ID of next T&C.
 *
 * @param string $version_handling
 *   Specify if a new 'version' or 'revision' ID should be returned.
 * @param string $language
 *   Language of T&C.
 *
 * @return array
 *   Array with next 'version' and 'revision'.
 */
function legal_version($version_handling, $language) {
  $versioning = NULL;
  $version = (int) \Drupal::database()
    ->select('legal_conditions', 'lc')
    ->fields('lc', [
    'version',
  ])
    ->orderBy('version', 'desc')
    ->range(0, 1)
    ->execute()
    ->fetchField();

  // Make new version.
  if ($version_handling == 'version') {
    $versioning['version'] = empty($version) ? 1 : $version + 1;
    $versioning['revision'] = 1;
  }

  // Make new revision.
  if ($version_handling == 'revision') {
    $revision = \Drupal::database()
      ->select('legal_conditions', 'lc')
      ->fields('lc', [
      'revision',
    ])
      ->condition('version', $version)
      ->condition('language', $language)
      ->orderBy('revision', 'DESC')
      ->execute()
      ->fetchField();
    $versioning['version'] = empty($version) ? 1 : $version;
    $versioning['revision'] = empty($revision) ? 1 : $revision + 1;
  }
  return $versioning;
}

/**
 * Check if user is exempt from Terms & Conditions.
 *
 * @param object $account
 *   User account object.
 *
 * @return bool
 *   TRUE if user is exempt, FALSE otherwise.
 */
function legal_user_is_exempt($account) {

  // User 1 is exempt from accepting T&Cs, no need to display T&Cs.
  if ($account
    ->id() == 1) {
    return TRUE;
  }
  $settings = \Drupal::config('legal.settings');
  $exempt_roles = $settings
    ->get('except_roles');
  $account_roles = $account
    ->getRoles(TRUE);
  if (count(array_intersect($exempt_roles, $account_roles))) {
    return TRUE;
  }
  return FALSE;
}

/**
 * Deny access and clean up cookies.
 */
function legal_deny_with_redirect() {
  user_cookie_delete('legal_hash');
  user_cookie_delete('legal_id');
  \Drupal::messenger()
    ->addMessage(t('Operation timed out. Please try to log in again.'));
  $response = new RedirectResponse('/');
  $response
    ->sendHeaders();
  exit;
}

/**
 * Implements hook_theme().
 */
function legal_theme() {
  $themes = [
    'legal_current_metadata' => [
      'render element' => 'form',
    ],
  ];
  return $themes;
}

Functions

Namesort descending Description
legal_accept_label The accept terms and conditions label.
legal_deny_with_redirect Deny access and clean up cookies.
legal_display_changes Get all changes since user last accepted.
legal_display_fields Form elements for displaying T&Cs to users.
legal_entity_info Implements hook_entity_info().
legal_form_user_form_alter Implements hook_form_FORM_ID_alter().
legal_form_user_register_form_alter Implements hook_form_FORM_ID_alter().
legal_get_accept Get last version of T&C accepted by a user.
legal_get_conditions Get latest T&C.
legal_help Implements hook_help().
legal_save_accept Save instance of a user accepting T&C.
legal_theme Implements hook_theme().
legal_user_insert Implements hook_ENTITY_TYPE_insert().
legal_user_is_exempt Check if user is exempt from Terms & Conditions.
legal_user_login Implements hook_user_login().
legal_user_update Implements hook_ENTITY_TYPE_update().
legal_version Determine version ID of next T&C.
legal_version_check Check if user has accepted latest version of T&C.