You are here

fontawesome.module in Font Awesome Icons 8.2

Drupal integration with Font Awesome, the iconic font for use with Bootstrap.

File

fontawesome.module
View source
<?php

/**
 * @file
 * Drupal integration with Font Awesome, the iconic font for use with Bootstrap.
 */
use Drupal\Core\Routing\RouteMatchInterface;
use Drupal\Core\Link;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\ContentEntityInterface;
use Drupal\Core\TypedData\Plugin\DataType\ItemList;

/**
 * Implements hook_help().
 */
function fontawesome_help($route_name, RouteMatchInterface $route_match) {
  switch ($route_name) {
    case 'help.page.fontawesome':
      return '<p><i class="far fa-font-awesome fa-2x"></i> ' . t('<a href=":fontawesome_url">Font Awesome</a> is an iconic font and CSS toolkit. Font Awesome gives you scalable vector icons that can instantly be customized — size, color, drop shadow, and anything that can be done with the power of CSS. For more information on how to use Font Awesome, see the <a href=":fontawesome_examples_page">Font Awesome Examples page</a>.', [
        ':fontawesome_url' => 'https://fontawesome.com',
        ':fontawesome_examples_page' => 'https://fontawesome.com/how-to-use/on-the-web/referencing-icons/basic-use',
      ]) . '</p>';
  }
}

/**
 * Implements hook_library_info_alter().
 */
function fontawesome_library_info_alter(&$libraries, $extension) {

  // Modify the Font Awesome library to use external file if user chose.
  if ($extension == 'fontawesome') {

    // Load the configuration settings.
    $configuration_settings = \Drupal::config('fontawesome.settings');

    // Have to modify the library if the user is using a CDN.
    if ($configuration_settings
      ->get('use_cdn')) {

      // First check if we're using everything.
      if (isset($libraries['fontawesome.' . $configuration_settings
        ->get('method')])) {
        _fontawesome_modify_library($libraries, NULL, $configuration_settings
          ->get('method'), $configuration_settings
          ->get('external_svg_location'));
      }

      // Determine the base for the CDN.
      $cdnComponents = parse_url($configuration_settings
        ->get('external_svg_location'));
      $cdnComponents['path'] = explode('/', $cdnComponents['path']);
      unset($cdnComponents['path'][count($cdnComponents['path']) - 1]);
      $cdnComponents['path'] = implode('/', $cdnComponents['path']) . '/';
      if (isset($libraries['fontawesome.' . $configuration_settings
        ->get('method') . '.base'])) {

        // Modify settings for the base file.
        $cdnBase = $cdnComponents;
        $cdnBase['path'] .= 'fontawesome.' . ($configuration_settings
          ->get('method') == 'webfonts' ? 'css' : 'js');
        _fontawesome_modify_library($libraries, 'base', $configuration_settings
          ->get('method'), _fontawesome_unparse_url($cdnBase));
      }

      // Modify settings for individual included files.
      foreach ([
        'solid',
        'regular',
        'light',
        'brands',
      ] as $libraryType) {
        if (isset($libraries['fontawesome.' . $configuration_settings
          ->get('method') . '.' . $libraryType])) {
          $cdnBase = $cdnComponents;
          $cdnBase['path'] .= $libraryType . '.' . ($configuration_settings
            ->get('method') == 'webfonts' ? 'css' : 'js');
          _fontawesome_modify_library($libraries, $libraryType, $configuration_settings
            ->get('method'), _fontawesome_unparse_url($cdnBase));
        }
      }

      // Modify the shim as well.
      if (isset($libraries['fontawesome.' . $configuration_settings
        ->get('method') . '.shim'])) {
        _fontawesome_modify_library($libraries, 'shim', $configuration_settings
          ->get('method'), $configuration_settings
          ->get('external_shim_location'));
      }
    }

    // Allow pseudo-elements in JS if selected.
    if ($configuration_settings
      ->get('allow_pseudo_elements') && $configuration_settings
      ->get('method') == 'svg') {

      // Modify the libraries to add pseudo elements tag.
      foreach ($libraries as $key => &$values) {
        if (substr($key, 0, 15) == 'fontawesome.svg') {
          $librarySettings = reset($values['js']);
          $librarySource = key($values['js']);

          // Font Awesome requires this script tag to enable pseudo elements.
          $librarySettings['attributes'] = [
            'data-search-pseudo-elements' => TRUE,
          ];
          $values['js'][$librarySource] = $librarySettings;
        }
      }
    }
  }
}

