You are here

corresponding_node_references.module in Corresponding node references 6.4

Same filename and directory in other branches
  1. 7.4 corresponding_node_references.module

File

corresponding_node_references.module
View source
<?php

/**
 *  Implementation hook_menu()
 */
function corresponding_node_references_menu() {
  $items = array();
  $items['admin/settings/corresponding_node_references'] = array(
    'title' => t('Corresponding node references'),
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'corresponding_node_references_settings_form',
    ),
    'access arguments' => array(
      'administer corresponding node references settings',
    ),
    'type' => MENU_NORMAL_ITEM,
  );
  $items['admin/settings/corresponding_node_references/references'] = array(
    'title' => t('Corresponding node references'),
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'corresponding_node_references_settings_form',
    ),
    'access arguments' => array(
      'administer corresponding node references settings',
    ),
    'type' => MENU_DEFAULT_LOCAL_TASK,
  );
  $items['admin/settings/corresponding_node_references/update'] = array(
    'title' => t('Update existing nodes'),
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'corresponding_node_references_update_form',
    ),
    'access arguments' => array(
      'administer corresponding node references settings',
    ),
    'type' => MENU_LOCAL_TASK,
  );
  return $items;
}

/**
 * Implementation hook_perm()
 */
function corresponding_node_references_perm() {
  return array(
    'administer corresponding node references settings',
  );
}

/**
 * The settings form
 */
