You are here

blockreference.module in Block reference 7

Same filename and directory in other branches
  1. 6 blockreference.module
  2. 7.2 blockreference.module

Defines a field type for referencing a block from a node.

File

blockreference.module
View source
<?php

/**
 * @file
 * Defines a field type for referencing a block from a node.
 */

/**
 * Implements hook_menu().
 */
function blockreference_menu() {
  $items = array();
  $items['blockreference/autocomplete/%/%/%'] = array(
    'title' => 'blockreference autocomplete',
    'page callback' => 'blockreference_autocomplete',
    'page arguments' => array(
      2,
      3,
      4,
    ),
    'access callback' => 'blockreference_autocomplete_access',
    'access arguments' => array(
      2,
      3,
    ),
    'type' => MENU_CALLBACK,
  );
  return $items;
}

/**
 * Implements hook_theme().
 */
function blockreference_theme() {
  return array(
    'blockreference_item_simple' => array(
      'variables' => array(
        'item' => NULL,
      ),
    ),
    'blockreference_formatter_default' => array(
      'variables' => array(
        'element' => NULL,
      ),
    ),
    'blockreference_formatter_without_title' => array(
      'variables' => array(
        'element' => NULL,
      ),
    ),
    'blockreference_formatter_title' => array(
      'variables' => array(
        'element' => NULL,
      ),
    ),
    'blockreference_formatter_plain' => array(
      'variables' => array(
        'element' => NULL,
      ),
    ),
  );
}

/**
 * Implements hook_field_info().
 */
function blockreference_field_info() {
  return array(
    'blockreference' => array(
      'label' => t('Block reference'),
      'description' => t('This field stores the ID of a related block as an integer value.'),
      'settings' => array(
        'referenceable_regions' => array(),
        'referenceable_modules' => array(),
        'referenceable_operator' => 'AND',
        'respect_visibility' => 0,
        'referenceable_theme' => 'default',
      ),
      'default_widget' => 'options_select',
      'default_formatter' => 'blockreference_default',
    ),
  );
}

/**
 * Implements hook_field_schema().
 */
function blockreference_field_schema($field) {
  $columns = array(
    'bid' => array(
      'type' => 'int',
      'unsigned' => TRUE,
      'not null' => FALSE,
    ),
  );
  return array(
    'columns' => $columns,
    'indexes' => array(
      'bid' => array(
        'bid',
      ),
    ),
    'foreign keys' => array(
      'bid' => array(
        'table' => 'block',
        'columns' => array(
          'bid' => 'bid',
        ),
      ),
    ),
  );
}

/**
 * Get an array of block modules, where the keys are the module short name and
 * the values are the module name as set in the .info file.
 */
function blockreference_get_block_modules() {
  $block_modules = array();

  // Get current list of modules
  $files = drupal_system_listing('/^' . DRUPAL_PHP_FUNCTION_PATTERN . '\\.module$/', 'modules', 'name', 0);

  // Get modules that define blocks.
  $modules = module_implements('block_info', TRUE);
  foreach ($modules as $module) {
    if (isset($files[$module])) {
      $file =& $files[$module];

      // Look for the info file.
      $file->info = drupal_parse_info_file(dirname($file->uri) . '/' . $file->name . '.info');
      $block_modules[$module] = isset($file->info['name']) ? $file->info['name'] : ucfirst($module);
    }
  }
  return $block_modules;
}

/**
 * Field referenceable themes.
 */
function blockreference_field_referenceable_themes($field) {
  switch ($field['settings']['referenceable_theme']) {
    case 'default':
      return array(
        variable_get('theme_default', 'bartik'),
      );
    case 'all':
      $themes = list_themes();
      return array_keys($themes);
    case 'not_admin':
      $themes = list_themes();
      unset($themes[variable_get('admin_theme', 'seven')]);
      return array_keys($themes);
  }
}

/**
 * Implements hook_field_settings_form().
 */
