You are here

eck.module in Entity Construction Kit (ECK) 7.3

Same filename and directory in other branches
  1. 8 eck.module
  2. 7 eck.module
  3. 7.2 eck.module

File

eck.module
View source
<?php

/**
 * @file
 * Useful functions and hook implementations for ECK.
 */
ctools_include('plugins');
module_load_include('inc', 'eck', 'eck.classes');
module_load_include('inc', 'eck', 'eck.entity_type');
module_load_include('inc', 'eck', 'eck.bundle');
module_load_include('inc', 'eck', 'eck.entity');
module_load_include('inc', 'eck', 'eck.default_properties');
module_load_include('inc', 'eck', 'eck.property_type');
module_load_include('inc', 'eck', 'eck.properties');
module_load_include('inc', 'eck', 'eck.property_behavior');
module_load_include('inc', 'eck', 'eck.permissions');
module_load_include('inc', 'eck', 'eck.schema');

// Integration with references dialog.
module_load_include('inc', 'eck', 'includes/eck.references_dialog');

/**
 * Load callback for %eckentity.
 */
function eckentity_load($id, $type) {
  try {
    $entity = entity_load_single($type, $id);
  } catch (Exception $ex) {
    drupal_not_found();
    exit;
  }
  return $entity;
}

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

/**
 * Implements hook_flush_caches().
 */
function eck_flush_caches() {

  // Reload all property types.
  eck_get_property_types(NULL, TRUE);

  // Reload all property widget types.
  eck_property_info_widget_types(NULL, TRUE);

  // Reload all ECK entity types.
  EntityType::loadAll(NULL, TRUE);

  // Reload all Bundles.
  Bundle::loadAll(NULL, TRUE);
}

/**
 * Implements hook_modules_enabled().
 */
function eck_modules_enabled($modules) {
  $reset_caches = FALSE;
  foreach ($modules as $module) {
    if (module_hook($module, 'eck_property_widget_info') || module_hook($module, 'eck_property_types')) {
      eck_flush_caches();
      break;
    }
  }
}

/**
 * Implements hook_modules_disabled().
 */
function eck_modules_disabled($modules) {
  $reset_caches = FALSE;
  foreach ($modules as $module) {
    if (module_hook($module, 'eck_property_widget_info') || module_hook($module, 'eck_property_types')) {
      eck_flush_caches();
      break;
    }
  }
}

/**
 * Implements hook_eck_default_properties().
 */
function eck_eck_default_properties() {
  $default_properties = array();
  $default_properties['title'] = array(
    'label' => "Title",
    'type' => "text",
    'behavior' => 'title',
  );
  $default_properties['uid'] = array(
    'label' => "Author",
    'type' => "integer",
    'behavior' => 'author',
  );
  $default_properties['created'] = array(
    'label' => "Created",
    'type' => "integer",
    'behavior' => 'created',
  );
  $default_properties['changed'] = array(
    'label' => "Changed",
    'type' => "integer",
    'behavior' => 'changed',
  );
  $default_properties['language'] = array(
    'label' => "Entity language",
    'type' => "language",
    'behavior' => 'language',
  );

  // Include uuid as default property if module_exists.
  if (module_exists('uuid')) {
    $default_properties['uuid'] = array(
      'label' => "UUID",
      'type' => "text",
      'behavior' => 'uuid',
    );
  }
  return $default_properties;
}

/**
 * Implements hook_ctools_plugin_directory().
 */
function eck_ctools_plugin_directory($owner, $plugin_type) {
  if ($owner == 'eck' && $plugin_type == 'property_behavior') {
    return 'plugins/' . $plugin_type;
  }
}

/**
 * Implements hook_ctools_plugin_type().
 */
function eck_ctools_plugin_type() {
  return array(
    'property_behavior' => array(),
  );
}

/**
 * Entity label callback.
 *
 * This is the callback function for an entities label
 * By default the label is the id of the entity, but a number of
 * hooks are defined to customize the label if needed
 *
 * @param object $entity
 *   an object as returned by entity_load().
 */
function eck__entity__label($entity) {
  $hook_names = array(
    "eck_entity_label",
    "eck_entity_{$entity->entityType()}_label",
    "eck_entity_{$entity->entityType()}_{$entity->bundle()}_label",
  );
  foreach ($hook_names as $hook_name) {
    $new_label = module_invoke_all($hook_name, $entity, $entity->id);
    $empty = empty($new_label);
    if (!$empty) {
      break;
    }
  }
  if (!$empty) {
    return $new_label[0];
  }
  else {
    return $entity->id;
  }
}

/**
 * Entity URI callback.
 *
 * @param object $entity
 *   an object as returned by entity_load().
 */
function eck__entity__uri($entity) {
  $ids = entity_extract_ids($entity
    ->entityType(), $entity);
  module_load_include('inc', 'eck', 'eck.entity');
  $crud_info = get_bundle_crud_info($entity
    ->entityType(), $entity
    ->bundle());
  $view_path = str_replace('%eckentity', $ids[0], $crud_info['view']['path']);
  return array(
    'path' => $view_path,
  );
}

