<?php /** * @file * Module file. */ use Drupal\Component\Utility\Html; use Drupal\Core\Access\AccessResult; use Drupal\Core\Entity\EntityInterface; use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Render\Element; use Drupal\Core\Session\AccountInterface; use Drupal\Core\Url; use Drupal\media_library\MediaLibraryState; use Drupal\uw_cfg_common\Service\UWService; use Drupal\webform\WebformInterface; use Drupal\webform\WebformSubmissionStorageInterface; /** * 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(); } } } /** * 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 { // 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'; } /** * 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', '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_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'; }