You are here

css_injector.module in CSS Injector 7.2

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

css_injector.module

Allows administrators to inject CSS into the page output based on configurable rules. Useful for adding simple CSS tweaks without modifying a site's official theme.

File

css_injector.module
View source
<?php

/**
 * @file css_injector.module
 *
 * Allows administrators to inject CSS into the page output based on
 * configurable rules. Useful for adding simple CSS tweaks without modifying
 * a site's official theme.
 */
define('CSS_INJECTOR_DIRECTORY_URI', 'public://css_injector');

/**
 * Implements hook_help().
 */
function css_injector_help($path, $arg) {
  $output = '';
  switch ($path) {
    case 'admin/config/development/css-injector':
      $output .= '<p>' . t('Use CSS injection rules to add small snippets of CSS to the page output when specific criteria are met. For example, a simple rule could change the page background color at night or float a particular div to the right on node editing pages.') . '</p>';
      break;
  }
  return $output;
}

/**
 * Implements hook_permission().
 */
function css_injector_permission() {
  return array(
    'administer css_injector' => array(
      'title' => t('Administer CSS injector'),
      'description' => t('Create and delete CSS snippets for the site.'),
      'restrict access' => TRUE,
    ),
  );
}

/**
 * Implementation of hook_ctools_plugin_directory().
 *
 * In order for CTools to be able to provide an interface for administering the
 * configuration presets, we have to expose the preset schema to the Export UI.
 */
function css_injector_ctools_plugin_directory($module, $plugin) {
  if ($module == 'ctools' && $plugin == 'export_ui') {
    return 'plugins/' . $plugin;
  }
}

/**
 * Implements 'load callback' for css_injector_rule exportables.
 */
function css_injector_rule_load($name) {
  ctools_include('export');
  $result = ctools_export_load_object('css_injector_rule', 'names', array(
    $name,
  ));
  if (isset($result[$name])) {
    return $result[$name];
  }
}

/**
 * Implements 'load multiple callback' for css_injector_rule exportables.
 */
function css_injector_rule_load_multiple(array $names) {
  ctools_include('export');
  $results = ctools_export_load_object('css_injector_rule', 'names', $names);
  return array_filter($results);
}

/**
 * Save a single CSS injector rule. This custom save callback is required in
 * order to write (or update) the CSS file on the filesystem
 */
function css_injector_rule_save($rule) {
  $schema = ctools_export_get_schema('css_injector_rule');
  $export = $schema['export'];

  // objects should have a serial primary key. If not, simply fail to write.
  if (empty($export['primary key'])) {
    return FALSE;
  }
  $key = $export['primary key'];
  if ($rule->export_type & EXPORT_IN_DATABASE) {

    // existing record.
    $update = array(
      $key,
    );
  }
  else {

    // new record.
    $update = array();
    $rule->export_type = EXPORT_IN_DATABASE;
  }

  // rule is passed by reference, and the crid is written into it upon save
  $success = drupal_write_record('css_injector_rule', $rule, $update);
  if ($success == FALSE || empty($rule->crid)) {
    return FALSE;
  }

  // write the CSS file to the filesystem
  $file_written = file_unmanaged_save_data($rule->css, _css_injector_rule_uri($rule->crid), FILE_EXISTS_REPLACE);
  return $file_written != FALSE && is_numeric($rule->crid);
}

/**
 * Delete a single CSS injector rule. Override is required to clean up the
 * filesystem
 */
function css_injector_rule_delete($rule) {
  $schema = ctools_export_get_schema('css_injector_rule');
  $export = $schema['export'];

  // If we were sent an object, get the export key from it. Otherwise
  // assume we were sent the export key.
  $value = is_object($rule) ? $rule->{$export['key']} : $rule;
  db_delete('css_injector_rule')
    ->condition($export['key'], $value)
    ->execute();

  // delete the CSS file to the filesystem
  return file_unmanaged_delete(_css_injector_rule_uri($rule->crid));
}

/**
 * Implements hook_init().
 *
 * Checks to see whether any CSS files should be added to the current page,
 * based on rules configured by the site administrator.
 */
