You are here

relation_ui.module in Relation 7

Provide administration interface for relation type bundles.

File

relation_ui.module
View source
<?php

/**
 * @file
 * Provide administration interface for relation type bundles.
 */

/**
 * Implements hook_menu().
 */
function relation_ui_menu() {
  $items['relation/%relation'] = array(
    'title callback' => 'relation_ui_page_title',
    'title arguments' => array(
      1,
    ),
    'access arguments' => array(
      'access relations',
    ),
    'page callback' => 'relation_ui_page',
    'page arguments' => array(
      1,
    ),
  );
  $items['relation/%relation/view'] = array(
    'title' => 'View',
    'type' => MENU_DEFAULT_LOCAL_TASK,
    'weight' => -10,
  );
  $items['relation/%relation/edit'] = array(
    'title' => 'Edit',
    'access arguments' => array(
      'edit relations',
    ),
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'relation_ui_edit_form',
      1,
    ),
    'type' => MENU_LOCAL_TASK,
  );
  $items['relation/%relation/delete'] = array(
    'title' => 'Delete',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'relation_ui_delete_confirm',
      1,
    ),
    'access arguments' => array(
      'delete relations',
    ),
    'weight' => 10,
    'type' => MENU_LOCAL_TASK,
  );
  if (module_exists('devel')) {
    $items['relation/%relation/devel'] = array(
      'title' => 'Devel',
      'page callback' => 'devel_load_object',
      'page arguments' => array(
        'relation',
        1,
      ),
      'access arguments' => array(
        'access devel information',
      ),
      'type' => MENU_LOCAL_TASK,
      'file path' => drupal_get_path('module', 'devel') . '/',
      'file' => 'devel.pages.inc',
      'weight' => 100,
    );
  }
  $items['admin/structure/relation'] = array(
    'title' => 'Relation types',
    'access arguments' => array(
      'administer relation types',
    ),
    'page callback' => 'relation_ui_type_list',
    'description' => 'Manage relation types, including relation properties (directionality, transitivity etc), available bundles, and fields.',
  );
  $items['admin/structure/relation/list'] = array(
    'title' => 'List',
    'type' => MENU_DEFAULT_LOCAL_TASK,
    'weight' => -10,
  );
  $items['admin/structure/relation/add'] = array(
    'title' => 'Add relation type',
    'access arguments' => array(
      'administer relation types',
    ),
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'relation_ui_type_form',
    ),
    'type' => MENU_LOCAL_ACTION,
    'description' => 'Add new relation types.',
  );
  $items['admin/structure/relation/import'] = array(
    'title' => 'Import relation type',
    'access arguments' => array(
      'use relation import',
    ),
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'relation_ui_type_import',
    ),
    'type' => MENU_LOCAL_ACTION,
    'description' => 'Import existing relation types.',
  );
  $items['admin/structure/relation/manage/%relation_type'] = array(
    'title' => 'Edit relation type',
    'title callback' => 'relation_ui_type_page_title',
    'title arguments' => array(
      4,
    ),
    'access arguments' => array(
      'administer relation types',
    ),
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'relation_ui_type_form',
      4,
    ),
    'description' => 'Edit an existing relation type, including relation properties (directionality, transitivity etc), available bundles, and fields.',
  );
  $items['admin/structure/relation/manage/%relation_type/edit'] = array(
    'title' => 'Edit',
    'type' => MENU_DEFAULT_LOCAL_TASK,
    'weight' => -10,
  );
  $items['admin/structure/relation/manage/%relation_type/delete'] = array(
    'title' => 'Delete',
    'page arguments' => array(
      'relation_ui_type_delete_confirm',
      4,
    ),
    'access arguments' => array(
      'administer relation types',
    ),
    'type' => MENU_LOCAL_TASK,
    'weight' => 20,
    'description' => 'Delete an existing relation type.',
  );
  if (module_exists('ctools')) {
    $items['admin/structure/relation/manage/%relation_type/export'] = array(
      'title' => 'Export',
      'page callback' => 'drupal_get_form',
      'page arguments' => array(
        'relation_export_relation_type',
        4,
      ),
      'access arguments' => array(
        'export relation types',
      ),
      'type' => MENU_LOCAL_TASK,
      'file' => 'relation.ctools.inc',
      'weight' => 10,
    );
  }
  $items['admin/config/development/generate/relation'] = array(
    'title' => 'Generate relations',
    'access arguments' => array(
      'administer relation types',
    ),
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'relation_ui_generate_form',
    ),
    'description' => 'Generate relations for testing.',
  );

  // Relations listing.
  $items['admin/content/relation'] = array(
    'title' => 'Relations',
    'file' => 'relation_ui.module',
    'page callback' => 'relation_ui_admin_content',
    'access arguments' => array(
      'administer relations',
    ),
    'description' => 'View, edit and delete all the available relations on your site.',
    'type' => MENU_LOCAL_TASK,
  );
  $items['relation/endpoints_type/autocomplete'] = array(
    'type' => MENU_CALLBACK,
    'access arguments' => array(
      'administer relations',
    ),
    'file' => 'relation_ui.module',
    'page callback' => 'relation_ui_endpoints_type_autocomplete',
  );
  return $items;
}

