You are here

asset_injector.module in Asset Injector 8.2

Same filename and directory in other branches
  1. 8 asset_injector.module

Contains module asset_injector.

File

asset_injector.module
View source
<?php

/**
 * @file
 * Contains module asset_injector.
 */
use Drupal\asset_injector\AssetFileStorage;
use Drupal\asset_injector\AssetInjectorInterface;
use Drupal\Core\Config\Entity\ConfigEntityType;
use Drupal\Core\Routing\RouteMatchInterface;
use Drupal\Core\Asset\AttachedAssetsInterface;
use Drupal\editor\Entity\Editor;

/**
 * Implements hook_help().
 */
function asset_injector_help($route_name, RouteMatchInterface $route_match) {
  switch ($route_name) {
    case 'help.page.asset_injector':
      $output = '<p>' . t('Use Asset injector rules to add small snippets of code to the page output when specific criteria are met. For example, a simple rule could float a particular div to the right on node editing pages.') . '</p>';
      return $output;
    default:
  }
}

/**
 * Implements hook_entity_type_build().
 *
 * Mark our entities for use in @see asset_injector_get_entity_types().
 * This way contrib modules can add additional assets by implementing
 * \Drupal\asset_injector\AssetInjectorInterface.
 */
function asset_injector_entity_type_build(array &$entity_types) {
  foreach ($entity_types as $entity_type) {
    if ($entity_type instanceof ConfigEntityType) {
      $interfaces = class_implements($entity_type
        ->getClass());
      if (isset($interfaces[AssetInjectorInterface::class])) {

        // Mark as ours.
        $entity_type
          ->set('asset_injector_entity_type', TRUE);

        // Add our duplicate-form.
        $path = $entity_type
          ->getLinkTemplate('canonical');
        $entity_type
          ->setLinkTemplate('duplicate-form', "{$path}/duplicate");
      }
    }
  }
}

/**
 * Implements hook_css_alter().
 */
function asset_injector_css_alter(array &$css, AttachedAssetsInterface $assets) {
  $libraries = $assets
    ->getLibraries();
  foreach (asset_injector_library_info_build() as $lib_id => $library) {
    if (in_array("asset_injector/{$lib_id}", $libraries) && !empty($library['css'])) {
      foreach ($library['css'] as $group) {
        foreach (array_keys($group) as $file) {
          $css[trim($file, '/ ')]['group'] = 999;
        }
      }
    }
  }
}

/**
 * Implements hook_ckeditor_css_alter().
 */
function asset_injector_ckeditor_css_alter(array &$css, Editor $editor) {
  foreach (asset_injector_get_assets(TRUE, [
    'asset_injector_css',
  ]) as $asset) {
    $css[] = file_create_url($asset
      ->internalFileUri());
  }
}

/**
 * Implements hook_library_info_build().
 *
 * Map the library IDs defined in @see asset_injector_page_attachments() to the
 * actual assets. Note that drupal prefixes the IDs with our module name so we
 * must not do that here.
 */
function asset_injector_library_info_build() {
  $libraries = [];
  foreach (asset_injector_get_assets() as $asset) {
    if ($library_info = $asset
      ->libraryInfo()) {
      $libraries[$asset
        ->libraryNameSuffix()] = $library_info;
    }
  }
  \Drupal::moduleHandler()
    ->alter('asset_injector_library_info_build', $libraries);
  return $libraries;
}

/**
 * Implements hook_page_attachments().
 *
 * Give the render system the IDs of the currently active assets (that may
 * depend on the current page and other context - think config overrides).
 * These IDs are mapped to the actual assets
 * in @see asset_injector_library_info_build().
 * Note that the IDs are namespaced with our module name.
 *
 * Concerning cache contexts: The config override system may introduce
 * additional cache contexts to aur assets. Think css that varies by domain.
 * By adding our assets as cacheble dependencies all contexts they may carry
 * apply to the rendered result.
 *
 * Note that the list_cache_tags (library_info) are not added here and need not,
 * as the caller already does it. Setting asset entities list_cache_tags to
 * library_info makes the library-info invalidate on asset change.
 * While changing and deleting of assets will trigger invalidation by their
 * individual cache tags, the list cache tags guarantees invalidation on new
 * asset creation.
 */
