<?php

/**
 * @file
 * Module file.
 */

use Drupal\Component\Utility\Html;
use Drupal\Core\Access\AccessResult;
use Drupal\Core\Entity\ContentEntityInterface;
use Drupal\Core\Entity\Display\EntityViewDisplayInterface;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Link;
use Drupal\Core\Render\Element;
use Drupal\Core\Session\AccountInterface;
use Drupal\Core\Url;
use Drupal\field\Entity\FieldStorageConfig;
use Drupal\fillpdf\Controller\HandlePdfController;
use Drupal\fillpdf\Form\FillPdfFormForm;
use Drupal\media_library\MediaLibraryState;
use Drupal\user\UserInterface;
use Drupal\uw_cfg_common\Service\UWService;
use Drupal\views\Plugin\views\query\QueryPluginBase;
use Drupal\views\ViewExecutable;
use Drupal\webform\WebformInterface;
use Drupal\webform\WebformSubmissionStorageInterface;
use Symfony\Component\HttpFoundation\RedirectResponse;

include_once 'includes/helper_functions.php';

/**
 * Implements hook_preprocess_form_element().
 *
 * Allows for use of label_class in form elements and will add
 * any class in label_classes to the label.
 */
function uw_cfg_common_preprocess_form_element(&$variables) {
  if (isset($variables['element']['#label_classes'])) {
    $variables['label']['#attributes']['class'] = $variables['element']['#label_classes'];
  }
}

/**
 * Implements template_preprocess_form_element_label().
 */
function uw_cfg_common_preprocess_form_element_label(&$variables) {

  // Check if we need to add the form required to the label.
  // Conditions are not the blank summary checkbox,
  // the id of the label contains edit-field-uw and has
  // either summary or position in the id.
  if (
    isset($variables['element']['#id']) &&
    $variables['element']['#id'] !== 'edit-field-uw-blank-summary-value' &&
    str_contains($variables['element']['#id'], 'edit-field-uw-') &&
    (
      str_contains($variables['element']['#id'], 'summary') ||
      str_contains($variables['element']['#id'], 'position')
    )
  ) {

    // Try and get the node type, by replacing the id of the label.
    $node_type = $variables['element']['#id'];
    $node_type = str_replace('edit-field-uw-', '', $node_type);
    $node_type = str_replace('-summary-0-value', '', $node_type);
    $node_type = str_replace('-position-0-value', '', $node_type);

    // The node types to place the form required on the label.
    $blank_summary_node_types = [
      'blog',
      'event',
      'news',
      'opportunity',
      'profile',
      'project',
    ];

    // If we are on a node that needs a form required
    // on the label add the class.
    if (in_array($node_type, $blank_summary_node_types)) {
      $variables['attributes']['class'][] = 'form-required';
    }
  }
}

/**
 * Implements hook_sendgrid_integration_categories_alter().
 *
 * Set the category for all sendmail as WCMS.
 */
function uw_cfg_common_sendgrid_integration_categories_alter($message, $categories) {
  $categories = ['WCMS'];

  return $categories;
}

/**
 * Implements hook_metatags_attachments_alter().
 */
function uw_cfg_common_metatags_attachments_alter(array &$metatag_attachments) {
  // Check if the image and og:image fields are empty.
  // We do this here instead of in hook_metatags_alter() because we want to
  // check if they're empty after tokens have been applied.
  // We also want to support the use case where they don't upload an image,
  // but do manually specify an image.
  // Because of where the information is stored, we have to loop to find it.
  $found_image_src = FALSE;
  $found_og_image = FALSE;
  foreach ($metatag_attachments['#attached']['html_head'] as $attachment) {
    if ($attachment[1] == 'image_src') {
      $found_image_src = TRUE;
    }
    elseif ($attachment[1] == 'og_image_0') {
      $found_og_image = TRUE;
    }
  }

  // Define what the fallback image is, so we only need to change it once.
  $fallback_image = 'https://uwaterloo.ca/university-of-waterloo-logo-152.png';

  // If the image_src field is missing, create one with the UWaterloo logo.
  if (!$found_image_src) {
    $metatag_attachments['#attached']['html_head'][] = [
      0 => [
        '#tag' => 'link',
        '#attributes' => [
          'rel' => "image_src",
          'href' => $fallback_image,
        ],
      ],
      1 => 'image_src',
    ];
  }

  // If the og:image field is missing, create one with the UWaterloo logo.
  if (!$found_og_image) {
    $metatag_attachments['#attached']['html_head'][] = [
      0 => [
        '#tag' => 'meta',
        '#attributes' => [
          'property' => "og:image",
          'content' => $fallback_image,
        ],
      ],
      1 => 'og_image_0',
    ];
  }
}

/**
 * Implements hook_entity_presave().
 */
function uw_cfg_common_entity_presave(EntityInterface $entity) {

  // Check if we are on a menu link.
  if ($entity->getEntityTypeId() == 'menu_link_content') {

    // Check that we are on a Information For (audience) link.
    if ($entity->menu_name->value == 'uw-menu-audience-menu') {

      // Invalid all the menu caching.
      \Drupal::cache('menu')->invalidateAll();

      // Rebuild all the menus.
      \Drupal::service('plugin.manager.menu.link')->rebuild();
    }
  }

  // On a node entity save, check if the responsive
  // image has created the derivatives so that things
  // like hero images will load when no image has yet
  // been rendered.  If we do not do this, most hero
  // images will not work.
  if ($entity->getEntityTypeId() == 'node') {

    // ISTWCMS-5846: if the leave summary blank is checked
    // on the node add/edit page, set the summary to NULL.
    // Check that the node has the field leave summary blank.
    if ($entity->hasField('field_uw_blank_summary')) {

      // If the leave summary blank is checked, set summary to NULL.
      if ($entity->field_uw_blank_summary->value) {

        // Get the node type from the entity.
        $node_type = $entity->getType();

        // Since all the summary fields are field_uw_ct_<node_type>_summary,
        // we need to get the node_type from the getType.  We need this
        // because the node_type has uw_ct_ in the name, so simply replacing
        // the uw_ct_ with nothing will give us the node_type.
        // the uw_ct_ with nothing will give us the node_type.
        $node_type = str_replace('uw_ct_', '', $node_type);

        // Since news has the content type with news_item, we need
        // to remove the _item to get the correct field name.
        $node_type = str_replace('_item', '', $node_type);

        // Set the field name using the notation from above.
        $field_name = 'field_uw_' . $node_type . '_summary';

        // Now set the summary to NULL using the field_name.
        $entity->$field_name = NULL;
      }
    }

    // If there is a hero image (media), continue to process.
    if (
      $entity->hasField('field_uw_hero_image') &&
      $media = $entity->field_uw_hero_image->entity
    ) {

      // Hero media exists, get file entity from media.
      if ($file = $media->field_media_image->entity) {

        // Load the image styles that are needed for the hero.
        $uw_styles = \Drupal::service('uw_cfg_common.uw_service')->uwGetResponsiveImageStyles();

        // Step through each of the image styles and ensure that
        // the derivative is created.
        foreach ($uw_styles as $uw_style) {

          // Load the image style.
          $style = \Drupal::entityTypeManager()
            ->getStorage('image_style')
            ->load($uw_style);

          // Get the styled image derivative.
          $destination = $style->buildUri($file->getFileUri());

          // If the derivative doesn't exist yet (as the image style may
          // have been added post launch), create it.
          if (!file_exists($destination)) {
            $style->createDerivative($file->getFileUri(), $destination);
          }
        }
      }
    }
  }
}

/**
 * Implements hook_form_FORM_ID_alter().
 *
 * Remove the None option from layout builder styles.
 */
function uw_cfg_common_form_layout_builder_configure_section_alter(array &$form, FormStateInterface $form_state, string $form_id): void {

  // Column separator group should not appear one column.
  if (isset($form['layout_builder_style_column_separator'])) {
    // Ensuring that none is selected for column separator by default.
    if (!isset($form['layout_builder_style_column_separator']['#default_value'])) {
      $form['layout_builder_style_column_separator']['#default_value'] = 'uw_lbs_column_separator_none';
    }
  }

  // Ensuring that none is selected for section separator by default.
  if (!isset($form['layout_builder_style_section_separator']['#default_value'])) {
    $form['layout_builder_style_section_separator']['#default_value'] = 'uw_lbs_section_separator_none';
  }

  // Ensuring that default is selected for section spacing by default.
  if (!isset($form['layout_builder_style_section_spacing']['#default_value'])) {
    $form['layout_builder_style_section_spacing']['#default_value'] = 'uw_lbs_section_spacing_default';
  }

  // Ensuring that the contained width is selected by default.
  if (!isset($form['layout_builder_style_default']['#default_value'])) {
    $form['layout_builder_style_default']['#default_value'] = 'uw_lbs_contained_width';
  }

  // Layout builder style - background style default and removing
  // empty(none) option.
  if ($form['layout_builder_style_section_background']['#default_value'] === NULL) {
    $form['layout_builder_style_section_background']['#default_value'] = 'uw_lbs_section_background_full_width';
  }
  unset($form['layout_builder_style_section_background']['#empty_option']);

  $config = $form_state->getFormObject()->getLayout()->getConfiguration();

  $form['layout_builder_background_type'] = [
    '#type' => 'select',
    '#title' => t('Background'),
    '#options' => [
      'none' => t('None'),
      'solid_color' => t('Solid colour'),
      'image' => t('Image'),
    ],
    '#weight' => 80,
    '#default_value' => $config['layout_builder_background_type'] ?? 'none',
  ];

  $form['layout_builder_background_color'] = [
    '#type' => 'select',
    '#title' => t('Background colour'),
    '#options' => [
      'neutral' => t('Light grey'),
      'org-default' => t('UW Gold'),
      'org-default-b' => t('UW Black'),
      'org-art' => t('Orange (Arts)'),
      'org-eng' => t('Purple (Engineering)'),
      'org-env' => t('Green (Environment)'),
      'org-ahs' => t('Teal (Health)'),
      'org-mat' => t('Pink (Mathematics)'),
      'org-sci' => t('Blue (Science)'),
      'org-school' => t('Red (school)'),
      'org-cgc' => t('Red (Conrad Grebel University College)'),
      'org-ren' => t('Green (Renison University College)'),
      'org-stj' => t('Green (St. Jerome’s University)'),
      'org-stp' => t('Blue (United College)'),
    ],
    '#weight' => 81,
    '#default_value' => $config['layout_builder_background_color'] ?? 'neutral',
    '#states' => [
      'visible' => [
        ':input[name="layout_builder_background_type"]' => ['value' => 'solid_color'],
      ],
    ],
  ];

  $form['layout_builder_background_image'] = [
    '#type' => 'media_library',
    '#allowed_bundles' => ['uw_mt_image'],
    '#title' => t('Background image'),
    '#default_value' => $config['layout_builder_background_image'] ?? NULL,
    '#description' => t('This image should be at least 1000x400 pixels, and will be proportionally scaled as needed to ensure it covers the entire section. This may mean that the top and bottom or left and right of the image get cut off. Images should not contain text or any other content that would require a description, and must be selected so that text over top can maintain sufficient colour contrast.'),
    '#weight' => 82,
    '#states' => [
      'visible' => [
        ':input[name="layout_builder_background_type"]' => ['value' => 'image'],
      ],
    ],
  ];

  $form['layout_builder_background_image_image_tint'] = [
    '#type' => 'select',
    '#title' => t('Image tint'),
    '#options' => [
      'none' => t('None'),
      'black' => t('Black (darken)'),
      'white' => t('White (lighten)'),
    ],
    '#weight' => 83,
    '#default_value' => $config['layout_builder_background_image_image_tint'] ?? 'none',
    '#description' => t('For some images, tinting the image darker or lighter can improve the accessibility and readability of the overlaid content.'),
    '#states' => [
      'visible' => [
        ':input[name="layout_builder_background_type"]' => ['value' => 'image'],
      ],
    ],
  ];

  $form['layout_builder_background_image_text_color'] = [
    '#type' => 'select',
    '#title' => t('Text colour'),
    '#options' => [
      'white' => t('White'),
      'white-black-shadow' => t('White with black shadow'),
      'black' => t('Black'),
      'black-white-shadow' => t('Black with white shadow'),
    ],
    '#weight' => 84,
    '#default_value' => $config['layout_builder_background_image_text_color'] ?? 'white',
    '#description' => t('Make sure to select the option that provides the best contrast from the image underneath.'),
    '#states' => [
      'visible' => [
        ':input[name="layout_builder_background_type"]' => ['value' => 'image'],
      ],
    ],
  ];

  // Adding #states to layout builder styles created using UI.
  $form['layout_builder_style_section_background']['#states'] = [
    'visible' => [
      ':input[name="layout_builder_background_type"]' => ['value' => 'image'],
      ':input[name="layout_builder_style_default"]' => ['!value' => 'uw_lbs_full_width'],
    ],
  ];

  $form['#validate'][] = 'uw_cfg_common_validate_section_configuration_validate';

  array_unshift($form['#submit'], 'uw_cfg_common_submit_section_configuration_submit');

}