function blockreference_field_settings_form($field, $instance, $has_data) {
  $settings = $field['settings'];
  $form = array();
  $regions = array(
    '' => t('Disabled'),
  );
  $referenceable_themes = blockreference_field_referenceable_themes($field);
  foreach ($referenceable_themes as $referenceable_theme) {
    $regions = $regions + system_region_list($referenceable_theme);
  }
  $form['referenceable_regions'] = array(
    '#type' => 'checkboxes',
    '#title' => t('Regions containing blocks that can be referenced'),
    '#multiple' => TRUE,
    '#default_value' => $settings['referenceable_regions'],
    '#options' => $regions,
    '#description' => t('If no regions are selected, blocks from all regions will be available.'),
  );
  $form['referenceable_modules'] = array(
    '#type' => 'checkboxes',
    '#title' => t('Modules defining blocks that can be referenced'),
    '#multiple' => TRUE,
    '#default_value' => $settings['referenceable_modules'],
    '#options' => blockreference_get_block_modules(),
    '#description' => t('If no modules are selected, blocks from all modules will be available.'),
  );
  $form['referenceable_operator'] = array(
    '#type' => 'radios',
    '#title' => t('Operator to use if referenceable regions and referenceable modules are selected.'),
    '#default_value' => $settings['referenceable_operator'],
    '#options' => array(
      'AND' => '<strong>AND</strong> - ' . t('Block must be both contained in a referenceable region and defined by a referenceable module.'),
      'OR' => '<strong>OR</strong> - ' . t('Block must be either contained in a referenceable region or defined by a referenceable module.'),
    ),
    '#description' => t('If regions and modules are selected, choose operator to use when retrieving blocks list.'),
  );
  $form['respect_visibility'] = array(
    '#type' => 'checkbox',
    '#title' => t('Respect block visibility settings'),
    '#default_value' => $settings['respect_visibility'],
    '#description' => t('Allows the block module to remove the block if it is restricted by visibility settings.'),
  );
  $themes = list_themes();
  $current_theme = $themes[variable_get('theme_default', 'bartik')]->info['name'];
  $admin_theme = $themes[variable_get('admin_theme', 'seven')]->info['name'];
  $form['referenceable_theme'] = array(
    '#type' => 'radios',
    '#title' => t('Themes that can provide blocks for selection.'),
    '#default_value' => $settings['referenceable_theme'],
    '#options' => array(
      'default' => t('Default theme (%theme) only.', array(
        '%theme' => $current_theme,
      )),
      'all' => t('All themes.'),
      'not_admin' => t('All themes except admin theme (%theme).', array(
        '%theme' => $admin_theme,
      )),
    ),
  );
  return $form;
}

/**
 * Implements hook_field_validate().
 *
 * Possible error codes:
 * - 'invalid_bid': bid is not valid for the field (not a valid block id, or the block is not referenceable).
 */
function blockreference_field_validate($entity_type, $entity, $field, $instance, $langcode, $items, &$errors) {

  // Extract bids to check.
  $ids = array();

  // First check non-numeric bid's to avoid losing time with them.
  foreach ($items as $delta => $item) {
    if (is_array($item) && !empty($item['bid'])) {
      if (is_numeric($item['bid'])) {
        $ids[] = $item['bid'];
      }
      else {
        $errors[$field['field_name']][$langcode][$delta][] = array(
          'error' => 'invalid_bid',
          'message' => t("%name: invalid input.", array(
            '%name' => $instance['label'],
          )),
        );
      }
    }
  }

  // Prevent performance hog if there are no ids to check.
  if ($ids) {
    $refs = _blockreference_potential_references($field, $ids, TRUE);
    foreach ($items as $delta => $item) {
      if (is_array($item)) {
        if (!empty($item['bid']) && !isset($refs[$item['bid']])) {
          $errors[$field['field_name']][$langcode][$delta][] = array(
            'error' => 'invalid_bid',
            'message' => t("%name: This block can't be referenced.", array(
              '%name' => $instance['label'],
            )),
          );
        }
      }
    }
  }
}

/**
 * Implements hook_field_is_empty().
 */
function blockreference_field_is_empty($item, $field) {

  // bid = 0 is empty too, which is exactly what we want.
  return empty($item['bid']);
}

/**
 * Implements hook_field_formatter_info().
 */
function blockreference_field_formatter_info() {
  $ret = array(
    'blockreference_default' => array(
      'label' => t('Default (title and content)'),
      'description' => t('Display the title and content of a block.'),
      'field types' => array(
        'blockreference',
      ),
    ),
    'blockreference_without_title' => array(
      'label' => t('Content only'),
      'description' => t('Display the content of a block.  Do not display the title.'),
      'field types' => array(
        'blockreference',
      ),
    ),
    'blockreference_title' => array(
      'label' => t('Title only'),
      'description' => t('Display the title of a block.  Do not display the content.'),
      'field types' => array(
        'blockreference',
      ),
    ),
    'blockreference_plain' => array(
      'label' => t('Info'),
      'description' => t('Display the info text of a block.'),
      'field types' => array(
        'blockreference',
      ),
    ),
  );
  return $ret;
}