/**
 * List all relation_types (page callback).
 */
function relation_ui_type_list() {
  $relation = relation_entity_info();
  $field_ui = module_exists('field_ui');
  $ctools = module_exists('ctools');
  $ops_count = 2 + 2 * $field_ui + $ctools;
  $headers = array(
    array(
      'data' => t('Name'),
      'width' => '40%',
    ),
    array(
      'data' => t('Operations'),
      'colspan' => $ops_count,
    ),
  );
  $rows = array();
  foreach ($relation['relation']['bundles'] as $type => $relation_type) {
    $url = 'admin/structure/relation/manage/' . $type;
    $row = array(
      l($relation_type['label'], $url),
    );
    $row[] = l(t('edit'), $url . '/edit');
    if ($field_ui) {
      $row[] = l(t('manage fields'), $url . '/fields');
      $row[] = l(t('display fields'), $url . '/display');
    }
    if ($ctools) {
      $row[] = l(t('export'), $url . '/export');
    }
    $row[] = empty($relation_type->in_code_only) ? l(t('delete'), $url . '/delete') : '&nbsp;';
    $rows[] = $row;
  }
  $output = array(
    '#theme' => 'table',
    '#header' => $headers,
    '#rows' => $rows,
    '#empty' => t('No relationship types available.', array(
      '@link' => l(t('Add relationship type'), url('admin/structure/relation/add')),
    )),
  );
  return $output;
}

/**
 * Relation type display/edit page title callback.
 */
function relation_ui_type_page_title($type) {
  return $type->label;
}

/**
 * Relation relation type bundle settings form.
 *
 * @param $relation_type
 *   Relation type machine name. If this is not provided, assume that we're
 *   creating a new relation type.
 */
