You are here

fc.module in Field Complete 7

Field Complete - Provides field-based completeness for any entity.

File

fc.module
View source
<?php

/**
 * @file
 * Field Complete - Provides field-based completeness for any entity.
 */

/**
 * Implements hook_entity_info_alter()
 */
function fc_entity_info_alter(&$entity_info) {
  foreach ($entity_info as &$info) {
    if ($info['fieldable'] && empty($info['fc handler class'])) {
      $info['fc handler class'] = 'fcComplete';
    }
  }
}
function fc_ctools_plugin_directory($module, $plugin) {
  static $modules = array(
    'fc' => array(
      'fields' => 'fields',
    ),
  );
  if (!empty($modules[$module][$plugin])) {
    return "plugins/{$modules[$module][$plugin]}";
  }
}
function fc_get_plugin($type) {
  static $plugins;
  if (empty($plugins)) {
    ctools_include('plugins');
    $plugins = ctools_get_plugins('fc', 'fields');
  }
  return !empty($plugins[$type]) ? $plugins[$type] : $plugins['default'];
}

/**
 * Implements hook_ctools_plugin_type().
 */
function fc_ctools_plugin_type() {
  module_load_include('registry.inc', 'fc');
  return _fc_ctools_plugin_type();
}

/**
 * Implements hook_menu().
 */
function fc_menu() {
  module_load_include('registry.inc', 'fc');
  return _fc_menu();
}

/**
 * Implements hook_permission().
 */
function fc_permission() {
  module_load_include('registry.inc', 'fc');
  return _fc_permission();
}

/**
 * Implements hook_theme().
 */
function fc_theme() {
  module_load_include('registry.inc', 'fc');
  return _fc_theme();
}

/**
 * Implements hook_theme_registry_alter().
 */
function fc_theme_registry_alter(&$registry) {
  $registry['form_element_label']['function'] = 'fc_form_element_label';
  $registry['field_multiple_value_form']['function'] = 'fc_field_multiple_value_form';
}

/**
 * Implements hook_views_api().
 */
function fc_views_api() {
  return array(
    'api' => 3,
    'path' => drupal_get_path('module', 'fc') . '/views',
  );
}

/**
 * Implements hook_field_widget_settings_form().
 *
 * This function adds the checkbox to every field to determine whether it
 * should be used in the completeness calculation. It is set differently
 * for every field instance.
 */
function fc_form_field_ui_field_edit_form_alter(&$form, &$form_state) {
  module_load_include('form.inc', 'fc');
  return _fc_form_field_ui_field_edit_form_alter($form, $form_state);
}
function fc_field_attach_after_build($element, &$form_state) {
  module_load_include('form.inc', 'fc');
  return _fc_field_attach_after_build($element, $form_state);
}

/**
 * Implements hook_entity_insert().
 */
function fc_entity_insert($entity, $entity_type) {
  if (fc_entity_is_enabled($entity_type)) {
    $entity->fc = fcComplete::build($entity_type, $entity);
    $entity->fc
      ->completeness();
    $entity->fc
      ->save();
  }
}

/**
 * Implements hook_entity_update().
 */
function fc_entity_update($entity, $entity_type) {
  if (fc_entity_is_enabled($entity_type)) {
    $entity->fc = fcComplete::build($entity_type, $entity);
    $entity->fc
      ->completeness();
    $entity->fc
      ->save();
  }
}

/**
 * Implements hook_entity_load().
 */
function fc_entity_load($entities, $entity_type) {
  if (fc_entity_is_enabled($entity_type)) {
    foreach ($entities as $entity) {
      $entity->fc = fcComplete::load($entity_type, $entity);
    }
  }
}

/**
 * Implements hook_entity_delete().
 */
function fc_entity_delete($entity, $entity_type) {
  if (fc_entity_is_enabled($entity_type)) {
    if (empty($entity->fc)) {
      $entity->fc = fcComplete::build($entity_type, $entity);
    }
    $entity->fc
      ->delete();
  }
}

