Skip to content
entityreference_prepopulate.module 12.5 KiB
Newer Older
Amitaibu's avatar
Amitaibu committed
<?php

/**
 * @file
 * Prepopulate entity reference values from URL.
 */

/**
 * Implements hook_ctools_plugin_directory().
 */
function entityreference_prepopulate_ctools_plugin_directory($module, $plugin) {
  if ($module == 'entityreference' || $module == 'ctools') {
Amitaibu's avatar
Amitaibu committed
    return 'plugins/' . $plugin;
  }
}

/**
 * Implements hook_field_create_instance().
 *
 * Add "default value function" setting to the field instance.
 * We have to do it from this hook, as we don't have another chance of setting
 * this option via the hook_field_info().
 */
function entityreference_prepopulate_field_create_instance($instance) {
  if (empty($instance['settings']['behaviors']['prepopulate']['status'])) {
    return;
  }

  $instance['default_value_function'] = 'entityreference_prepopulate_field_default_value';
  field_update_instance($instance);
}

/**
Amitai Burstein's avatar
Amitai Burstein committed
 * Implements hook_field_update_instance().
Amitaibu's avatar
Amitaibu committed
 */
function entityreference_prepopulate_field_update_instance($instance, $prior_instance) {
  if (empty($instance['settings']['behaviors']['prepopulate'])) {
    return;
  }
  if (isset($prior_instance['settings']['behaviors']['prepopulate']['status']) && $instance['settings']['behaviors']['prepopulate']['status'] == $prior_instance['settings']['behaviors']['prepopulate']['status']) {
    // Nothing changed.
    return;
  }

  $instance['default_value_function'] = !empty($instance['settings']['behaviors']['prepopulate']['status']) ? 'entityreference_prepopulate_field_default_value' : '';
  field_update_instance($instance);
}


/**
 * Field default value callback.
 *
 * Set the default from the URL context. This works even if the widget is
 * not shown, e.g. due to restricted field access.
 */
function entityreference_prepopulate_field_default_value($entity_type, $entity, $field, $instance, $langcode) {
  if (module_exists('og') && og_is_group_audience_field($instance['field_name'])) {
    return entityreference_prepopulate_og_field_default_value($entity_type, $entity, $field, $instance, $langcode);
  }
  if ($items = entityreference_prepopulate_get_values($field, $instance)) {
Amitaibu's avatar
Amitaibu committed
    // TODO: Check field cardinality.
Amitaibu's avatar
Amitaibu committed
    return $items;
  }
}

/**
 * Default value callback for Organic groups; Filter results by field-mode.
 */