function relation_ui_type_form($form, &$form_state, $relation_type = array(), $op = 'edit') {
  $form['#write_record_keys'] = array();
  $form['#attached']['css'] = array(
    drupal_get_path('module', 'relation') . '/relation_ui.css',
  );
  if ($relation_type) {
    $relation_type = (object) $relation_type;
    if (empty($relation_type->in_code_only)) {
      $form['#write_record_keys'][] = 'relation_type';
    }
  }
  else {
    $relation_type = (object) array(
      'relation_type' => '',
      'label' => '',
      'reverse_label' => '',
      'bundles' => array(),
      'directional' => FALSE,
      'transitive' => FALSE,
      'r_unique' => FALSE,
      'min_arity' => 2,
      'max_arity' => 2,
      'source_bundles' => array(),
      'target_bundles' => array(),
    );
  }
  $form['labels'] = array(
    '#type' => 'container',
    '#attributes' => array(
      'class' => array(
        'relation-type-form-table',
      ),
    ),
    '#suffix' => '<div class="clearfix"></div>',
  );
  $form['labels']['name'] = array(
    // use 'name' for /misc/machine-name.js
    '#type' => 'textfield',
    '#title' => t('Label'),
    '#description' => t('Display name of the relation type. This is also used as the predicate in natural language formatters (ie. if A is related to B, you get "A [label] B")'),
    '#default_value' => $relation_type->label,
    '#size' => 40,
    '#required' => TRUE,
  );
  $form['labels']['relation_type'] = array(
    '#type' => 'machine_name',
    '#default_value' => $relation_type->relation_type,
    '#maxlength' => 32,
    '#disabled' => $relation_type->relation_type,
    '#machine_name' => array(
      'source' => array(
        'labels',
        'name',
      ),
      'exists' => 'relation_type_load',
    ),
  );
  $form['labels']['reverse_label'] = array(
    '#type' => 'textfield',
    '#size' => 40,
    '#title' => t('Reverse label'),
    '#description' => t('Reverse label of the relation type. This is used as the predicate by formatters of directional relations, when you need to display the reverse direction (ie. from the target entity to the source entity). If this is not supplied, the forward label is used.'),
    '#default_value' => $relation_type->reverse_label,
    '#states' => array(
      'visible' => array(
        ':input[name="directional"]' => array(
          'checked' => TRUE,
        ),
        ':input[name="advanced[max_arity]"]' => array(
          '!value' => '1',
        ),
      ),
      'required' => array(
        ':input[name="directional"]' => array(
          'checked' => TRUE,
        ),
        ':input[name="advanced[max_arity]"]' => array(
          '!value' => '1',
        ),
      ),
    ),
  );
  $form['directional'] = array(
    '#type' => 'checkbox',
    '#title' => 'Directional',
    '#description' => t('A directional relation is one that does not imply the same relation in the reverse direction. For example, a "likes" relation is directional (A likes B does not neccesarily mean B likes A), whereas a "similar to" relation is non-directional (A similar to B implies B similar to A. Non-directional relations are also known as symmetric relations.'),
    '#default_value' => $relation_type->directional,
    '#states' => array(
      'invisible' => array(
        ':input[name="advanced[max_arity]"]' => array(
          'value' => '1',
        ),
      ),
    ),
  );

  // More advanced options, hide by default.
  $form['advanced'] = array(
    '#type' => 'fieldset',
    '#title' => t('Advanced options'),
    '#collapsible' => TRUE,
    '#collapsed' => TRUE,
    '#weight' => 50,
    '#tree' => TRUE,
  );
  $form['advanced']['transitive'] = array(
    '#type' => 'checkbox',
    '#title' => t('Transitive'),
    '#description' => t('A transitive relation implies that the relation passes through intermediate entities (ie. A=>B and B=>C implies that A=>C). For example "Ancestor" is transitive: your ancestor\'s ancestor is also your ancestor. But a "Parent" relation is non-transitive: your parent\'s parent is not your parent, but your grand-parent.'),
    '#default_value' => $relation_type->transitive,
    '#states' => array(
      'invisible' => array(
        ':input[name="advanced[max_arity]"]' => array(
          'value' => '1',
        ),
      ),
    ),
  );
  $form['advanced']['r_unique'] = array(
    '#type' => 'checkbox',
    '#title' => t('Unique'),
    '#description' => t('Whether relations of this type are unique (ie. they can not contain exactly the same end points as other relations of this type).'),
    '#default_value' => $relation_type->r_unique,
  );

  // these should probably be changed to numerical (validated) textfields.
  $options = array(
    '1' => '1',
    '2' => '2',
    '3' => '3',
    '4' => '4',
    '5' => '5',
    '6' => '6',
    '7' => '7',
    '8' => '8',
  );
  $form['advanced']['min_arity'] = array(
    '#type' => 'select',
    '#title' => t('Minimum Arity'),
    '#options' => $options,
    '#description' => t('Minimum number of entities joined by relations of this type (e.g. three siblings in one relation). <em>In nearly all cases you will want to leave this set to 2</em>.'),
    '#default_value' => $relation_type->min_arity ? $relation_type->min_arity : 2,
  );
  $options = array(
    '1' => '1',
    '2' => '2',
    '3' => '3',
    '4' => '4',
    '5' => '5',
    '6' => '6',
    '7' => '7',
    '8' => '8',
    '0' => t('Infinite'),
  );
  $form['advanced']['max_arity'] = array(
    '#type' => 'select',
    '#title' => t('Maximum Arity'),
    '#options' => $options,
    '#description' => t('Maximum number of entities joined by relations of this type. <em>In nearly all cases you will want to leave this set to 2</em>.'),
    '#default_value' => isset($relation_type->max_arity) ? $relation_type->max_arity : 2,
  );
  $counter = 0;
  foreach (entity_get_info() as $entity_type => $entity) {
    $bundles[$entity['label']]["{$entity_type}:*"] = 'all ' . $entity['label'] . ' bundles';
    $counter += 2;
    if (isset($entity['bundles'])) {
      foreach ($entity['bundles'] as $bundle_id => $bundle) {
        $bundles[$entity['label']]["{$entity_type}:{$bundle_id}"] = $bundle['label'];
        $counter++;
      }
    }
  }
  $form['endpoints'] = array(
    '#type' => 'container',
    '#attributes' => array(
      'class' => array(
        'relation-type-form-table',
      ),
    ),
    '#suffix' => '<div class="clearfix"></div>',
  );
  $form['endpoints']['source_bundles'] = array(
    '#type' => 'select',
    '#title' => t('Available source bundles'),
    '#options' => $bundles,
    '#size' => max(12, $counter),
    '#default_value' => $relation_type->source_bundles,
    '#multiple' => TRUE,
    '#required' => TRUE,
    '#description' => 'Bundles that are not selected will not be available as sources for directional, or end points of non-directional relations relations. Ctrl+click to select multiple. Note that selecting all bundles also include bundles not yet created for that entity type.',
  );
  $form['endpoints']['target_bundles'] = array(
    '#type' => 'select',
    '#title' => t('Available target bundles'),
    '#options' => $bundles,
    '#size' => max(12, $counter),
    '#default_value' => $relation_type->target_bundles,
    '#multiple' => TRUE,
    '#description' => 'Bundles that are not selected will not be available as targets for directional relations. Ctrl+click to select multiple.',
    '#states' => array(
      'visible' => array(
        ':input[name="directional"]' => array(
          'checked' => TRUE,
        ),
        ':input[name="advanced[max_arity]"]' => array(
          '!value' => '1',
        ),
      ),
      'required' => array(
        ':input[name="directional"]' => array(
          'checked' => TRUE,
        ),
        ':input[name="advanced[max_arity]"]' => array(
          '!value' => '1',
        ),
      ),
    ),
  );
  $form['submit'] = array(
    '#type' => 'submit',
    '#weight' => 100,
    '#value' => t('Save'),
  );
  return $form;
}