/**
 * Implements hook_field_formatter_view().
 */
function blockreference_field_formatter_view($entity_type, $entity, $field, $instance, $langcode, $items, $display) {
  $element = array();
  $formatter = str_replace('blockreference_', '', $display['type']);
  foreach ($items as $delta => $item) {
    $element[$delta] = array(
      '#theme' => 'blockreference_formatter_' . $formatter,
      '#element' => array(
        'entity_type' => &$entity_type,
        'entity' => &$entity,
        'field' => &$field,
        'instance' => &$instance,
        'langcode' => &$langcode,
        'item' => $item,
        'display' => &$display,
      ),
    );
  }
  return $element;
}

/**
 * Implements hook_field_widget_info().
 */
function blockreference_field_widget_info() {
  return array(
    'blockreference_autocomplete' => array(
      'label' => t('Autocomplete text field'),
      'description' => t('Display the list of referenceable blocks as a textfield with autocomplete behaviour.'),
      'field types' => array(
        'blockreference',
      ),
      'settings' => array(
        'size' => 60,
        'autocomplete_path' => 'blockreference/autocomplete',
      ),
    ),
    'blockreference_select_sort' => array(
      'label' => t('Select list (with drag-and-drop sort) DEPRECATED'),
      'description' => '<b>' . t('Deprecated in favor of <a href="http://drupal.org/multiple_selects">Multiple Selects</a>') . '</b>',
      'field types' => array(
        'blockreference',
      ),
      'behaviors' => array(
        'multiple values' => FIELD_BEHAVIOR_DEFAULT,
        'default value' => FIELD_BEHAVIOR_DEFAULT,
      ),
    ),
  );
}

/**
 * Implements hook_field_widget_info_alter().
 */
function blockreference_field_widget_info_alter(&$info) {
  $info['options_select']['field types'][] = 'blockreference';
  $info['options_buttons']['field types'][] = 'blockreference';
  if (module_exists('multiple_selects')) {
    $info['multiple_selects']['field types'][] = 'blockreference';
  }
}

/**
 * Implements hook_field_widget_settings_form().
 */
function blockreference_field_widget_settings_form($field, $instance) {
  $widget = $instance['widget'];
  $defaults = field_info_widget_settings($widget['type']);
  $settings = array_merge($defaults, $widget['settings']);
  $form = array();
  if ($widget['type'] == 'blockreference_autocomplete') {
    $form['size'] = array(
      '#type' => 'textfield',
      '#title' => t('Size of textfield'),
      '#default_value' => $settings['size'],
      '#element_validate' => array(
        '_element_validate_integer_positive',
      ),
      '#required' => TRUE,
    );
  }
  return $form;
}

/**
 * Implements hook_field_widget_form().
 */
function blockreference_field_widget_form(&$form, &$form_state, $field, $instance, $langcode, $items, $delta, $element) {
  switch ($instance['widget']['type']) {
    case 'blockreference_autocomplete':
      $current_bid = isset($items[$delta]['bid']) ? $items[$delta]['bid'] : NULL;
      $element += array(
        '#type' => 'textfield',
        '#default_value' => $current_bid,
        '#autocomplete_path' => $instance['widget']['settings']['autocomplete_path'] . '/' . $instance['entity_type'] . '/' . $field['field_name'] . '/' . (int) $current_bid,
        '#size' => $instance['widget']['settings']['size'],
        '#element_validate' => array(
          'blockreference_autocomplete_validate',
        ),
        '#value_callback' => 'blockreference_autocomplete_value',
      );
      $element = array(
        'bid' => $element,
      );
      break;
    case 'blockreference_select_sort':
      $element = array(
        '#type' => 'blockreference_select_sort',
        '#default_value' => isset($items[$delta]) ? $items[$delta] : NULL,
      );
      break;
  }
  return $element;
}

/**
 * Value callback for a blockreference autocomplete element.
 *
 * Replace the block bid with a block title.
 */