/**
 * Validation for section background.
 *
 * @param array $form
 *   Form render array.
 * @param \Drupal\Core\Form\FormStateInterface $form_state
 *   Form state.
 */
function uw_cfg_common_validate_section_configuration_validate(array &$form, FormStateInterface $form_state) {

  $type = $form_state->getValue('layout_builder_background_type');

  if ($type === 'image' && empty($form_state->getValue('layout_builder_background_image'))) {

    // Get the values from the form state.
    $values = $form_state->getValues();

    // If this is image, ensure one is selected.
    if (
      $values['layout_builder_background_type'] === 'image' &&
      empty($values['layout_builder_background_image'])
    ) {
      $form_state->setErrorByName(
        'layout_builder_background_type',
        t('Background image is required!')
      );
    }
  }
}

/**
 * Submit handler for section background.
 *
 * @param array $form
 *   Form render array.
 * @param \Drupal\Core\Form\FormStateInterface $form_state
 *   Form state object.
 */
function uw_cfg_common_submit_section_configuration_submit(array &$form, FormStateInterface $form_state) {
  $layout_builder_background = $form_state->getValues();
  $type = $layout_builder_background['layout_builder_background_type'] ?? 'none';

  /** @var \Drupal\Core\Layout\LayoutDefault $layout */
  $layout = $form_state->getFormObject()->getLayout();
  $config = $layout->getConfiguration();

  // Reset all previously stored values.
  $config['layout_builder_background_type'] = $type;
  $config['layout_builder_background_color'] = NULL;
  $config['layout_builder_background_image'] = NULL;
  $config['layout_builder_background_image_image_tint'] = $layout_builder_background['layout_builder_background_image_image_tint'] ?? 'none';
  $config['layout_builder_background_image_text_color'] = $layout_builder_background['layout_builder_background_image_text_color'] ?? 'white';

  if ($type !== 'none') {
    if ($type === 'solid_color') {
      $config['layout_builder_background_color'] = $layout_builder_background['layout_builder_background_color'] ?? 'neutral';
    }
    elseif ($type === 'image') {
      $config['layout_builder_background_image'] = $layout_builder_background['layout_builder_background_image'];
    }
  }
  else {
    $config['layout_builder_background_color'] = NULL;
    $config['layout_builder_background_image_image_tint'] = NULL;
    $config['layout_builder_background_image_text_color'] = NULL;
  }

  $layout->setConfiguration($config);
}

/**
 * Implements hook_form_FORM_ID_alter().
 *
 * Configure admin/structure/webform/config/submissions.
 */
function uw_cfg_common_form_webform_admin_config_submissions_form_alter(array &$form, FormStateInterface $form_state, string $form_id): void {
  // Remove undesired features.
  $form['views_settings']['#access'] = FALSE;
}

/**
 * Implements hook_form_FORM_ID_alter().
 *
 * Remove all ability to set CSS classes, CSS styles and custom attributes
 * in admin/structure/webform/manage/{webform_id}.
 */
function uw_cfg_common_form_webform_ui_element_form_alter(array &$form, FormStateInterface $form_state, string $form_id): void {
  // Advanced -> Wrapper Attributes.
  $form['properties']['wrapper_attributes']['#access'] = FALSE;
  // Advanced -> Element Attributes.
  $form['properties']['element_attributes']['#access'] = FALSE;
  // Advanced -> Label Attributes.
  $form['properties']['label_attributes']['#access'] = FALSE;
  // Advanced -> Submission Display -> Display Wrapper Attributes.
  $form['properties']['display']['format_attributes']['#access'] = FALSE;
  // Advanced -> Summary Attributes.
  $form['properties']['summary_attributes']['#access'] = FALSE;
  // Advanced -> Title Attributes.
  $form['properties']['title_attributes']['#access'] = FALSE;
}

/**
 * Implements hook_form_FORM_ID_alter().
 *
 * Configure admin/structure/webform/manage/WEBFORM_ID/access.
 */
function uw_cfg_common_form_webform_settings_access_form_alter(array &$form, FormStateInterface $form_state, string $form_id): void {
  /** @var \Drupal\webform\WebformInterface $webform */
  $webform = $form_state->getFormObject()->getEntity();

  // Choose access control method.
  $form['access']['create']['uw_access_control_method'] = [
    '#type' => 'radios',
    '#title' => t('Choose who can view and submit the form'),
    '#options' => [
      'all' => t('Everyone'),
      'auth' => t('Users who are logged in'),
      'group' => t('Users specified by Active Directory groups'),
      'user' => t('Users specified below'),
      'anon' => t('Users who are logged out (for anonymous submission)'),
    ],
    '#default_value' => $webform->getThirdPartySetting('uw_cfg_common', 'access_control_method') ?: 'all',
    '#required' => TRUE,
    '#weight' => -50,
  ];

  // Access by Active Directory group.
  $form['access']['create']['uw_ad_access'] = [
    '#type' => 'details',
    '#title' => t('Access control by Active Directory group'),
    '#required' => TRUE,
    '#open' => TRUE,
    '#states' => [
      'visible' => [
        'input[name="access[create][uw_access_control_method]"]' => ['value' => 'group'],
      ],
    ],
  ];
  $form['access']['create']['uw_ad_access']['ad_require_groups'] = [
    '#type' => 'textarea',
    '#title' => t('Limit form submission to these Active Directory groups'),
    '#description' => t('Put one Active Directory group per line. To complete the form, the user must be in at least one of these groups. Leave blank to allow everyone.'),
    '#default_value' => implode("\r\n", $webform->getThirdPartySetting('uw_cfg_common', 'ad_require_groups') ?: []),
  ];
  $form['access']['create']['uw_ad_access']['ad_deny_groups'] = [
    '#type' => 'textarea',
    '#title' => t('Prevent form submission for these Active Directory groups'),
    '#description' => t('Put one Active Directory group per line. To complete the form, the user must not be in any of these groups. Leave blank to allow everyone.'),
    '#default_value' => implode("\r\n", $webform->getThirdPartySetting('uw_cfg_common', 'ad_deny_groups') ?: []),
  ];

  // Validate and submit handler to save UW settings.
  $form['actions']['submit']['#validate'][] = '_uw_cfg_common_form_webform_settings_access_form_validate';
  $form['actions']['submit']['#submit'][] = '_uw_cfg_common_form_webform_settings_access_form_submit';

  // Users control is hidden or required, the latter when authz by user.
  $access_rule = [
    'input[name="access[create][uw_access_control_method]"]' => ['value' => 'user'],
  ];
  $form['access']['create']['users']['#states']['required'][] = $access_rule;
  $form['access']['create']['users']['#states']['visible'][] = $access_rule;

  // Remove sections for access control that should not be available.
  $sections_to_remove = [
    'update_any',
    'update_own',
    'delete_own',
    'administer',
    'configuration',
  ];
  foreach ($sections_to_remove as $section) {
    $form['access'][$section]['#access'] = FALSE;
  }

  // Remove all but user-based access for submissions and test.
  $permissions_to_edit = [
    'create',
    'view_any',
    'delete_any',
    'purge_any',
    'view_own',
    'test',
  ];
  $access_types_to_remove = [
    'roles',
    'permissions',
  ];
  foreach ($permissions_to_edit as $permission) {
    foreach ($access_types_to_remove as $type) {
      $form['access'][$permission][$type]['#access'] = FALSE;
    }
  }
}

/**
 * Form validate handler.
 */