/**
 * Validate relation type form.
 */
function relation_ui_type_form_validate($form, &$form_state) {
  $max_arity = $form_state['values']['advanced']['max_arity'];
  $min_arity = $form_state['values']['advanced']['min_arity'];

  // Empty max arity indicates infinite arity
  if ($max_arity && $min_arity > $max_arity) {
    form_set_error('min_arity', t('Minmum arity must be less than or equal to maximum arity.'));
  }
}

/**
 * Submit data from bundle settings page.
 */
function relation_ui_type_form_submit($form, &$form_state) {
  $relation_type = $form_state['values']['relation_type'];
  $min_arity = $form_state['values']['advanced']['min_arity'];
  $max_arity = $form_state['values']['advanced']['max_arity'];
  $record = array(
    'relation_type' => $relation_type,
    'min_arity' => $min_arity,
    'max_arity' => $max_arity,
    'label' => $form_state['values']['name'],
    'reverse_label' => $form_state['values']['reverse_label'],
    'directional' => $form_state['values']['directional'],
    'transitive' => $form_state['values']['advanced']['transitive'],
    'r_unique' => $form_state['values']['advanced']['r_unique'],
    'source_bundles' => $form_state['values']['source_bundles'],
    'target_bundles' => $form_state['values']['target_bundles'],
  );
  relation_type_save($record, $form['#write_record_keys']);
  $form_state['redirect'] = "admin/structure/relation/edit/{$relation_type}";
  drupal_set_message(t('The %relation_type relation type has been saved.', array(
    '%relation_type' => $relation_type,
  )));
}

/**
 * Menu callback; deletes a single relation type.
 */
