<?php /** * @file * Theme file for uw_fdsu_theme_resp. */ use Drupal\Core\Url; use Drupal\file\Plugin\Field\FieldType\FileFieldItemList; use Drupal\image\Plugin\Field\FieldType\ImageItem; use Drupal\Core\Form\FormStateInterface; use Drupal\node\Entity\Node; /** * @file * Functions to support theming. */ require_once dirname(__FILE__) . '/includes/views.inc'; /** * Implements hook_preprocess_HOOK(). * * Setting the faculty class colour and favicon information. */ function uw_fdsu_theme_resp_preprocess_html(&$variables) { // Adding the faculty colour class to the body. $variables['attributes']['class'][] = theme_get_setting('wcms_colour_scheme', 'uw_fdsu_theme_resp') ? theme_get_setting('wcms_colour_scheme', 'uw_fdsu_theme_resp') : 'org-default'; $variables['uw_admin_page'] = \Drupal::service('uw_cfg_common.uw_analytics')->administrationPage(); // Add the fav icons. _uw_fdsu_theme_resp_add_favicons($variables); // Get the route name. $route_name = \Drupal::routeMatch()->getRouteName(); // Perform actions based on the route. switch ($route_name) { // Add Javascript only on the user login page. case 'user.login': $variables['#attached']['library'][] = 'uw_fdsu_theme_resp/user.login'; break; // The header/page title on contributors listing page browser // tab shows "Reference author: " in front of the author's name. case 'entity.bibcite_contributor.canonical': $variables['head_title']['title'] = t('Reference author:') . ' ' . $variables['head_title']['title']; break; // The header/page title on keywords listing page browser // tab shows "Reference keyword: " in front of the keyword. case 'entity.bibcite_keyword.canonical': $variables['head_title']['title'] = t('Reference keyword:') . ' ' . $variables['head_title']['title']; break; // Adding the alternate link to listing pages. case 'view.uw_view_blogs.blog_page': case 'view.uw_view_events.event_page': case 'view.uw_view_news_items.news_page': // Get the alternate link based on the route. switch ($route_name) { case 'view.uw_view_blogs.blog_page': $link = _uw_fdsu_theme_resp_get_alternate_link( 'uw_view_blogs', 'uw_public_blog_feed', 'Blogs feed' ); break; case 'view.uw_view_events.event_page': $link = _uw_fdsu_theme_resp_get_alternate_link( 'uw_view_events', 'uw_public_event_feed', 'Events feed' ); break; case 'view.uw_view_news_items.news_page': $link = _uw_fdsu_theme_resp_get_alternate_link( 'uw_view_news_items', 'uw_public_news_feed', 'News feed' ); break; } // Add the alternate link to the page. $variables['#attached']['html_head_link'][] = $link; break; } // Get the current path. $parts = explode('/', \Drupal::service('path.current')->getPath()); // If the last in the array parts is not layout, // add a class so that we traget anything but // the layout pages. if (end($parts) !== 'layout') { $variables['attributes']['class'][] = 'not-layout-page'; } else { $variables['attributes']['class'][] = 'layout-page'; } // Add UW to page title, if not already there. _uw_add_page_title($variables); } /** * Function to get the alternate link. * * @param string $view_machine_name * The machine name of the view. * @param string $view_display_name * The display name of the view. * @param string $title * The title of the alternate link. */ function _uw_fdsu_theme_resp_get_alternate_link( string $view_machine_name, string $view_display_name, string $title ) { // Get the URL to RSS feed. $url = Url::fromRoute('view.' . $view_machine_name . '.' . $view_display_name); $url->setOptions(['query' => \Drupal::request()->query->all()]); // Return the link for the alternate. return [ [ 'rel' => 'alternate', 'href' => $url->toString(), 'title' => $title, 'type' => 'application/rss+xml', ], ]; } /** * Implements hook_preprocess_responsive_image(). */ function uw_fdsu_theme_resp_preprocess_responsive_image(&$variables) { // Step through each of the responsive image objects and get out source info. foreach ($variables['sources'] as $source) { // Set the a variable with the actual source info that we need. $new_sources[] = [ 'srcset' => $source['srcset']->value(), 'media' => $source['media']->value(), 'type' => $source['type']->value(), ]; } // Set the sources variable to the new sources. $variables['sources'] = $new_sources; } /** * Implements hook_preprocess_region(). */ function uw_fdsu_theme_resp_preprocess_region(&$variables) { // Get the region from variables. $region = $variables['elements']['#region']; // Set 'search_local' variable if not set. if (!isset($variables['search_local'])) { $variables['search_show_local'] = theme_get_setting('wcms_search_show_local', 'uw_fdsu_theme_resp'); } // Variables that we want to have access to regardless of region. $variables['branding_level'] = theme_get_setting('wcms_branding_level', 'uw_fdsu_theme_resp') ? theme_get_setting('wcms_branding_level', 'uw_fdsu_theme_resp') : 'full'; // The menu style is set based on selection if the level is 'full'. if ($variables['branding_level'] == 'full') { $variables['branding_menu_style'] = theme_get_setting('wcms_branding_menu_style', 'uw_fdsu_theme_resp') ? theme_get_setting('wcms_branding_menu_style', 'uw_fdsu_theme_resp') : 'compressed'; } // The menu style is set to 'compressed' if the level is not 'full'. else { $variables['branding_menu_style'] = 'compressed'; } // If we are on the header, add the correct header classes // TO DO: Store the colour scheme used for the theme // (i.e. faculty colour and get the correct class here). if ($region == "header") { // Get the home page link. $variables['home_link'] = Url::fromRoute('<front>')->setAbsolute()->toString(); // Set the main menu variable. $variables['main_menu'] = \Drupal::service('uw_cfg_common.uw_menu_items')->getMenuTree('main', TRUE, TRUE); // Set the secondary menu variable. $variables['secondary_menu'] = \Drupal::service('uw_cfg_common.uw_menu_items')->getMenuTree('uw-menu-audience-menu', TRUE, TRUE); // The class that is used for the header. $variables['classes'][] = 'uw-header'; // Get the site name for placing inside the menu. $config = \Drupal::config('system.site'); $variables['site_name'] = $config->get('name'); $variables['subtitle'] = $config->get('slogan'); $variables['faculty'] = theme_get_setting('wcms_colour_scheme', 'uw_fdsu_theme_resp') ? theme_get_setting('wcms_colour_scheme', 'uw_fdsu_theme_resp') : 'org-default'; // Create the stream context to use with file_get_contents // so it times out after a certain period instead of // waiting indefinitely. Timeout is in seconds. $context = stream_context_create([ 'http' => [ 'timeout' => 3, ], ]); // Global message content. $global_message = file_get_contents('https://uwaterloo.ca/global-message.html', 0, $context); // If there is something to display, make sure to include css file, and // pass message as render array, then use render twig filter to avoid using // raw twig filter. Trimming message before sending to template, this // will remove any empty lines/spaces that message may have. if (!empty($global_message)) { $variables['global_message'] = ['#markup' => trim($global_message)]; } } // ISTWCMS-4847. // If we are on the main content section, the check if not a node and add // appropriate classes to display proper width. if ($region == 'content') { // Get the route name. $route_match = \Drupal::routeMatch(); // If we are on not on a node, add appropriate classes to show // proper width. $route_name = $route_match->getRouteName(); $exemptions = [ 'entity.node.canonical', 'entity.node.latest_version', 'layout_builder.overrides.node.view', ]; if (!in_array($route_name, $exemptions)) { $variables['classes'][] = 'layout'; $variables['classes'][] = 'uw-contained-width'; } } if ($region == 'footer') { $variables['social_media'] = \Drupal::service('uw_cfg_common.uw_menu_items') ->getMenuTree( 'uw-menu-global-social-media', FALSE ); $variables['footer_menu'] = \Drupal::service('uw_cfg_common.uw_menu_items') ->getMenuTree( 'uw-menu-global-footer', FALSE ); } $variables['language'] = \Drupal::languageManager()->getCurrentLanguage(); } /** * Implements hook_FORM_ID_alter(). * * Add settings for colour scheme. */ function uw_fdsu_theme_resp_form_system_theme_settings_alter(&$form, FormStateInterface &$form_state, $form_id = NULL) { // Work-around for a core bug affecting admin themes. See issue #943212. if (isset($form_id)) { return; } // Fieldset for search options. $form['search_options'] = [ '#type' => 'details', '#open' => TRUE, '#title' => t('Search options'), ]; // Search options checkbox field. $form['search_options']['wcms_search_show_local'] = [ '#type' => 'checkbox', '#title' => t("Show the 'on this site' search option"), '#default_value' => theme_get_setting('wcms_search_show_local', 'uw_fdsu_theme_resp') ?? TRUE, ]; // Fieldset for colour scheme. $form['colour_scheme'] = [ '#type' => 'details', '#open' => TRUE, '#title' => t('Colour scheme'), ]; // Colour scheme select list. $form['colour_scheme']['wcms_colour_scheme'] = [ '#type' => 'select', '#default_value' => theme_get_setting('wcms_colour_scheme', 'uw_fdsu_theme_resp') ? theme_get_setting('wcms_colour_scheme', 'uw_fdsu_theme_resp') : 'default', '#description' => t("Select a color scheme to use"), '#options' => _uw_cfg_common_get_faculty_color_options(), ]; // Fieldset for branding options. $form['branding_options'] = [ '#type' => 'details', '#open' => TRUE, '#title' => t('Branding options'), ]; // Branding select option. $form['branding_options']['wcms_branding_level'] = [ '#type' => 'select', '#options' => [ 'full' => t('Full University branding'), 'generic' => t('Generic with University wordmark'), 'generic_barebones' => t('Fully generic'), ], '#default_value' => theme_get_setting('wcms_branding_level', 'uw_fdsu_theme_resp') ?: 'full', ]; $form['branding_options']['wcms_branding_menu_style'] = [ '#type' => 'select', '#options' => [ 'compressed' => t('Compressed ("jump to") global menu'), 'full' => t('Fully visible global menu'), ], "#states" => [ 'visible' => [ 'select[name="wcms_branding_level"]' => ['value' => 'full'], ], ], '#default_value' => theme_get_setting('wcms_branding_menu_style', 'uw_fdsu_theme_resp') ?: 'compressed', ]; } /** * Add favicons to the page when called from hook_preprocess_html(). * * @param array $variables * The variables array. */ function _uw_fdsu_theme_resp_add_favicons(array &$variables) { // Remove Drupal's favicon. foreach ($variables['page']['#attached']['html_head_link'] as $id => $html_head_link) { if (isset($html_head_link[0]['rel']) && $html_head_link[0]['rel'] == 'shortcut icon') { unset($variables['page']['#attached']['html_head_link'][$id]); break; } } // Specify new favicon locations. // Based on https://evilmartians.com/chronicles/how-to-favicon-in-2021-six-files-that-fit-most-needs. $favicon_base_path = base_path() . \Drupal::service('extension.list.theme')->getPath('uw_fdsu_theme_resp'); $favicon = [ 'rel' => 'icon', 'href' => $favicon_base_path . '/favicon.ico', ]; $variables['page']['#attached']['html_head_link'][] = [$favicon]; $favicon_svg = [ 'rel' => 'icon', 'href' => $favicon_base_path . '/icon.svg', 'type' => 'image/svg+xml', ]; $variables['page']['#attached']['html_head_link'][] = [$favicon_svg]; $favicon_apple = [ 'rel' => 'apple-touch-icon', 'href' => $favicon_base_path . '/apple-touch-icon.png', ]; $variables['page']['#attached']['html_head_link'][] = [$favicon_apple]; // Manifest needs a full URL. $favicon_base_path = \Drupal::request()->getSchemeAndHttpHost() . $favicon_base_path; $manifest = [ 'icons' => [ [ 'src' => $favicon_base_path . '/icon-192.png', 'type' => 'image/png', 'sizes' => '192x192', ], [ 'src' => $favicon_base_path . '/icon-512.png', 'type' => 'image/png', 'sizes' => '512x512', ], ], ]; $manifest = urlencode(json_encode($manifest)); $favicon_manifest = [ 'rel' => 'manifest', 'href' => 'data:application/manifest+json,' . $manifest, ]; $variables['page']['#attached']['html_head_link'][] = [$favicon_manifest]; } /** * Get the specified field value from a paragraph. * * @param object $paragraph * The paragraph object. * @param string $field * The name of the field. * * @return mixed * The field value. */ function _uw_fdsu_theme_resp_get_field_value_from_paragraph($paragraph, $field) { // Get the field value. $field_value = $paragraph->get($field)->first(); return $field_value->getValue()['value']; } /** * Set the variables required for a responsive image. * * @param array $variables * The variables array. * @param \Drupal\image\Plugin\Field\FieldType\ImageItem $field * The field object. * @param string $responsive_image_style * The ID of the responsive image style. * * @return array|null * An array with keys: * - sources: An array of image sources. * - alt: The alternative text. */ function _uw_fdsu_theme_resp_add_responsive_image_variables(array &$variables, ImageItem $field, string $responsive_image_style) : ?array { // If there is a file present, set responsive image variables. if ($file = $field->entity) { // Set uri and image style id. $variables['uri'] = $file->getFileUri(); $variables['responsive_image_style_id'] = $responsive_image_style; // Call template function from responsive image core module. // It sets variables for srcset, media, type and img_element // for the responsive image style. template_preprocess_responsive_image($variables); // Step through each source and get string values. $sources = []; foreach ($variables['sources'] as $source) { $sources[] = [ 'srcset' => $source['srcset']->value(), 'media' => $source['media']->value(), 'type' => $source['type']->value(), ]; } return [ 'sources' => $sources, 'alt' => $field->get('alt')->getValue(), ]; } return NULL; } /** * Get image properties from an image field. * * @param \Drupal\file\Plugin\Field\FieldType\FileFieldItemList $field * The image field. * * @return array|null * An array of image properties or NULL if $field does not contain the * required information. */ function _uw_fdsu_theme_resp_get_image_info(FileFieldItemList $field) { // If there is an image, process it. if ($img_entity = $field->first()) { // If we can load a file, grab the info about the file. if ($file_entity = $img_entity->get('entity')->getTarget()) { return [ 'src' => \Drupal::service('file_url_generator')->generateAbsoluteString($file_entity->get('uri')->getString()), 'alt' => $img_entity->get('alt')->getString(), ]; } } } /** * Implements hook_preprocess_node(). * * Set variables for node and teaser. */ function uw_fdsu_theme_resp_preprocess_node(&$variables) { // The UW service object. $uwService = \Drupal::service('uw_cfg_common.uw_service'); $nodeContent = \Drupal::service('uw_cfg_common.uw_node_content'); // The types of nodes the need preprocessing. $nodes_to_preprocess = $uwService->uwGetNodePreprocessing('full'); // Teaser to be preprocessed. $teasers_to_preprocess = $uwService->uwGetNodePreprocessing('teaser'); // If there is a node that needs preprocessing, // set the appropriate variables. if (in_array($variables['node']->getType(), $nodes_to_preprocess) || in_array($variables['node']->getType(), $teasers_to_preprocess)) { // If on a teaser page get the variables for teaser. if ($variables['view_mode'] == 'teaser' && in_array($variables['node']->getType(), $teasers_to_preprocess)) { $variables['teaser'] = $nodeContent->getNodeContent($variables['node'], 'teaser', 'all'); } // If on a node page get the variables for now. if ($variables['view_mode'] == 'full' && in_array($variables['node']->getType(), $nodes_to_preprocess )) { $variables['node_data'] = $nodeContent->getNodeContent($variables['node'], 'full', 'all'); $variables['node_data']['content'] = $variables['content']; } // Unset the content variable, so that we do not get // a second print of all the content. unset($variables['content']); } } /** * Implements hook_preprocess_block(). * * Add the admin_label and css classes if we are in layout builder. */ function uw_fdsu_theme_resp_preprocess_block(&$variables) { // Look at page title block to see if we have a featured image. // If we do then, set variable to not show page title. if ($variables['plugin_id'] == 'page_title_block') { // Set the media variable to no, we will only // change if there is a media. $variables['media'] = 'no'; // Load the node. $node = \Drupal::routeMatch()->getParameter('node'); // If this is a node, check for contact and set if we // have to hide the title. if ($node = \Drupal::routeMatch()->getParameter('node')) { // Get the media flags. $media_flags = \Drupal::service('uw_cfg_common.uw_service')->uwGetMediaFlags($node); // Set the show title flag. $variables['show_title'] = $media_flags['has_media'] || $media_flags['has_portrait'] ? FALSE : TRUE; } // ISTWCMS-4943: ensure that we get a node object. // If node is not object by this point, probably on // a revision page where node is an integer, so load // the node. if ($node && !is_object($node)) { $node = Node::load($node); } // If there is a node, check that it has a media. if ($node) { // The UW service object. $uwService = \Drupal::service('uw_cfg_common.uw_service'); $variables['media'] = $uwService->uwCheckNodeForMedia($node); } } // If we are in layout builder (this is set much earlier in // the page load process), then continue to look if we need // to add the admin_label and css classes. if (isset($variables['in_layout_builder']) && $variables['in_layout_builder'] == TRUE) { // Get the block manager object. $blockManager = \Drupal::service('plugin.manager.block'); // Get the plugin definitions for the block, using the // plugin_id of the block. $plugin_definitions = $blockManager->getDefinition($variables['plugin_id']); // The admin labels to exclude, these are ones that we do not // want the admin label to appear on the layout builder page. $admin_labels_to_exclude = [ 'Messages', 'Tabs', 'Page title', 'Global header', 'Main navigation', 'Site Footer block', ]; // Check if the admin_label is an object, we need to do this // because some admin labels are translatable and are therefore // translatable markup objects (usually through code way of // building blocks. Others are not, if translations are not turned // on for this block (usually through the GUI way of creating a block, // where the translation option is not set). if (is_object($plugin_definitions['admin_label'])) { // The admin label is a translatable markup, so just render // the object to set the admin label variable.. $admin_label = $plugin_definitions['admin_label']->render(); } else { // The admin label is not an object so just set the // admin label variable. $admin_label = $plugin_definitions['admin_label']; } // If the admin label is not in the ones to exclude add it // to the template variables and set the class to be used // to display the admin label. if (!in_array($admin_label, $admin_labels_to_exclude)) { // ISTWCMS-6062, add publication to reference block admin // labels when in layout builder. if ($admin_label == 'Reference search') { $admin_label = 'Publication reference search'; } if ($admin_label == 'Reference teaser') { $admin_label = 'Publication reference teaser'; } $variables['admin_label'] = $admin_label; $variables['attributes']['class'][] = 'in-layout-builder'; } } } /** * Implements template_preprocess_container(). */ function uw_fdsu_theme_resp_preprocess_container(&$variables) { // Ensure that we are on a layout page. if (\Drupal::routeMatch()->getRouteName() == 'layout_builder.overrides.node.view') { // Ensure that we are on the first layout builder container. if ( isset($variables['attributes']['class']) && $variables['attributes']['class'][0] == 'layout-builder' ) { // The UW service object. $uwService = \Drupal::service('uw_cfg_common.uw_service'); // The UW node content service. $uwNodeContent = \Drupal::service('uw_cfg_common.uw_node_content'); // The list of content types that will have header // and footer in layout builder pages. $content_types = $uwService->uwGetNodePreprocessing('layout_container'); // Get the node object. $node = \Drupal::routeMatch()->getParameter('node'); // If there is a node object, and it is a content // type that has header and footer for layout pages, // get the header and footer. if ($node && in_array($node->getType(), $content_types)) { // Get the node object. $node = \Drupal::routeMatch()->getParameter('node'); // Set variables for featured image. $variables['node_type'] = str_replace('_', '-', $node->getType()); $variables['media'] = $uwService->uwCheckNodeForMedia($node); $variables['media_flags'] = $uwService->uwGetMediaFlags($node); // If there is a node object, get the header and footer data. if ($node) { $variables['header_data'] = $uwNodeContent->getNodeContent($node, 'full', 'header'); $variables['footer_data'] = $uwNodeContent->getNodeContent($node, 'full', 'footer'); // If this is an event, add the ical url so that it displays // inside of layout builder. if ($node->getType() == 'uw_ct_event') { $variables['header_data']['ical'] = _uw_ct_event_get_ical_link($node->id()); } } } } } } /** * Implements template_preprocess_details(). */ function uw_fdsu_theme_resp_preprocess_details(&$variables) { // ISTWCMS-6711: this comes from the label not having the // correct for in it. So we are going to check if this is // a better exposed filter and if so then add the correct // based on the type of exposed filter it is. The date // filter needs special treatment, since it is using the // wrapper as the data drupal selector, and we need it to // use the -value. If it is not a bef, then check if there // is a data drupal selector and use it as the form field id. if ( isset($variables['element'][0]['#context']['#plugin_type']) && $variables['element'][0]['#context']['#plugin_type'] == 'bef' ) { // If this is a date picker, then change the form field id // to use -value instead of -wrapper. If not then just use // the straight data drupal selector. if ($variables['element'][0]['#context']['#plugin_id'] == 'bef_datepicker') { $variables['form_field_id'] = str_replace( '-wrapper', '-value', $variables['element'][0]['#attributes']['data-drupal-selector'] ); } else { $variables['form_field_id'] = $variables['element'][0]['#attributes']['data-drupal-selector']; } } else { // Search input needs a special form id, so setting that, if not // We continue the old fix. if (isset($variables['element']['search'])) { $variables['form_field_id'] = $variables['element']['search']['#attributes']['id']; } else { // If there is an element, process it. if (isset($variables['element'])) { // Step through each of the indexes and ensure that we // have an actual element, which means it will be an array. foreach ($variables['element'] as $index => $value) { // If this is an array, then we have an element to look at. if (is_array($value)) { // One more check to ensure that we have an element. if (isset($variables['element'][$index]['#type'])) { // Get the id based on where the id is stored, either it // is by itself or within the attributes. if (isset($variables['element'][$index]['#id'])) { $variables['form_field_id'] = $variables['element'][$index]['#id']; } elseif (isset($variables['element'][$index]['#attributes']['id'])) { $variables['form_field_id'] = $variables['element'][$index]['#attributes']['id']; } // Break out of the loop to save computational time. break; } } } } // If there is a data drupal selector use it for form field id. elseif (isset($variables['element'][0]['#attributes']['data-drupal-selector'])) { $variables['form_field_id'] = $variables['element'][0]['#attributes']['data-drupal-selector']; } } } }