You are here

skinr_context.module in Skinr 8.2

Same filename and directory in other branches
  1. 7.2 skinr_context/skinr_context.module

Provides Skinr integration with Context.

File

skinr_context/skinr_context.module
View source
<?php

/**
 * Implements hook_hook_info().
 */
function skinr_context_hook_info() {
  $hooks['skinr_context_group_defaults'] = array(
    'group' => 'skinr_default',
  );
  return $hooks;
}

/**
 * Implements hook_module_implements_alter().
 */
function skinr_context_module_implements_alter(&$implementations, $hook) {
  if ($hook == 'modules_enabled') {

    // Make our version of hook_modules_enabled() happen before that of
    // skinr core. This way skin settings groups are imported before skin
    // configurations.
    $group = $implementations['skinr_context'];
    unset($implementations['skinr_context']);
    $implementations = array_reverse($implementations, TRUE);
    $implementations['skinr_context'] = $group;
    $implementations = array_reverse($implementations, TRUE);
  }
}

/**
 * @file
 * Provides Skinr integration with Context.
 */

/**
 * Validate a skin group object.
 *
 * @param $group
 *   A skin group object.
 *
 * @return
 *   TRUE on success, FALSE on failure.
 */
function skinr_context_group_validate(&$group) {
  if (empty($group->gid) || empty($group->module) || empty($group->element) || empty($group->title)) {
    return FALSE;
  }
  if (!isset($group->conditions) || !is_array($group->conditions)) {
    return FALSE;
  }
  return TRUE;
}

/**
 * Save a skin group object.
 *
 * @param $group
 *   A skin settings group object.
 *
 * @return
 *   TRUE on success, FALSE on failure.
 */
function skinr_context_group_save(&$group) {

  // Make sure we're getting valid data.
  if (!skinr_context_group_validate($group)) {
    return FALSE;
  }

  // Load the stored skin settings group object, if any.
  if (!isset($group->original)) {

    // Load an uncached version of the skin settings object.
    if (!($group->original = skinr_context_group_load_unchanged($group->gid))) {
      unset($group->original);
    }
  }

  // Let modules modify the node before it is saved to the database.
  module_invoke_all('skinr_context_group_presave', $group);

  // Clear the static loading cache. We do it ahead of saving to ensure
  // skinr_context_skinr_skin_presave(), invoked through skinr_skin_save(),
  // loads an updated $group.
  // @todo Once we have a more granular reset for skinr_skin_load_multiple(), we
  //   need to use it here.
  drupal_static_reset('skinr_context_group_load_multiple');

  // Clear context's cache.
  context_invalidate_cache();
  if (!empty($group->original)) {

    // Record exists, so let's update.
    $status = drupal_write_record('skinr_groups', $group, 'gid');

    // When status changes, update status of linked skin settings.
    if ($group->status != $group->original->status) {
      $params = array(
        'gid' => $group->gid,
        'status' => $group->status ? 0 : 1,
      );

      // Update status of linked skin configurations.
      $sids = skinr_context_group_get_sids($params);
      foreach ($sids as $sid) {
        $skin = entity_load('skin', $sid, TRUE);
        $skin->original = clone $skin;
        $skin->status = $group->status;
        skinr_skin_save($skin);
      }
    }
    module_invoke_all('skinr_context_group_update', $group);
  }
  else {

    // Insert a new record.
    $status = drupal_write_record('skinr_groups', $group);
    module_invoke_all('skinr_context_group_insert', $group);
  }

  // Clear internal properties.
  unset($group->original);
  return $status;
}

/**
 * Delete a skin group object.
 *
 * @param $gid
 *   The skin settings group ID.
 */
function skinr_context_group_delete($gid) {
  skinr_context_group_delete_multiple(array(
    $gid,
  ));
}

/**
 * Delete multiple skin settings group objects.
 *
 * @param $gids
 *   An array of skin settings group IDs.
 */
