Skip to content
Snippets Groups Projects
uw_cfg_common.module 20.9 KiB
Newer Older
Liam Morland's avatar
Liam Morland committed
<?php

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

use Drupal\Component\Utility\Html;
use Drupal\Core\Access\AccessResult;
Igor Biki's avatar
Igor Biki committed
use Drupal\Core\Entity\EntityInterface;
Liam Morland's avatar
Liam Morland committed
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Render\Element;
use Drupal\Core\Session\AccountInterface;
use Drupal\media_library\MediaLibraryState;
use Drupal\uw_cfg_common\Service\UWService;
use Drupal\webform\WebformInterface;
use Drupal\webform\WebformSubmissionStorageInterface;
Liam Morland's avatar
Liam Morland committed

 * 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',
    ];
Igor Biki's avatar
Igor Biki committed
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();
    }
  }
}

Liam Morland's avatar
Liam Morland committed
/**
 * 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 {
Liam Morland's avatar
Liam Morland committed
  // Remove the None option from layout builder styles.
  unset($form['layout_builder_style']['#empty_option']);

  // Ensuring that the contained width is selected by default.
  $form['layout_builder_style']['#default_value'] = $form['layout_builder_style']['#default_value'] ?: 'uw_lbs_contained_width';
Liam Morland's avatar
Liam Morland committed
}
/**
 * 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().
 *
 * 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 {
  // 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;
/**
 * 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/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);
}

/**
 * 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_node().
 */
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) {
      return [];
    }

    $render = $plugin_block->build();

    $variables['uw_content_moderation_form'] = $render;
  }
}
/**
 * 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',
    // 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,
 * 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_form_alter().
 *
 * Hide/disable metatag information on our nodes.
 */
function uw_cfg_common_form_alter(array &$form, FormStateInterface $form_state, string $form_id): void {

  // 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.*add_form/', $form_id) || preg_match('/node_uw.*edit_form/', $form_id)) {

    // 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;
      $form['field_uw_meta_tags']['widget'][0]['twitter_cards']['twitter_cards_page_url']['#access'] = FALSE;
    }
  }
}

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