function blockreference_autocomplete_value($element, $input = FALSE, $form_state) {
  if ($input === FALSE) {

    // We're building the displayed 'default value': expand the raw bid into
    // "block title [bid:n]".
    $bid = $element['#default_value'];
    if (!empty($bid)) {
      $result = db_query('SELECT module, delta FROM {block} WHERE bid = :bid', array(
        ':bid' => $bid,
      ));
      $block = $result
        ->fetchObject();
      $info = module_invoke($block->module, 'block_info');
      if (isset($info[$block->delta])) {
        $value = $info[$block->delta]['info'];
        $value .= ' [bid:' . $bid . ']';
        return $value;
      }
      else {

        // If you want these missing blocks to fail silently, without warning, set this variable to 0.
        if (variable_get('blockreference_warn_about_missing_blocks', 1)) {
          drupal_set_message(t("Delta %delta (bid @bid) is unknown in the block list coming from module %module, but it's still referenced in field %field.", array(
            '@bid' => $bid,
            '%delta' => $block->delta,
            '%module' => $block->module,
            '%field' => $element['#field_name'],
          )), 'warning');
        }
        return '';
      }
    }
  }
}

/**
 * Validation callback for a blockreference autocomplete element.
 */
function blockreference_autocomplete_validate($element, &$form_state, $form) {
  $field = field_info_field($element['#field_name']);
  $instance = field_info_instance($element['#entity_type'], $element['#field_name'], $element['#bundle']);
  $current_bid = $element['#default_value'];
  $value = $element['#value'];
  $bid = NULL;
  if (!empty($value)) {

    // Check whether we have an explicit "[bid:n]" input.
    preg_match('/^(?:\\s*|(.*) )?\\[\\s*bid\\s*:\\s*(\\d+)\\s*\\]$/', $value, $matches);
    if (!empty($matches)) {

      // Explicit bid. Check that the 'title' part matches the actual title for
      // the bid.
      list(, $title, $bid) = $matches;
      if (!empty($title)) {
        $result = db_query('SELECT module, delta FROM {block} WHERE bid = :bid', array(
          ':bid' => $bid,
        ));
        $block = $result
          ->fetchObject();
        $info = module_invoke($block->module, 'block_info');
        $real_title = $info[$block->delta]['info'];
        if (trim($title) != trim($real_title)) {
          form_error($element, t('%name: Title mismatch. Please check your selection.', array(
            '%name' => $instance['label'],
          )));
        }
      }
    }
    else {

      // No explicit bid (the submitted value was not populated by autocomplete
      // selection). Get the bid of a referencable block from the entered title.
      $bids = _blockreference_potential_references($field, array(
        $current_bid,
      ), FALSE, $value, TRUE);
      if ($bids) {

        // @todo The best thing would be to present the user with an
        // additional form, allowing the user to choose between valid
        // candidates with the same title. ATM, we pick the first
        // matching candidate...
        $bid = $bids ? key($bids) : 0;
      }
      else {
        form_error($element, t('%name: Found no valid block with that title.', array(
          '%name' => $instance['label'],
        )));
      }
    }
  }

  // Set the element's value as the node id that was extracted from the entered
  // input.
  form_set_value($element, $bid, $form_state);
}

/**
 * Implements hook_field_widget_error().
 */
function blockreference_field_widget_error($element, $error, $form, &$form_state) {
  form_error($element['bid'], $error['message']);
}

/**
 * Fetch an array of all candidate referenced blocks.
 *
 * @param $field
 *  Array containing field data.
 * @param $current_bids
 *  Array of current bids.
 * @param $return_full_blocks
 *  Whether to return full blocks.
 * @param $string
 *  Filter string to match blocks.
 * @param $exact_string
 *  Strictly match string like for validation.
 *
 * @return
 *  An array of whatever is needed.
 */
function _blockreference_potential_references($field, $current_bids = array(), $return_full_blocks = FALSE, $string = '', $exact_string = FALSE) {
  $results =& drupal_static(__FUNCTION__, array());

  // Create unique id for static cache.
  $cid = $field['field_name'] . ':' . (int) $return_full_blocks . ':' . $string . ':' . (int) $exact_string;
  if (!isset($results[$cid])) {
    $references = _blockreference_potential_references_standard($field, $current_bids, $return_full_blocks, $string, $exact_string);

    // Incorrect legacy alter.
    drupal_alter('blockreference_potential_references', $references, $field, $current_bids, $return_full_blocks, $string, $exact_string);

    // Correct new alter.
    $context = compact('field', 'current_bids', 'return_full_blocks', 'string', 'exact_string');
    drupal_alter('blockreference_potential_references2', $references, $context);

    // Store the results.
    $results[$cid] = !empty($references) ? $references : array();
  }
  return $results[$cid];
}