/**
 * Modifies library inclusions to use CDN files when necessary.
 *
 * @param array $libraries
 *   The libraries inclusion array.
 * @param string $librarySuffix
 *   The suffix of the library being modified.
 * @param string $type
 *   The type of library we are modifying.
 * @param string $cdnLocation
 *   The location of the CDN file being used.
 */
function _fontawesome_modify_library(array &$libraries, $librarySuffix, $type, $cdnLocation) {

  // Determine the name of the library.
  $libraryName = 'fontawesome.' . $type;
  if (!empty($librarySuffix)) {
    $libraryName .= '.' . $librarySuffix;
  }

  // Load the configuration settings.
  $configuration_settings = \Drupal::config('fontawesome.settings');

  // Handle SVG method.
  if ($type == 'svg') {
    $librarySettings = array_shift($libraries[$libraryName]['js']);
    $librarySettings['type'] = 'external';
    $librarySettings['attributes']['crossorigin'] = 'anonymous';

    // Add the integrity check if set.
    if (!empty($configuration_settings
      ->get('external_svg_integrity'))) {
      $librarySettings['attributes']['integrity'] = $configuration_settings
        ->get('external_svg_integrity');
    }
    $libraries[$libraryName]['js'] = [
      $cdnLocation => $librarySettings,
    ];
  }
  elseif ($type == 'webfonts') {
    $librarySettings = array_shift($libraries[$libraryName]['css']['theme']);
    $librarySettings['type'] = 'external';

    // TODO: add integrity and crossorigin to CSS.
    // See https://www.drupal.org/project/drupal/issues/2716115.
    $libraries[$libraryName]['css']['theme'] = [
      $cdnLocation => $librarySettings,
    ];
  }
}

/**
 * Unparses a CDN URL for use with individual Font Awesome file inclusions.
 *
 * @param array $parsed
 *   Array containing URL parsed data.
 *
 * @return string
 *   The unparsed URL for the CDN.
 */
function _fontawesome_unparse_url(array $parsed) {
  $get = function ($key) use ($parsed) {
    return isset($parsed[$key]) ? $parsed[$key] : NULL;
  };
  $pass = $get('pass');
  $user = $get('user');
  $userinfo = $pass !== NULL ? "{$user}:{$pass}" : $user;
  $port = $get('port');
  $scheme = $get('scheme');
  $query = $get('query');
  $fragment = $get('fragment');
  $authority = ($userinfo !== NULL ? "{$userinfo}@" : '') . $get('host') . ($port ? ":{$port}" : '');
  return (strlen($scheme) ? "{$scheme}:" : '') . (strlen($authority) ? "//{$authority}" : '') . $get('path') . (strlen($query) ? "?{$query}" : '') . (strlen($fragment) ? "#{$fragment}" : '');
}

/**
 * Implements hook_ckeditor_css_alter().
 *
 * This function allows for the proper functionality of the icons inside the
 * CKEditor when using Webfonts with CSS as the Font Awesome display method.
 *
 * See fontawesome_editor_js_settings_alter() for allowing the use of the icons
 * inside CKEditor when using the SVG with JS display method.
 */
function fontawesome_ckeditor_css_alter(&$css, $editor) {

  // Attach the main library if we're using the CSS webfonts method..
  if (\Drupal::config('fontawesome.settings')
    ->get('method') == 'webfonts') {

    // Load the library.
    $fontawesome_library = \Drupal::service('library.discovery')
      ->getLibraryByName('fontawesome', 'fontawesome.webfonts');

    // Attach it's CSS.
    $css[] = $fontawesome_library['css'][0]['data'];

    // Attach the shim CSS if needed.
    if (Drupal::config('fontawesome.settings')
      ->get('use_shim')) {

      // Load the library.
      $fontawesome_library_shim = \Drupal::service('library.discovery')
        ->getLibraryByName('fontawesome', 'fontawesome.webfonts.shim');

      // Attach it's CSS.
      $css[] = $fontawesome_library_shim['css'][0]['data'];
    }
  }
}