function css_injector_init() {

  // load all the rules
  ctools_include('export');
  $rules = ctools_export_load_object('css_injector_rule');
  foreach ($rules as $name => $rule) {

    // check if the rule actually exists in the db... required if files exist w/o db entry
    if (!isset($rule->crid)) {
      continue;
    }

    // check if the rule is disabled in the ctools UI, if so skip it
    if (isset($rule->disabled) && $rule->disabled == TRUE) {
      continue;
    }

    // check the page visibility settings
    if (_css_injector_visibility_pages($rule) == FALSE) {
      continue;
    }

    // add the css
    $code = $rule->inline == 1 ? $rule->css : _css_injector_rule_path($rule->crid);
    drupal_add_css($code, array(
      'type' => $rule->inline == 1 ? 'inline' : 'file',
      'media' => $rule->media,
      // this group has the highest weight
      'group' => CSS_THEME,
      'every_page' => FALSE,
      // safe guard to ensure inline files are never preprocessed
      'preprocess' => $rule->inline == 1 ? FALSE : $rule->preprocess,
      // since we're trying to give the administrator complete control, we'll
      // move JS that this module has added to a high weight, higher even than
      // the theme's CSS files. Currently the weight is 200 + the crid, which is
      // currently higher than Bartik's CSS.
      'weight' => 200 + $rule->crid,
    ));
  }
}

/**
 * Based on visibility setting this function returns TRUE if the CSS injector
 * rule code should be added to the current page and otherwise FALSE.
 *
 * Code ported from googleanalytics.module
 */
function _css_injector_visibility_pages($rule) {
  $visibility = $rule->page_visibility;
  $setting_pages = $rule->page_visibility_pages;

  // Match path if necessary.
  if (!empty($setting_pages)) {

    // Convert path to lowercase. This allows comparison of the same path
    // with different case. Ex: /Page, /page, /PAGE.
    $pages = drupal_strtolower($setting_pages);
    if ($visibility < 2) {

      // Convert the Drupal path to lowercase
      $path = drupal_strtolower(drupal_get_path_alias($_GET['q']));

      // Compare the lowercase internal and lowercase path alias (if any).
      $page_match = drupal_match_path($path, $pages);
      if ($path != $_GET['q']) {
        $page_match = $page_match || drupal_match_path($_GET['q'], $pages);
      }

      // When $visibility has a value of 0, the tracking code is displayed on
      // all pages except those listed in $pages. When set to 1, it
      // is displayed only on those pages listed in $pages.
      $page_match = !($visibility xor $page_match);
    }
    elseif (module_exists('php')) {
      $page_match = php_eval($setting_pages);
    }
    else {
      $page_match = FALSE;
    }
  }
  else {
    $page_match = TRUE;
  }
  return $page_match;
}

/**
 * Helper function to get file path for a rule.
 * This will get the path relative to DRUPAL_ROOT,
 * as in 'sites/default/files/css_injector/css_injector_99.css'.
 *
 * @param $crid
 *   The CSS injector rule unique ID
 */
function _css_injector_rule_path($crid) {
  if (!empty($crid)) {
    $local_path = file_create_url(_css_injector_rule_uri($crid));

    // Now remove the part before the drupal root.
    $local_path = preg_replace('/^' . preg_quote($GLOBALS['base_url'], '/') . '\\//', '', $local_path);
    return $local_path;
  }
  return NULL;
}

/**
 * Return the URI for a crid.
 *
 * @param $crid
 *   The CSS injector rule unique ID
 */
function _css_injector_rule_uri($crid) {
  if (!empty($crid)) {
    return CSS_INJECTOR_DIRECTORY_URI . '/css_injector_' . $crid . '.css';
  }
}

Functions

Namesort descending Description
css_injector_ctools_plugin_directory Implementation of hook_ctools_plugin_directory().
css_injector_help Implements hook_help().
css_injector_init Implements hook_init().
css_injector_permission Implements hook_permission().
css_injector_rule_delete Delete a single CSS injector rule. Override is required to clean up the filesystem
css_injector_rule_load Implements 'load callback' for css_injector_rule exportables.
css_injector_rule_load_multiple Implements 'load multiple callback' for css_injector_rule exportables.
css_injector_rule_save Save a single CSS injector rule. This custom save callback is required in order to write (or update) the CSS file on the filesystem
_css_injector_rule_path Helper function to get file path for a rule. This will get the path relative to DRUPAL_ROOT, as in 'sites/default/files/css_injector/css_injector_99.css'.
_css_injector_rule_uri Return the URI for a crid.
_css_injector_visibility_pages Based on visibility setting this function returns TRUE if the CSS injector rule code should be added to the current page and otherwise FALSE.

Constants