/**
 * Build an array of all candidate referenced blocks.
 *
 * @param $field
 *  Array containing field data.
 * @param $current_bids
 *  Array of current bids.
 * @param $return_full_blocks
 *  Whether to return full blocks.
 * @param $string
 *  Filter string to match blocks.
 * @param $exact_string
 *  Strictly match string like for validation.
 *
 * @return
 *  An array of whatever is needed.
 */
function _blockreference_potential_references_standard($field, $current_bids = array(), $return_full_blocks = FALSE, $string = '', $exact_string = FALSE) {
  static $block_info = array();
  $related_regions = array();
  $related_modules = array();
  $related_clauses = array();
  $args = array();
  $settings =& $field['settings'];

  // Handle related regions - this will be used in extra query conditions.
  if (isset($settings['referenceable_regions']) && is_array($settings['referenceable_regions'])) {
    $settings['referenceable_regions'] = array_filter($settings['referenceable_regions']);
    foreach ($settings['referenceable_regions'] as $key => $related_region) {
      if ($related_region) {
        $placeholder = ':region_' . $key;
        $related_regions[] = 'region = ' . $placeholder;
        $args[$placeholder] = $related_region;
      }
    }
  }
  if (!empty($related_regions)) {
    $related_clauses[] = implode(' OR ', $related_regions);
  }

  // Handle related modules - this will be used in extra query conditions.
  if (isset($settings['referenceable_modules']) && is_array($settings['referenceable_modules'])) {
    $settings['referenceable_modules'] = array_filter($settings['referenceable_modules']);
    foreach ($settings['referenceable_modules'] as $key => $related_module) {
      if ($related_module) {
        $placeholder = ':module_' . $key;
        $related_modules[] = 'module = ' . $placeholder;
        $args[$placeholder] = $related_module;
      }
    }
  }
  if (!empty($related_modules)) {
    $related_clauses[] = implode(' OR ', $related_modules);
  }

  // Assemble the extra query condition.
  $related_operator = !empty($settings['referenceable_operator']) ? $settings['referenceable_operator'] : 'AND';
  $related_clause = !empty($related_clauses) ? ' AND ((' . implode(') ' . $related_operator . ' (', $related_clauses) . '))' : '';
  $args[':themes'] = blockreference_field_referenceable_themes($field);

  // Assemble the query.
  $result = db_query('SELECT bid, module, delta, title, region, theme ' . 'FROM {block} ' . "WHERE theme IN (:themes) " . $related_clause, $args);

  // Execute the query, test each row, and return the blocks or block info.
  $rows = array();
  foreach ($result as $block) {
    if (count($args[':themes']) <= 1) {

      // If there were not multiple themes, don't track the theme.
      unset($block->theme);
    }
    if (!isset($block_info[$block->module])) {
      $block_info[$block->module] = module_invoke($block->module, 'block_info');
    }
    if (isset($block_info[$block->module][$block->delta]['info'])) {
      $block->info = $block_info[$block->module][$block->delta]['info'];
      if ($exact_string && !empty($string) && $string == $block->info || !empty($string) && (!empty($block->info) && stripos($block->info, $string) !== FALSE || !empty($block->title) && stripos($block->title, $string) !== FALSE || !empty($block->module) && stripos($block->module . ' ' . $block->delta, $string) !== FALSE || !empty($block->bid) && stripos($block->bid, $string) !== FALSE) || empty($string)) {
        $rows[$block->bid] = $return_full_blocks ? $block : $block->info;
      }
    }
  }

  // Default sort.
  if ($return_full_blocks) {
    _blockreference_sort_blocks($rows);
  }
  else {
    natcasesort($rows);
  }
  return $rows;
}

/**
 * Menu access callback for the autocomplete path.
 *
 * Check for both 'edit' and 'view' access in the unlikely event
 * a user has edit but not view access.
 */
function blockreference_autocomplete_access($entity_type, $field_name) {
  return user_access('access content') && ($field = field_info_field($field_name)) && field_access('view', $field, $entity_type) && field_access('edit', $field, $entity_type);
}

/**
 * Menu callback for the autocomplete results.
 */