// Utility functions for building completeness
function fc_default_field_cardinality($function, $field_items, $instance, $field) {

  // Fetch an array of completed items
  $completed = array();
  $function($field_items, $instance, $field, $completed);

  // Test each field item to determine if it's not empty = complete
  // we also need to take into account cardinality
  $cardinality = $field['cardinality'];
  if ($cardinality == FIELD_CARDINALITY_UNLIMITED) {

    // If we can have unlimited then we must have at least one,
    // but if we have a number of items make the cardinality
    // equal to the total. This means that someone can create a
    // while bunch of items but they must all be complete for the
    // field itself to counted as complete.
    $cardinality = empty($field_items) ? 1 : count($field_items);
  }

  // It's complete as long as we have /at least/ the cardinality
  return count(array_filter($completed)) >= $cardinality;
}

/**
 * Implements hook_fc_instances_alter().
 *
 * Remove all fields that are deleted or specifically
 * not intended to be included in completeness.
 */
function fc_fc_instances_alter(&$instances, $entity_type, $entity, $options) {
  foreach ($instances as $field_name => $instance) {
    if (empty($instance['settings']['fc']['fc_include']) || !empty($instance['deleted'])) {
      $instances[$field_name]['settings']['fc']['disable'] = TRUE;
    }
  }
}

/**
 * Implements hook_field_attach_form().
 */
function fc_field_attach_form($entity_type, $entity, &$form, &$form_state, $langcode) {
  module_load_include('form.inc', 'fc');
  return _fc_field_attach_form($entity_type, $entity, $form, $form_state, $langcode);
}
function fc_form_pre_render($form) {
  module_load_include('form.inc', 'fc');
  return _fc_form_pre_render($form);
}

/**
 * Modified form_element_label theme function.
 */
function fc_form_element_label($variables) {
  $element = $variables['element'];

  // This is also used in the installer, pre-database setup.
  $t = get_t();

  // If title and required marker are both empty, output no label.
  if (empty($element['#title'])) {
    return '';
  }

  // If the element is required, a required marker is appended to the label.
  $required = !empty($element['#required']) ? theme('form_required_marker', array(
    'element' => $element,
  )) : '';
  $complete = !empty($element['#field_complete']) ? theme('form_complete_marker', array(
    'element' => $element,
  )) : '';
  $title = filter_xss_admin($element['#title']);
  $attributes = array();

  // Style the label as class option to display inline with the element.
  if ($element['#title_display'] == 'after') {
    $attributes['class'] = 'option';
  }
  elseif ($element['#title_display'] == 'invisible') {
    $attributes['class'] = 'element-invisible';
  }
  if (!empty($element['#id'])) {
    $attributes['for'] = $element['#id'];
  }

  // The leading whitespace helps visually separate fields from inline labels.
  return ' <label' . drupal_attributes($attributes) . '>' . $t('!title !required', array(
    '!title' => $title,
    '!required' => $required . $complete,
  )) . "</label>\n";
}

/**
 * Returns HTML for an individual form element.
 *
 * Combine multiple values into a table with drag-n-drop reordering.
 * TODO : convert to a template.
 *
 * @param $variables
 *   An associative array containing:
 *   - element: A render element representing the form element.
 *
 * @ingroup themeable
 */