function relation_ui_type_delete_confirm($form, &$form_state, $relation_type) {
  $form['relation_type'] = array(
    '#type' => 'value',
    '#value' => $relation_type->relation_type,
  );
  $form['label'] = array(
    '#type' => 'value',
    '#value' => $relation_type->label,
  );
  $message = t('Are you sure you want to delete the %label relation type?', array(
    '%label' => $relation_type->label,
  ));
  $caption = '';
  $num_relations = relation_query()
    ->propertyCondition('relation_type', $relation_type->relation_type)
    ->count()
    ->execute();
  if ($num_relations) {
    $caption .= '<p>' . format_plural($num_relations, 'The %label relation type is used by 1 relation on your site. If you remove this relation type, you will not be able to edit  %label relations and they may not display correctly.', 'The %label relation type is used by @count relations on your site. If you remove %label, you will not be able to edit %label relations and they may not display correctly.', array(
      '%label' => $relation_type->label,
      '@count' => $num_relations,
    )) . '</p>';
  }
  $caption .= '<p>' . t('This action cannot be undone.') . '</p>';
  return confirm_form($form, $message, 'admin/structure/relation', $caption, t('Delete'));
}

/**
 * Process relation type delete confirm submissions.
 */
function relation_ui_type_delete_confirm_submit($form, &$form_state) {
  relation_type_delete($form_state['values']['relation_type']);
  $t_args = array(
    '%label' => $form_state['values']['label'],
  );
  drupal_set_message(t('The %label relation type has been deleted.', $t_args));
  watchdog('relation', 'Deleted the %label relation type.', $t_args, WATCHDOG_NOTICE);

  // @TODO: relation_types_rebuild() ?;
  menu_rebuild();
  $form_state['redirect'] = 'admin/structure/relation';
  return;
}

/**
 * Generate relations
 */
function relation_ui_generate_form($form, &$form_state) {
  $types = relation_get_types();
  if (empty($types)) {
    $form['explanation']['#markup'] = t("Before you can generate relations, you need to define one or more !link.", array(
      "!link" => l(t('relation types'), 'admin/structure/relation'),
    ));
    return $form;
  }
  foreach ($types as $id => $type) {
    $types[$id] = $type->label;
  }
  $form['relation_types'] = array(
    '#type' => 'checkboxes',
    '#title' => t('Relation types'),
    '#description' => t('Select relation types to create relations from. If no types are selected, relations will be generated for all types.'),
    '#options' => $types,
  );
  $form['relation_number'] = array(
    '#type' => 'textfield',
    '#title' => t('How many relations would you like to generate of each type?'),
    '#default_value' => 10,
    '#size' => 10,
  );
  $form['relation_kill'] = array(
    '#type' => 'checkbox',
    '#title' => t('Kill all existing relations first.'),
  );
  $form['submit'] = array(
    '#type' => 'submit',
    '#value' => t('Generate'),
  );
  return $form;
}
function relation_ui_generate_form_submit($form, &$form_state) {
  $number = $form_state['values']['relation_number'];
  $types = $form_state['values']['relation_types'];
  $kill = $form_state['values']['relation_kill'];
  if (is_numeric($number) && $number > 0) {
    include_once drupal_get_path('module', 'relation') . '/relation.drush.inc';
    $types = array_keys(array_filter($types));
    $rids = relation_generate_relations($number, $types, $kill);
  }
}

/**
 * Filter form for relation administration list.
 */
function relation_ui_filter_form($form, $form_state) {
  if (isset($_SESSION['relation_filters'])) {
    foreach ($_SESSION['relation_filters'] as $filter => $values) {
      ${$filter} = $values;
    }
  }
  $bundles = array();
  $types = relation_get_types();
  foreach ($types as $name => $relation) {
    $bundles[$name] = $relation->label;
  }
  $form['filters'] = array(
    '#type' => 'fieldset',
    '#title' => t('Show only items where'),
    '#theme' => 'exposed_filters__relation',
  );
  $form['filters']['bundle'] = array(
    '#title' => t('relation type'),
    '#type' => 'select',
    '#default_value' => isset($bundle) ? array_keys($bundle) : NULL,
    '#multiple' => TRUE,
    '#options' => $bundles,
    '#size' => count($bundles) < 10 ? count($bundles) : 10,
  );
  $form['filters']['endpoints_entity_type'] = array(
    '#title' => t('endpoint type'),
    '#type' => 'textfield',
    '#default_value' => isset($endpoints_entity_type) ? $endpoints_entity_type : NULL,
    '#autocomplete_path' => 'relation/endpoints_type/autocomplete',
  );
  $form['filters']['endpoints_entity_id'] = array(
    '#title' => t('endpoint ID'),
    '#type' => 'textfield',
    '#default_value' => isset($endpoints_entity_id) ? $endpoints_entity_id : NULL,
  );
  $form['filters']['actions'] = array(
    '#type' => 'actions',
    '#attributes' => array(
      'class' => array(
        'container-inline',
      ),
    ),
  );
  $form['filters']['actions']['submit'] = array(
    '#type' => 'submit',
    '#value' => t('Filter'),
  );
  $form['filters']['actions']['reset'] = array(
    '#type' => 'submit',
    '#value' => t('Reset'),
  );
  $form['#submit'] = array(
    'relation_ui_filter_form_submit',
  );
  return $form;
}

