You are here

add_to_head.module in Add To Head 8

Same filename and directory in other branches
  1. 6 add_to_head.module
  2. 7 add_to_head.module

Add To Head allows arbitrary insertion of code into the head of the page based on path selection.

File

add_to_head.module
View source
<?php

/**
 * @file
 * Add To Head allows arbitrary insertion of code into the head of the page
 * based on path selection.
 */
use Drupal\Core\Render\Markup;

/**
 * Helper to get the settings.
 * @param string  $scope  (Optional) - filter the scopes of the returned settings.
 * @return array   Array of profiles, including those defined by other modules.
 */
function add_to_head_get_settings($scope = NULL) {

  // Fetch the profile information stored in the DB.
  $settings = \Drupal::config('add_to_head.settings')
    ->get('add_to_head_profiles');
  $settings = $settings ? $settings : array();

  // Allow other modules to alter profile settings. Additional profiles may be added here.
  \Drupal::moduleHandler()
    ->alter('add_to_head_profiles', $settings);
  if ($scope) {
    $settings = array_filter($settings, function ($profile) use ($scope) {
      return $profile['scope'] == $scope;
    });
  }
  return $settings;
}

/**
 * Helper to set settings back to the Database
 * @param $settings   array   Array of profiles to store.
 */
function add_to_head_set_settings($settings) {
  \Drupal::configFactory()
    ->getEditable('add_to_head.settings')
    ->set('add_to_head_profiles', $settings)
    ->save();
}

/**
 * Is this profile visible?
 *
 * @param $profile
 * @return bool
 */
function _add_to_head_profile_visible($profile) {
  return add_to_head_match_page($profile) && add_to_head_match_role($profile);
}

/**
 * Implements hook_page_attachments_alter().
 */
function add_to_head_page_attachments_alter(array &$attachments) {

  // Inject code into the 'head' scope.
  $settings = add_to_head_get_settings('head');

  // If applicable, append each profile's code to the output.
  foreach ($settings as $profile) {
    if (_add_to_head_profile_visible($profile)) {

      // Add to output
      $attachments['#attached']['html_head'][] = [
        [
          '#type' => 'markup',
          '#markup' => \Drupal\Core\Render\Markup::create($profile['code']),
          '#suffix' => "\n",
        ],
        'add-to-head--' . $profile['name'],
      ];
    }
  }
}

/**
 * Implements hook_page_bottom().
 */
function add_to_head_page_bottom(array &$page_bottom) {

  // Inject code into the 'scripts' scope.
  $settings = add_to_head_get_settings('scripts');

  // If applicable, append each profile's code to the output.
  foreach ($settings as $profile) {
    if (_add_to_head_profile_visible($profile)) {

      // Add to output
      $page_bottom['add_to_head'][] = [
        [
          '#type' => 'markup',
          '#markup' => Markup::create($profile['code']),
          '#suffix' => "\n",
          '#cache' => [
            'keys' => [
              'add_to_head',
            ],
            'contexts' => [
              'user.permissions',
            ],
          ],
        ],
        'add-to-head--' . $profile['name'],
      ];
    }
  }
}

/**
 * Implements hook_css_alter().
 */
function add_to_head_css_alter(&$css, \Drupal\Core\Asset\AttachedAssetsInterface $assets) {
  $settings = add_to_head_get_settings('styles');

  // If applicable, append each profile's code to the output.
  foreach ($settings as $profile) {
    if (_add_to_head_profile_visible($profile)) {

      // @TODO - this does not work yet.
      //      $css[$profile['code']] = [
      //        'type' => 'inline',
      //      ];
    }
  }
}

/**
 * Determines if code should be displayed on a particular page.
 *
 * Originally from block_list().
 *
 * @param array $profile
 *   The profile to check against
 * @param string $path
 *   Allow injection of non-current path for checking.
 *
 * @return boolean
 *   TRUE if the code should be displayed on the page; FALSE otherwise.
 */
function add_to_head_match_page(array $profile, $path = NULL) {

  // Determine if the code should be visible on the current page.
  $visibility = isset($profile['paths']['visibility']) ? $profile['paths']['visibility'] : 'exclude';
  $paths = isset($profile['paths']['paths']) ? $profile['paths']['paths'] : '';

  // Get the current path.
  if (!isset($path)) {
    $path = \Drupal::service('path.current')
      ->getPath();
  }

  // Compare with the internal and path alias (if any).
  $page_match = \Drupal::service('path.matcher')
    ->matchPath($path, $paths);
  if (!empty($_GET['q']) && $path != $_GET['q']) {
    $page_match = $page_match || \Drupal::service('path.matcher')
      ->matchPath($_GET['q'], $paths);
  }

  // Check alias
  if (!$page_match) {
    $alias = \Drupal::service('path_alias.manager')
      ->getAliasByPath($path);
    $page_match = \Drupal::service('path.matcher')
      ->matchPath($alias, $paths);
  }
  $visibility = $visibility == 'exclude' ? 0 : 1;

  // When $visibility has a value of 0, the code is displayed on
  // all pages except those listed in $paths. When set to 1, it
  // is displayed only on those pages listed in $paths.
  return !($visibility xor $page_match);
}

/**
 * Determines if code should be displayed for a particular role.
 *
 * @param array $profile
 *   The profile to check against
 * @param array $user_roles
 *   Allow injection of non-current user roles
 *
 * @return boolean
 *   TRUE if the code should be displayed on the page; FALSE otherwise.
 */
function add_to_head_match_role($profile, $user_roles = NULL) {

  // Determine if the code should be visible given the current user's roles.
  $visibility = isset($profile['roles']) && isset($profile['roles']['visibility']) ? $profile['roles']['visibility'] : 'exclude';
  $roles = isset($profile['roles']) && isset($profile['roles']['list']) ? $profile['roles']['list'] : array();
  if (!isset($user_roles)) {
    $user_roles = \Drupal::currentUser()
      ->getRoles();
  }
  $common_roles = array_intersect($roles, $user_roles);
  if ($visibility == 'exclude') {

    // If any common roles, no match.
    return empty($common_roles);
  }
  else {

    // Visibility is include. Match on any common roles.
    return !empty($common_roles);
  }
}

Functions

Namesort descending Description
add_to_head_css_alter Implements hook_css_alter().
add_to_head_get_settings Helper to get the settings.
add_to_head_match_page Determines if code should be displayed on a particular page.
add_to_head_match_role Determines if code should be displayed for a particular role.
add_to_head_page_attachments_alter Implements hook_page_attachments_alter().
add_to_head_page_bottom Implements hook_page_bottom().
add_to_head_set_settings Helper to set settings back to the Database
_add_to_head_profile_visible Is this profile visible?