function _uw_cfg_common_form_webform_settings_access_form_validate(array $form, FormStateInterface $form_state): void {
  // Validate UW settings.
  $access_control_method = [
    'access',
    'create',
    'uw_access_control_method',
  ];
  $access_control_method = $form_state->getValue($access_control_method);
  switch ($access_control_method) {
    // Validate AD groups.
    case 'group':
      $fields = [
        'ad_require_groups' => NULL,
        'ad_deny_groups' => NULL,
      ];
      foreach (array_keys($fields) as $field) {
        // Get groups.
        $setting = [
          'access',
          'create',
          'uw_ad_access',
          $field,
        ];
        $fields[$field] = uw_cfg_common_array_split_clean($form_state->getValue($setting));
        $form_state->setValue($setting, $fields[$field]);

        // Raise error for invalid groups.
        foreach ($fields[$field] as $group) {
          if (!preg_match('/^[A-Za-z0-9_& -]+$/', $group)) {
            $form_state->setError(
              $form['access']['create']['uw_ad_access'][$field],
              t(
                'Invalid group: %group.',
                ['%group' => substr($group, 0, 100)]
              )
            );
            break;
          }
        }
      }

      // Raise error if no groups are entered.
      if (!array_filter($fields)) {
        $form_state->setError(
          $form['access']['create']['uw_ad_access'],
          t('Provide at least one group to use for access control.')
        );
      }
      // Fall-through.
    case 'all':
    case 'auth':
    case 'anon':
      // Except for case 'user', ensure no user access constraint is set.
      $form_state->setValue(['access', 'create', 'users'], NULL);
      break;
  }
}

/**
 * Form submit handler.
 */
function _uw_cfg_common_form_webform_settings_access_form_submit(array $form, FormStateInterface $form_state): void {
  // Save UW settings.
  if ($webform = $form_state->getFormObject()->getEntity()) {
    // Access control method.
    $access_control_method = [
      'access',
      'create',
      'uw_access_control_method',
    ];
    $access_control_method = $form_state->getValue($access_control_method);
    $webform->setThirdPartySetting('uw_cfg_common', 'access_control_method', $access_control_method);

    // Only save groups if that is the access method. Otherwise, they would be
    // saved without having been validated.
    if ($access_control_method === 'group') {
      // AD group access.
      foreach (['ad_require_groups', 'ad_deny_groups'] as $field) {
        $setting = [
          'access',
          'create',
          'uw_ad_access',
          $field,
        ];
        $setting = $form_state->getValue($setting);
        $webform->setThirdPartySetting('uw_cfg_common', $field, $setting);
      }
    }

    $webform->save();
  }
}

/**
 * Implements hook_form_FORM_ID_alter().
 *
 * Configure admin/structure/webform/manage/WEBFORM_ID/settings/confirmation.
 */
function uw_cfg_common_form_webform_settings_confirmation_form_alter(array &$form, FormStateInterface $form_state, string $form_id): void {
  // Remove undesirable Webform submission confirmation types.
  // The 'modal' type is just a different way to display the message. Disable
  // for consistency.
  unset($form['confirmation_type']['confirmation_type']['#options']['modal']);
  // The 'none' type is only useful along with a custom handler which provides
  // the confirmation message.
  unset($form['confirmation_type']['confirmation_type']['#options']['none']);

  // Remove undesired features.
  $form['confirmation_attributes_container']['#access'] = FALSE;
  $form['back']['back_container']['confirmation_back_attributes_container']['#access'] = FALSE;
}

/**
 * Implements hook_form_FORM_ID_alter().
 *
 * Configure admin/structure/webform/manage/WEBFORM_ID/settings.
 */
function uw_cfg_common_form_webform_settings_form_alter(array &$form, FormStateInterface $form_state, string $form_id): void {
  // Remove undesired features.
  $form['ajax_settings']['#access'] = FALSE;
}

/**
 * Implements hook_form_FORM_ID_alter().
 *
 * Configure admin/structure/webform/manage/WEBFORM_ID/handlers.
 */
function uw_cfg_common_form_webform_handler_form_alter(array &$form, FormStateInterface $form_state, string $form_id): void {
  // Add help text to 'SEND FROM (WEBSITE/DOMAIN)' in webform email handler.
  if ($form['#webform_handler_plugin_id'] === 'email') {
    $form['settings']['from']['#description'] = t('This must be an <strong>@uwaterloo.ca</strong> email address for sending to succeed. Please consider using the reply-to email option instead when emails are not limited to campus accounts.');

    // To email: remove 'Site email address' option under Other.
    array_shift($form['settings']['to']['to_mail']['to_mail']['#options']['Other']);

    // From email: remove 'Site email address' option under Other.
    array_shift($form['settings']['from']['from_mail']['from_mail']['#options']['Other']);

    // CC email: remove 'Site email address' option under Other.
    array_shift($form['settings']['to']['cc_mail']['cc_mail']['#options']['Other']);

    // CC email: Set 'Webform author email address' as default.
    $form['settings']['to']['cc_mail']['cc_mail']['#default_value'] = "[webform:author:mail]";

    // BCC email: remove 'Site email address' option under Other.
    array_shift($form['settings']['to']['bcc_mail']['bcc_mail']['#options']['Other']);

    // BCC email: Set 'Webform author email address' as default.
    $form['settings']['to']['bcc_mail']['bcc_mail']['#default_value'] = "[webform:author:mail]";

    // Reply-to email: remove 'Site email address' option under Other.
    array_shift($form['settings']['reply_to']['reply_to']['reply_to']['#options']['Other']);

    // Reply-to email: Set 'Webform author email address' as default.
    $form['settings']['reply_to']['reply_to']['reply_to']['#default_value'] = "[webform:author:mail]";
  }
}

/**
 * Implements hook_form_FORM_ID_alter().
 *
 * Configure admin/structure/webform/manage/WEBFORM_ID/settings/form.
 */
function uw_cfg_common_form_webform_settings_form_form_alter(array &$form, FormStateInterface $form_state, string $form_id): void {
  // Unset the source entity settings in webforms.
  $form['form_behaviors']['form_prepopulate_source_entity']['#access'] = FALSE;
  $form['form_behaviors']['form_prepopulate_source_entity_required']['#access'] = FALSE;
  $form['form_behaviors']['form_prepopulate_source_entity_type']['#access'] = FALSE;

  // Remove undesired features.
  $form['access_denied']['#access'] = FALSE;
  $form['custom_settings']['#access'] = FALSE;
  $form['form_behaviors']['form_autofocus']['#access'] = FALSE;
  $form['form_behaviors']['form_disable_back']['#access'] = FALSE;
  $form['form_behaviors']['form_novalidate']['#access'] = FALSE;
  $form['form_behaviors']['form_required']['#access'] = FALSE;
  $form['form_behaviors']['form_reset']['#access'] = FALSE;
  $form['form_behaviors']['form_submit_back']['#access'] = FALSE;
  $form['form_settings']['form_attributes']['#access'] = FALSE;
}

/**
 * Implements hook_form_FORM_ID_alter().
 *
 * Configure admin/structure/webform/manage/WEBFORM_ID/settings/submissions.
 */
function uw_cfg_common_form_webform_settings_submissions_form_alter(array &$form, FormStateInterface $form_state, string $form_id): void {
  // Remove undesired features.
  $form['access_denied']['#access'] = FALSE;
  $form['submission_behaviors']['form_convert_anonymous']['#access'] = FALSE;
  $form['submission_behaviors']['submission_log']['#access'] = FALSE;
  $form['submission_behaviors']['token_update']['#access'] = FALSE;
  $form['views_settings']['#access'] = FALSE;
}

/**
 * Implements hook_ENTITY_TYPE_create().
 */
function uw_cfg_common_webform_create(WebformInterface $webform) {
  // Submission purge settings. Set the default to purge drafts after 28 days.
  $webform->setSetting('purge', WebformSubmissionStorageInterface::PURGE_DRAFT);
  $webform->setSetting('purge_days', 28);

  // On admin/structure/webform/manage/FORM/settings/confirmation, default
  // "Confirmation type" to "inline".
  $webform->setSetting('confirmation_type', WebformInterface::CONFIRMATION_INLINE);

  // Set so that uw_cfg_common_webform_build_access_denied_alter() will run.
  // This value is tested for in Webform::preRenderWebformElement().
  // This value appears on
  // admin/structure/webform/manage/WEBFORM_ID/settings/form.
  $webform->setSetting('form_access_denied', WebformInterface::ACCESS_DENIED_PAGE);
}

/**
 * Implements hook_toolbar_alter().
 *
 * Remove the Manage link from the toolbar for authenticated users.
 */
function uw_cfg_common_toolbar_alter(&$items) {
  // Get the current user.
  $current_user = \Drupal::currentUser();

  // Remove the "manage" people for non-admin users.
  if (!$current_user->hasPermission('access manage toolbar item')) {
    // Remove "Manage" toolbar item.
    unset($items['administration']);
  }

  // Add "people" and "reports" links to "Workbench".
  // Note: 'dashboards' is renamed in
  // uw_dashboard_toolbar_alter().
  $links = [
    'entity.user.collection' => t('People'),
    'system.admin_reports' => t('Reports'),
  ];
  foreach ($links as $route => $title) {
    $url = Url::fromRoute($route);
    if ($url->access()) {
      $items['dashboards']['tray']['dashboards']['#items'][] = [
        '#type' => 'link',
        '#title' => $title,
        '#url' => $url,
      ];
    }
  }
}

/**
 * Implements hook_preprocess_HOOK().
 */
function uw_cfg_common_preprocess_node(&$variables) {

  // Get the current path.
  $path = explode('/', \Drupal::service('path.current')->getPath());

  // The paths to place the content moderation block on.  Made this
  // an array to future proof, if there are more pages later.
  $paths_for_content_moderation = ['latest'];

  // ISTWCMS-4493: adding class if section has full width.
  // If there is a sidebar on the node, check all sections for full width.
  if (isset($variables['sidebar'])) {

    // Get the layouts from the node.
    $layouts = $variables['node']->layout_builder__layout->getValue();

    // Step through each of the layouts and check for full width.
    foreach ($layouts as $layout) {

      // Get the layout settings from the section.
      $settings = $layout['section']->getLayoutSettings();

      // If the layout builder style is set to full width, then set
      // the classes variable for the node and exit the loop.
      if (isset($settings['layout_builder_styles_style']) &&
        $settings['layout_builder_styles_style'] == "uw_lbs_full_width"
      ) {

        // Add a class to the node for full width on a section.
        $variables['attributes']['class'][] = 'uw-section-has-full-width';

        // Break out of the loop to save computational time.
        break;
      }
    }
  }

  // Check if we are to add the content moderation place.
  if (in_array(end($path), $paths_for_content_moderation)) {

    // Add the content moderation block.
    $variables['uw_content_moderation_form'] = \Drupal::formBuilder()->getForm('Drupal\content_moderation\Form\EntityModerationForm', $variables['node']);
  }
  else {

    $block_manager = \Drupal::service('plugin.manager.block');

    $plugin_block = $block_manager->createInstance('uw_cbl_content_moderation', []);

    $access_result = $plugin_block->access(\Drupal::currentUser());

    // Return empty render array if user doesn't have access.
    // $access_result can be boolean or an AccessResult class.
    if (
      is_object($access_result) &&
      $access_result->isForbidden() ||
      is_bool($access_result)
      && $access_result
    ) {

      $variables['uw_content_moderation_form'] = $plugin_block->build();
    }
  }

  // Set the media flags for the node.
  $variables['media_flags'] = \Drupal::service('uw_cfg_common.uw_service')->uwGetMediaFlags($variables['node'], $variables['view_mode']);
}