/**
 * Submit handler for filter form
 */
function relation_ui_filter_form_submit($form, $form_state) {
  form_state_values_clean($form_state);
  if ($form_state['triggering_element']['#value'] == t('Reset')) {
    unset($_SESSION['relation_filters']);
  }
  else {
    $_SESSION['relation_filters'] = $form_state['values'];
  }
}

/**
 * Apply filters for relation administration filters based on session.
 *
 * @param $query
 *   A SelectQuery to which the filters should be applied.
 */
function relation_ui_build_filter_query(SelectQueryInterface $query) {
  if (!empty($_SESSION['relation_filters'])) {
    $query
      ->join('field_data_endpoints', 'e', 'e.entity_id = r.rid');
    $query
      ->distinct();
    foreach ($_SESSION['relation_filters'] as $filter => $values) {
      if (empty($values)) {
        continue;
      }
      switch ($filter) {
        case 'bundle':
          $query
            ->condition('r.relation_type', array_keys($values), 'IN');
          break;
        case 'endpoints_entity_type':
          $query
            ->condition('e.endpoints_entity_type', $values);
          break;
        case 'endpoints_entity_id':
          $query
            ->condition('e.endpoints_entity_id', $values);
          break;
      }
    }
  }
}

/**
 * Menu callback for admin/content/relation. Displays all relations on the site.
 */
function relation_ui_admin_content() {

  // Set up header row.
  $header = array(
    'label' => array(
      'data' => t('Title'),
      'field' => 'r.rid',
      'sort' => 'asc',
    ),
    'type' => array(
      'data' => t('Type'),
      'field' => 'r.relation_type',
    ),
    t('Relation'),
    'operations' => array(
      'data' => t('Operations'),
      'colspan' => '2',
    ),
  );

  // Grab all relations.
  $query = db_select('relation', 'r')
    ->extend('PagerDefault')
    ->extend('TableSort');
  $query
    ->fields('r', array(
    'rid',
    'relation_type',
  ));
  relation_ui_build_filter_query($query);
  $query
    ->limit(50)
    ->orderByHeader($header);
  $relations = $query
    ->execute();
  $form = drupal_get_form('relation_ui_filter_form');
  $filter_form = drupal_render($form);
  return $filter_form . theme('relation_ui_admin_content', array(
    'relations' => $relations,
    'header' => $header,
  ));
}

/**
 * Generate a table of all relations on this site.
 */