/**
 * Implements hook_schema_alter().
 */
function eck_schema_alter(&$schema) {
  if (db_table_exists('eck_entity_type')) {

    // When something requests an entity's info, the hook_schema is called to
    // get the information about the entity's table, so we need to provide that
    // information in the hook.
    // Get all the entity types that have been create
    // (all the rows in eck_entity_type table).
    foreach (EntityType::loadAll() as $entity_type) {

      // The function eck__entity_type__schema returns a schema for that
      // entity type given and entity_type object.
      $schema = array_merge($schema, array(
        "eck_{$entity_type->name}" => eck__entity_type__schema($entity_type),
      ));

      // Allow properties to modify the schema.
      $vars = array(
        'entity_type' => $entity_type,
        'schema' => $schema,
      );

      // The behavior hook should return its modifications in the same format
      // that they were sent to it. So we would be expecting an array with an
      // entity_type key, and a schema key.
      $vars = eck_property_behavior_invoke_plugin_alter($entity_type, 'schema', $vars);
      if (array_key_exists('schema', $vars) && isset($vars['schema'])) {
        $schema = array_merge($schema, $vars['schema']);
      }
    }
  }
}

/**
 * Creates a table showing a group of entities.
 *
 * @param array $entities
 *   the entities to create the table from
 * @param bool $select
 *   a boolean value that will determine whether the
 *   table is a select table or a regular table
 */
function entity_table($entities, $select = FALSE) {
  module_load_include('inc', 'eck', 'eck.entity');

  // This is information set up for each bundle in the hook_entity_info
  // look there for more details.
  $crud_info = NULL;
  $rows = array();
  $header = array(
    t('Name'),
    array(
      'data' => t('Operations'),
      'colspan' => '1',
    ),
  );
  $info = NULL;
  foreach ($entities as $entity) {
    $info = array();
    $entity_type = $entity
      ->entityType();
    $bundle = $entity
      ->bundle();
    $id = $entity->id;
    if ($crud_info == NULL) {
      $crud_info = get_bundle_crud_info($entity_type, $bundle);
    }
    $allowed_operations = '';
    $destination = drupal_get_destination();

    // Check that the user has permissions to edit.
    if (eck_access('update', 'entity', $entity)) {
      $edit_path = str_replace('%', $id, $crud_info['edit']['path']);
      $allowed_operations = l(t('edit'), $edit_path, array(
        'query' => $destination,
      ));
    }

    // Check that the user has permissions to delete.
    if (eck_access('delete', 'entity', $entity)) {
      $delete_path = str_replace('%', $id, $crud_info['delete']['path']);
      $allowed_operations .= ($allowed_operations ? '<br />' : '') . l(t('delete'), $delete_path, array(
        'query' => $destination,
      ));
    }
    $uri = entity_uri($entity_type, $entity);
    if (eck_access("view", "entity", $entity)) {
      $row = array(
        l(entity_label($entity_type, $entity), $uri['path'], $uri['options']),
      );
    }
    else {
      $row = array(
        entity_label($entity_type, $entity),
      );
    }
    $row[] = array(
      'data' => $allowed_operations,
    );
    $info['entity'] = $entity;
    drupal_alter("entity_{$entity_type}_{$bundle}_tr", $row, $info);
    $info['bundle'] = $bundle;
    drupal_alter("entity_{$entity_type}_tr", $row, $info);
    $info['entity_type'] = $entity_type;
    drupal_alter("entity_tr", $row, $info);
    $rows[$id] = $row;
  }
  if ($info) {
    unset($info['entity']);
    drupal_alter("entity_th", $header, $info);
    unset($info['entity_type']);
    drupal_alter("entity_{$entity_type}_th", $header, $info);
    unset($info['bundle']);
    drupal_alter("entity_{$entity_type}_{$bundle}_th", $header, $info);
  }
  if ($select) {
    if (!isset($entity_type)) {
      return array(
        '#theme' => 'table',
        '#header' => $header,
        '#rows' => $rows,
      );
    }
    else {
      return drupal_get_form("entity_table_select_{$entity_type}_{$bundle}", $entity_type, $bundle, $header, $rows);
    }
  }
  else {
    return array(
      '#theme' => 'table',
      '#header' => $header,
      '#rows' => $rows,
    );
  }
}

/**
 * Implements hook_entity_info().
 *
 * The Entity information for all the entity types created with eck.
 */
function eck_entity_info() {
  module_load_include('inc', 'eck', 'eck.entity_type');
  $info = array();

  // Get all the names of all the entity types from the eck table
  // for each of the created entity types add its info to the $info array.
  foreach (EntityType::loadAll() as $entity_type) {

    // eck__entity_info creates the entity_info for each entity type.
    $info = array_merge($info, eck__entity_type__info($entity_type));
  }
  return $info;
}

/**
 * Implements hook_entity_info_alter().
 */