function corresponding_node_references_settings_form() {
  $form['intro'] = array(
    '#value' => t('Check which node references should listen to each other.
  When checking a check box a reference on node type A to node B will automatically update the node reference field on node B
  adding an entrie which points to node A.'),
  );

  //only node reference instances which are multiple, sinlge mode is posible see corresponding_nodereferences_form_alter()
  $result = db_query("SELECT cnf.field_name, cnf.global_settings FROM {content_node_field} cnf WHERE cnf.type = '%s'", 'nodereference');
  while ($row = db_fetch_object($result)) {
    $data = unserialize($row->global_settings);
    foreach ($data['referenceable_types'] as $reference) {
      if ($reference != '0') {
        $references[$row->field_name][] = $reference;
      }
    }
  }
  $result = db_query("SELECT cnfi.field_name, cnfi.type_name FROM {content_node_field_instance} cnfi INNER JOIN {content_node_field} cnf ON cnf.field_name = cnfi.field_name WHERE cnf.type = '%s'", 'nodereference');
  while ($row = db_fetch_object($result)) {
    if (!empty($references[$row->field_name])) {
      foreach ($references[$row->field_name] as $reference) {
        $fields_to_compare[] = array(
          'field_type' => $row->field_name,
          'node_type' => $row->type_name,
          'reference' => $reference,
        );
      }
    }
  }
  if (!empty($fields_to_compare)) {
    $corr_noderefs = array();
    foreach ($fields_to_compare as $field) {
      foreach ($fields_to_compare as $second_field) {
        if ($field['node_type'] == $second_field['reference'] && $second_field['node_type'] == $field['reference']) {
          if (!array_key_exists($field['node_type'] . ' ' . $field['field_type'] . ' ' . $second_field['node_type'] . ' ' . $second_field['field_type'], $corr_noderefs) && !array_key_exists($second_field['node_type'] . ' ' . $second_field['field_type'] . ' ' . $field['node_type'] . ' ' . $field['field_type'], $corr_noderefs)) {
            $corr_noderefs[$field['node_type'] . ' ' . $field['field_type'] . ' ' . $second_field['node_type'] . ' ' . $second_field['field_type']] = array(
              'node_type_1' => $field['node_type'],
              'field_1' => $field['field_type'],
              'node_type_2' => $second_field['node_type'],
              'field_2' => $second_field['field_type'],
            );
          }
        }
      }
    }
    if (!empty($corr_noderefs)) {
      $count = 0;
      foreach ($corr_noderefs as $key => $corr_noderef) {
        $formated_label = corresponding_node_references_format_label($key);
        $formated_key = str_replace(' ', '*', $key);
        $mapping[] = $formated_key;
        $form['values'][$count] = array(
          '#type' => 'fieldset',
        );
        $form['values'][$count]['enabled_' . $count] = array(
          '#type' => 'checkbox',
          '#default_value' => db_result(db_query("SELECT enabled FROM {corresponding_node_references} WHERE node_types_content_fields = '%s'", $formated_key)),
          '#title' => $formated_label,
        );
        $count++;
      }

      //We are using a hidden field to submit the configuration because on some systems the checkbox name length is limited

      //, using the key would cause the length to be too long see issue #558612
      $form['mapping'] = array(
        '#type' => 'hidden',
        '#value' => serialize($mapping),
      );
      $form['submit'] = array(
        '#type' => 'submit',
        '#value' => t('Save'),
      );
    }
    else {
      $form['text'] = array(
        '#value' => '<div>' . t('There are no node types which have a corresponding node reference') . '</div>',
      );
    }
  }
  return $form;
}

/**
 * Submit function for settings form
 */
function corresponding_node_references_settings_form_submit($form, $form_values) {
  db_query("UPDATE {corresponding_node_references} SET enabled = %d", 0);
  $query_values = array();
  $mapping = unserialize($form_values['values']['mapping']);
  foreach ($form_values['values'] as $key => $value) {
    $keys = explode('_', $key);
    if ($keys[0] == 'enabled') {
      $query_values[$mapping[$keys[1]]] = $value;
    }
  }
  foreach ($query_values as $key => $value) {
    db_query("DELETE FROM {corresponding_node_references} WHERE node_types_content_fields='%s'", $key);
    db_query("INSERT INTO {corresponding_node_references} (node_types_content_fields, enabled) VALUES ('%s', %d)", $key, $value);
  }
  drupal_set_message(t('The configuration has been saved'));
}

/**
 * Formats a label
 */
function corresponding_node_references_format_label($key) {
  $key = explode(' ', $key);
  return t('Field instance:"!field1" on Node type:"!type1" <b>Corresponds with</b> Field instance:"!field2" on Node type:"!type2"', array(
    '!type1' => $key[0],
    '!field1' => $key[1],
    '!type2' => $key[2],
    '!field2' => $key[3],
  ));
}

/**
 * Implementation of hook_nodeapi()
 */
function corresponding_node_references_nodeapi(&$node, $op, $a3 = NULL, $a4 = NULL) {
  switch ($op) {
    case 'insert':
    case 'update':
    case 'delete':
      corresponding_node_references_process_node($op, $node);
      break;
  }
}

/**
 * Process a node's corresponding node references.
 *
 * @param $op the operation being performed on the node.
 * @param $node the node object
 * @param $process_unchanged whether or not to process node reference fields whose values have not changed.
 */
function corresponding_node_references_process_node($op, $node, $process_unchanged = FALSE) {
  module_load_include('inc', 'corresponding_node_references', 'corresponding_node_references.crud');
  $result = db_query("SELECT node_types_content_fields FROM {corresponding_node_references} WHERE enabled = %d", 1);
  while ($row = db_fetch_object($result)) {
    $key = explode('*', $row->node_types_content_fields);
    switch ($node->type) {
      case $key[0]:
        $args = array(
          $node,
          $key[1],
          $key[2],
          $key[3],
          $process_unchanged,
        );
        $function = 'corresponding_node_references_' . $op;
        call_user_func_array($function, $args);
        if ($key[0] != $key[2]) {
          break;
        }

      // Fall through.
      case $key[2]:
        $args = array(
          $node,
          $key[3],
          $key[0],
          $key[1],
          $process_unchanged,
        );
        $function = 'corresponding_node_references_' . $op;
        call_user_func_array($function, $args);
        break;
    }
  }
}

/**
 * The update form.
 * Allows updating of current nodes.
 */
function corresponding_node_references_update_form() {
  $form = array();
  $form['intro'] = array(
    '#value' => t('This will update all the existing nodes for the selected content types so that their node reference fields are in sync.') . '<br />' . t('This process may take a long time depending on the number of nodes you are updating.') . '<br />' . t('When the process is finished you will see a count of the number of nodes that were updated.'),
  );
  $form['types'] = array(
    '#type' => 'checkboxes',
    '#title' => t('Node types'),
    '#options' => node_get_types('names'),
    '#description' => t('Select the node types that you want to update.'),
  );
  $form['limit'] = array(
    '#type' => 'select',
    '#title' => t('Number of nodes to process at once'),
    '#options' => array(
      10 => t('10'),
      20 => t('20'),
      30 => t('30'),
      50 => t('50'),
      75 => t('75'),
      100 => t('100'),
      125 => t('125'),
      150 => t('150'),
      200 => t('200'),
      250 => t('250'),
      300 => t('300'),
      350 => t('350'),
      400 => t('400'),
      450 => t('450'),
      500 => t('500'),
    ),
    '#default_value' => 50,
    '#description' => t('This process is done in batches. This is the number of nodes processed in each batch. If necessary, reduce the number of nodes to prevent timeouts and memory errors while processing.'),
  );
  $form['submit'] = array(
    '#type' => 'submit',
    '#value' => 'submit',
  );
  return $form;
}

/**
 * The update form.
 * Allows updating of current nodes.
 */
function corresponding_node_references_update_form_validate($form, &$form_state) {
  $types = array_filter($form_state['values']['types']);
  if (empty($types)) {
    form_set_error('types', t('You must select at least one node type.'));
  }
}

/**
 * The update form.
 * Allows updating of current nodes.
 */
function corresponding_node_references_update_form_submit($form, &$form_state) {
  $types = array_filter($form_state['values']['types']);
  $types = array_keys($types);
  corresponding_node_references_batch_index_remaining($types, $form_state['values']['limit']);
}

/**
 * Submit a batch job to index the remaining, unindexed content.
 */
function corresponding_node_references_batch_index_remaining($types, $limit) {
  $batch = array(
    'operations' => array(
      array(
        'corresponding_node_references_batch_update_existing_nodes',
        array(
          $types,
          $limit,
        ),
      ),
    ),
    'finished' => 'corresponding_node_references_batch_update_existing_finished',
    'title' => t('Processing'),
    'init_message' => t('Preparing to update corresponding node references for existing nodes...'),
    'progress_message' => t('Processing nodes...'),
    'error_message' => t('Corresponding node references existing node update has encountered an error.'),
  );
  batch_set($batch);
}

/**
 * Batch Operation Callback
 *
 * @see corresponding_node_references_batch_index_remaining()
 */
function corresponding_node_references_batch_update_existing_nodes($types, $limit, &$context) {

  // If we are on our first time through.
  if (!isset($context['sandbox']['progress'])) {
    $context['sandbox']['progress'] = 0;
    $context['sandbox']['current_node'] = 0;
    $context['sandbox']['max'] = db_result(db_query("SELECT COUNT(DISTINCT nid) FROM {node} WHERE type IN (" . db_placeholders($types, 'varchar') . ")", $types));
  }
  $nids = array();
  $args = $types;
  $args[] = $context['sandbox']['current_node'];

  // Get node IDs to update.
  $result = db_query_range("SELECT nid\n                              FROM {node}\n                             WHERE type IN (" . db_placeholders($types, 'varchar') . ")\n                               AND nid > %d\n                          ORDER BY nid", $args, 0, $limit);
  while ($row = db_fetch_object($result)) {
    $node = node_load($row->nid);
    corresponding_node_references_process_node('update', $node, TRUE);

    // Update our progress information.
    $context['sandbox']['progress']++;
    $context['sandbox']['current_node'] = $node->nid;
    $context['message'] = t('Processed @current of @total nodes', array(
      '@current' => $context['sandbox']['progress'],
      '@total' => $context['sandbox']['max'],
    ));
  }

  // Inform the batch engine that we are not finished,
  // and provide an estimation of the completion level we reached.
  if ($context['sandbox']['progress'] != $context['sandbox']['max']) {
    $context['finished'] = $context['sandbox']['progress'] / $context['sandbox']['max'];
  }

  // Put the total into the results section when we're finished so we can
  // show it to the admin.
  if ($context['finished']) {
    $context['results']['count'] = $context['sandbox']['progress'];
  }
}

/**
 * Batch 'finished' callback.
 *
 * @see corresponding_node_references_batch_index_remaining()
 */
function corresponding_node_references_batch_update_existing_finished($success, $results, $operations) {
  if ($success) {
    $type = 'status';
    $message = format_plural($results['count'], '1 node processed successfully.', '@count nodes processed successfully.');
  }
  else {
    $type = 'error';

    // An error occurred.
    // $operations contains the operations that remained unprocessed.
    $error_operation = reset($operations);
    $message = 'An error occurred while processing ' . $error_operation[0] . ' with arguments:' . print_r($error_operation[0], TRUE);
  }
  drupal_set_message($message, $type);
}

Functions

Namesort descending Description
corresponding_node_references_batch_index_remaining Submit a batch job to index the remaining, unindexed content.
corresponding_node_references_batch_update_existing_finished Batch 'finished' callback.
corresponding_node_references_batch_update_existing_nodes Batch Operation Callback
corresponding_node_references_format_label Formats a label
corresponding_node_references_menu Implementation hook_menu()
corresponding_node_references_nodeapi Implementation of hook_nodeapi()
corresponding_node_references_perm Implementation hook_perm()
corresponding_node_references_process_node Process a node's corresponding node references.
corresponding_node_references_settings_form The settings form
corresponding_node_references_settings_form_submit Submit function for settings form
corresponding_node_references_update_form The update form. Allows updating of current nodes.
corresponding_node_references_update_form_submit The update form. Allows updating of current nodes.
corresponding_node_references_update_form_validate The update form. Allows updating of current nodes.