function skinr_context_group_delete_multiple($gids) {
  $transaction = db_transaction();
  if (!empty($gids)) {
    $groups = skinr_context_group_load_multiple($gids);
    try {
      foreach ($groups as $gid => $group) {
        module_invoke_all('skinr_context_group_delete', $group);

        // Delete all skin settings associated with this group.
        $params = array(
          'gid' => $gid,
        );
        $sids = skinr_context_group_get_sids($params);
        entity_delete_multiple('skin', $sids);
      }

      // Delete after calling hooks so that they can query node tables as needed.
      db_delete('skinr_groups')
        ->condition('gid', $gids, 'IN')
        ->execute();
    } catch (Exception $e) {
      $transaction
        ->rollback();
      watchdog_exception('skinr', $e);
      throw $e;
    }

    // Clear the skinr_context_group_load_multiple cache.
    drupal_static_reset('skinr_context_group_load_multiple');

    // Clear context's cache.
    context_invalidate_cache();
  }
}

/**
 * Load a skin settings group object from the database.
 *
 * @param $gid
 *   The skin settings group ID.
 *
 * @return
 *   A fully-populated skin settings group object.
 */
function skinr_context_group_load($gid = NULL) {
  $gids = isset($gid) ? array(
    $gid,
  ) : array();
  $group = skinr_context_group_load_multiple($gids);
  return $group ? reset($group) : FALSE;
}

/**
 * Load skin settings group objects from the database.
 *
 * This function should be used whenever you need to load more than one skin
 * configuration group from the database. Skin settings groups are loaded
 * into memory and will not require database access if loaded again during the
 * same page request.
 *
 * @see skinr_context_group_get_gids()
 *
 * @param $gids
 *   An array of skin settings group IDs.
 *
 * @return
 *   An array of skin settings group objects indexed by gid.
 */
function skinr_context_group_load_multiple($gids = array()) {

  // @todo Do we want to write a more granular cache reset?
  $groups =& drupal_static(__FUNCTION__, array());

  // Create a new variable which is either a prepared version of the $gids
  // array for later comparison with cached skin group objects, or FALSE
  // if no $gids were passed. The $gids array is reduced as items are loaded
  // from cache, and we need to know if it's empty for this reason to avoid
  // querying the database when all requested skin group objects are
  // loaded from cache.
  $passed_gids = !empty($gids) ? array_flip($gids) : FALSE;
  if ($passed_gids) {
    $gids = array_keys(array_diff_key($passed_gids, $groups));
  }

  // Load any remaining skin groups from the database. This is the
  // case if $gids is set to FALSE (so we load all groups), or if there are any
  // gids left to load.
  if ($gids === FALSE || $gids) {

    // Build the query.
    $query = db_select('skinr_groups', 'g')
      ->fields('g', array(
      'gid',
      'module',
      'element',
      'title',
      'description',
      'conditions',
      'condition_mode',
      'weight',
      'status',
    ));
    if ($gids !== FALSE) {
      $query
        ->condition('gid', $gids);
    }
    $queried_groups = $query
      ->execute()
      ->fetchAllAssoc('gid');
    foreach ($queried_groups as $gid => $group) {

      // Unserialize options array.
      $queried_groups[$gid]->conditions = unserialize($group->conditions);

      // Let modules modify the skin settings group.
      module_invoke_all('skinr_context_group_load', $queried_groups[$gid]);
    }
    $groups += $queried_groups;
  }

  // Ensure that the returned array is ordered the same as the original
  // $gids array if this was passed in and remove any invalid gids.
  if ($passed_gids) {

    // Remove any invalid gids from the array.
    $passed_gids = array_intersect_key($passed_gids, $groups);
    $return = array();
    foreach ($passed_gids as $gid => $ignore) {
      $return[$gid] = $groups[$gid];
    }
  }
  else {
    $return = $groups;
  }
  return $return;
}

/**
 * Load an uncached version of a skin settings group object.
 *
 * @param $gid
 *   The skin settings group ID.
 *
 * @return
 *   A fully-populated skin settings group object.
 */