function theme_relation_ui_admin_content($variables) {
  $relations = $variables['relations'];
  $header = $variables['header'];
  $types = relation_get_types();
  $rows = array();
  if (empty($relations)) {

    // Give a message if there are no relations returned.
    $message = t('There are currently no relations on your site.');
    $rows[] = array(
      array(
        'data' => $message,
        'colspan' => 5,
      ),
    );
  }
  else {
    foreach ($relations as $relation) {

      // Load the relation.
      $r = relation_load($relation->rid);

      // Get the endpoints for this relation.
      $endpoints = field_get_items('relation', $r, 'endpoints');
      $relation_entities = array();
      if (!empty($endpoints)) {
        foreach ($endpoints as $endpoint) {
          $entities = entity_load($endpoint['entity_type'], array(
            $endpoint['entity_id'],
          ));
          $entity = reset($entities);
          $title = entity_label($endpoint['entity_type'], $entity);
          $path = entity_uri($endpoint['entity_type'], $entity);

          // Logic to process how the different entities return a uri.
          // see this issue: http://drupal.org/node/1057242
          if ($endpoint['entity_type'] == 'file') {
            $path = array(
              'path' => file_create_url($path),
            );
          }
          if ($endpoint['entity_type'] == 'taxonomy_vocabulary') {
            $path = array(
              'path' => 'admin/structure/taxonomy/' . $entity->machine_name,
            );
          }
          $relation_entities[] = array(
            'title' => $title,
            'path' => $path['path'],
          );
        }
      }

      // Build the column for the relation entities.
      $relation_column = array();
      foreach ($relation_entities as $entity) {
        $relation_column[] = l($entity['title'], $entity['path']);
      }

      // Build the rows to pass to the table theme function.
      // Directional is implemented, not sure how well it works.
      $destination = drupal_get_destination();
      $options = array(
        'attributes' => array(),
        'query' => array(
          'destination' => $destination['destination'],
        ),
      );
      $endpoint_separator = ', ';
      $type_label = '';

      // Get the type for this relation
      if (isset($types[$r->relation_type])) {
        $type = $types[$r->relation_type];
        $endpoint_separator = $type->directional ? " → " : " ↔ ";
        $type_label = $relation->relation_type;
      }
      $relation_uri = entity_uri('relation', $relation);
      $rows[] = array(
        l(t('Relation') . ' ' . $relation->rid, $relation_uri['path']),
        $type_label,
        implode($endpoint_separator, $relation_column),
        user_access('edit relations') ? l(t('Edit'), $relation_uri['path'] . '/edit', $options) : '',
        user_access('delete relations') ? l(t('Delete'), $relation_uri['path'] . '/delete', $options) : '',
      );
    }
  }
  return theme('table', array(
    'header' => $header,
    'rows' => $rows,
  )) . theme('pager');
}

/**
 * Page callback to import a relation.
 */
function relation_ui_type_import($form) {
  $form['name'] = array(
    '#type' => 'textfield',
    '#title' => t('Relation type name'),
    '#description' => t('Enter the machine name to use for this relation type if it is different from the relation type to be imported. Leave blank to use the name of the relation type below.'),
  );
  $form['relation_type'] = array(
    '#type' => 'textarea',
    '#rows' => 10,
  );
  $form['import'] = array(
    '#type' => 'submit',
    '#value' => t('Import'),
  );
  return $form;
}
function relation_ui_type_import_validate($form, &$form_state) {
  if (drupal_substr($form_state['values']['relation_type'], 0, 5) == '<?php') {
    $form_state['values']['relation_type'] = drupal_substr($form_state['values']['relation_type'], 5);
  }
  ob_start();
  eval($form_state['values']['relation_type']);
  ob_end_clean();
  if (!is_object($relation_type)) {
    return form_error($form['relation_type'], t('Unable to interpret relation type code.'));
  }

  // relation_type name must be alphanumeric or underscores, no other punctuation.
  if (!empty($form_state['values']['name']) && preg_match('/[^a-zA-Z0-9_]/', $form_state['values']['name'])) {
    form_error($form['name'], t('Relation type name must be alphanumeric or underscores only.'));
  }
  if ($form_state['values']['name']) {
    $relation_type->relation_type = $form_state['values']['name'];
  }
  if (relation_type_load($relation_type->relation_type)) {
    form_set_error('name', t('A relation type by that name already exists; please choose a different name'));
  }
  $form_state['relation_type'] = $relation_type;
}
function relation_ui_type_import_submit($form, &$form_state) {
  relation_type_save($form_state['relation_type']);
  $relation_type = $form_state['relation_type']->relation_type;
  drupal_set_message(t('Successfully imported @relation_type', array(
    '@relation_type' => $relation_type,
  )));
  $form_state['redirect'] = 'admin/structure/relation/manage/' . $relation_type;
}

/**
 * Relation display page title callback.
 */
function relation_ui_page_title($relation) {
  return entity_label('relation', $relation);
}

/**
 * Relation display page. Currently only displays related entities.
 *
 * @TODO: implement directionality, possibly give more details on entities?
 */
function relation_ui_page($relation) {
  $view_mode = 'full';
  return relation_view($relation);
}

/**
 * Relation edit form.
 */