/**
 * Get the layout builder styles as options.
 *
 * @param \Drupal\field\Entity\FieldStorageConfig $definition
 *   The field definition.
 * @param \Drupal\Core\Entity\ContentEntityInterface|null $entity
 *   The entity being created if applicable.
 * @param bool $cacheable
 *   Boolean indicating if the results are cacheable.
 *
 * @return array
 *   An array of possible key and value options.
 *
 * @see options_allowed_values()
 */
function _uw_cfg_common_get_layout_builder_styles(
  FieldStorageConfig $definition,
  ContentEntityInterface $entity = NULL,
  $cacheable
) {

  // Get all the styles for sections.
  $all_styles = _layout_builder_styles_retrieve_by_type('section');
  // Array of options.
  $options = [];

  // Step through each style and get the info.
  foreach ($all_styles as $style) {
    // Only load styles from the "default" group,
    // which contains the section widths.
    // Needed for when other section styles ship.
    if ($style->getGroup() == 'default') {
      $options[$style->id()] = $style->label();
    }
  }

  return $options;
}

/**
 * Implements hook_page_attachments().
 */
function uw_cfg_common_page_attachments(array &$page) {
  $page['#attached']['library'][] = 'uw_cfg_common/uw_mathjax';

  // Load uw_cfg_common module analytics configuration.
  $config = \Drupal::config('uw_cfg_common.google_settings');

  if ($config && $gso = $config->get('uw_cfg_common_google_site_ownership')) {
    $data = [
      '#tag' => 'meta',
      '#attributes' => [
        'name' => 'google-site-verification',
        'content' => $gso,
      ],
    ];

    // Attach tag to HEAD section.
    $page['#attached']['html_head'][] = [$data, 'uw-google-site-verification'];
  }

  $admin_page = \Drupal::service('uw_cfg_common.uw_analytics')->administrationPage();

  // Get the code from config and inject to the page.
  if (!$admin_page && !empty($config->get('uw_cfg_common_ga_account'))) {
    $code = Html::escape($config->get('uw_cfg_common_ga_account'));

    $firstLetter = strtolower(substr($code, 0, 1));
    $snippet = _uw_cfg_common_google_analytics_snippet($firstLetter, $code);

    if ($firstLetter === 'g') {
      $external_script_data = [
        '#tag' => 'script',
        '#attributes' => [
          'async' => TRUE,
          'src' => 'https://www.googletagmanager.com/gtag/js?id=' . $code,
        ],
      ];
      $page['#attached']['html_head'][] = [
        $external_script_data,
        'uw-google-tag-manager',
      ];
    }

    $analytics = [
      '#type' => 'html_tag',
      '#tag' => 'script',
      '#value' => $snippet,
    ];

    $page['#attached']['html_head'][] = [
      $analytics,
      'uw-google-analytics',
    ];
  }
}

/**
 * Returns Google Analytics snippet code.
 *
 * @param string $firstLetter
 *   First letter of the code in lowercase.
 * @param string $code
 *   Analytics code, could be G-9999999999 or UA-99999999-99.
 * @param int $code_id
 *   Code order number.
 *
 * @return string
 *   Code snippet to be injected to html page in script tag.
 */
function _uw_cfg_common_google_analytics_snippet($firstLetter, $code, $code_id = 0): string {
  $snippet['u'] = "(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','https://www.google-analytics.com/analytics.js','ga');

ga('create', '@gtag', 'auto', {'name': 'tracker@code_id'});
ga('tracker@code_id.send', 'pageview');";

  $snippet['g'] = "window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', '@gtag');";

  $output = str_replace('@gtag', $code, $snippet[$firstLetter]);

  if ($firstLetter === 'u') {
    $output = str_replace('@code_id', $code_id, $output);
  }

  return $output;
}

/**
 * Implements hook_form_FORM_ID_alter().
 *
 * Set the default of preview mode disabled.
 */
function uw_cfg_common_form_node_type_add_form_alter(&$form, FormStateInterface $form_state, $form_id) {
  $form['submission']['preview_mode']['#default_value'] = 0;
}

/**
 * Implements hook_form_FORM_ID_alter().
 *
 * Node edit form: node/NID/edit.
 *
 * Prevent certain changes to the home page.
 */
function uw_cfg_common_form_node_uw_ct_web_page_edit_form_alter(array &$form, FormStateInterface $form_state, string $form_id): void {
  // No changes for those with access.
  if (\Drupal::currentUser()->hasPermission('bypass home page protection')) {
    return;
  }

  // Do not allow the home page to be parent of any item.
  unset($form['menu']['link']['menu_parent']['#options']['main:uw_base_profile.front_page']);

  // Early return if not editing home page.
  $nid = (int) \Drupal::routeMatch()->getRawParameter('node');
  if (!UWService::nodeIsHomePage($nid)) {
    return;
  }

  // Remove access to certain controls.
  $form['path']['#access'] = FALSE;
  $form['promote']['#access'] = FALSE;
  $form['sticky']['#access'] = FALSE;
  // For 'menu', setting #access did not work for non-admins. So, also hide the
  // sub-components and make it a container so that nothing appears on the page.
  $form['menu']['#access'] = FALSE;
  $form['menu']['#type'] = 'container';
  $form['menu']['enabled']['#access'] = FALSE;
  $form['menu']['link']['#access'] = FALSE;

  // Hide delete link if no access. This should happen by itself, but does not.
  if (!$form['actions']['delete']['#url']->access()) {
    $form['actions']['delete']['#access'] = FALSE;
  }
}

/**
 * Implements hook_form_FORM_ID_alter().
 *
 * Menu edit form: admin/structure/menu/manage/{menu}.
 */
function uw_cfg_common_form_menu_edit_form_alter(array &$form, FormStateInterface $form_state, string $form_id): void {
  // Hide links to menu edit and delete for non-admin.
  if (!\Drupal::currentUser()->hasPermission('administer menu')) {
    foreach (Element::children($form['links']['links']) as $element_key) {
      $form['links']['links'][$element_key]['operations']['#access'] = FALSE;
    }
  }

  // Prevent certain changes to the home page.
  //
  // No changes for those with access.
  if (\Drupal::currentUser()->hasPermission('bypass home page protection')) {
    return;
  }

  // Return early if not editing "Main navigation" menu.
  if (!isset($form['links']['links']['menu_plugin_id:uw_base_profile.front_page'])) {
    return;
  }

  // Remove access to home page controls.
  $form['links']['links']['menu_plugin_id:uw_base_profile.front_page']['enabled']['#access'] = FALSE;
  $form['links']['links']['menu_plugin_id:uw_base_profile.front_page']['operations']['#access'] = FALSE;
  $form['links']['links']['menu_plugin_id:uw_base_profile.front_page']['weight']['#access'] = FALSE;
  // Make home page not draggable.
  $key = array_search('draggable', $form['links']['links']['menu_plugin_id:uw_base_profile.front_page']['#attributes']['class'], TRUE);
  unset($form['links']['links']['menu_plugin_id:uw_base_profile.front_page']['#attributes']['class'][$key]);
}

/**
 * Implements hook_form_FORM_ID_alter().
 *
 * Menu link edit form: admin/structure/menu/item/ID/edit.
 *
 * Do not allow the home page to be parent of any item.
 */
function uw_cfg_common_form_menu_link_content_menu_link_content_form_alter(array &$form, FormStateInterface $form_state, string $form_id): void {
  // No changes for those with access.
  if (\Drupal::currentUser()->hasPermission('bypass home page protection')) {
    return;
  }

  // Do not allow the home page to be parent of any item.
  unset($form['menu_parent']['#options']['main:uw_base_profile.front_page']);
}

/**
 * Implements hook_preprocess_HOOK().
 */
function uw_cfg_common_preprocess_responsive_image(&$variables) {

  // Get the current path.
  $current_path = \Drupal::service('path.current')->getPath();

  // Explode the current path so we can check where we are.
  $current_path_parts = explode('/', $current_path);

  // Get the media library parameters, we will use this
  // if we are on a media library page/modal.
  $media_lib_parameters = \Drupal::request()->query->get('media_library_opener_parameters');

  // If the current path has a node or media library params,
  // we need to alter the image styles.
  if (
    $current_path_parts[1] == 'node' ||
    $media_lib_parameters
  ) {

    // If we are on a contact image, remove all styles
    // but those for portraits.
    if (
      isset($media_lib_parameters['bundle']) &&
      $media_lib_parameters['bundle'] == 'uw_ct_contact' ||
      end($current_path_parts) == 'uw_ct_contact'
    ) {

      // Get the styles used for portraits.
      $uw_styles = \Drupal::service('uw_cfg_common.uw_service')->getCropImageStyles('portrait');
    }
    else {

      // Get the styles used for responsive.
      $uw_styles = \Drupal::service('uw_cfg_common.uw_service')->getCropImageStyles('responsive');
    }

    // Step through each of the sources and see if we are.
    // to use it.
    foreach ($variables['sources'] as $index => $source) {

      // Get the srcset.
      $srcset = $source->storage()['srcset']->render();

      // Break into parts so that we can check for image styles.
      $srcset_parts = explode('/', $srcset);

      // Step through each of the srcset parts.
      foreach ($srcset_parts as $sp) {

        // Ensure that we are on an image style.
        if (strpos($sp, 'uw_is') !== FALSE) {

          // If not in the list of image styles, remove
          // it from the sources.
          if (!in_array($sp, $uw_styles)) {
            unset($variables['sources'][$index]);
          }
        }
      }
    }
  }
}

/**
 * Implements hook_form_FORM_ID_alter().
 *
 * Disable user url alias.
 */