function skinr_context_group_load_unchanged($gid) {

  // Load an uncached version of the skin settings object. Specify order to
  // ensure consistent import/export.
  $group = db_query("SELECT gid, module, element, title, description, conditions, condition_mode, weight, status FROM {skinr_groups} WHERE gid = :gid", array(
    ':gid' => $gid,
  ))
    ->fetchObject();

  // Unserialize options array.
  if ($group) {
    $group->conditions = unserialize($group->conditions);

    // Let modules modify the skin settings group.
    module_invoke_all('skinr_context_group_load', $group);
  }
  return $group;
}

/**
 * Returns all default skin settings group objects.
 */
function _skinr_context_group_get_defaults() {
  $default_groups =& drupal_static(__FUNCTION__);
  if (!isset($default_groups)) {
    if ($cached = cache_get('skinr_context_group_defaults')) {
      $default_skins = $cached->data;
      return $default_skins;
    }

    // Don't use module_invoke_all() to prevent oddly merged defaults.
    $default_groups = array();
    foreach (module_implements('skinr_context_group_defaults') as $module) {
      $function = $module . '_skinr_context_group_defaults';
      if (function_exists($function)) {
        $result = call_user_func_array($function, array());
        if (isset($result) && is_array($result)) {
          $default_groups = array_merge($default_groups, $result);
        }
        elseif (isset($result)) {
          $default_groups[] = $result;
        }
      }
    }

    // Let modules modify the skin settings group.
    drupal_alter('skinr_context_group_defaults', $default_groups);
    cache_set('skinr_context_group_defaults', $default_groups);
  }
  return $default_groups;
}

/**
 * Imports default skin settings group objects from code into database.
 *
 * @param $module_name
 *   Limit importing of defaults to a single module.
 * @param $force
 *   If FALSE (default) the default skin settings group will only be imported if
 *   it doesn't exist, or if storage is default in code. If TRUE, the skin
 *   settings group in code will always be overwritten.
 *
 * @return
 *  If an import failed, returns FALSE. If all imports succeeded, returns TRUE.
 */
function skinr_context_group_defaults_import($module_name = NULL, $force = FALSE) {
  if (isset($module_name)) {
    if (!($default_groups = module_invoke($module_name, 'skinr_context_group_defaults'))) {
      $default_groups = array();
    }
    drupal_alter('skinr_context_group_defaults', $default_groups);
  }
  else {
    $default_groups = _skinr_context_group_get_defaults();
  }
  $status = TRUE;
  foreach ($default_groups as $group) {
    $status = skinr_context_group_import($group, $force) && $status;
  }
  return $status;
}

/**
 * Revert a skin settings group object that's stored in code to its default state.
 *
 * @param $gid
 *   The skin settings group ID.
 */
function skinr_context_group_revert($gid) {
  $default_groups = _skinr_context_group_get_defaults();
  if (!isset($default_groups[$gid])) {
    return FALSE;
  }
  return skinr_context_group_import($default_groups[$gid], TRUE);
}

/**
 * Import a skin settigns group object as defined in skinr_context_group_defaults().
 *
 * @param $default_group
 *   A skin settings group object.
 * @param $force
 *   If FALSE (default) the default skin settings group will only be imported if
 *   it doesn't exist, or if storage is default in code. If TRUE, the skin
 *   settings group in code will always be overwritten.
 *
 * @return
 *  If import failed, returns FALSE. If it succeeded, returns TRUE.
 */
function skinr_context_group_import($default_group, $force = FALSE) {

  // Make sure we don't modify the cached default group array.
  $group = clone $default_group;

  // Functionality is abstracted for use in skinr_context_group_storage().
  _skinr_context_group_import($group);
  if (!$force) {

    // Load existing skin configuration from DB.
    if ($db_group = skinr_context_group_load_unchanged($group->gid)) {

      // Sync status.
      $group->status = $db_group->status;
      if ($group != $db_group) {

        // Group exists and is overridden, so cancel import.
        watchdog('skinr', 'Canceled import of group with ID %gid. It is overridden.', array(
          '%gid' => $group->gid,
        ), WATCHDOG_WARNING);
        return FALSE;
      }
    }
  }
  if ($status = skinr_context_group_save($group)) {
    watchdog('skinr', 'Imported group with ID %gid.', array(
      '%gid' => $group->gid,
    ), WATCHDOG_NOTICE);
  }
  else {
    watchdog('skinr', 'Failed to import group with ID %gid.', array(
      '%gid' => $group->gid,
    ), WATCHDOG_WARNING);
  }
  return $status;
}
function _skinr_context_group_import(&$group) {

  // Let modules modify the skin settings group.
  drupal_alter('skinr_context_group_import', $group);
}