function relation_ui_edit_form($form, &$form_state, $relation) {
  $form_state['relation'] = $relation;
  field_attach_form('relation', $relation, $form, $form_state);
  $form['actions']['#weight'] = 100;
  $form['actions']['save'] = array(
    '#type' => 'submit',
    '#value' => t('Save'),
  );
  return $form;
}
function relation_ui_edit_form_submit($form, &$form_state) {
  $relation = $form_state['relation'];
  entity_form_submit_build_entity('relation', $relation, $form, $form_state);
  $rid = relation_save($relation);
  $relation_uri = entity_uri('relation', $relation);
  if ($rid) {
    $form_state['redirect'] = $relation_uri['path'];
  }
}

/**
 * Menu callback: ask for confirmation of relation deletion.
 */
function relation_ui_delete_confirm($form, &$form_state, $relation) {
  $form['#relation'] = $relation;

  // Always provide entity id in the same form key as in the entity edit form.
  $form['rid'] = array(
    '#type' => 'value',
    '#value' => $relation->rid,
  );
  $relation_uri = entity_uri('relation', $relation);
  return confirm_form($form, t('Are you sure you want to delete relation %rid?', array(
    '%rid' => $relation->rid,
  )), $relation_uri['path'], t('This action cannot be undone.'), t('Delete'), t('Cancel'));
}

/**
 * Executes relation deletion.
 */
function relation_ui_delete_confirm_submit($form, &$form_state) {
  if ($form_state['values']['confirm']) {
    $relation = $form['#relation'];
    relation_delete($form_state['values']['rid']);
    watchdog('relation', '@type: deleted %title.', array(
      '@type' => $relation->relation_type,
      '%title' => $relation->rid,
    ));
    drupal_set_message(t('@type %title has been deleted.', array(
      '@type' => $relation->relation_type,
      '%title' => $relation->rid,
    )));
  }
  $form_state['redirect'] = '<front>';
}

/**
 * Implements hook_theme().
 */
function relation_ui_theme() {
  $theme = array(
    'relation_ui_admin_content' => array(
      'variables' => array(
        'relations' => NULL,
      ),
    ),
  );
  return $theme;
}

/**
 * Autocomplete function for endpoint type filter.
 */
function relation_ui_endpoints_type_autocomplete($string = '') {
  $result = db_query("SELECT distinct endpoints_entity_type FROM {field_data_endpoints} WHERE endpoints_entity_type LIKE :string", array(
    ":string" => db_like($string) . '%',
  ));
  $matches = array();
  while ($res = $result
    ->fetchObject()) {
    $matches[$res->endpoints_entity_type] = $res->endpoints_entity_type;
  }
  drupal_json_output($matches);
}

Functions

Namesort descending Description
relation_ui_admin_content Menu callback for admin/content/relation. Displays all relations on the site.
relation_ui_build_filter_query Apply filters for relation administration filters based on session.
relation_ui_delete_confirm Menu callback: ask for confirmation of relation deletion.
relation_ui_delete_confirm_submit Executes relation deletion.
relation_ui_edit_form Relation edit form.
relation_ui_edit_form_submit
relation_ui_endpoints_type_autocomplete Autocomplete function for endpoint type filter.
relation_ui_filter_form Filter form for relation administration list.
relation_ui_filter_form_submit Submit handler for filter form
relation_ui_generate_form Generate relations
relation_ui_generate_form_submit
relation_ui_menu Implements hook_menu().
relation_ui_page Relation display page. Currently only displays related entities.
relation_ui_page_title Relation display page title callback.
relation_ui_theme Implements hook_theme().
relation_ui_type_delete_confirm Menu callback; deletes a single relation type.
relation_ui_type_delete_confirm_submit Process relation type delete confirm submissions.
relation_ui_type_form Relation relation type bundle settings form.
relation_ui_type_form_submit Submit data from bundle settings page.
relation_ui_type_form_validate Validate relation type form.
relation_ui_type_import Page callback to import a relation.
relation_ui_type_import_submit
relation_ui_type_import_validate
relation_ui_type_list List all relation_types (page callback).
relation_ui_type_page_title Relation type display/edit page title callback.
theme_relation_ui_admin_content Generate a table of all relations on this site.