/**
 * Check to make sure that Font Awesome is installed.
 *
 * @return bool
 *   Flag indicating if the library is properly installed.
 */
function fontawesome_check_installed() {

  // Load the configuration settings.
  $configuration_settings = \Drupal::config('fontawesome.settings');

  // If this module is not configured to load the fontawesome assets, exit.
  if (!$configuration_settings
    ->get('load_assets')) {
    return TRUE;
  }

  // Throw error if library file not found.
  if ($configuration_settings
    ->get('use_cdn')) {
    return !empty($configuration_settings
      ->get('external_svg_location'));
  }
  elseif ($configuration_settings
    ->get('method') == 'webfonts') {

    // Webfonts method.
    $fontawesome_library = \Drupal::service('library.discovery')
      ->getLibraryByName('fontawesome', 'fontawesome.webfonts');
    return file_exists(DRUPAL_ROOT . '/' . $fontawesome_library['css'][0]['data']);
  }
  else {

    // SVG method.
    $fontawesome_library = \Drupal::service('library.discovery')
      ->getLibraryByName('fontawesome', 'fontawesome.svg');
    return file_exists(DRUPAL_ROOT . '/' . $fontawesome_library['js'][0]['data']);
  }
}

/**
 * Implements hook_page_attachments().
 *
 * Purposefully only load on page requests and not hook_init(). This is
 * required so it does not increase the bootstrap time of Drupal when it isn't
 * necessary.
 */
function fontawesome_page_attachments(array &$page) {

  // Load the configuration settings.
  $configuration_settings = \Drupal::config('fontawesome.settings');

  // Don't include fontawesome if the user has opted out of loading it.
  if (!$configuration_settings
    ->get('load_assets')) {
    return TRUE;
  }

  // Throw error if library file not found.
  if (!fontawesome_check_installed()) {
    \Drupal::messenger()
      ->addWarning(t('The Font Awesome library could not be found. Please verify Font Awesome is installed correctly or that the CDN has been activated and properly configured. Please see the @adminPage and the Font Awesome module README file for more details.', [
      '@adminPage' => Link::createFromRoute(t('admin page'), 'fontawesome.admin_settings')
        ->toString(),
    ]));
    return;
  }

  // Determine which files we are using.
  $activeFiles = [
    'use_solid_file' => is_null($configuration_settings
      ->get('use_solid_file')) === TRUE ? TRUE : $configuration_settings
      ->get('use_solid_file'),
    'use_regular_file' => is_null($configuration_settings
      ->get('use_regular_file')) === TRUE ? TRUE : $configuration_settings
      ->get('use_regular_file'),
    'use_light_file' => is_null($configuration_settings
      ->get('use_light_file')) === TRUE ? TRUE : $configuration_settings
      ->get('use_light_file'),
    'use_brands_file' => is_null($configuration_settings
      ->get('use_brands_file')) === TRUE ? TRUE : $configuration_settings
      ->get('use_brands_file'),
    'use_duotone_file' => is_null($configuration_settings
      ->get('use_duotone_file')) === TRUE ? TRUE : $configuration_settings
      ->get('use_duotone_file'),
  ];

  // First check if we're using everything.
  if (count(array_unique($activeFiles)) == 1) {

    // Attach the main library.
    $page['#attached']['library'][] = 'fontawesome/fontawesome.' . $configuration_settings
      ->get('method');
  }
  else {
    if ($activeFiles['use_solid_file']) {
      $page['#attached']['library'][] = 'fontawesome/fontawesome.' . $configuration_settings
        ->get('method') . '.solid';
    }
    if ($activeFiles['use_regular_file']) {
      $page['#attached']['library'][] = 'fontawesome/fontawesome.' . $configuration_settings
        ->get('method') . '.regular';
    }
    if ($activeFiles['use_light_file']) {
      $page['#attached']['library'][] = 'fontawesome/fontawesome.' . $configuration_settings
        ->get('method') . '.light';
    }
    if ($activeFiles['use_brands_file']) {
      $page['#attached']['library'][] = 'fontawesome/fontawesome.' . $configuration_settings
        ->get('method') . '.brands';
    }
    if ($activeFiles['use_duotone_file']) {
      $page['#attached']['library'][] = 'fontawesome/fontawesome.' . $configuration_settings
        ->get('method') . '.duotone';
    }
  }

  // Attach the shim file if needed.
  if ($configuration_settings
    ->get('use_shim')) {
    $page['#attached']['library'][] = 'fontawesome/fontawesome.' . $configuration_settings
      ->get('method') . '.shim';
  }
}