function fc_field_multiple_value_form($variables) {
  $element = $variables['element'];
  $output = '';
  if ($element['#cardinality'] > 1 || $element['#cardinality'] == FIELD_CARDINALITY_UNLIMITED) {
    $table_id = drupal_html_id($element['#field_name'] . '_values');
    $order_class = $element['#field_name'] . '-delta-order';
    $required = !empty($element['#required']) ? theme('form_required_marker', $variables) : '';
    $complete = !empty($element['#field_complete']) ? theme('form_complete_marker', array(
      'element' => $element,
    )) : '';
    $header = array(
      array(
        'data' => '<label>' . t('!title !required', array(
          '!title' => $element['#title'],
          '!required' => $required . $complete,
        )) . "</label>",
        'colspan' => 2,
        'class' => array(
          'field-label',
        ),
      ),
      t('Order'),
    );
    $rows = array();

    // Sort items according to '_weight' (needed when the form comes back after
    // preview or failed validation)
    $items = array();
    foreach (element_children($element) as $key) {
      if ($key === 'add_more') {
        $add_more_button =& $element[$key];
      }
      else {
        $items[] =& $element[$key];
      }
    }
    usort($items, '_field_sort_items_value_helper');

    // Add the items as table rows.
    foreach ($items as $key => $item) {
      $item['_weight']['#attributes']['class'] = array(
        $order_class,
      );
      $delta_element = drupal_render($item['_weight']);
      $cells = array(
        array(
          'data' => '',
          'class' => array(
            'field-multiple-drag',
          ),
        ),
        drupal_render($item),
        array(
          'data' => $delta_element,
          'class' => array(
            'delta-order',
          ),
        ),
      );
      $rows[] = array(
        'data' => $cells,
        'class' => array(
          'draggable',
        ),
      );
    }
    $output = '<div class="form-item">';
    $output .= theme('table', array(
      'header' => $header,
      'rows' => $rows,
      'attributes' => array(
        'id' => $table_id,
        'class' => array(
          'field-multiple-table',
        ),
      ),
    ));
    $output .= $element['#description'] ? '<div class="description">' . $element['#description'] . '</div>' : '';
    $output .= '<div class="clearfix">' . drupal_render($add_more_button) . '</div>';
    $output .= '</div>';
    drupal_add_tabledrag($table_id, 'order', 'sibling', $order_class);
  }
  else {
    foreach (element_children($element) as $key) {

      // The #field_complete flag is inherited so we have to re-check
      // whether this specific field should have a 'complete' marker.
      // There's probably a better way of doing this.
      $subelement =& $element[$key];
      if (!empty($subelement['#field_complete']) && !empty($subelement['#entity'])) {
        list(, , $bundle) = entity_extract_ids($subelement['#entity_type'], $subelement['#entity']);
        $instance = field_info_instance($subelement['#entity_type'], $element['#field_name'], $bundle);
        $subelement['#field_complete'] = !empty($instance['settings']['fc']['fc_include']);
        foreach ($subelement['#columns'] as $column) {
          if (!empty($subelement[$column])) {
            $subelement[$column]['#field_complete'] = $subelement['#field_complete'];
          }
        }
      }
      $output .= drupal_render($subelement);
    }
  }
  return $output;
}

/**
 * Theme preprocess function for theme_field() and field.tpl.php.
 *
 * @see theme_field()
 * @see field.tpl.php
 */
function fc_preprocess_field(&$variables) {
  $element = $variables['element'];
  $types = fc_enabled_entity_types();
  if (in_array($element['#entity_type'], $types)) {
    $instance = field_info_instance($element['#entity_type'], $element['#field_name'], $element['#bundle']);
    $fc_extras = array();
    if (!empty($element['#formatter']) && $element['#formatter'] == 'editable') {
      $variables['classes_array'][] = drupal_html_class('field-formatter-' . $element['#formatter']);
      $variables['field_is_editable'] = TRUE;
      if (!empty($instance['settings']['fc']['fc_include'])) {
        $variables['classes_array'][] = 'field-complete';
        $variables['field_is_complete'] = TRUE;
        $fc_extras = array(
          '#theme' => 'form_complete_marker',
        );
      }
      if (!empty($instance['required'])) {
        $variables['classes_array'][] = 'field-required';
        $variables['field_is_required'] = TRUE;
        $fc_extras = array(
          '#theme' => 'form_required_marker',
        );
      }
    }
    $variables['fc_extras'] = $fc_extras;
  }
}