/**
 * Output a settings group object as code suitable for skinr_context_group_defaults().
 *
 * @param $group
 *   A skin settings group object.
 * @param $prefix
 *   A string to prefix the code with, used to indent the resulting code.
 *
 * @return
 *   A string.
 */
function skinr_context_group_export($group, $prefix = '') {

  // Make sure we don't modify the cached group object.
  $group = clone $group;

  // Let modules modify the skin settings group.
  drupal_alter('skinr_context_group_export', $group, $prefix);
  $output = skinr_object_export($group, 'group', $prefix);
  $output .= $prefix . "\$groups['{$group->gid}'] = \$group;\n";
  return $output;
}

/**
 * Returns a skin settings group's storage method.
 *
 * @param $skin
 *   A skin settings group.
 *
 * @return
 *   SKINR_STORAGE_IN_DATABASE if stored in the database,
 *   SKINR_STORAGE_IN_CODE if stored in code,
 *   SKINR_STORAGE_IN_CODE_OVERRIDDEN if stored in code and overridden in db.
 */
function skinr_context_group_storage($group) {
  $default_groups = _skinr_context_group_get_defaults();
  $storage = SKINR_STORAGE_IN_DATABASE;
  if (isset($default_groups[$group->gid])) {
    $default_group = clone $default_groups[$group->gid];

    // Make sure group has same processing as import.
    _skinr_context_group_import($default_group);

    // API version is only used for export.
    unset($default_group->api_version);

    // Status shouldn't influence overridden.
    $default_group->status = $group->status;
    $storage = SKINR_STORAGE_IN_CODE;
    if ($default_group != $group) {

      // Default was overridden.
      $storage = SKINR_STORAGE_IN_CODE_OVERRIDDEN;
    }
  }
  return $storage;
}

/**
 * Get skin settings group IDs.
 *
 * @param $filter_by
 *   An associative array whose keys are:
 *   - module: (optional) The module.
 *   - element: (optional) The element ID.
 *
 * @return
 *   An array of skin settings group IDs.
 */
function skinr_context_group_get_gids($filter_by = array()) {
  $query = db_select('skinr_groups', 'g')
    ->fields('g', array(
    'gid',
  ));
  if (isset($filter_by['module'])) {
    $query
      ->condition('module', $filter_by['module']);
  }
  if (isset($filter_by['element'])) {
    $query
      ->condition('element', $filter_by['element']);
  }

  // Take weight into account.
  $query
    ->orderBy('weight');
  $query
    ->orderBy('gid');
  return $query
    ->execute()
    ->fetchCol();
}

/**
 * Get skin settings IDs for grouped skin settings.
 *
 * @param $filter_by
 *   An associative array whose keys are:
 *   - theme: (optional) The theme.
 *   - module: (optional) The module.
 *   - element: (optional) The element ID.
 *   - gid: (optional) The group ID.
 *   - skin: (optional) The skin name.
 *   - status: (optional) Boolean indicating whether or not this skin
 *     configuration is enabled.
 *
 * @return
 *   An array of skin settings IDs.
 */
function skinr_context_group_get_sids($filter_by = array()) {
  $query = db_select('skinr_skins', 's');
  $query
    ->join('skinr_group_skins', 'gs', 's.sid = gs.sid');
  $query
    ->fields('s', array(
    'sid',
  ));
  if (isset($filter_by['theme'])) {
    $query
      ->condition('s.theme', $filter_by['theme']);
  }
  if (isset($filter_by['module'])) {
    $query
      ->condition('s.module', $filter_by['module']);
  }
  if (isset($filter_by['element'])) {
    $query
      ->condition('s.element', $filter_by['element']);
  }
  if (isset($filter_by['gid'])) {
    $query
      ->condition('gs.gid', $filter_by['gid']);
  }
  if (isset($filter_by['skin'])) {
    $query
      ->condition('s.skin', $filter_by['skin']);
  }
  if (isset($filter_by['status'])) {
    $query
      ->condition('s.status', $filter_by['status']);
  }
  return $query
    ->execute()
    ->fetchCol();
}