/**
 * Implements hook_theme().
 */
function fontawesome_theme($existing, $type, $theme, $path) {
  return [
    'fontawesomeicons' => [
      'variables' => [
        'icons' => NULL,
        'layers' => FALSE,
      ],
    ],
    'fontawesomeicon' => [
      'variables' => [
        'tag' => 'i',
        'name' => NULL,
        'style' => NULL,
        'settings' => NULL,
        'transforms' => NULL,
        'mask' => NULL,
        'css' => NULL,
      ],
    ],
  ];
}

/**
 * Implements hook_theme_suggestions_HOOK_alter().
 */
function fontawesome_theme_suggestions_fontawesomeicon(array $variables) {

  // Suggest a template with the icon name if it exists.
  $suggestions = [];
  if (!empty($variables['name'])) {
    $suggestions[] = $variables['theme_hook_original'] . '__' . $variables['name'];
  }
  return $suggestions;
}

/**
 * Implements hook_theme_registry_alter().
 */
function fontawesome_theme_registry_alter(&$theme_registry) {

  /*
   * By default, Drupal 8 does not include theme suggestions from inside the
   * module in which they were created, so we must add them manually here.
   */
  $path = drupal_get_path('module', 'fontawesome');
  $fontawesome_templates = drupal_find_theme_templates($theme_registry, '.html.twig', $path);
  foreach ($fontawesome_templates as &$fontawesome_template) {
    $fontawesome_template['type'] = 'module';
  }
  $theme_registry += $fontawesome_templates;
}

/**
 * Implements hook_icon_providers().
 */
function fontawesome_icon_providers() {
  $providers['fontawesome'] = [
    'title' => 'Font Awesome',
    'url' => 'http://fontawesome.io',
  ];
  return $providers;
}

/**
 * Implements hook_icon_bundle_configure().
 */
function fontawesome_icon_bundle_configure(&$settings, &$form_state, &$complete_form) {
  $bundle = $form_state['bundle'];
  if ($bundle['provider'] === 'fontawesome') {
    $settings['tag'] = [
      '#type' => 'select',
      '#title' => t('HTML Markup'),
      '#description' => t('Choose the HTML markup tag that Font Awesome icons should be created with. Typically, this is a %tag tag, however it can be changed to suite the theme requirements.', [
        '%tag' => '<' . $bundle['settings']['tag'] . '>',
      ]),
      '#options' => array_combine([
        'i',
        'span',
      ], [
        'i',
        'span',
      ]),
      '#default_value' => $bundle['settings']['tag'],
    ];
  }
}

/**
 * Implements hook_preprocess_icon_RENDER_HOOK().
 */
function fontawesome_preprocess_icon_sprite(&$variables) {
  $bundle =& $variables['bundle'];
  if ($bundle['provider'] === 'fontawesome') {

    // Remove the default "icon" class.
    $key = array_search('icon', $variables['attributes']['class']);
    if ($key !== FALSE) {
      unset($variables['attributes']['class'][$key]);
    }

    // TODO: need to add the correct class depending on icon type.
    // Add the necessary FA identifier class.
    $variables['attributes']['class'][] = 'fas';

    // Prepend the icon with the FA prefix (which will be used as the class).
    $variables['icon'] = 'fa-' . $variables['icon'];
  }
}

/**
 * Implements hook_icon_bundles().
 *
 * TODO: this is waiting on an 8.x release of Icon API.
 */
function fontawesome_icon_bundles() {
  $bundles['fontawesome'] = [
    'title' => 'Font Awesome',
    'provider' => 'fontawesome',
    'render' => 'sprite',
    'settings' => [
      'tag' => 'i',
    ],
    'icons' => \Drupal::service('fontawesome.font_awesome_manager')
      ->getIcons(),
  ];
  return $bundles;
}

/**
 * Implements hook_entity_presave().
 */
