Something went wrong on our end
uw_cfg_common.module 46.01 KiB
<?php
/**
* @file
* Module file.
*/
use Drupal\Component\Utility\Html;
use Drupal\Core\Access\AccessResult;
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\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\webform\WebformInterface;
use Drupal\webform\WebformSubmissionStorageInterface;
/**
* 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 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') {
// 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);
}
}
}
}
// If there is a type of media, ensure that we do only have
// values in the fields that are selected.
if ($entity->hasField('field_uw_type_of_media')) {
// Get the type of media.
$type_of_media = $entity->field_uw_type_of_media->value;
// If it is null then set the hero image to null.
if ($type_of_media == NULL) {
$entity->set('field_uw_hero_image', NULL);
}
}
}
}
/**
* 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 {
// Ensuring that the contained width is selected by default.
$form['layout_builder_style_default']['#default_value']['uw_lbs_contained_width'] = '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 {
/** @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.');
}
}
/**
* 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) {
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_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_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;
}
}
// 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'])) {
$form['field_uw_hero_image']['#states'] = [
'visible' => [
[
'select[name="field_uw_type_of_media"]' => [
['value' => 'image'],
],
],
],
];
}
// 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]);
}
}
}
}
}
}
}
}
/**
* 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;
}
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();
$query->condition('webform_id', $webform_id);
$query->condition('uid', $uid);
$query->condition('in_draft', 0);
$query->sort('created', 'ASC');
$entity_ids = $query->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'];
}
}