/**
 * Helper function to create a context object from a skin settings group.
 *
 * @param $group
 *   Skinr settings group object.
 *
 * @return
 *   A context object.
 */
function skinr_context_group_to_context($group) {
  $context = (object) array(
    'name' => 'skinr_group__' . $group->gid,
    'description' => !empty($group->description) ? t('@title: @description', array(
      '@title' => $group->title,
      '@description' => $group->description,
    )) : check_plain($group->title),
    'tag' => 'Skinr',
    'conditions' => $group->conditions,
    'reactions' => array(),
    'condition_mode' => $group->condition_mode,
  );
  return $context;
}

/**
 * Implements hook_ctools_plugin_api().
 */
function skinr_context_ctools_plugin_api($module, $api) {
  if ($module == "context" && $api == "context") {
    return array(
      "version" => 3,
    );
  }
}

/**
 * Implements hook_context_default_contexts().
 */
function skinr_context_context_default_contexts() {
  $contexts = array();
  foreach (skinr_context_group_load_multiple(FALSE) as $group) {
    $context = skinr_context_group_to_context($group);
    $context->disabled = FALSE;
    $context->api_version = 3;
    $contexts[$context->name] = $context;
  }
  return $contexts;
}

/**
 * Implements hook_skinr_skin_presave().
 */
function skinr_context_skinr_skin_presave($skin) {
  if (!empty($skin->gid)) {

    // Load group object.
    if ($group = skinr_context_group_load($skin->gid)) {
      if (!$group->status) {

        // Disable skin status if group is disabled.
        $skin->status = 0;
      }
    }
  }
}

/**
 * Implements hook_skinr_skin_insert().
 */
function skinr_context_skinr_skin_insert($skin) {
  if (!empty($skin->gid)) {
    $gs = (object) array(
      'gid' => $skin->gid,
      'sid' => $skin->sid,
    );
    drupal_write_record('skinr_group_skins', $gs);
  }
}

/**
 * Implements hook_entity_delete().
 */
function skinr_context_entity_delete(Drupal\Core\Entity\EntityInterface $entity) {
  if ($entity
    ->getEntityTypeId() !== 'skin') {
    return;
  }
  if (!empty($entity->gid)) {
    db_delete('skinr_group_skins')
      ->condition('sid', $entity->sid)
      ->execute();
  }
}

/**
 * Implements hook_entity_load().
 */
function skinr_context_entity_load(array $entities, $entity_type_id) {
  if ($entity_type_id !== 'skin') {
    return;
  }
  foreach ($entities as $skin) {
    $query = db_select('skinr_group_skins', 'gs');
    $query
      ->fields('gs', array(
      'gid',
    ));
    $query
      ->condition('sid', $skin->sid);
    $skin->gid = $query
      ->execute()
      ->fetchField();
  }
}

/**
 * Implements hook_skinr_skin_defaults_alter().
 */
function skinr_context_skinr_skin_defaults_alter(&$default_skins) {
  foreach ($default_skins as &$skin) {
    if (!isset($skin->gid)) {
      _skinr_context_add_default_group($skin);
    }
  }
}

/**
 * Helper function to add a new default group to a skin configuration.
 */