/**
 * Implements hook_form_profile2_form_alter().
 *
 * Profile2 behaves very non-standardly with its edit-form handling
 * so we have to try to see if we can't get round its nonsense.
 */
function fc_form_profile2_form_alter(&$form, &$form_state) {
  module_load_include('form.inc', 'fc');
  return _fc_form_profile2_form_alter($form, $form_state);
}
function fc_get_page_entity($entity_type) {
  $entity = NULL;
  $entity_info = entity_get_info($entity_type);
  if ($entity_type == 'profile2') {

    // Special handling for profile2 (again)
    $router_item = menu_get_item();
    foreach ($router_item['page_arguments'] as $arg) {
      if ($arg instanceof Profile) {
        $entity = $arg;
        break;
      }
    }
  }
  else {

    // Figure out the URL structure, build a dummy entity
    // then fetch its URL.
    $dummy = (object) array(
      $entity_info['entity keys']['id'] => 0,
    );
    $uri = $entity_info['uri callback']($dummy);
    $path = explode('/', $uri['path']);
    $pos = 0;
    while (($el = array_shift($path)) && ++$pos) {
      if (is_numeric($el)) {
        break;
      }
    }

    // Get the actual entity for this page
    $entity = menu_get_object($entity_type, $pos);
  }
  return $entity;
}

//-----------------------------------------------------------

/**
 * Return all entity_types that can benefit from Field Complete
 */
function fc_entity_types() {
  static $entity_types = array();
  foreach (entity_get_info() as $entity_type => $entity_info) {
    if (empty($entity_info['uri callback']) || empty($entity_info['fieldable'])) {

      // There's no display URI for this entity so it'll never appear
      // rendered, or it's not fieldable so Field Complete doesn't apply
      continue;
    }
    $entity_types[$entity_type] = $entity_info;
  }
  return $entity_types;
}

/**
 * Returns the list of enabled entity types
 */
function fc_enabled_entity_types() {
  return variable_get('fc_entity_types', array(
    'node' => 'node',
    'user' => 'user',
  ));
}

/**
 * Returns TRUE if the given entity is enabled with fc, FALSE otherwise.
 */
function fc_entity_is_enabled($entity_type) {
  if (!empty($entity_type) && is_string($entity_type)) {
    $types = fc_enabled_entity_types();
    return in_array($entity_type, $types, true);
  }
  return FALSE;
}

Functions

Namesort descending Description
fc_ctools_plugin_directory
fc_ctools_plugin_type Implements hook_ctools_plugin_type().
fc_default_field_cardinality
fc_enabled_entity_types Returns the list of enabled entity types
fc_entity_delete Implements hook_entity_delete().
fc_entity_info_alter Implements hook_entity_info_alter()
fc_entity_insert Implements hook_entity_insert().
fc_entity_is_enabled Returns TRUE if the given entity is enabled with fc, FALSE otherwise.
fc_entity_load Implements hook_entity_load().
fc_entity_types Return all entity_types that can benefit from Field Complete
fc_entity_update Implements hook_entity_update().
fc_fc_instances_alter Implements hook_fc_instances_alter().
fc_field_attach_after_build
fc_field_attach_form Implements hook_field_attach_form().
fc_field_multiple_value_form Returns HTML for an individual form element.
fc_form_element_label Modified form_element_label theme function.
fc_form_field_ui_field_edit_form_alter Implements hook_field_widget_settings_form().
fc_form_pre_render
fc_form_profile2_form_alter Implements hook_form_profile2_form_alter().
fc_get_page_entity
fc_get_plugin
fc_menu Implements hook_menu().
fc_permission Implements hook_permission().
fc_preprocess_field Theme preprocess function for theme_field() and field.tpl.php.
fc_theme Implements hook_theme().
fc_theme_registry_alter Implements hook_theme_registry_alter().
fc_views_api Implements hook_views_api().