function uw_cfg_common_form_user_register_form_alter(array &$form, FormStateInterface $form_state, string $form_id): void {

  // Disable the user url alias, we do not want to remove it
  // or visual hide it as we still want it to be submitted
  // and saved.
  $form['path']['#access'] = FALSE;
}

/**
 * Implements hook_form_alter().
 *
 * Hide/disable metatag information on our nodes.
 */
function uw_cfg_common_form_alter(array &$form, FormStateInterface $form_state, string $form_id): void {

  // Hide hide_branding field in simple sitemap settings.
  if ($form_id == 'simple_sitemap_settings_form' && isset($form['settings']['hide_branding'])) {
    unset($form['settings']['hide_branding']);
  }

  // Only blog, event and news item has 'Image' option.
  $hero_image_form_ids = [
    'node_uw_ct_blog_form',
    'node_uw_ct_blog_edit_form',
    'node_uw_ct_event_form',
    'node_uw_ct_event_edit_form',
    'node_uw_ct_news_item_form',
    'node_uw_ct_news_item_edit_form',
  ];
  if (in_array($form_id, $hero_image_form_ids)) {
    // Set required when hero image field shows.
    if (isset($form['field_uw_hero_image']['widget'])) {
      $form['field_uw_hero_image']['widget']['#required'] = TRUE;
    }
  }

  // Prevent redirects to pilots or staging URLs.
  if ($form_id == 'redirect_redirect_form' || $form_id == 'redirect_redirect_edit_form') {
    $form['#validate'][] = '_uw_cfg_common_redirect_destination_validation';
  }

  // ISTWCMS-5846: if we are on a UW content type which is
  // the notation of node_uw_ct_<node_type>_form of the
  // form_id, then we need to add the status to the
  // summary field.
  if (preg_match("/node_uw_ct_.*form/", $form_id)) {

    // Only add states if we have the blank summary field.
    if (isset($form['field_uw_blank_summary'])) {

      // Since we are unable to simply get the node type in format that
      // we need to use it (i.e. event, news, blog, etc.), we need to
      // take the form_id and manipulate it to get us the node type.
      // First is the remove the node_uw_ct_, then the _edit and
      // finally the _form.
      $node_type = str_replace('node_uw_ct_', '', $form_id);
      $node_type = str_replace('_edit', '', $node_type);
      $node_type = str_replace('_form', '', $node_type);
      $node_type = str_replace('_quick_node_clone', '', $node_type);

      // We need to add this because news has the name news_item in its
      // content type, so we need to remove that to just get news
      // to be used in the field name.
      $node_type = str_replace('_item', '', $node_type);

      // If the node type is an opportunity, we have to give
      // the specific field name.
      if ($node_type == 'opportunity') {
        $field_name = 'field_uw_opportunity_position';
      }
      // Now since all the rest of the field names
      // are the same, field_uw_<node_type>_summary,
      // we can get the proper field name.
      else {
        $field_name = 'field_uw_' . $node_type . '_summary';
      }

      // Set the states of the summary, required and visible.
      $form[$field_name]['widget'][0]['#states'] = [
        'visible' => [
          ':input[name="field_uw_blank_summary[value]"]' => ['checked' => FALSE],
        ],
      ];

      // Add custom validation for blank summaries.
      $form['#validate'][] = '_uw_cfg_common_blank_summaries_validation';
    }
  }

  // ISTWCMS-4648: removing revisions from layout builder page.
  if (\Drupal::routeMatch()->getRouteName() == 'layout_builder.overrides.node.view') {
    $form['revision']['#access'] = FALSE;
    $form['advanced']['#access'] = FALSE;
  }

  // Ensure that we are on a UW content type node.
  if (preg_match('/^node_uw_ct_.*_form$/', $form_id)) {

    // Add custom validation for alias.
    $form['#validate'][] = '_uw_cfg_common_alias_validate';

    // Ensure that the node has metatag information.
    if (isset($form['field_uw_meta_tags'])) {

      // Hide elements under advanced settings.
      $form['field_uw_meta_tags']['widget'][0]['advanced']['canonical_url']['#access'] = FALSE;
      $form['field_uw_meta_tags']['widget'][0]['advanced']['shortlink_url']['#access'] = FALSE;

      // Hide elements under open graph.
      $form['field_uw_meta_tags']['widget'][0]['open_graph']['og_type']['#access'] = FALSE;
      $form['field_uw_meta_tags']['widget'][0]['open_graph']['og_url']['#access'] = FALSE;
      $form['field_uw_meta_tags']['widget'][0]['open_graph']['og_updated_time']['#access'] = FALSE;
      $form['field_uw_meta_tags']['widget'][0]['open_graph']['og_locale']['#access'] = FALSE;

      // Hide elements under Twitter settings.
      $form['field_uw_meta_tags']['widget'][0]['twitter_cards']['twitter_cards_page_url']['#access'] = FALSE;
    }
  }

  // If there is a type of media field, it means that we are
  // on a node add/edit page, so add the states for the
  // actual media types in the hero section.
  if (isset($form['field_uw_type_of_media'])) {

    // Get the node type from the form id.
    $node_type = str_replace('node_uw_ct_', '', $form_id);
    $node_type = str_replace('_edit', '', $node_type);
    $node_type = str_replace('_form', '', $node_type);
    $node_type = str_replace('_item', '', $node_type);

    // Content types to remove the hero image from media.
    $remove_hero_image_nodes = [
      'web_page',
      'catalog',
      'contact',
      'opportunity',
      'profile',
      'project',
      'service',
    ];

    // If the node type can not have hero image, remove the option.
    if (in_array($node_type, $remove_hero_image_nodes)) {
      unset($form['field_uw_type_of_media']['widget']['#options']['image']);
    }

    // Set the states for the hero image.
    $form['field_uw_hero_image']['#states'] = [
      'visible' => [
        [
          'select[name="field_uw_type_of_media"]' => [
            ['value' => 'image'],
          ],
        ],
      ],
    ];

    // Set the states for the banner.
    $form['group_banner']['#states'] = [
      'visible' => [
        [
          'select[name="field_uw_type_of_media"]' => [
            ['value' => 'banner'],
          ],
        ],
      ],
    ];

    // Set the states for the banner settings.
    $form['field_uw_text_overlay_style']['#states'] = [
      'visible' => [
        [
          'select[name="field_uw_type_of_media"]' => [
            ['value' => 'banner'],
          ],
        ],
      ],
    ];

    // Set the states for the banner settings.
    $form['field_uw_media_width']['#states'] = [
      'visible' => [
        [
          'select[name="field_uw_type_of_media"]' => [
            ['value' => 'banner'],
          ],
          'select[name="field_uw_text_overlay_style"]' => [
            ['value' => 'full-width'],
          ],
        ],
      ],
    ];

    // Set the states for the banner settings, if it exists.
    if (isset($form['field_uw_page_title_big_text'])) {

      // ISTWCMS-6313.
      // The next two are for existing content types that do not have
      // the default values and options set for page title big text.
      if (empty($form['field_uw_page_title_big_text']['widget']['#default_value'])) {
        $form['field_uw_page_title_big_text']['widget']['#default_value'][0] = 0;
      }
      if (isset($form['field_uw_page_title_big_text']['widget']['#options']['_none'])) {
        unset($form['field_uw_page_title_big_text']['widget']['#options']['_none']);
      }

      // The form state for page title as big text.
      $form['field_uw_page_title_big_text']['#states'] = [
        'visible' => [
          [
            'select[name="field_uw_type_of_media"]' => [
              ['value' => 'banner'],
            ],
            'select[name="field_uw_text_overlay_style"]' => [
              ['!value' => 'split'],
            ],
          ],
        ],
      ];
    }

    // ISTWCMS-6313: adding the JS on the node add/edit page,
    // that will check for enable/disable big text in banner image.
    $form['#attached']['library'][] = 'uw_cfg_common/uw_cfg_common';

    // Add our custom validation for banners.
    $form['#validate'][] = '_uw_cfg_common_banner_validate';
  }

  // If we are on the media upload form, we want to restrict
  // what crops are available.
  if ($form_id == 'media_library_add_form_upload') {

    // If the crop widget is on the form, unset certain crops.
    if (isset($form['media'][0]['fields']['field_media_image']['widget'][0]['#crop_list'])) {

      // Get the parameters from the request, this was the only
      // way to get out what the bundle was.  Since this is a new
      // form call from media_library, we could not use get current
      // path or uri, since it would only return /media_library.
      // The media library parameters has everything listed for the
      // node and the node types.
      $media_lib_parameters = \Drupal::request()->query->get('media_library_opener_parameters');

      // If there are media lib parameters, process them.
      if ($media_lib_parameters) {

        // If there is a bundle on the parameters, continue
        // to process.
        if (isset($media_lib_parameters['bundle'])) {

          // If this is a contact, remove all the responsive crops.
          // If anything else, remove the portrait crop.
          if ($media_lib_parameters['bundle'] == 'uw_ct_contact') {
            foreach ($form['media'][0]['fields']['field_media_image']['widget'][0]['#crop_list'] as $index => $crop) {
              if ($crop !== 'uw_crop_portrait') {
                unset($form['media'][0]['fields']['field_media_image']['widget'][0]['#crop_list'][$index]);
              }
            }
          }
          else {
            foreach ($form['media'][0]['fields']['field_media_image']['widget'][0]['#crop_list'] as $index => $crop) {
              if ($crop == 'uw_crop_portrait') {
                unset($form['media'][0]['fields']['field_media_image']['widget'][0]['#crop_list'][$index]);
              }
            }
          }
        }
      }
    }
  }
}

/**
 * Validates submission values for alias on nodes.
 */
function _uw_cfg_common_alias_validate(array &$form, FormStateInterface $form_state): void {

  // Get the values from the form state.
  $values = $form_state->getValues();

  if (isset($values['path'][0]['alias'])) {
    // List of urls which should not be an alias.
    $urls = [
      'blog',
      'events',
      'news',
      'projects',
      'profile',
      'profiles',
      'contacts',
      'service',
      'opportunities',
      'user',
      'users',
    ];

    // Get the alias from the form state values.
    $alias = $values['path'][0]['alias'];
    $orig_alias = $alias;

    // Trim any surrounding slashes from the alias to
    // ensure that we are getting exact matches for the
    // predefined alias from above. Some users will add
    // slashes before and after the alias, so just
    // easier to check without slashes.
    $alias = trim($alias, '/');

    // Check if the alias exists if yes, sets error.
    // We are checking three cases, the first is if
    // the alias is in the predefined list. The second
    // is if the alias is just /, or just a series of slashes,
    // we have to check here because we removed all the
    // slashes with the alias variable. The last is to check
    // if the alias has any form of strictly admin, so /admin/,
    // admin, admin/, and admin/something are not allowed,
    // but something like admin-meeting would be.
    if (
      in_array($alias, $urls) ||
      preg_match('/^\/+$/', $orig_alias) ||
      preg_match('/^admin(?:\/.*)?$/', $alias)
    ) {

      // Set an error message if alias exists.
      $form_state->setError($form['path']['widget'][0]['alias'], t('The alias "@url" is a reserved path that cannot be used.', ['@url' => $orig_alias]));
    }
  }
}