function _skinr_context_add_default_group(&$skin) {

  // Lookup existing group. Grab the one with the lowest weight for this set.
  $params = array(
    'module' => $skin->module,
    'element' => $skin->element,
  );
  $gids = skinr_context_group_get_gids($params);
  $gid = reset($gids);
  if (!$gid) {

    // Create a group.
    $title = t('Default');
    $group = (object) array(
      'gid' => $skin->module . ':' . $skin->element . ':' . strtolower($title),
      'module' => $skin->module,
      'element' => $skin->element,
      'title' => $title,
      'description' => '',
      'conditions' => array(
        'sitewide' => array(
          'values' => array(
            1 => 1,
          ),
        ),
      ),
      'condition_mode' => CONTEXT_CONDITION_MODE_OR,
      'weight' => 0,
      'status' => 1,
    );
    skinr_context_group_save($group);
    $gid = $group->gid;
  }
  $skin->gid = $gid;

  // Simulate insert to ensure group is linked.

  //skinr_context_skinr_skin_insert($skin);
}

/**
 * Function used by uasort to sort classes by weight.
 *
 * @see skinr_context_skinr_preprocess_alter()
 */
function skinr_context_sort_weight($a, $b) {
  $a_weight = is_object($a) && isset($a->weight) ? $a->weight : 0;
  $b_weight = is_object($b) && isset($b->weight) ? $b->weight : 0;
  if ($a_weight == $b_weight) {
    return 0;
  }
  return $a_weight < $b_weight ? -1 : 1;
}

/**
 * Implements hook_skinr_preprocess_alter().
 */
function skinr_context_skinr_preprocess_alter(&$skins, $context) {
  $contexts = context_active_contexts();
  foreach ($skins as $key => $skin) {
    if (!empty($skin->gid) && ($group = skinr_context_group_load($skin->gid))) {

      // Remove skins for groups that arent in the right context.
      if (!isset($contexts['skinr_group__' . $skin->gid])) {
        unset($skins[$key]);
      }

      // Set group based weight on skins.
      $skin->weight = $group->weight;
    }
  }

  // Reorder by weight.
  uasort($skins, 'skinr_context_sort_weight');
}

/**
 * Implements hook_modules_enabled().
 */
function skinr_context_modules_enabled($modules) {
  foreach ($modules as $module) {
    skinr_context_group_defaults_import($module);
  }
}

Functions

Namesort descending Description
skinr_context_context_default_contexts Implements hook_context_default_contexts().
skinr_context_ctools_plugin_api Implements hook_ctools_plugin_api().
skinr_context_entity_delete Implements hook_entity_delete().
skinr_context_entity_load Implements hook_entity_load().
skinr_context_group_defaults_import Imports default skin settings group objects from code into database.
skinr_context_group_delete Delete a skin group object.
skinr_context_group_delete_multiple Delete multiple skin settings group objects.
skinr_context_group_export Output a settings group object as code suitable for skinr_context_group_defaults().
skinr_context_group_get_gids Get skin settings group IDs.
skinr_context_group_get_sids Get skin settings IDs for grouped skin settings.
skinr_context_group_import Import a skin settigns group object as defined in skinr_context_group_defaults().
skinr_context_group_load Load a skin settings group object from the database.
skinr_context_group_load_multiple Load skin settings group objects from the database.
skinr_context_group_load_unchanged Load an uncached version of a skin settings group object.
skinr_context_group_revert Revert a skin settings group object that's stored in code to its default state.
skinr_context_group_save Save a skin group object.
skinr_context_group_storage Returns a skin settings group's storage method.
skinr_context_group_to_context Helper function to create a context object from a skin settings group.
skinr_context_group_validate Validate a skin group object.
skinr_context_hook_info Implements hook_hook_info().
skinr_context_modules_enabled Implements hook_modules_enabled().
skinr_context_module_implements_alter Implements hook_module_implements_alter().
skinr_context_skinr_preprocess_alter Implements hook_skinr_preprocess_alter().
skinr_context_skinr_skin_defaults_alter Implements hook_skinr_skin_defaults_alter().
skinr_context_skinr_skin_insert Implements hook_skinr_skin_insert().
skinr_context_skinr_skin_presave Implements hook_skinr_skin_presave().
skinr_context_sort_weight Function used by uasort to sort classes by weight.
_skinr_context_add_default_group Helper function to add a new default group to a skin configuration.
_skinr_context_group_get_defaults Returns all default skin settings group objects.
_skinr_context_group_import