function eck_entity_info_alter(&$info) {
  foreach (EntityType::loadAll() as $entity_type) {
    $entity_type_info = $info[$entity_type->name];
    $entity_type_info = eck_property_behavior_invoke_plugin_alter($entity_type, 'entity_info', $entity_type_info);
    if ($entity_type_info) {
      $info[$entity_type->name] = $entity_type_info;
    }
  }
}

/**
 * Implements hook_field_extra_fields().
 */
function eck_field_extra_fields() {
  $extra = array();
  foreach (EntityType::loadAll() as $entity_type) {
    foreach (Bundle::loadByEntityType($entity_type) as $bundle) {
      foreach ($entity_type->properties as $property_name => $property_info) {
        if (!empty($bundle->config['extra_fields'][$property_name]['form'])) {
          $extra[$entity_type->name][$bundle->name]['form'][$property_name] = array(
            'label' => $bundle->config['extra_fields'][$property_name]['form']['label'],
            'description' => t('Entity property: %type', array(
              '%type' => $property_info['type'],
            )),
            'weight' => 0,
          );
        }
        if (!empty($bundle->config['extra_fields'][$property_name]['display'])) {
          $extra[$entity_type->name][$bundle->name]['display'][$property_name] = array(
            'label' => $bundle->config['extra_fields'][$property_name]['display']['label'],
            'description' => t('Entity property: %type', array(
              '%type' => $property_info['type'],
            )),
            'weight' => 0,
          );
        }
      }
    }
  }
  return $extra;
}

/**
 * Entity type load.
 */
function entity_type_load($entity_type_name) {
  return EntityType::loadByName($entity_type_name);
}

/**
 * Bundle load.
 */
function bundle_load($entity_type_name, $bundle_name) {
  return Bundle::loadByMachineName("{$entity_type_name}_{$bundle_name}");
}

/**
 * Comply with requirements probably no one will use this.
 */
function _eck_fake_exists() {
  return FALSE;
}

/**
 * Implements hook_entity_property_info().
 */
function eck_entity_property_info() {
  module_load_include('inc', 'entity', 'entity.info');
  $info = array();
  foreach (EntityType::loadAll() as $entity_type) {
    $properties = $entity_type->properties;
    $stuff = entity_metadata_convert_schema("eck_{$entity_type->name}");
    foreach ($stuff as $key => $property) {

      // Use user defined label for property.
      if (isset($properties[$key])) {
        $property['label'] = $properties[$key]['label'];
      }
      $property['setter callback'] = "entity_property_verbatim_set";
      $property['getter callback'] = 'entity_property_verbatim_get';
      $property['description'] = $property['label'];

      // A couple of alter hooks so a module can alter the property info
      // of a given property, or even a specific property on a
      // specific entity_type.
      drupal_alter("entity_property_{$key}_info", $property);
      drupal_alter("entity_property_{$entity_type->name}_{$key}_info", $property);
      if ($key == 'type') {
        $property['label'] = t('!entity_type type', array(
          '!entity_type' => $entity_type->name,
        ));
        $property['type'] = 'token';
        $property['description'] = t('The type of this :entity_type entity.', array(
          ':entity_type' => $entity_type->name,
        ));
        $property['options list'] = 'EntityDefaultMetadataController::bundleOptionsList';
        $property['required'] = TRUE;
      }
      $stuff[$key] = $property;
    }
    $info[$entity_type->name]['properties'] = $stuff;
  }
  return $info;
}

/**
 * Implements hook_entity_property_info_alter().
 */
function eck_entity_property_info_alter(&$info) {
  foreach (EntityType::loadAll() as $entity_type) {
    $entity_property_info = $info[$entity_type->name];
    $entity_property_info = eck_property_behavior_invoke_plugin_alter($entity_type, 'property_info', $entity_property_info);
    foreach ($entity_type->properties as $property => $stuff) {
      foreach (array(
        'setter',
        'getter',
        'validation',
      ) as $function_name) {
        if (eck_property_behavior_implements($entity_type, $property, $function_name)) {
          $entity_property_info['properties'][$property]["{$function_name} callback"] = "eck_property_behavior_{$function_name}";
        }
      }
    }
    if ($entity_property_info) {
      $info[$entity_type->name] = $entity_property_info;
    }
  }
}

/**
 * Get property label.
 */
function eck_get_property_label($name) {
  $info = hook_eck_property_info();
  return $info[$name]['label'];
}

/**
 * Implements hook_features_api().
 */
function eck_features_api() {
  return array(
    'eck_entity_type' => array(
      'name' => t('Entity Types'),
      'feature_source' => TRUE,
      'file' => drupal_get_path('module', 'eck') . '/eck.features.inc',
      'default_hook' => 'eck_entity_type_info',
    ),
    'eck_bundle' => array(
      'name' => t('Bundles'),
      'feature_source' => TRUE,
      'file' => drupal_get_path('module', 'eck') . '/eck.features.inc',
      'default_hook' => 'eck_bundle_info',
    ),
  );
}