/**
 * Validates submission values for redirects.
 */
function _uw_cfg_common_redirect_destination_validation(array &$form, FormStateInterface $form_state) {

  // Get the values from the form state.
  $values = $form_state->getValues();

  // Prevent redirects to pilots or staging URLs.
  if (isset($values['redirect_redirect'][0]['uri'])) {
    $uri = $values['redirect_redirect'][0]['uri'];
    if (preg_match("/^https?:\/\/(?:pilots|staging)\.uwaterloo\.ca\/?.*/", $uri)) {
      $form_state->setError($form['redirect_redirect']['widget'][0], t('Redirecting to a staging/pilots site is not allowed as these are temporary sites not intended for public use. For redirects within a site, please use an internal path.'));
    }
  }
}

/**
 * Validates submission values for banners on nodes.
 */
function _uw_cfg_common_blank_summaries_validation(array &$form, FormStateInterface $form_state) {

  // Get the values from the form state.
  $values = $form_state->getValues();

  // Get the node type.
  $node_type = str_replace('node_uw_ct_', '', $form['#form_id']);
  $node_type = str_replace('_edit', '', $node_type);
  $node_type = str_replace('_form', '', $node_type);
  $node_type = str_replace('_item', '', $node_type);
  $node_type = str_replace('_quick_node_clone', '', $node_type);

  // If the node type is an opportunity, we have to give
  // the specific field name, if not just use the field name.
  if ($node_type == 'opportunity') {
    $field_name = 'field_uw_opportunity_position';
  }
  else {
    $field_name = 'field_uw_' . $node_type . '_summary';
  }

  // If the blank summary is not checked and the summary
  // is null, then set an error.
  if (!$values['field_uw_blank_summary']['value']) {
    if ($values[$field_name][0]['value'] == '') {
      $form_state->setError($form[$field_name]['widget'][0], t('Summary/position field is required.'));
    }
  }
}

/**
 * Validates submission values for banners on nodes.
 */
function _uw_cfg_common_banner_validate(array &$form, FormStateInterface $form_state) {

  // Get the values of the form state.
  $values = $form_state->getValues();

  // If there is a type of media set and it is banners,
  // then perform the validation.
  if (
    isset($values['field_uw_type_of_media']) &&
    !empty($values['field_uw_type_of_media']) &&
    $values['field_uw_type_of_media'][0]['value'] == 'banner'
  ) {

    // Since the add more button is in the values, and there is
    // a mix of integers and strings as keys, we need to count
    // the array keys and if it is less than or equal to one
    // means that the user has not entered a banner at all.
    if (count(array_keys($values['field_uw_banner'])) <= 1) {

      $form_state->setError($form['field_uw_banner'], t('You must add at least one banner.'));
    }
    // At least one banner, now check that there is an
    // image in each banner.
    else {

      // Step through all the values of banners and check for an image.
      foreach ($values['field_uw_banner'] as $key => $value) {

        // Ensure that the key is an integer, since there is an add_more
        // in the values.  We also need to check if there is "top" in the
        // array, as this is what paragraphs uses for closed items.  These
        // closed items have already been validated so if there is a closed
        // item we can just ignore it, only open items, meaning it has values
        // in the fields needs to be validated.
        if (is_int($key) && !$value['top']) {

          // If there is no selection on the media, then there is no image,
          // so set the error.
          // @todo fix so that inline errors show.
          if (!isset($value['subform']['field_uw_ban_image']['selection'])) {

            // Set the error that an image is missing.
            $form_state->setError($form['field_uw_banner']['widget'][$key]['subform']['field_uw_ban_image'], t('Banner image field is required.'));
          }
        }
      }
    }
  }
}

/**
 * Implements hook_field_widget_WIDGET_TYPE_form_alter().
 */
function uw_cfg_common_field_widget_entity_reference_paragraphs_form_alter(&$element, &$form_state, $context) {

  // If this is a banner image, add the required manually.
  if ($element['#paragraph_type'] == 'uw_para_image_banner') {

    // If this is not a block, add the class for required
    // on the image, we need to do this so that when using
    // banners with media, the required does not work properly
    // when banners are not selected.
    // If it is a block, just add the required to the element.
    if (!isset($context['form']['#block'])) {

      // If there already is classes set, then add to the array.
      // If no classes yet, add as an array.
      if (isset($element['subform']['field_uw_ban_image']['widget']['#attributes']['class'])) {
        $element['subform']['field_uw_ban_image']['widget']['#attributes']['class'][] = 'form-required';
      }
      else {
        $element['subform']['field_uw_ban_image']['widget']['#attributes']['class'] = ['form-required'];
      }
    }
    else {
      $element['subform']['field_uw_ban_image']['widget']['#required'] = TRUE;
    }
  }
}

/**
 * Implements hook_field_widget_WIDGET_TYPE_form_alter().
 */
function uw_cfg_common_field_widget_media_library_widget_form_alter(array &$element, FormStateInterface $form_state, array $context): void {
  /** @var \Drupal\Core\Routing\RouteMatchInterface $route_match */
  $route_match = \Drupal::routeMatch();

  if ($route_match->getRouteName() === 'layout_builder.add_block') {
    /** @var \Drupal\media_library\MediaLibraryState $state */
    $state = $element['open_button']['#media_library_state'];
    $openerParameters = $state->getOpenerParameters();
    $openerParameters['plugin_id'] = $route_match->getParameters()->get('plugin_id');
    $new_state = MediaLibraryState::create($state->getOpenerId(), $state->getAllowedTypeIds(), $state->getSelectedTypeId(), $state->getAvailableSlots(), $openerParameters);
    $element['open_button']['#media_library_state'] = $new_state;
  }
}

/**
 * Implements hook_ENTITY_TYPE_create_access().
 */
function uw_cfg_common_block_content_create_access(AccountInterface $account, array $context, string $entity_bundle): AccessResult {
  $route_name = \Drupal::routeMatch()->getRouteName();

  if ($route_name === 'media_library.ui') {
    /** @var \Drupal\media_library\MediaLibraryState $state */
    $state = MediaLibraryState::fromRequest(\Drupal::request());
    $openerParameters = $state->getOpenerParameters();

    // If the plugin ID exists within the opener parameters, we know
    // the media library is being used on the layout builder form.
    if (isset($openerParameters['plugin_id']) && substr($openerParameters['plugin_id'], 0, 12) === 'inline_block') {

      if ($account->hasPermission('create and edit custom blocks')) {
        return AccessResult::allowed();
      }
    }
  }

  // No opinion.
  return AccessResult::neutral();
}

/**
 * Implements hook_ENTITY_TYPE_access() for webform entities.
 */
function uw_cfg_common_webform_access(WebformInterface $webform, string $operation, AccountInterface $account): AccessResult {
  // Always allow access for Form editor so they can see the forms they create.
  if ($account->hasPermission('create webform')) {
    return AccessResult::neutral();
  }

  // Allow access to submissions for Form results access.
  if ($account->hasPermission('view any webform submission') && $operation === 'submission_view_any') {
    return AccessResult::neutral();
  }

  switch ($webform->getThirdPartySetting('uw_cfg_common', 'access_control_method')) {
    case 'anon':
      if (!$account->isAnonymous()) {
        return AccessResult::forbidden();
      }
      break;

    case 'auth':
      if (!$account->isAuthenticated()) {
        return AccessResult::forbidden();
      }
      break;

    case 'group':
      // Must be authenticated for group auth.
      if (!$account->isAuthenticated()) {
        return AccessResult::forbidden();
      }
      // Access control by Active Directory group.
      // Convert all groups to lowercase for case-insensitive matching.
      $user_ad_groups = uw_cfg_common_get_user_ad_groups() ?: [];
      $user_ad_groups = array_map('mb_strtolower', $user_ad_groups);
      // Required group. If at least one is provided, the user must be in it.
      $ad_require_groups = $webform->getThirdPartySetting('uw_cfg_common', 'ad_require_groups');
      $ad_require_groups = array_map('mb_strtolower', $ad_require_groups);
      if ($ad_require_groups && !array_intersect($ad_require_groups, $user_ad_groups)) {
        return AccessResult::forbidden();
      }
      // Deny group. If at least one is provided, the user must not be in it.
      $ad_deny_groups = $webform->getThirdPartySetting('uw_cfg_common', 'ad_deny_groups');
      $ad_deny_groups = array_map('mb_strtolower', $ad_deny_groups);
      if ($ad_deny_groups && array_intersect($ad_deny_groups, $user_ad_groups)) {
        return AccessResult::forbidden();
      }
      break;

    case 'user':
      // Must be authenticated for group auth.
      if (!$account->isAuthenticated()) {
        return AccessResult::forbidden();
      }

      // Get all users when selecting 'Users specified below' under
      // admin/structure/webform/manage/WEBFORM_ID/access.
      $create_user_ids = $webform->getAccessRules()['create']['users'];

      // If the logged in user is not a specified user, get access denied.
      if (!in_array($account->id(), $create_user_ids)) {
        return AccessResult::forbidden();
      }
      break;
  }

  return AccessResult::neutral();
}

/**
 * Implements hook_views_plugins_field_alter().
 */
function uw_cfg_common_views_plugins_field_alter(array &$plugins): void {
  // Replace Drupal\views\Plugin\views\field\Dropbutton with UW version.
  $plugins['dropbutton']['class'] = 'Drupal\uw_cfg_common\Plugin\views\field\UWDropbutton';
}

/**
 * Split text on EOL, trim, remove blanks and duplicates, and return as array.
 *
 * @param string|null $data
 *   The data to act on.
 *
 * @return string[]
 *   The data as an array.
 */
function uw_cfg_common_array_split_clean(?string $data): array {
  $data = preg_split('/[\r\n]+/', $data);
  $data = array_map('trim', $data);
  $data = array_filter($data);
  $data = array_unique($data);
  return $data;
}