function blockreference_autocomplete($entity_type, $field_name, $current_bid, $string = '') {

  // If the request has a '/' in the search text, then the menu system will have
  // split it into multiple arguments, recover the intended $string.
  $args = func_get_args();

  // Shift off the $entity_type argument.
  array_shift($args);

  // Shift off the $field_name argument.
  array_shift($args);

  // Shift off the $current_bid argument.
  array_shift($args);
  $string = implode('/', $args);
  $field = field_info_field($field_name);
  $matches = array();
  $blocks = _blockreference_potential_references($field, array(
    $current_bid,
  ), TRUE, $string);
  foreach ($blocks as $bid => $block) {
    $matches[_blockreference_item($field, $block) . ' [bid:' . $bid . ']'] = _blockreference_item($field, $block);
  }
  drupal_json_output($matches);
}

/**
 * Implements hook_options_list().
 */
function blockreference_options_list($field, $instance, $entity_type, $entity) {
  $current_bids = array();
  if (!empty($entity->{$field['field_name']})) {
    foreach ($entity->{$field['field_name']} as $language) {
      foreach ($language as $delta) {
        $current_bids[] = $delta;
      }
    }
  }
  return _blockreference_potential_references($field, $current_bids);
}

/**
 * Implements hook_content_migrate_instance_alter().
 *
 * Use this to tweak the conversion of instance or widget settings from the D6
 * style to the D7 style for specific situations not handled by basic
 * conversion, as when formatter or widget names or settings are changed.
 */
function blockreference_content_migrate_instance_alter(&$instance_value) {

  // The module name for the instance was corrected
  // by the change in blockreference_content_migrate_field_alter().
  if (isset($instance_value['module']) && $instance_value['module'] == 'blockreference') {

    // The formatter names changed, all are prefixed
    // with 'blockreference_'.
    foreach ($instance_value['display'] as $context => $settings) {
      $instance_value['display'][$context]['type'] = 'blockreference_' . $settings['type'];
    }
    switch ($instance_value['widget']['type']) {
      case 'blockreference_autocomplete':
        $instance_value['widget']['type'] = 'blockreference_autocomplete';
        $instance_value['widget']['module'] = 'blockreference';
        break;
      case 'blockreference_select':
        $instance_value['widget']['type'] = 'options_select';
        $instance_value['widget']['module'] = 'options';
        break;
      case 'blockreference_buttons':
        $instance_value['widget']['type'] = 'options_buttons';
        $instance_value['widget']['module'] = 'options';
    }
  }
}

/**
 * Helper function for theming normal block views, returns appropriate block.
 */
function blockreference_formatter_get_block($element) {
  if (!empty($element['item']['bid']) && is_numeric($element['item']['bid'])) {
    $bid = $element['item']['bid'];
    $query = db_select('block', 'b');
    $result = $query
      ->fields('b')
      ->condition('b.bid', $bid)
      ->addTag('block_load')
      ->addTag('translatable')
      ->execute();
    $blocks = $result
      ->fetchAllAssoc('bid');
    if (isset($blocks[$bid])) {
      if ($element['field']['settings']['respect_visibility']) {

        // Set status to 1 manually, so block.module actually evaluates visibility.
        $blocks[$bid]->status = 1;

        // Allow block.module and other modules to modify the block list.
        drupal_alter('block_list', $blocks);
      }
      if (isset($blocks[$bid])) {
        return $blocks[$bid];
      }
    }
  }
  return FALSE;
}

/**
 * Theme function for simple display of a block item.
 */
function theme_blockreference_item_simple($variables) {
  $output = $variables['item']->info;
  if (!empty($variables['item']->theme)) {
    $themes = list_themes();
    $theme_key = $variables['item']->theme;
    $output .= ' [' . $themes[$theme_key]->info['name'] . ']';
  }
  return $output;
}

/**
 * Theme function for 'default' blockreference field formatter.
 */
function theme_blockreference_formatter_default($variables) {
  $element =& $variables['element'];
  $output = '';
  $block = blockreference_formatter_get_block($element);
  if ($block) {
    $bid = $block->module . '_' . $block->delta;
    if ($block_content = _block_render_blocks(array(
      $block,
    ))) {
      $build = _block_get_renderable_array($block_content);
      $build[$bid]['#blockreference_element'] = $element;
      $output = drupal_render($build);
    }
  }
  return $output;
}