/**
 * Implements hook_menu().
 */
function eck_menu() {
  $menu = array();
  module_load_include('inc', 'eck', 'eck.entity_type');
  $menu = array_merge(eck__entity_type__menu(), $menu);
  return $menu;
}

/**
 * Callback for checking multiple permissions.
 *
 * @param array $perms
 *   An array of permissions to be checked against.
 * @param bool $b_own
 *   (optional) Flag to indicate if we should also check ownership permissions
 *   which are the same as the usual permissions, but will be postfixed with
 *   ' own'. Defaults to FALSE.
 * @param object $account
 *   (optional) The account to check permissions against. Defaults to the
 *   current user.
 */
function eck__multiple_access_check($perms, $b_own = FALSE, $account = NULL) {
  if (!isset($account)) {
    $account = $GLOBALS['user'];
  }
  foreach ($perms as $perm) {
    if (user_access($perm, $account)) {
      return TRUE;
    }
  }
  if (!$b_own) {
    return FALSE;
  }

  // Check for entity author field and user ownership permissions.
  foreach ($perms as $perm) {
    if (user_access($perm . ' own', $account)) {
      return TRUE;
    }
  }

  // The user does not have any of the supplied permissions.
  return FALSE;
}

/**
 * Implements hook_forms().
 */
function eck_forms($form_id, $args) {
  $forms = array();
  if (strpos($form_id, 'entity_table_select_') === 0) {
    $forms[$form_id] = array(
      'callback' => 'entity_table_select',
    );
  }
  elseif (strpos($form_id, 'eck__entity__form_') === 0) {
    $forms[$form_id] = array(
      'callback' => 'eck__entity__form',
    );
  }
  return $forms;
}

/**
 * Helper function for the entities_table.
 *
 * This function creates a select table.
 *
 * @param array $form
 *   A form arrary as returned by drupal_get_form.
 * @param array $state
 *   The form state, this is also provided by the Form API.
 * @param string $entity_type
 *   the type of the entities that will be in the table.
 * @param string $bundle
 *   The bundle of the entity that will be in the table.
 * @param array $header
 *   An array for the table header for more info look at theme_table.
 * @param array $rows
 *   The rows of the table for more info on what this should look like look
 *   at theme_table.
 */
function entity_table_select($form, &$state, $entity_type, $bundle, $header, $rows) {
  $form['entity_type'] = array(
    '#type' => 'value',
    '#value' => $entity_type,
  );
  $form['bundle'] = array(
    '#type' => 'value',
    '#value' => $bundle,
  );
  $form['entity_table'] = array(
    // '#theme' => 'table',
    '#type' => 'tableselect',
    '#header' => $header,
    '#options' => $rows,
  );
  return $form;
}

/*******************************************************************************
 *            Property widget info and hooks
 ******************************************************************************/

/**
 * Returns an array of widget type options for an ECK property type.
 *
 * If no property type is provided, returns a nested array of all widget types,
 * keyed by property type.
 */
function eck_property_widget_type_options($property_type = NULL, $by_label = FALSE) {
  $options =& drupal_static(__FUNCTION__);
  if (!isset($options)) {
    $options = array();
    $property_types = eck_get_property_types();
    foreach (eck_property_info_widget_types() as $name => $widget_type) {
      foreach ($widget_type['property types'] as $widget_property_type) {

        // Check that the field type exists.
        if (isset($property_types[$widget_property_type])) {
          $options[$widget_property_type][$name] = $widget_type['label'];
        }
      }
    }
  }
  if (isset($property_type)) {
    return !empty($options[$property_type]) ? $options[$property_type] : array();
  }
  if ($by_label) {
    $property_types = eck_get_property_types();
    $options_by_label = array();
    foreach ($options as $property_type => $widgets) {
      $options_by_label[$property_types[$property_type]['label']] = $widgets;
    }
    return $options_by_label;
  }
  return $options;
}

/**
 * Returns information of property widgets from hook_eck_property_widget_info.
 *
 * @param string $widget_type
 *   (optional) A widget type name. If omitted, all widget types will be
 *   returned.
 *
 * @param bool $reset
 *   Forces rebuild of the property widget cache.
 *
 * @return array
 *   Either a single widget type description, as provided by
 *   hook_eck_property_widget_info(), or an array of all existing widget types,
 *   keyed by widget type name.
 */