/**
 * Return the Active Directory groups that the current user is part of.
 *
 * @return null|string[]
 *   An array of Active Directory group names or NULL if unable to determine.
 */
function uw_cfg_common_get_user_ad_groups(): ?array {
  $attributes = \Drupal::service('simplesamlphp_auth.manager')->getAttributes();
  return $attributes['http://schemas.xmlsoap.org/claims/Group'] ?? NULL;
}

/**
 * Implements hook_preprocess_HOOK().
 *
 * Custom access denied messages with login/logout links.
 */
function uw_cfg_common_preprocess_webform_access_denied(array &$variables): void {
  $webform = $variables['webform'];

  $message = NULL;
  switch ($webform->getThirdPartySetting('uw_cfg_common', 'access_control_method')) {
    case 'auth':
    case 'group':
    case 'user':
      // If authenticated access and anonymous user, login.
      if (\Drupal::currentUser()->isAnonymous()) {
        $route = 'user.login';
        $message = 'You must <a href="@url">login to view this form</a>.';
      }
      break;

    case 'anon':
      // If anonymous access and authenticated user, logout.
      if (\Drupal::currentUser()->isAuthenticated()) {
        $route = 'user.logout';
        $message = 'This form must be completed anonymously. You must <a href="@url">logout to view this form</a>.';
      }
      break;
  }

  // Set a custom message only if a message has been chosen above.
  if ($message) {
    $options = ['query' => \Drupal::destination()->getAsArray()];
    $url = Url::fromRoute($route, [], $options);
    // phpcs:ignore Drupal.Semantics.FunctionT.NotLiteralString
    $message = '<p>' . t($message, ['@url' => $url->toString()]) . '</p>';

    $variables['message']['#markup'] = $message;
  }
}

/**
 * Implements hook_entity_type_alter().
 */
function uw_cfg_common_entity_type_alter(array &$entity_types) {

  // Add validation constraint to the node entity.
  $entity_types['node']->addConstraint('UwMedia');
}

/**
 * Implements hook_simplesamlphp_auth_user_attributes().
 *
 * Add role expiry records for all roles populated automatically from
 * simpleSAMLphp attributes by simplesamlphp_auth module.
 */
function uw_cfg_common_simplesamlphp_auth_user_attributes(UserInterface $account, array $attributes): bool {
  // Get the SimplesamlphpDrupalAuth service.
  $drupalauth = \Drupal::service('simplesamlphp_auth.drupalauth');

  $roles_to_expire = $drupalauth->getMatchingRoles();

  // Get the role_expire API.
  $role_expire = \Drupal::service('role_expire.api');

  $expiry_timestamp = time() + 24 * 60 * 60;

  foreach ($roles_to_expire as $rid) {
    // Add role_expiry for the account.
    $role_expire->writeRecord($account->id(), $rid, $expiry_timestamp);
  }

  // This implementation does not alter $account, so return is always FALSE.
  return FALSE;
}

/**
 * Return the most recent completed Webform submission for a user on a form.
 *
 * @param string $webform_id
 *   The entity ID of the Webform.
 * @param int $uid
 *   The user ID of the user.
 *
 * @return int|null
 *   The submission ID or NULL if there are none.
 */
function uw_cfg_common_get_most_recent_webform_submission(string $webform_id, int $uid): ?int {
  // Load submission IDs from webform_submission storage.
  $webform_submission_storage = \Drupal::entityTypeManager()->getStorage('webform_submission');
  $query = $webform_submission_storage->getQuery();
  $entity_ids = $query
    ->condition('webform_id', $webform_id)
    ->condition('uid', $uid)
    ->condition('in_draft', 0)
    ->sort('created', 'ASC')
    ->accessCheck(FALSE)
    ->execute();

  // If there is at least one, return the last as integer, otherwise NULL.
  return $entity_ids ? (int) end($entity_ids) : NULL;
}

/**
 * Implements hook_fillpdf_populate_pdf_context_alter().
 */
function uw_cfg_common_fillpdf_populate_pdf_context_alter(array &$context): void {
  // If there are no webform_submission entities but there is at least one
  // webform entity, add the most recent submission for each webform.
  // Only do this for authenticated users.
  $current_uid = (int) \Drupal::currentUser()->id();
  if ($current_uid && empty($context['entity_ids']['webform_submission']) && !empty($context['entity_ids']['webform'])) {
    foreach ($context['entity_ids']['webform'] as $webform_id) {
      $entity_id = uw_cfg_common_get_most_recent_webform_submission($webform_id, $current_uid);
      if ($entity_id) {
        $context['entity_ids']['webform_submission'][$entity_id] = $entity_id;
      }
    }
  }
}

/**
 * Implements hook_ENTITY_TYPE_view().
 *
 * Provide download links to associated PDFs on Layout Builder pages containing
 * a Webform block. Only the first such block is used.
 */
function uw_cfg_common_node_view(array &$build, EntityInterface $entity, EntityViewDisplayInterface $display, string $view_mode): void {
  // Do nothing unless view_mode full.
  if ($view_mode !== 'full') {
    return;
  }

  // Do nothing unless user is authenticated.
  $current_user = \Drupal::currentUser();
  if (!$current_user->id()) {
    return;
  }

  // Find the first Layout Builder block that contains a Webform.
  $webform = NULL;
  foreach (Element::children($build['_layout_builder']) as $child) {
    foreach (Element::children($build['_layout_builder'][$child]) as $grand_child) {
      foreach (Element::children($build['_layout_builder'][$child][$grand_child]) as $great_grand_child) {
        $element = $build['_layout_builder'][$child][$grand_child][$great_grand_child];
        if (isset($element['content']['#webform']) && $element['content']['#webform'] instanceof WebformInterface) {
          $webform = $element['content']['#webform'];
          break 3;
        }
      }
    }
  }

  // Do nothing unless:
  // Node is Webform.
  // Webform "Show the notification about previous submissions" is enabled.
  if (!$webform || !$webform->getSetting('form_previous_submissions')) {
    return;
  }

  // Do nothing if fillpdf_form entity type does not exist.
  $fillpdf_form_exists = \Drupal::entityTypeManager()->hasDefinition('fillpdf_form');
  if (!$fillpdf_form_exists) {
    return;
  }

  // Load the FillPDF form for this Webform.
  $fillpdf_form_storage = \Drupal::entityTypeManager()->getStorage('fillpdf_form');
  $query = $fillpdf_form_storage->getQuery();
  $query->condition('default_entity_type', 'webform');
  $query->condition('default_entity_id', $webform->id());
  $fillpdf_forms = $query->execute();
  $fillpdf_forms = $fillpdf_form_storage->loadMultiple($fillpdf_forms);

  // Do nothing unless there are PDFs associated with this Webform.
  if (!$fillpdf_forms) {
    return;
  }

  // Do nothing unless there is a Webform submission by this user.
  // Find the most recent submission to this Webform by the current user.
  $webform_submission_entity_id = uw_cfg_common_get_most_recent_webform_submission($webform->id(), $current_user->id());
  if (!$webform_submission_entity_id) {
    return;
  }

  // For each FillPDF form, check access and generate a link with filename.
  $pdf_links = [];

  $fillpdf_link_manipulator = \Drupal::service('fillpdf.link_manipulator');
  $fillpdf_access_helper = \Drupal::service('fillpdf.access_helper');
  $handle_pdf_controller = HandlePdfController::create(\Drupal::getContainer());

  foreach ($fillpdf_forms as $fid => $fillpdf_form) {
    $parameters = ['fid' => $fid];
    $pdf_link = $fillpdf_link_manipulator->generateLink($parameters);

    $context = $fillpdf_link_manipulator->parseLink($pdf_link);

    $has_generate_pdf_access = $fillpdf_access_helper->canGeneratePdfFromContext($context, $current_user);

    if ($has_generate_pdf_access) {
      $handle_pdf_controller->alterContext($context);
      $filename = $handle_pdf_controller->getFilename($context);
      $pdf_links[] = Link::fromTextAndUrl($filename, $pdf_link);
    }
  }

  // Do nothing if no links to display.
  if (!$pdf_links) {
    return;
  }

  // Create status message.
  $message = [
    '#theme' => 'item_list',
    '#items' => $pdf_links,
    '#prefix' => t('Download PDF version of your form:'),
  ];
  $message = \Drupal::service('renderer')->render($message);
  \Drupal::messenger()->addStatus($message);
}

/**
 * Implements hook_form_FORM_ID_alter().
 *
 * Configure admin/structure/fillpdf/FID.
 */
function uw_cfg_common_form_fillpdf_form_edit_form_alter(array &$form, FormStateInterface $form_state, string $form_id): void {
  // We only use FillPDF with Webform, so hide the entity type selector and
  // change the title of the entity selector.
  $form['default_entity_type']['#access'] = FALSE;
  $form['default_entity_id']['#title'] = t('Default Webform');
}

/**
 * Implements hook_fillpdf_form_form_pre_form_build_alter().
 */
function uw_cfg_common_fillpdf_form_form_pre_form_build_alter(FillPdfFormForm $fillpdf_form_form): void {
  // We only use FillPDF with Webform, so set the default entity type for any
  // FillPDF form that does not have one to 'webform'.
  $fillpdf_form = $fillpdf_form_form->getEntity();
  $default_entity_type = $fillpdf_form->getDefaultEntityType();
  if (!$default_entity_type) {
    $fillpdf_form->set('default_entity_type', 'webform')->save();
  }
}

/**
 * Implements template_preprocess_html().
 */
function uw_cfg_common_preprocess_html(&$variables): void {
  // Setting the meta tag for web forms getting indexed
  // if not used any content.
  if ($variables['root_path'] === 'webform') {
    $noindex = [
      '#tag' => 'meta',
      '#attributes' => [
        'name' => 'robots',
        'content' => 'noindex',
      ],
    ];
    $variables['page']['#attached']['html_head'][] = [$noindex, 'noindex'];
  }
}

/**
 * Set dynamic allowed values for the type of media field.
 *
 * @param \Drupal\field\Entity\FieldStorageConfig $definition
 *   The field definition.
 * @param \Drupal\Core\Entity\ContentEntityInterface|null $entity
 *   The entity being created if applicable.
 * @param bool $cacheable
 *   Boolean indicating if the results are cacheable.
 *
 * @return array
 *   An array of possible key and value options.
 *
 * @see options_allowed_values()
 */
