Something went wrong on our end
uw_cfg_common.module 48.26 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 {
// 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 {
/** @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 {
// Define all names in an array.
$names = [
'ct_names' => [
'uw_ct_blog',
'uw_ct_contact',
'uw_ct_catalog_item',
'uw_ct_event',
'uw_ct_expand_collapse_group',
'uw_ct_news_item',
'uw_ct_opportunity',
'uw_ct_profile',
'uw_ct_project',
'uw_ct_service',
'uw_ct_sidebar',
'uw_ct_site_footer',
'uw_ct_web_page',
],
'vocab_names' => [
'uw_vocab_blog_tags',
'uw_vocab_contact_group',
'uw_vocab_audience',
'uw_vocab_catalog_categories',
'uw_vocab_catalogs',
'uw_tax_event_tags',
'uw_tax_event_type',
'uw_vocab_news_tags',
'uw_vocab_profile_type',
'uw_vocab_project_roles',
'uw_vocab_project_topics',
'uw_vocab_service_categories',
],
'media_names' => [
'uw_mt_file',
'uw_mt_icon',
'uw_mt_image',
'uw_mt_local_video',
'uw_mt_remote_video',
'uw_mt_vimeo_banner_video',
],
];
// Get current user.
$user = \Drupal::currentUser();
// Loop all names in array.
foreach ($names as $key => $type_names) {
foreach ($type_names as $name) {
$custom_waring_message = t('CAUTION. This will permanently delete this piece of content; this action cannot be undone. If anything references this content, it may cause visual or structural issues on that page. Make sure you have removed or updated all references before deleting.');
// Use custom warning message for deleting nodes, terms and medias.
// The user has 'delete any' and 'delete own' permissions.
if ($key == 'ct_names') {
if (($form_id == 'node_' . $name . '_delete_form') &&
($user->hasPermission('delete any ' . $name . ' content') ||
$user->hasPermission('delete own ' . $name . ' content'))) {
$form['description']['#markup'] = $custom_waring_message;
break;
}
}
// The user has 'delete terms' permission.
if ($key == 'vocab_names') {
if (($form_id == 'taxonomy_term_' . $name . '_delete_form') &&
$user->hasPermission('delete terms in ' . $name)) {
$form['description']['#markup'] = $custom_waring_message;
break;
}
}
// The user has 'delete any' permission.
if ($key == 'media_names') {
if (($form_id == 'media_' . $name . '_delete_form') &&
($user->hasPermission('delete any ' . $name . ' media') ||
$user->hasPermission('delete own ' . $name . ' media'))) {
$form['description']['#markup'] = $custom_waring_message;
break;
}
}
}
}
// 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();
}
}