function eck_property_info_widget_types($widget_type = NULL, $reset = FALSE) {
  global $language;
  static $widget_types;

  // The _info() hooks invoked below include translated strings, so each
  // language is cached separately.
  $langcode = $language->language;
  if ($reset) {
    $widget_types = NULL;

    // Clear all languages.
    cache_clear_all('property_widget_types:', 'cache_eck', TRUE);
  }
  if (!isset($widget_types)) {
    if ($cached = cache_get("property_widget_types:{$langcode}", 'cache_eck')) {
      $widget_types = $cached->data;
    }
    else {
      $widget_types = array();

      // Populate property widget types.
      foreach (module_implements('eck_property_widget_info') as $module) {
        $module_widget_types = (array) module_invoke($module, 'eck_property_widget_info');
        foreach ($module_widget_types as $name => $widget_info) {

          // Provide defaults.
          $widget_info += array(
            'type' => $name,
            'label' => t("@name", array(
              '@name' => $name,
            )),
            'settings' => array(),
            'property types' => array(),
            'file' => FALSE,
            'file type' => 'inc',
            'description' => '',
            'value callback' => '',
          );
          $widget_types[$name] = $widget_info;
          $widget_types[$name]['module'] = $module;
        }
      }
      drupal_alter('eck_property_widget_info', $widget_types);
      uasort($widget_types, 'drupal_sort_weight');
      cache_set("property_widget_types:{$langcode}", $widget_types, 'cache_eck');
    }
  }
  if (isset($widget_type) && isset($widget_types[$widget_type])) {
    return $widget_types[$widget_type];
  }
  return $widget_types;
}

/**
 * Implements hook_eck_property_widget_info().
 *
 * Defines some default property widgets that come with ECK.
 *
 * @see eck_property_info_widget_types()
 */
function eck_eck_property_widget_info() {
  $widget_types = array(
    'text' => array(
      'label' => t('Text'),
      'settings' => array(
        'size' => 60,
        'max_length' => 255,
      ),
      'property types' => array(
        'text',
        'integer',
        'positive_integer',
        'decimal',
        'blob',
        'datetime',
        'uuid',
      ),
      'file' => 'eck.property_widgets',
    ),
    'options' => array(
      'label' => t('Options'),
      'settings' => array(
        'options' => '',
      ),
      'property types' => array(
        'text',
        'integer',
        'positive_integer',
        'decimal',
      ),
      'file' => 'eck.property_widgets',
    ),
    'language_toggle' => array(
      'label' => t('Language toggle'),
      'property types' => array(
        'language',
      ),
      'file' => 'eck.property_widgets',
    ),
  );
  return $widget_types;
}

/*******************************************************************************
 *            Form Alters
 ******************************************************************************/

/**
 * Adds a manage properties section.
 *
 * To the field overview 'Manage fields' form that allows for the inclusion of
 * properties as "extra fields" on entity forms.
 */