function _uw_cfg_common_allowed_media_types(FieldStorageConfig $definition, ContentEntityInterface $entity = NULL, $cacheable) {

  return [
    'banner' => 'Banner',
    'image' => 'Image',
  ];
}

/**
 * Implements hook_views_query_alter().
 */
function uw_cfg_common_views_query_alter(ViewExecutable $view, QueryPluginBase $query) {
  // Redirect page default sort updated, added redirect id to sort, on top of
  // created datetime sort.
  if ($view->id() === 'redirect') {
    $query->addOrderBy('redirect', 'rid');
  }
}

/**
 * Implements hook_field_widget_single_element_WIDGET_TYPE_form_alter().
 */
function uw_cfg_common_field_widget_single_element_link_default_form_alter(array &$element, FormStateInterface $form_state, array $context) {

  // Get field information from context.
  $field_definition = $context['items']->getFieldDefinition();

  // Set custom description only for event map field.
  if ($field_definition->getName() == 'field_uw_event_map') {
    $element['uri']['#description'] = t('Optional: provide a link to a map with the event location (e.g. https://uwaterloo.ca/map/). This must be an external URL such as https://example.com.');
  }
  else {

    // Set custom description for all link fields except event map.
    $element['uri']['#description'] = t('Start typing the title of a piece of content to select it. You can also enter an internal path such as /blog or an external URL such as https://example.com. Enter &lt;front&gt; to link to the front page.');

    // Set custom description for google maps.
    if ($field_definition->getName() == 'field_gmaps_embedded_url') {
      $element['uri']['#description'] = t('Google Maps starts with https://www.google.com/maps/embed?pb=<br>Google My Maps starts with https://www.google.com/maps/d/u/1/embed?mid=');
    }

    // Set custom description only for event host field.
    if ($field_definition->getName() == 'field_uw_event_host') {
      $element['uri']['#description'] .= ' ' . t('Enter &lt;nolink&gt; to display link text only.');
    }

    // Set custom description only for timeline link field.
    if ($field_definition->getName() == 'field_uw_timeline_link') {
      $element['uri']['#description'] = t('Links the entire content to a URL. If entered, do not use links inside the content itself.') . ' ' . $element['uri']['#description'];
    }

    // Set custom description only for banner link field.
    if ($field_definition->getName() == 'field_uw_ban_link') {
      $element['uri']['#description'] = t('Provide an optional link for this banner.') . ' ' . $element['uri']['#description'];
    }
  }

  // Add link uri field element validation function.
  $element['uri']['#element_validate'][] = '_uw_cfg_common_uw_link_validator';
}

/**
 * Link uri field validation function.
 */
function _uw_cfg_common_uw_link_validator($element, &$form_state, $form) {

  $uri = $element['#value'];

  // Get the field name and set it up in the format used by setErrorByName.
  $fieldname = rtrim($element['#name'], ']');
  $pos = strpos($fieldname, '[');
  $fieldname = substr_replace($fieldname, '][', $pos, 1);

  // Buttons are just not allowed.
  if ($uri == '<button>') {
    $form_state->setErrorByName($fieldname, t('The &lt;button&gt; value is not supported for this field.'));
  }

  if ($uri == '<nolink>') {
    // Don't allow blank link text when <nolink> is used.
    if ($fieldname == 'field_uw_event_host][0][uri') {
      $link_text = $form_state->getValue('field_uw_event_host')[0]['title'] ?? '';
      if ($link_text == '') {
        $form_state->setErrorByName('field_uw_event_host][0][title', t('You must provide link text when using &lt;nolink&gt; as the URL.'));
      }
    }
    else {
      // Nolink is not allowed, with one exception (event host field).
      $form_state->setErrorByName($fieldname, t('The &lt;nolink&gt; value is not supported for this field.'));
    }
  }

  // Prevent adding pilots or staging URLs in link fields.
  if (preg_match("/^https?:\/\/(?:pilots|staging)\.uwaterloo\.ca\/?.*/", $uri)) {
    $form_state->setErrorByName($fieldname, t('Linking to a staging/pilots site is not allowed as these are temporary sites not intended for public use. For links within a site, please use an internal path.'));
  }
}

/**
 * Implements template_preprocess_layout.
 */
function uw_cfg_common_preprocess_layout(&$variables) {

  // Get the settings from the content.
  $settings = $variables['content']['#settings'] ?? NULL;

  // If there are settings, then process them to get out
  // the options for the section.
  if (
    $settings &&
    isset($settings['layout_builder_background_type']) &&
    $settings['layout_builder_background_type'] !== 'none'
  ) {

    // Get the background type from the settings.
    $type = $settings['layout_builder_background_type'];

    // If the type is a solid color and the color is set,
    // then add a class to the variables.
    if (
      $type === 'solid_color' &&
      $color = $settings['layout_builder_background_color']
    ) {

      // Add the class to the variables.
      $variables['attributes']['class'][] = 'uw-section__background--' . $color;

      // If there are classes on the section, remove the ones
      // that are put in by default for background image and
      // solid color.
      if (isset($variables['attributes']['class'])) {
        $variables['attributes']['class'] = _uw_cfg_common_remove_extra_classes($variables['attributes']['class']);
      }
    }
    // If the background type is an image, get the image and
    // add the sources to the variables.
    elseif ($type === 'image') {

      // Get the mid from the settings.
      $media_id = $settings['layout_builder_background_image'];

      // If there is a media id, then get the sources for the
      // picture element and to the variables.
      if ($media_id) {

        // The UW service.
        /** @var \Drupal\uw_cfg_common\Service\UWServiceInterface $uw_service */
        $uw_service = \Drupal::service('uw_cfg_common.uw_service');

        // Load the media entity.
        $media_entity = \Drupal::service('entity_type.manager')->getStorage('media')->load($media_id);

        // Set the sources for the picture element to the variables.
        $variables['uw_section']['options']['image'] = $uw_service->prepareResponsiveImage($media_entity, 'uw_ris_media');

        // Fix the issue with portrait crops in the section background image,
        // by removing the portrait cut.
        foreach ($variables['uw_section']['options']['image']['sources'] as $index => $source) {
          if ($source->toArray()['media'] == 'all and (min-width: 1em)') {
            unset($variables['uw_section']['options']['image']['sources'][$index]);
          }
        }

        // Add the class to the section.
        $variables['attributes']['class'][] = 'uw-section__background-image';
      }

      // Add the image tint to the variables.
      $variables['uw_section']['options']['image_tint'] = $settings['layout_builder_background_image_image_tint'] ?? 'none';

      // Add the image tint.
      $tint_color_class = 'uw-section__tint-color--' . $variables['uw_section']['options']['image_tint'];
      $variables['attributes']['class'][] = $tint_color_class;

      // Add the image text color to the variables.
      $variables['uw_section']['options']['image_color'] = $settings['layout_builder_background_image_text_color'] ?? 'white';

      // Add the text color.
      $text_color_class = 'uw-section__text-color--' . $variables['uw_section']['options']['image_color'];
      $variables['attributes']['class'][] = $text_color_class;
    }
  }
  else {

    // If there are classes on the section, remove the ones
    // that are put in by default for background image and
    // solid color.
    if (isset($variables['attributes']['class'])) {
      $variables['attributes']['class'] = _uw_cfg_common_remove_extra_classes($variables['attributes']['class']);
    }
  }

  // Classes to check for.
  $classes['uw-section-spacing'] = 'uw-section-spacing--default';
  $classes['uw-section-separator'] = 'uw-section-separator--none';
  $classes['uw-column-separator'] = 'uw-column-separator--none';

  // Only check if classes exists if there are already claseses.
  // If there are no classes just add them.
  if (isset($variables['attributes']['class'])) {

    // Step through each of the classes and see if we need to
    // add it to the layout.
    foreach ($classes as $needle => $class) {

      // If there is no class for section spacing, add it
      // to the layout classes.
      if (empty(preg_grep('/' . $needle . '/i', $variables['attributes']['class']))) {
        $variables['attributes']['class'][] = $class;
      }
    }
  }
  else {

    // Step through each of the classes and add them.
    foreach ($classes as $class) {
      $variables['attributes']['class'][] = $class;
    }
  }

}

/**
 * Function to remove extra classes.
 *
 * @param array $classes
 *   Array of classes.
 *
 * @return array
 *   Array of classes with removals
 */
function _uw_cfg_common_remove_extra_classes(array $classes): array {

  // The classes to be removed.
  $classes_to_remove = [
    'uw-section__background--contained',
    'uw-section__background--full-width',
  ];

  // Step through each of the classes and remove
  // the extra classes that are not required.
  foreach ($classes as $index => $value) {

    // If class is in the classes to remove array,
    // remove it from the classes array.
    if (in_array($value, $classes_to_remove)) {
      unset($classes[$index]);
    }
  }

  return $classes;
}

/**
 * Implements hook_views_pre_build().
 */
function uw_cfg_common_views_pre_build(ViewExecutable $view) {

  // If on an events view, ensure that parameters are correct.
  if ($view->id() == 'uw_view_events') {

    /** @var \Symfony\Component\HttpFoundation\Request $request */
    $request = $view->getRequest();

    /** @var \Drupal\Core\Http\InputBag $query */
    $query = $request->query;

    // Get all the parameters from the request.
    $parameters = $query->all();

    // Flag to see if parameter is removed.
    $parameter_remove = FALSE;

    // Step through each of the parameters to see
    // if we need to remove them.
    foreach ($parameters as $index => $value) {

      // If this is a date and not an array, we need
      // to remove the parameter.
      if ($index == 'date' && !is_array($value)) {

        // Remove the date parameter and set the flag.
        unset($parameters['date']);
        $parameter_remove = TRUE;
      }

      // If the parameter is type or audience, ensure that it is
      // an array for the wcms3 filters.
      if (
        ($index == 'type' || $index == 'audience') &&
        !is_array($parameters[$index])
      ) {

        // Unset the parameter and set the flag.
        unset($parameters[$index]);
        $parameter_remove = TRUE;
      }

      // If the flag is set that parameters were removed,
      // then set a redirect to the same page with the
      // correct parameters.
      if ($parameter_remove) {

        // Get the request uri and remove everything after the ?.
        $url = strtok($request->getRequestUri(), '?');

        // Set the internal url using the parameters.
        $url = Url::fromUri('internal:' . $url, ['query' => $parameters]);

        // Set up the redirect and send it.
        $redirect = new RedirectResponse($url->toString());
        $redirect->send();
      }
    }
  }
}