function fontawesome_entity_presave(EntityInterface $entity) {
  if ($entity instanceof ContentEntityInterface) {

    // Loop over the fields.
    foreach ($entity
      ->getFields() as $fields) {
      if ($fields instanceof ItemList) {

        // If this is a text field (uses an editor).
        if (in_array($fields
          ->getFieldDefinition()
          ->getType(), [
          'text',
          'text_long',
          'text_with_summary',
        ])) {
          foreach ($fields as $field) {

            // Find and replace SVG strings with original icon HTML.
            $fieldValue = $field
              ->getValue();
            $fieldValue['value'] = preg_replace('%<svg .*?class="svg-inline--fa.*?<\\/svg><!--\\s?(<(span|i).*?<\\/(span|i)>).*\\s?-->%', '$1', $fieldValue['value']);
            $field
              ->setValue($fieldValue);
          }
        }
      }
    }
  }
}

/**
 * Implements hook_editor_js_settings_alter().
 *
 * This function allows for the proper functionality of the icons inside the
 * CKEditor when using SVG with JS as the Font Awesome display method. This
 * function also provides for the use of empty tags inside the CKEditor. These
 * tags are normally stripped, which makes the traditional method of using
 * Font Awesome unworkable. Allowing those tags here lets users use the methods
 * of including icons described in all of the Font Awesome guides and docs.
 *
 * See fontawesome_ckeditor_css_alter() for allowing the use of the icons
 * inside CKEditor when using the Webfonts with CSS display method.
 */
function fontawesome_editor_js_settings_alter(array &$settings) {

  // Load the configuration settings.
  $configuration_settings = \Drupal::config('fontawesome.settings');

  // Attach our JS libraries as needed for loading inside the editor.
  if ($configuration_settings
    ->get('method') == 'svg') {

    // SVG mode requires loading javascript.
    $fontawesome_library = \Drupal::service('library.discovery')
      ->getLibraryByName('fontawesome', 'fontawesome.svg');
    if (!$configuration_settings
      ->get('use_cdn')) {
      $fontawesome_library['js'][0]['data'] = base_path() . $fontawesome_library['js'][0]['data'];
    }
    $settings['editor']['fontawesome']['fontawesomeLibraries']['primary'] = $fontawesome_library['js'][0]['data'];

    // Load the shim file as well if needed.
    if ($configuration_settings
      ->get('use_shim')) {
      $fontawesome_library = \Drupal::service('library.discovery')
        ->getLibraryByName('fontawesome', 'fontawesome.svg.shim');
      if (!$configuration_settings
        ->get('use_cdn')) {
        $fontawesome_library['js'][0]['data'] = base_path() . $fontawesome_library['js'][0]['data'];
      }
      $settings['editor']['fontawesome']['fontawesomeLibraries']['shim'] = $fontawesome_library['js'][0]['data'];
    }
  }

  // Attach the list of allowed empty tags.
  $settings['editor']['fontawesome']['allowedEmptyTags'] = [
    'i',
    'span',
  ];
}

Functions

Namesort descending Description
fontawesome_check_installed Check to make sure that Font Awesome is installed.
fontawesome_ckeditor_css_alter Implements hook_ckeditor_css_alter().
fontawesome_editor_js_settings_alter Implements hook_editor_js_settings_alter().
fontawesome_entity_presave Implements hook_entity_presave().
fontawesome_help Implements hook_help().
fontawesome_icon_bundles Implements hook_icon_bundles().
fontawesome_icon_bundle_configure Implements hook_icon_bundle_configure().
fontawesome_icon_providers Implements hook_icon_providers().
fontawesome_library_info_alter Implements hook_library_info_alter().
fontawesome_page_attachments Implements hook_page_attachments().
fontawesome_preprocess_icon_sprite Implements hook_preprocess_icon_RENDER_HOOK().
fontawesome_theme Implements hook_theme().
fontawesome_theme_registry_alter Implements hook_theme_registry_alter().
fontawesome_theme_suggestions_fontawesomeicon Implements hook_theme_suggestions_HOOK_alter().
_fontawesome_modify_library Modifies library inclusions to use CDN files when necessary.
_fontawesome_unparse_url Unparses a CDN URL for use with individual Font Awesome file inclusions.