function eck_form_field_ui_field_overview_form_alter(&$form, &$form_state) {
  if (array_key_exists($form['#entity_type'], eck_entity_info())) {
    $entity_type = entity_type_load($form['#entity_type']);
    $bundle = bundle_load($form['#entity_type'], $form['#bundle']);
    $admin_path = _field_ui_bundle_admin_path($bundle->entity_type, $bundle->name);
    $max_weight = field_info_max_weight($form['#entity_type'], $form['#bundle'], 'form');
    $widget_types = eck_property_info_widget_types();
    $widget_type_options = eck_property_widget_type_options(NULL, TRUE);
    $properties = array();
    $extra_fields = !empty($bundle->config['extra_fields']) && is_array($bundle->config['extra_fields']) ? $bundle->config['extra_fields'] : array();
    foreach ($entity_type->properties as $property => $info) {
      if (empty($extra_fields[$property]['form'])) {
        $properties[$property] = $info;
      }
      else {
        $admin_property_path = $admin_path . '/properties/' . $property;
        unset($form['fields'][$property]['type']['#cell_attributes']);
        unset($form['fields'][$property]['edit']);
        unset($form['fields'][$property]['delete']);
        $key = $extra_fields[$property]['form']['widget']['type'];
        $table_elements = array(
          'widget_type' => array(
            '#type' => 'link',
            '#title' => t("@widget_label", array(
              "@widget_label" => $widget_types[$key]['label'],
            )),
            '#href' => $admin_property_path . '/widget-type',
            '#options' => array(
              'attributes' => array(
                'title' => t('Change widget type.'),
              ),
            ),
          ),
          'edit' => array(
            '#type' => 'link',
            '#title' => t('edit'),
            '#href' => $admin_property_path,
            '#options' => array(
              'attributes' => array(
                'title' => t('Edit property management settings.'),
              ),
            ),
          ),
          'delete' => array(
            '#type' => 'link',
            '#title' => t('remove'),
            '#href' => $admin_property_path . '/remove',
            '#options' => array(
              'attributes' => array(
                'title' => t('Remove management of this property.'),
              ),
            ),
          ),
        );
        $form['fields'][$property] += $table_elements;
      }
    }

    // Additional row: add properties as extra fields.
    if ($properties && $widget_type_options) {

      // Build list of options.
      $properties_options = array();
      foreach ($properties as $property => $info) {
        $text = t('@type: @label (@property)', array(
          '@type' => $info['type'],
          '@label' => $info['label'],
          '@property' => $property,
        ));
        $properties_options[$property] = truncate_utf8($text, 80, FALSE, TRUE);
      }
      asort($properties_options);
      $name = '_eck_add_extra_field';
      $form['fields'][$name] = array(
        '#attributes' => array(
          'class' => array(
            'draggable',
            'tabledrag-leaf',
            'add-new',
          ),
        ),
        '#row_type' => 'add_new_field',
        '#region_callback' => 'field_ui_field_overview_row_region',
        'label' => array(
          '#type' => 'textfield',
          '#title' => t('Extra field label'),
          '#title_display' => 'invisible',
          '#size' => 15,
          '#description' => t('Label'),
          '#attributes' => array(
            'class' => array(
              'label-textfield',
            ),
          ),
          '#prefix' => '<div class="label-input"><div class="add-new-placeholder">' . t('Manage a property') . '</div>',
          '#suffix' => '</div>',
        ),
        'weight' => array(
          '#type' => 'textfield',
          '#default_value' => $max_weight + 3,
          '#size' => 3,
          '#title_display' => 'invisible',
          '#title' => t('Weight for added field'),
          '#attributes' => array(
            'class' => array(
              'field-weight',
            ),
          ),
          '#prefix' => '<div class="add-new-placeholder">&nbsp;</div>',
        ),
        'parent_wrapper' => array(
          'parent' => array(
            '#type' => 'select',
            '#title' => t('Parent for extra field'),
            '#title_display' => 'invisible',
            '#options' => $form['fields']['#parent_options'],
            '#empty_value' => '',
            '#attributes' => array(
              'class' => array(
                'field-parent',
              ),
            ),
            '#prefix' => '<div class="add-new-placeholder">&nbsp;</div>',
            '#parents' => array(
              'fields',
              $name,
              'parent',
            ),
          ),
          'hidden_name' => array(
            '#type' => 'hidden',
            '#default_value' => $name,
            '#attributes' => array(
              'class' => array(
                'field-name',
              ),
            ),
          ),
        ),
        'field_name' => array(
          '#type' => 'select',
          '#title' => t('Extra field'),
          '#title_display' => 'invisible',
          '#options' => $properties_options,
          '#empty_option' => t('- Select a property -'),
          '#description' => t('Existing entity property to manage.'),
          '#attributes' => array(
            'class' => array(
              'eck-property-type-select',
            ),
          ),
          '#cell_attributes' => array(
            'colspan' => 2,
          ),
          '#prefix' => '<div class="add-new-placeholder">&nbsp;</div>',
        ),
        'widget_type' => array(
          '#type' => 'select',
          '#title' => t('Widget for managed property'),
          '#title_display' => 'invisible',
          '#options' => $widget_type_options,
          '#empty_option' => t('- Select a widget -'),
          '#description' => t('Form element to edit the property data.'),
          '#attributes' => array(
            'class' => array(
              'eck-widget-type-select',
            ),
          ),
          '#cell_attributes' => array(
            'colspan' => 3,
          ),
          '#prefix' => '<div class="add-new-placeholder">&nbsp;</div>',
        ),
      );

      // Include ECK specific validation and submit handling for this form.
      $form['#validate'][] = 'eck_form_field_ui_field_overview_form_validate';
      $form['#submit'][] = 'eck_form_field_ui_field_overview_form_submit';

      // Attach handling for property selection.
      $form['#attached']['js'][] = drupal_get_path('module', 'eck') . '/eck.field_ui.js';

      // Add settings for the update selects behavior.
      $js_properties = array();
      foreach ($properties as $property => $info) {
        $js_properties[$property] = array(
          'label' => $info['label'],
          'type' => $info['type'],
        );
      }
      $form['#attached']['js'][] = array(
        'type' => 'setting',
        'data' => array(
          'eckProperties' => $js_properties,
          'eckPropertyWidgetTypes' => eck_property_widget_type_options(),
        ),
      );
    }
  }
}

/**
 * Validates the add property as extra field of field_ui_field_overview_form().
 *
 * @see eck_form_field_ui_field_overview_form_alter()
 * @see field_ui_field_overview_form_validate()
 */
function eck_form_field_ui_field_overview_form_validate($form, &$form_state) {

  // The form element might be absent if no existing fields can be added to
  // this bundle.
  if (isset($form_state['values']['fields']['_eck_add_extra_field'])) {
    $extra_field = $form_state['values']['fields']['_eck_add_extra_field'];

    // Validate if any information was provided in the 'add existing field' row.
    $array = array(
      $extra_field['label'],
      $extra_field['field_name'],
      $extra_field['widget_type'],
    );
    if (array_filter($array)) {

      // Missing label.
      if (!$extra_field['label']) {
        form_set_error('fields][_eck_add_extra_field][label', t('Add property as extra field: you need to provide a label.'));
      }

      // Missing existing field name.
      if (!$extra_field['field_name']) {
        form_set_error('fields][_eck_add_extra_field][field_name', t('Add property as extra field: you need to select a field.'));
      }

      // Wrong property for this bundle.
      $entity_type = entity_type_load($form['#entity_type']);
      if (!isset($entity_type->properties[$extra_field['field_name']])) {
        form_set_error('fields][_eck_add_extra_field][field_name', t('Add property as extra field: invalid property for this bundle.'));
      }

      // Missing widget type.
      if (!$extra_field['widget_type']) {
        form_set_error('fields][_eck_add_extra_field][widget_type', t('Add property as extra field: you need to select a widget.'));
      }
      elseif ($extra_field['field_name'] && ($existing_field = field_info_field($extra_field['field_name']))) {
        $entity_type = entity_type_load($form['#entity_type']);
        $widget_types = eck_property_widget_type_options($entity_type->properties[$extra_field['field_name']]['type']);
        if (!isset($widget_types[$extra_field['widget_type']])) {
          form_set_error('fields][_eck_add_extra_field][widget_type', t('Add property as extra field: invalid widget.'));
        }
      }
    }
  }
}