function entityreference_prepopulate_og_field_default_value($entity_type, $entity, $field, $instance, $langcode) {
  if (empty($instance['field_mode'])) {
  if ($items = entityreference_prepopulate_get_values($field, $instance)) {
    // Filter out the items that don't match the field-mode.
    $gids = array();
    foreach ($items as $item) {
      $gids[] = $item['target_id'];
    }
    if (!$valid_ids = entityreference_get_selection_handler($field, $instance, $entity_type, $entity)->validateReferencableEntities($gids)) {
      return;
    }
    $valid_items = array();
    foreach ($valid_ids as $valid_id) {
      $valid_items[] = array('target_id' => $valid_id);
    }
    return $valid_items;
  }
}

/**
 * Implements hook_field_attach_form().
 */
function entityreference_prepopulate_field_attach_form($entity_type, $entity, &$form, &$form_state, $langcode) {
  list($id,,$bundle) = entity_extract_ids($entity_type, $entity);

  if (!empty($form_state['triggering_element']['#ajax'])) {
    // We are inside AJAX, so values can't be taken from URL at the
  // Check if there is a field that needs to be prepopulated attached to the
  // given entity.
  $found = FALSE;
  foreach (field_info_instances($entity_type, $bundle) as $instance) {
    if (!empty($instance['settings']['behaviors']['prepopulate']['status'])) {
      $found = TRUE;
      break;
    }
  }

  if (!$found) {
    return;
  }

  foreach (element_children($form_state['field']) as $field_name) {
    foreach ($form_state['field'][$field_name] as $lang => $value) {
      $instance = $value['instance'];
Amitaibu's avatar
Amitaibu committed
      if (empty($instance['settings']['behaviors']['prepopulate']['status'])) {
        continue;
      }
      $settings = $instance['settings']['behaviors']['prepopulate'];
      if ((!empty($settings['skip_perm']) && user_access($settings['skip_perm'])) || ($id && empty($settings['action_on_edit']))) {
        // User has access to skip the action, or the entity is already
        // saved, but "Apply action on edit", is disabled.

      // Store prepopulated values in the form state to make them persistent,
      // in case the form is rebuilt by AJAX requests.
      if ($values = entityreference_prepopulate_get_values($field, $instance, TRUE)) {
        $form_state['entityreference_prepopulate'][$instance['entity_type']][$instance['bundle']][$field['field_name']] = $values;
      }

      if ($values || ($id && !empty($settings['action_on_edit']))) {
        // New entity with prepopualte values, or an existing entity,
        // we might need to disable/ hide the group-audience field.
Amitaibu's avatar
Amitaibu committed
        if ($settings['action'] == 'disable') {
          $form[$field_name][$lang]['#disabled'] = TRUE;
        }
        elseif ($settings['action'] == 'hide') {
          // We don't hide the field via hook_field_access(), as the
          // default value won't be set.
          $form[$field_name]['#access'] = FALSE;
      elseif (in_array($settings['fallback'], array('form_error', 'redirect'))) {
        $message = t('Field @label must be populated via URL.', array('@label' => $instance['label']));
        if ($settings['fallback'] == 'form_error') {
          form_error($form, $message);
        }
        elseif ($settings['fallback'] == 'redirect') {
          drupal_set_message($message, 'notice');
          drupal_goto();
        }
Amitaibu's avatar
Amitaibu committed
/**
 * Implements hook_field_access().
 */
function entityreference_prepopulate_field_access($op, $field, $entity_type, $entity, $account) {
Amitaibu's avatar
Amitaibu committed
  if ($op != 'edit' || $field['type'] != 'entityreference') {
Amitaibu's avatar
Amitaibu committed
    return;
  }

  if (empty($entity)) {
    // $entity might be NULL, so return early.
    // @see field_access().
    return;
  }

  list($id,,$bundle) = entity_extract_ids($entity_type, $entity);
  if ($id) {
    // Entity is already saved.
    return;
  }

Amitaibu's avatar
Amitaibu committed
  $instance = field_info_instance($entity_type, $field['field_name'], $bundle);
  if (empty($instance['settings']['behaviors']['prepopulate']['status'])) {
    return;
  }

  $settings = $instance['settings']['behaviors']['prepopulate'];
  if (!empty($settings['skip_perm']) && user_access($settings['skip_perm'])) {
    return;
  }
  $items = entityreference_prepopulate_get_values($field, $instance);
Amitaibu's avatar
Amitaibu committed

  if (empty($items) && $settings['fallback'] == 'hide') {
Amitaibu's avatar
Amitaibu committed
    return FALSE;
Amitaibu's avatar
Amitaibu committed
  }
}

/**
 * Wrapper function to get context (e.g. from URL or OG-context).
 *
 * @param $field
 *   The field info array.
 * @param $instance
 *   The instance info array.
 * @param $flat_array.
 *   TRUE if the group IDs should be sent as a simple array. FALSE, if we
 *   need to build array suited for field API's $items value.
 */
function entityreference_prepopulate_get_values($field, $instance, $flat_array = FALSE, $validate = TRUE) {
  $settings = $instance['settings']['behaviors']['prepopulate'];

  if (!empty($settings['og_context']) && module_exists('og_context')) {
    return entityreference_prepopulate_get_values_from_og_context($field, $instance, $flat_array, $validate);
  return entityreference_prepopulate_get_values_from_url($field, $instance, $flat_array, $validate);
function entityreference_prepopulate_get_values_from_og_context($field, $instance, $flat_array = FALSE, $validate = TRUE) {
  $field_name = $field['field_name'];

  if (!og_is_group_audience_field($field_name) || !$og_context = og_context()) {
    return;
  }

  if ($og_context['group_type'] != $field['settings']['target_type']) {
    // Context is of invalid group-type.
    return;
  }

  $items = array();
  $items[] = !$flat_array ? array('target_id' => $og_context['gid']) : $og_context['gid'];
  return $items;
}

Amitaibu's avatar
Amitaibu committed
/**
 * Get values for prepopulating fields, via URL.
 *
 * @param $field
 *   The field info array.
 * @param $instance
 *   The instance info array.
 *   TRUE if the group IDs should be sent as a simple array. FALSE, if we
 *   need to build array suited for field API's $items value.
 *
 * @see
 *   entityreference_prepopulate_get_values()
Amitaibu's avatar
Amitaibu committed
 */
function entityreference_prepopulate_get_values_from_url($field, $instance, $flat_array = FALSE, $validate = TRUE) {
Amitaibu's avatar
Amitaibu committed
  $cache = &drupal_static(__FUNCTION__, array());
  $field_name = $field['field_name'];
  $identifier = $instance['entity_type'] . ':' . $instance['bundle'] . ':' . $field_name . ':' . $flat_array;
Amitaibu's avatar
Amitaibu committed
  if (isset($cache[$identifier])) {
    return $cache[$identifier];
  }

  // Do nothing when prepopulate is disabled for this field.
  if (!$instance['settings']['behaviors']['prepopulate']['status']) {
Amitaibu's avatar
Amitaibu committed
    return;
  }

  // Get the value from the URL if possible.
  if (!empty($_GET[$field_name]) && is_string($_GET[$field_name])) {
  else {
    // Try to get the form out of cache.
    $form_build_id = isset($_GET['form_build_id']) ? $_GET['form_build_id'] : isset($_POST['form_build_id']) ? $_POST['form_build_id'] : NULL;
    $form_state = array();
    $form = form_get_cache($form_build_id, $form_state);
Amitaibu's avatar
Amitaibu committed

    // If successful, get the value from the form_state.
    if (isset($form_state['entityreference_prepopulate'][$instance['entity_type']][$instance['bundle']][$field_name])) {
      $ids = $form_state['entityreference_prepopulate'][$instance['entity_type']][$instance['bundle']][$field_name];
    }
    // If not, do nothing and return.
    else {
      $cache[$identifier] = FALSE;
      return;
    }
  // Check if the IDs are valid, and get filter out the ones that are not valid.
  if ($validate) {
    $handler = entityreference_get_selection_handler($field, $instance);
    if (!$ids = $handler->validateReferencableEntities($ids)) {
      $cache[$identifier] = FALSE;
      return;
    }
Amitaibu's avatar
Amitaibu committed
  }

  // Check access to the provided entities.
Amitaibu's avatar
Amitaibu committed
  $target_type = $field['settings']['target_type'];
  entity_load($target_type, $ids);
  $items = array();
  foreach ($ids as $id) {
    $entity = entity_load_single($target_type, $id);
    if (entity_access('view', $target_type, $entity)) {
      $items[] = !$flat_array ? array('target_id' => $id) : $id;
Amitaibu's avatar
Amitaibu committed
    }
  }
  $cache[$identifier] = $items;
Amitaibu's avatar
Amitaibu committed
  return $items;
}

/**
 * Return a form element with crafted links to create nodes for a group.
 *
 * @param $entity_type
 *   The entity type of the referenced entity.
 * @param $entity_id
 *   The entity ID of the referenced entity.
 * @param $destination
 *   Optional; The destination after a node is created. Defaults to the
 *   destination passed in the URL if exists, otherwise back to the current
 *   page.
 * @param $types
 *   Optional; An array of type names. Restrict the created links to the given
 *   types.
 */
function entityreference_prepopulate_create_node_links($entity_type, $entity_id, $field_name, $destination = NULL, $types = NULL) {
  $wrapper = entity_metadata_wrapper($entity_type, $entity_id);
  $field = field_info_field($field_name);

  $entity = entity_load_single($entity_type, $entity_id);
  list(,, $bundle) = entity_extract_ids($entity_type, $entity);

  $types = isset($types) ? $types : array_keys(node_type_get_types());
  $names = array();
  foreach ($types as $type_name) {
    if ($field['settings']['target_type'] != $entity_type) {
      // The entity type isn't referenced by the field.
      continue;
    }

    if (!empty($field['settings']['handler_settings']['target_bundles']) && !in_array($bundle, $field['settings']['handler_settings']['target_bundles'])) {
      // The entity bundle isn't referenced by the field.
      continue;
    }

    $instance = field_info_instance('node', $field_name, $type_name);
    if (empty($instance['settings']['behaviors']['prepopulate']['status'])) {
      // The field doesn't exist on the node type, or doesn't have prepopulate
      // enabled.
      continue;
    }

    if (!node_access('create', $type_name)) {
      continue;
    }

    $names[$type_name] = node_type_get_name($type_name);
  }

  if (empty($names)) {
    return;
  }

  // Sort names.
  asort($names);

  // Build links.
  $options = array(
    'query' => array($field_name => $entity_id) + drupal_get_destination(),
  );

  $items = array();
  foreach ($names as $type => $name) {
    $items[] = array('data' => l($name, 'node/add/' . str_replace('_', '-', $type), $options));
  }

  $element = array();
  $element['entityreference_prepopulate'] = array(
    '#theme' => 'item_list',
    '#items' => $items,
  );

  return $element;
}