/**
 * Theme function for 'without_title' blockreference field formatter.
 */
function theme_blockreference_formatter_without_title($variables) {
  $element = $variables['element'];
  $output = '';
  $block = blockreference_formatter_get_block($element);
  if ($block) {
    $bid = $block->module . '_' . $block->delta;
    if ($block_content = _block_render_blocks(array(
      $block,
    ))) {
      $rendered_block = reset($block_content);
      $rendered_block->subject = '';
      $build = _block_get_renderable_array($block_content);
      $build[$bid]['#blockreference_element'] = $element;
      $output = drupal_render($build);
    }
  }
  return $output;
}

/**
 * Theme function for 'plain' blockreference field formatter.
 */
function theme_blockreference_formatter_plain($variables) {
  $element =& $variables['element'];
  $output = '';
  $block = blockreference_formatter_get_block($element);
  if ($block) {
    $info = module_invoke($block->module, 'block_info');
    $output = $info[$block->delta]['info'];
  }
  return $output;
}

/**
 * Theme function for 'title' blockreference field formatter.
 */
function theme_blockreference_formatter_title($variables) {
  $element =& $variables['element'];
  $output = '';
  $block = blockreference_formatter_get_block($element);
  if ($block) {
    if ($block_content = _block_render_blocks(array(
      $block,
    ))) {
      $rendered_block = reset($block_content);
      $output = $rendered_block->subject;
    }
  }
  return $output;
}

/**
 * Implements hook_field_views_data().
 *
 * In addition to the default field information we add the relationship for
 * views to connect back to the block table.
 */
function blockreference_field_views_data($field) {

  // No module_load_include(): this hook is invoked from
  // views/modules/field.views.inc, which is where that function is defined.
  $data = field_views_field_default_views_data($field);
  $key = key($field['storage']['details']);
  $storage = $field['storage']['details'][$key];
  foreach ($storage as $age => $table_data) {
    $table = key($table_data);
    $columns = current($table_data);
    $id_column = $columns['bid'];
    if (isset($data[$table])) {
      $data[$table][$id_column]['relationship'] = array(
        'base' => 'block',
        'field' => 'bid',
        'base field' => 'bid',
        'label' => $field['field_name'],
      );
    }
  }
  return $data;
}

/**
 * Implements hook_element_info().
 */
function blockreference_element_info() {
  return array(
    'blockreference_select_sort' => array(
      '#input' => TRUE,
      '#columns' => array(
        'bid',
      ),
      '#delta' => 0,
      '#process' => array(
        'blockreference_select_sort_process',
      ),
    ),
  );
}

/**
 * Process callback for a blockreference_select_sort element.
 *
 * @see blockreference_element_info().
 */
function blockreference_select_sort_process($element, $form_state, $form) {
  if (user_access('administer modules')) {
    drupal_set_message(t('Block reference select lists (with drag-and-drop sort) are deprecated.  Please use the <a href="!field_collection">Field collection</a> module.', array(
      '!field_collection' => 'http://www.drupal.org/project/field_collection',
    )), 'error', FALSE);
  }
  $field_name = $element['#parents'][0];
  $language = $element['#parents'][1];
  $field = $form_state['field'][$field_name];
  $instance = $field[$language]['instance'];
  $current_bid = isset($element['#value'][$element['#columns'][0]]) ? $element['#value'][$element['#columns'][0]] : '';
  $element[$element['#columns'][0]] = array(
    '#type' => 'select',
    '#options' => blockreference_list_values($field, $language, $current_bid),
    '#multiple' => 0,
    '#default_value' => $current_bid,
    '#delta' => $element['#delta'],
    '#columns' => $element['#columns'],
    '#title' => $instance['label'],
    '#required' => $element['#required'],
    '#description' => isset($element['#description']) ? $element['#description'] : '',
  );
  return $element;
}

/**
 * Build the select options for a blockreference_select_sort element.
 */
function blockreference_list_values($field, $language, $current_bid) {
  $instance = $field[$language]['instance'];
  $options = _blockreference_potential_references($field[$language]['field'], array(
    $current_bid,
  ), TRUE);
  foreach ($options as $key => $value) {
    $options[$key] = _blockreference_item($field, $value);
  }
  natcasesort($options);
  if (!$instance['required']) {
    $options = array(
      0 => ' - ' . t('none') . ' - ',
    ) + $options;
  }
  return $options;
}