/**
 * Form submission handler for ECK's additions to field_ui_field_overview_form.
 *
 * @see eck_form_field_ui_field_overview_form_alter()
 * @see eck_form_field_ui_field_overview_form_validate()
 */
function eck_form_field_ui_field_overview_form_submit($form, &$form_state) {
  if (isset($form_state['values']['fields']['_eck_add_extra_field'])) {
    $extra_field_settings = $form_state['values']['fields']['_eck_add_extra_field'];

    // Submit only if information was provided in the 'add existing field' row.
    $array = array(
      $extra_field_settings['label'],
      $extra_field_settings['field_name'],
      $extra_field_settings['widget_type'],
    );
    if (array_filter($array)) {
      $entity_type = entity_type_load($form['#entity_type']);
      $bundle = bundle_load($form['#entity_type'], $form['#bundle']);
      $property_name = $extra_field_settings['field_name'];

      // Add extra field configuration for this property to the bundle.
      $config = !empty($bundle->config) ? $bundle->config : array();
      $widget_info = eck_property_info_widget_types($extra_field_settings['widget_type']);
      $extra_field = array(
        'label' => $extra_field_settings['label'],
        'description' => $widget_info['description'],
        'widget' => array(
          'type' => $extra_field_settings['widget_type'],
          'settings' => $widget_info['settings'],
        ),
      );

      // If the property behavior supplies a default value function then make
      // sure we use it.
      if ($default_function = eck_property_behavior_implements($entity_type, $property_name, 'default_value')) {
        $extra_field['default_value_function'] = $default_function;
      }

      // Pull in the default..default value from the property type schema.
      $schema = eck_get_property_type_schema($entity_type->properties[$property_name]['type']);
      $extra_field['default_value'] = $schema['default'];
      $config['extra_fields'][$property_name]['form'] = $extra_field;
      $bundle->config = $config;

      // Save the bundle.
      $bundle
        ->save();
      Bundle::loadAll(NULL, TRUE);
    }
  }
}

/**
 * Implements hook_entity_presave().
 */
function eck_entity_presave($entity, $entity_type) {
  $entity_type = EntityType::loadByName($entity_type);

  // This is an eck entity.
  if ($entity_type) {
    eck_property_behavior_invoke_plugin($entity_type, 'entity_save', array(
      'entity' => $entity,
    ));
  }
}

/**
 * Implements hook_entity_delete().
 */
function eck_entity_delete($entity, $entity_type) {
  $entity_type = EntityType::loadByName($entity_type);

  // This is an eck entity.
  if ($entity_type) {
    eck_property_behavior_invoke_plugin($entity_type, 'entity_delete', array(
      'entity' => $entity,
    ));
  }
}

/**
 * Implements hook_entity_view_alter().
 */
function eck_entity_view_alter(&$build) {
  $entity_types = EntityType::loadAll();
  $this_entity_type = $build['#entity_type'];
  foreach ($entity_types as $et) {
    if ($et->name == $this_entity_type) {
      $entity = $build['#entity'];
      $this_bundle = $entity
        ->bundle();

      // Lets add contextual links to our entities in eck you can change
      // the paths of any of the possible operations, since contextual links
      // are dependent on the hierarchy of those paths, changing the paths
      // could cause of contextual links not to work correctly.
      $build['#contextual_links']['eck'] = array(
        "{$this_entity_type}/{$this_bundle}",
        array(
          $entity->id,
        ),
      );
    }
  }
}

/**
 * Helper function to order objects with a name in alphabetical order.
 */
function eck_alphabetical_cmp($a, $b) {
  return strcasecmp($a->name, $b->name);
}

/**
 * Implements hook_eck_property_types().
 */