function asset_injector_page_attachments(array &$attachments) {

  /** @var \Drupal\Core\Render\RendererInterface $renderer */
  $renderer = \Drupal::service('renderer');

  /** @var \Drupal\Core\Asset\AttachedAssetsInterface $asset */
  foreach (asset_injector_get_assets(TRUE) as $asset) {
    $attachments['#attached']['library'][] = 'asset_injector/' . $asset
      ->libraryNameSuffix();
    $renderer
      ->addCacheableDependency($attachments, $asset);
  }
}

/**
 * Get all available assets.
 *
 * @param bool|null $active
 *   Get only active (true), inactive (false), or all (null) assets.
 * @param array $types
 *   Array of entity type ids to limit the return.
 *
 * @return \Drupal\asset_injector\AssetInjectorInterface[]
 *   Assets from css & js injectors.
 */
function asset_injector_get_assets($active = NULL, array $types = []) {

  /** @var \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager */
  $entity_type_manager = \Drupal::entityTypeManager();
  $assets = [];
  foreach (asset_injector_get_entity_types($types) as $entity_type_id => $entity_type) {
    $entity_type_storage = $entity_type_manager
      ->getStorage($entity_type_id);
    $asset_ids = $entity_type_storage
      ->getQuery()
      ->execute();
    foreach ($entity_type_storage
      ->loadMultiple($asset_ids) as $asset) {

      // Get both active and not active assets.
      if (is_null($active)) {
        $assets[] = $asset;
      }
      else {
        $access = $asset
          ->access('view');

        // Get only active assets.
        if ($active && $access) {
          $assets[] = $asset;
        }
        elseif (!$active && !$access) {
          $assets[] = $asset;
        }
      }
    }
  }
  return $assets;
}

/**
 * Get asset entity types.
 *
 * @param array $types
 *   Basic array of entity type ids to limit the return of..
 *
 * @return array
 *   Keyed array of entities defined as asset injector types.
 *
 * @see asset_injector_entity_type_build()
 */
function asset_injector_get_entity_types(array $types = []) {
  $asset_entity_types =& drupal_static(__FUNCTION__);
  if (!isset($asset_entity_types)) {
    $entity_types = \Drupal::entityTypeManager()
      ->getDefinitions();
    $asset_entity_types = [];
    foreach ($entity_types as $entity_type_id => $entity_type) {
      if ($entity_type
        ->get('asset_injector_entity_type')) {
        $asset_entity_types[$entity_type_id] = $entity_type;
      }
    }
  }
  if (!empty($types)) {
    return array_intersect_key($asset_entity_types, array_flip($types));
  }
  return $asset_entity_types;
}

/**
 * Implements hook_cache_flush().
 *
 * Delete all asset files.
 */
function asset_injector_cache_flush() {
  try {
    AssetFileStorage::deleteAllFiles();
  } catch (\Exception $e) {
    \Drupal::messenger()
      ->addWarning(t('Unable to regenerate the Asset Injector assets. This is due to a file permission issue, please contact the site administrator.'));
  }
}

/**
 * Implements hook_preprocess_page().
 *
 * Insert <noscript> code into page region.
 */
function asset_injector_preprocess_page(&$vars) {
  foreach (asset_injector_get_assets(TRUE, [
    'asset_injector_js',
  ]) as $asset) {
    if (empty($asset->noscript)) {
      continue;
    }
    $active_theme = \Drupal::service('theme.manager')
      ->getActiveTheme()
      ->getName();

    // Support legacy code where region was a string vs an array.
    $no_script_regions = is_array($asset->noscriptRegion) ? $asset->noscriptRegion : [
      $active_theme => $asset->noscriptRegion,
    ];
    if (!empty($no_script_regions[$active_theme])) {
      $region = $no_script_regions[$active_theme];
      $vars['page'][$region][$asset->id . '-noscript'] = [
        '#type' => 'inline_template',
        '#template' => '<noscript>{{ code | raw }}</noscript>',
        '#context' => [
          'code' => $asset->noscript,
        ],
      ];
    }
  }
}