/**
 * Helper to sort block objects.
 */
function _blockreference_sort_blocks(&$blocks) {
  uasort($blocks, '_blockreference_block_sorter');
}

/**
 * Helper to sort block titles.
 */
function _blockreference_block_sorter($block1, $block2) {
  return strnatcasecmp($block1->info, $block2->info);
}

/**
 * Get the HTML for a block display title.
 */
function _blockreference_item($field, $item, $html = FALSE) {
  $output = theme('blockreference_item_simple', array(
    'item' => $item,
  ));
  $output = $html ? check_plain($output) : $output;
  return $output;
}

/**
 * Implements hook_node_view().
 *
 * Pre-render blockreference fields attached to nodes to ensure forms are rendered early enough.
 */
function blockreference_node_view($node, $view_mode) {
  foreach (element_children($node->content) as $field_name) {
    $field = $node->content[$field_name];

    // This is a blockreference field.
    if (isset($field['#field_type']) && $field['#field_type'] == 'blockreference') {

      // Unset renderable field items.
      foreach (element_children($field) as $n) {
        unset($node->content[$field_name][$n]);
      }

      // Render.
      $html = render($field);

      // Add back to node content.
      $keep_meta = array_flip(array(
        '#weight',
        '#title',
        '#access',
        '#label_display',
        '#field_name',
        '#entity_type',
        '#bundle',
      ));
      $node->content[$field_name] = array(
        '#markup' => $html,
      ) + array_intersect_key($field, $keep_meta);
    }
  }
}

/**
 * Implements hook_migrate_api().
 */
function blockreference_migrate_api() {
  return array(
    'api' => 2,
    'field handlers' => array(
      'MigrateBlockReferenceFieldHandler',
    ),
  );
}

Functions

Namesort descending Description
blockreference_autocomplete Menu callback for the autocomplete results.
blockreference_autocomplete_access Menu access callback for the autocomplete path.
blockreference_autocomplete_validate Validation callback for a blockreference autocomplete element.
blockreference_autocomplete_value Value callback for a blockreference autocomplete element.
blockreference_content_migrate_instance_alter Implements hook_content_migrate_instance_alter().
blockreference_element_info Implements hook_element_info().
blockreference_field_formatter_info Implements hook_field_formatter_info().
blockreference_field_formatter_view Implements hook_field_formatter_view().
blockreference_field_info Implements hook_field_info().
blockreference_field_is_empty Implements hook_field_is_empty().
blockreference_field_referenceable_themes Field referenceable themes.
blockreference_field_schema Implements hook_field_schema().
blockreference_field_settings_form Implements hook_field_settings_form().
blockreference_field_validate Implements hook_field_validate().
blockreference_field_views_data Implements hook_field_views_data().
blockreference_field_widget_error Implements hook_field_widget_error().
blockreference_field_widget_form Implements hook_field_widget_form().
blockreference_field_widget_info Implements hook_field_widget_info().
blockreference_field_widget_info_alter Implements hook_field_widget_info_alter().
blockreference_field_widget_settings_form Implements hook_field_widget_settings_form().
blockreference_formatter_get_block Helper function for theming normal block views, returns appropriate block.
blockreference_get_block_modules Get an array of block modules, where the keys are the module short name and the values are the module name as set in the .info file.
blockreference_list_values Build the select options for a blockreference_select_sort element.
blockreference_menu Implements hook_menu().
blockreference_migrate_api Implements hook_migrate_api().
blockreference_node_view Implements hook_node_view().
blockreference_options_list Implements hook_options_list().
blockreference_select_sort_process Process callback for a blockreference_select_sort element.
blockreference_theme Implements hook_theme().
theme_blockreference_formatter_default Theme function for 'default' blockreference field formatter.
theme_blockreference_formatter_plain Theme function for 'plain' blockreference field formatter.
theme_blockreference_formatter_title Theme function for 'title' blockreference field formatter.
theme_blockreference_formatter_without_title Theme function for 'without_title' blockreference field formatter.
theme_blockreference_item_simple Theme function for simple display of a block item.
_blockreference_block_sorter Helper to sort block titles.
_blockreference_item Get the HTML for a block display title.
_blockreference_potential_references Fetch an array of all candidate referenced blocks.
_blockreference_potential_references_standard Build an array of all candidate referenced blocks.
_blockreference_sort_blocks Helper to sort block objects.