function eck_eck_property_types() {
  $property_types = array();
  $property_types['decimal'] = array(
    'label' => t("Decimal"),
    'class' => "DecimalPropertyType",
  );
  $property_types['integer'] = array(
    'label' => t("Integer"),
    'class' => "IntegerPropertyType",
  );
  $property_types['text'] = array(
    'label' => t("Text"),
    'class' => "TextPropertyType",
  );
  $property_types['fixed_size_text'] = array(
    'label' => t("Fixed Size Text"),
    'class' => "FixedSizeTextPropertyType",
  );
  $property_types['long_text'] = array(
    'label' => t("Long Text"),
    'class' => "LongTextPropertyType",
  );
  $property_types['blob'] = array(
    'label' => t("Blob"),
    'class' => "BlobPropertyType",
  );
  $property_types['datetime'] = array(
    'label' => t("Date/Time"),
    'class' => "DatetimePropertyType",
  );
  $property_types['language'] = array(
    'label' => t("Language"),
    'class' => "LanguagePropertyType",
  );
  $property_types['uuid'] = array(
    'label' => t("UUID"),
    'class' => "UUIDPropertyType",
  );
  $property_types['positive_integer'] = array(
    'label' => t("Positive Integer"),
    'class' => "PositiveIntegerPropertyType",
  );
  return $property_types;
}

/**
 * Implements hook_init().
 */
function eck_init() {

  // Lets initialize our global caches.
  global $_eck_entity_types_cache;
  global $_eck_bundles_cache;
  $_eck_entity_types_cache = new ECKCache("entity_types");
  $_eck_bundles_cache = new ECKCache("bundles");
  if (variable_get("eck_clear_caches", FALSE)) {
    variable_set("eck_clear_caches", FALSE);
    eck_clean_up();
  }
}

/**
 * Clean up.
 */
function eck_clean_up() {
  drupal_get_schema(NULL, TRUE);
  entity_info_cache_clear();
  entity_flush_caches();
  menu_rebuild();
}

/**
 * Implements hook_system_info_alter().
 *
 * If there are entity types created, lets not let the user disable the module.
 * If the eck_entity_cache module is installed, eck can not be disabled.
 */
function eck_system_info_alter(&$info, $file, $type) {
  if ($type == "module" && array_key_exists("name", $info) && $info['name'] == "Entity Construction Kit") {
    $done = FALSE;
    $entity_types = EntityType::loadAll();
    if (!empty($entity_types)) {
      $info['required'] = TRUE;
      $info['explanation'] = "Entity types created by ECK are still present in the system.";
      $done = TRUE;
    }

    // If ECK EntityCache is installed, we can not disable or uninstall ECK.
    if (!$done) {
      $query = db_select("system", "s");
      $query
        ->fields('s', array(
        'schema_version',
      ));
      $query
        ->condition('s.name', 'eck_entitycache', '=');
      $query
        ->condition('s.type', 'module', '=');
      $result = $query
        ->execute();
      foreach ($result as $row) {
        $schema_version = $row->schema_version;
        if ($schema_version == 0) {
          $info['required'] = TRUE;
          $info['explanation'] = "ECK EntityCache must be uninstalled before you can disable/uninstall ECK";
        }
      }
    }
  }
}

Functions

Namesort descending Description
bundle_load Bundle load.
eckentity_load Load callback for %eckentity.
eck_alphabetical_cmp Helper function to order objects with a name in alphabetical order.
eck_clean_up Clean up.
eck_ctools_plugin_directory Implements hook_ctools_plugin_directory().
eck_ctools_plugin_type Implements hook_ctools_plugin_type().
eck_eck_default_properties Implements hook_eck_default_properties().
eck_eck_property_types Implements hook_eck_property_types().
eck_eck_property_widget_info Implements hook_eck_property_widget_info().
eck_entity_delete Implements hook_entity_delete().
eck_entity_info Implements hook_entity_info().
eck_entity_info_alter Implements hook_entity_info_alter().
eck_entity_presave Implements hook_entity_presave().
eck_entity_property_info Implements hook_entity_property_info().
eck_entity_property_info_alter Implements hook_entity_property_info_alter().
eck_entity_view_alter Implements hook_entity_view_alter().
eck_features_api Implements hook_features_api().
eck_field_extra_fields Implements hook_field_extra_fields().
eck_flush_caches Implements hook_flush_caches().
eck_forms Implements hook_forms().
eck_form_field_ui_field_overview_form_alter Adds a manage properties section.
eck_form_field_ui_field_overview_form_submit Form submission handler for ECK's additions to field_ui_field_overview_form.
eck_form_field_ui_field_overview_form_validate Validates the add property as extra field of field_ui_field_overview_form().
eck_get_property_label Get property label.
eck_init Implements hook_init().
eck_menu Implements hook_menu().
eck_modules_disabled Implements hook_modules_disabled().
eck_modules_enabled Implements hook_modules_enabled().
eck_property_info_widget_types Returns information of property widgets from hook_eck_property_widget_info.
eck_property_widget_type_options Returns an array of widget type options for an ECK property type.
eck_schema_alter Implements hook_schema_alter().
eck_system_info_alter Implements hook_system_info_alter().
eck_views_api Implements hook_views_api().
eck__entity__label Entity label callback.
eck__entity__uri Entity URI callback.
eck__multiple_access_check Callback for checking multiple permissions.
entity_table Creates a table showing a group of entities.
entity_table_select Helper function for the entities_table.
entity_type_load Entity type load.
_eck_fake_exists Comply with requirements probably no one will use this.