You are here

apachesolr.admin.inc in Apache Solr Search 6.3

Administrative pages for the Apache Solr framework.

File

apachesolr.admin.inc
View source
<?php

/**
 * @file
 *   Administrative pages for the Apache Solr framework.
 */

/**
 * Form to delete a search environment
 * @param $environment
 *   The environment to delete
 */
function apachesolr_environment_delete_form(&$form_state, $environment) {
  $form = array();
  $form['env_id'] = array(
    '#type' => 'value',
    '#value' => $environment['env_id'],
  );
  if (isset($environment['export_type']) && $environment['export_type'] == 3) {
    $verb = t('Revert');
  }
  else {
    $verb = t('Delete');
  }
  return confirm_form($form, t('Are you sure you want to !verb search environment %name?', array(
    '%name' => $environment['name'],
    '!verb' => strtolower($verb),
  )), 'admin/settings/apachesolr', t('This action cannot be undone.'), $verb, t('Cancel'));
}
function apachesolr_environment_delete_form_submit($form, &$form_state) {
  if (apachesolr_environment_delete($form_state['values']['env_id'])) {
    drupal_set_message(t('The search environment was deleted'));
  }

  // Regardlessly of the destination parameter we want to go to another page
  unset($_GET['destination']);
  $form_state['redirect'] = 'admin/settings/apachesolr/settings';
}
function apachesolr_environment_edit_delete_submit($form, &$form_state) {
  $form_state['redirect'] = 'admin/settings/apachesolr/settings/' . $form_state['values']['env_id'] . '/delete';

  // Regardlessly of the destination parameter we want to go to another page
  unset($_GET['destination']);
  drupal_get_destination();
}
function apachesolr_environment_settings_page($environment = NULL) {
  if (empty($environment)) {
    $env_id = apachesolr_default_environment();
    $environment = apachesolr_environment_load($env_id);
  }
  $env_id = $environment['env_id'];

  // Initializes output with information about which environment's setting we are
  // editing, as it is otherwise not transparent to the end user.
  $output['apachesolr_environment'] = theme('apachesolr_settings_title', $env_id);
  $output['form'] = drupal_get_form('apachesolr_environment_edit_form', $environment);
  $build_output = NULL;
  foreach ($output as $build_result) {
    $build_output .= $build_result;
  }
  return $build_output;
}
function apachesolr_environment_clone_form(&$form_state, $environment) {
  $form = array();
  $form['env_id'] = array(
    '#type' => 'value',
    '#value' => $environment['env_id'],
  );
  return confirm_form($form, t('Are you sure you want to clone search environment %name?', array(
    '%name' => $environment['name'],
  )), 'admin/settings/apachesolr', '', t('Clone'), t('Cancel'));
}
function apachesolr_environment_clone_form_submit($form, &$form_state) {
  if (apachesolr_environment_clone($form_state['values']['env_id'])) {
    drupal_set_message(t('The search environment was cloned'));
  }
  $form_state['redirect'] = 'admin/settings/apachesolr/settings';
}
function apachesolr_environment_clone_submit($form, &$form_state) {
  $form_state['redirect'] = 'admin/settings/apachesolr/settings/' . $form_state['values']['env_id'] . '/clone';
}

/**
 * Form builder for adding/editing a Solr environment used as a menu callback.
 */
function apachesolr_environment_edit_form(&$form_state, $environment = NULL) {
  if (empty($environment)) {
    $environment = array();
  }
  $environment += array(
    'env_id' => '',
    'name' => '',
    'url' => '',
    'service_class' => '',
    'conf' => array(
      'service_class_conf' => '',
    ),
  );
  $form['url'] = array(
    '#type' => 'textfield',
    '#title' => t('Solr server URL'),
    '#default_value' => $environment['url'],
    '#description' => t('Example: http://localhost:8983/solr'),
    '#required' => TRUE,
  );
  $is_default = $environment['env_id'] == apachesolr_default_environment();
  $form['make_default'] = array(
    '#type' => 'checkbox',
    '#title' => t('Make this Solr search environment the default'),
    '#default_value' => $is_default,
    '#disabled' => $is_default,
  );
  $form['name'] = array(
    '#type' => 'textfield',
    '#title' => t('Description'),
    '#default_value' => $environment['name'],
    '#required' => TRUE,
  );
  if (empty($environment['env_id'])) {
    $key = '#default_value';
    $disabled = FALSE;
  }
  else {
    $key = '#value';
    $disabled = TRUE;
  }
  $form['env_id'] = array(
    '#type' => 'textfield',
    '#title' => t('Environment id'),
    '#description' => '',
    '#required' => TRUE,
    '#size' => 30,
    '#maxlength' => 32,
    '#disabled' => $disabled,
    // Cannot change it once set.
    $key => $environment['env_id'],
    '#description' => t('A unique machine-readable identifier for the search page configuration. It must only contain lowercase letters, numbers, and underscores.'),
  );
  $form['service_class'] = array(
    '#type' => 'value',
    '#value' => $environment['service_class'],
  );

  // D6 needs more info to load the class
  $service_class_info = isset($environment['conf']['service_class_info']) ? $environment['conf']['service_class_info'] : NULL;
  $form['service_class_info'] = array(
    '#type' => 'value',
    '#value' => $service_class_info,
  );
  $form['conf'] = array(
    '#tree' => TRUE,
  );
  $form['conf']['apachesolr_read_only'] = array(
    '#type' => 'radios',
    '#title' => t('Index write access'),
    '#default_value' => isset($environment['conf']['apachesolr_read_only']) ? $environment['conf']['apachesolr_read_only'] : APACHESOLR_READ_WRITE,
    '#options' => array(
      APACHESOLR_READ_WRITE => t('Read and write (normal)'),
      APACHESOLR_READ_ONLY => t('Read only'),
    ),
    '#description' => t('<em>Read only</em> stops this site from sending updates to this search environment. Useful for development sites.'),
  );
  $form['actions']['save'] = array(
    '#type' => 'submit',
    '#redirect' => 'admin/settings/apachesolr/settings',
    '#validate' => array(
      'apachesolr_environment_edit_validate',
    ),
    '#submit' => array(
      'apachesolr_environment_edit_submit',
    ),
    '#value' => t('Save'),
  );
  $form['actions']['save_edit'] = array(
    '#type' => 'submit',
    '#validate' => array(
      'apachesolr_environment_edit_validate',
    ),
    '#submit' => array(
      'apachesolr_environment_edit_submit',
    ),
    '#value' => t('Save and edit'),
  );
  $form['actions']['test'] = array(
    '#type' => 'submit',
    '#validate' => array(
      'apachesolr_environment_edit_validate',
    ),
    '#submit' => array(
      'apachesolr_environment_edit_test_submit',
    ),
    '#value' => t('Test connection'),
  );
  if (!empty($environment['env_id']) && !$is_default) {
    $form['actions']['delete'] = array(
      '#type' => 'submit',
      '#submit' => array(
        'apachesolr_environment_edit_delete_submit',
      ),
      '#value' => t('Delete'),
    );
  }
  $referer_uri = referer_uri();
  $request_uri = request_uri();

  // In case the API did not remove the query params, we remove it forcefully
  $request_uri = strtok($request_uri, '?');

  // Remove leading slash
  $request_uri = ltrim($request_uri, '/');
  $request_uri = url($request_uri, array(
    'absolute' => 'true',
  ));

  // Redirect cancel link to the search pages if the referer is the same page or
  // when there is no referer.
  if ($referer_uri == $request_uri || empty($referer_uri)) {
    $cancel_link = url('admin/settings/apachesolr/settings', array(
      'absolute' => TRUE,
    ));
  }
  else {
    $cancel_link = $referer_uri;
  }
  $form['actions']['cancel'] = array(
    '#type' => 'markup',
    '#value' => l(t('Cancel'), $cancel_link),
  );
  return $form;
}
function apachesolr_environment_edit_test_submit($form, &$form_state) {
  $ping = apachesolr_server_status($form_state['values']['url'], $form_state['values']['service_class'], $form_state['values']['service_class_info']);
  if ($ping) {
    drupal_set_message(t('Your site has contacted the Apache Solr server.'));
  }
  else {
    drupal_set_message(t('Your site was unable to contact the Apache Solr server.'), 'error');
  }
  apachesolr_environment_save($form_state['values']);

  //$form_state['rebuild'] = TRUE;
}
function apachesolr_environment_edit_validate($form, &$form_state) {
  $parts = parse_url($form_state['values']['url']);
  foreach (array(
    'scheme',
    'host',
    'path',
  ) as $key) {
    if (empty($parts[$key])) {
      form_set_error('url', t('The Solr server URL needs to include a !part', array(
        '!part' => $key,
      )));
    }
  }
  if (isset($parts['port'])) {

    // parse_url() should always give an integer for port. Since drupal_http_request()
    // also uses parse_url(), we don't need to validate anything except the range.
    $pattern = empty($parts['user']) ? '@://[^:]+:([^/]+)@' : '#://[^@]+@[^:]+:([^/]+)#';
    preg_match($pattern, $form_state['values']['url'], $m);
    if (empty($m[1]) || !ctype_digit($m[1]) || $m[1] < 1 || $m[1] > 65535) {
      form_set_error('port', t('The port has to be an integer between 1 and 65535.'));
    }
    else {

      // Normalize the url by removing extra slashes and whitespace.
      $form_state['values']['url'] = trim($form_state['values']['url'], "/ \t\r\n\0\v");
    }
  }
  $env_id = strtolower(preg_replace(array(
    '/[^a-zA-Z0-9]+/',
    '/-+/',
    '/^-+/',
    '/-+$/',
  ), array(
    '_',
    '_',
    '',
    '',
  ), $form_state['values']['env_id']));
  if ($form_state['values']['env_id'] != $env_id && !empty($env_id)) {
    form_set_error('env_id', t('A unique machine-readable identifier for the search page configuration. It must only contain lowercase letters, numbers, and underscores.'));
  }
  elseif (empty($form['env_id']['#disabled']) && apachesolr_environment_load($env_id)) {
    form_set_error('env_id', t('That indentifier is already in use.'));
  }
}
function apachesolr_environment_edit_submit($form, &$form_state) {
  apachesolr_environment_save($form_state['values']);
  if (!empty($form_state['values']['make_default'])) {
    apachesolr_set_default_environment($form_state['values']['env_id']);
  }
  cache_clear_all('apachesolr:environments', 'cache_apachesolr');
  drupal_set_message(t('The %name search environment has been saved.', array(
    '%name' => $form_state['values']['name'],
  )));

  // for some reason $_GET was not sufficient.
  if (isset($_REQUEST['destination'])) {
    unset($_REQUEST['destination']);
  }
  if (isset($form_state['clicked_button']['#redirect'])) {
    $form_state['redirect'] = array(
      $form_state['clicked_button']['#redirect'],
    );
  }
  else {
    $form_state['redirect'] = array(
      $_GET['q'],
    );
  }
}

/**
 * Check to see if the facetapi module is installed, and if not put up
 * a message.
 *
 * Only call this function if the user is already in a position for this to
 * be useful.
 */
function apachesolr_check_facetapi() {
  if (!module_exists('facetapi')) {
    $filename = db_result(db_query("SELECT filename FROM {system} WHERE type = 'module' AND name = 'facetapi' LIMIT 1"));
    if ($filename && file_exists($filename)) {
      drupal_set_message(t('If you <a href="@modules">enable the facetapi module</a>, Apache Solr Search will provide you with configurable facets.', array(
        '@modules' => url('admin/modules'),
      )));
    }
    else {
      drupal_set_message(t('If you install the facetapi module from !href, Apache Solr Search will provide you with configurable facets.', array(
        '!href' => url('http://drupal.org/project/facetapi'),
      )));
    }
  }
}

/**
 * Form builder for general settings used as a menu callback.
 */
function apachesolr_settings(&$form_state) {
  $form = array();
  $rows = array();

  // Environment settings
  $id = apachesolr_default_environment();
  $environments = apachesolr_load_all_environments();
  $default_environment = apachesolr_default_environment();
  apachesolr_check_facetapi();

  // Check if core search is still running
  if (variable_get('search_cron_limit', 0) != 0) {
    drupal_set_message(t('It seems that Drupal core search is not disabled.
      This can cause performance impact at cron runs.
      Please !url the per cron value to 0', array(
      '!url' => l('set', 'admin/settings/search'),
    )), 'warning');
  }

  // Reserve a row for the default one
  $rows[$default_environment] = array();
  foreach ($environments as $environment_id => $data) {

    // Define all the Operations
    $confs = array();
    $ops = array();

    // Whenever facetapi is enabled we also enable our operation link
    if (module_exists('facetapi')) {
      $confs['facets'] = array(
        'class' => 'operation',
        'data' => l(t('Facets'), 'admin/settings/apachesolr/settings/' . $data['env_id'] . '/facets', array(
          'query' => array(
            'destination' => $_GET['q'],
          ),
        )),
      );
    }

    // These are our result and bias settings
    if (module_exists('apachesolr_search')) {
      $confs['result_bias'] = array(
        'class' => 'operation',
        'data' => l(t('Bias'), 'admin/settings/apachesolr/settings/' . $data['env_id'] . '/bias', array(
          'query' => array(
            'destination' => $_GET['q'],
          ),
        )),
      );
    }
    $confs['index'] = array(
      'class' => 'operation',
      'data' => l(t('Index'), 'admin/settings/apachesolr/settings/' . $data['env_id'] . '/index'),
    );
    $ops['edit'] = array(
      'class' => 'operation',
      'data' => l(t('Edit'), 'admin/settings/apachesolr/settings/' . $data['env_id'] . '/edit', array(
        'query' => array(
          'destination' => $_GET['q'],
        ),
      )),
    );
    $ops['clone'] = array(
      'class' => 'operation',
      'data' => l(t('Clone'), 'admin/settings/apachesolr/settings/' . $data['env_id'] . '/clone', array(
        'query' => array(
          'destination' => $_GET['q'],
        ),
      )),
    );
    $env_name = l($data['name'], 'admin/settings/apachesolr/settings/' . $data['env_id'] . '/edit', array(
      'query' => array(
        'destination' => $_GET['q'],
      ),
    ));

    // Is this row our default environment?
    if ($environment_id == $default_environment) {
      $env_name = t('!environment <em>(Default)</em>', array(
        '!environment' => $env_name,
      ));
      $env_class_row = 'default-environment';
    }
    else {
      $env_class_row = '';

      // For every non-default we add a delete link
      // Allow to revert a search environment or to delete it
      $delete_value = '';
      if (!isset($data['in_code_only'])) {
        if (isset($data['type']) && $data['type'] == 'Overridden') {
          $delete_value = array(
            'class' => 'operation',
            'data' => l(t('Revert'), 'admin/settings/apachesolr/settings/' . $data['env_id'] . '/delete'),
          );
        }
        elseif ($environment_id != $default_environment) {
          $delete_value = array(
            'class' => 'operation',
            'data' => l(t('Delete'), 'admin/settings/apachesolr/settings/' . $data['env_id'] . '/delete'),
          );
        }
      }
      $ops['delete'] = $delete_value;
    }

    // When we are receiving a http POST (so the page does not show) we do not
    // want to check the statusses of any environment
    $class = '';
    if (empty($form_state['input'])) {
      $service_class_info = isset($data['conf']['service_class_info']) ? $data['conf']['service_class_info'] : NULL;
      $class = apachesolr_server_status($data['url'], $data['service_class'], $service_class_info) ? 'ok' : 'error';
    }
    $headers = array(
      array(
        'data' => t('Name'),
        'colspan' => 2,
      ),
      t('URL'),
      array(
        'data' => t('Configuration'),
        'colspan' => count($confs),
      ),
      array(
        'data' => t('Operations'),
        'colspan' => count($ops),
      ),
    );
    $rows[$environment_id] = array(
      'data' => array(
        // Cells
        array(
          'class' => 'status-icon',
          'data' => '<div title="' . $class . '"><span class="element-invisible">' . $class . '</span></div>',
        ),
        array(
          'class' => $env_class_row,
          'data' => $env_name,
        ),
        check_plain($data['url']),
      ),
      'class' => array(
        drupal_strtolower($class),
      ),
    );

    // Add the links to the page
    $rows[$environment_id]['data'] = array_merge($rows[$environment_id]['data'], $confs);
    $rows[$environment_id]['data'] = array_merge($rows[$environment_id]['data'], $ops);
  }
  $links['add_page'] = array(
    'title' => 'Add search environment',
    'href' => 'admin/settings/apachesolr/settings/add',
  );
  $form['apachesolr_host_settings']['actions'] = array(
    '#value' => '<ul class="action-links">' . theme('links', $links) . '</ul>',
  );
  $form['apachesolr_host_settings']['table'] = array(
    '#value' => theme('table', $headers, array_values($rows), array(
      'class' => array(
        'admin-apachesolr',
      ),
    )),
  );
  $form['advanced'] = array(
    '#type' => 'fieldset',
    '#title' => t('Advanced configuration'),
    '#collapsed' => TRUE,
    '#collapsible' => TRUE,
  );
  $form['advanced']['apachesolr_set_nodeapi_messages'] = array(
    '#type' => 'radios',
    '#title' => t('Extra help messages for administrators'),
    '#description' => t('Adds notices to a page whenever Drupal changed content that needs reindexing'),
    '#default_value' => variable_get('apachesolr_set_nodeapi_messages', 1),
    '#options' => array(
      0 => t('Disabled'),
      1 => t('Enabled'),
    ),
  );

  // Number of Items to index
  $numbers = drupal_map_assoc(array(
    1,
    5,
    10,
    20,
    50,
    100,
    200,
  ));
  $default_cron_limit = variable_get('apachesolr_cron_limit', 50);

  // apachesolr_cron_limit may be overridden in settings.php. If its current
  // value is not among the default set of options, add it.
  if (!isset($numbers[$default_cron_limit])) {
    $numbers[$default_cron_limit] = $default_cron_limit;
  }
  $form['advanced']['apachesolr_cron_limit'] = array(
    '#type' => 'select',
    '#title' => t('Number of items to index per cron run'),
    '#default_value' => $default_cron_limit,
    '#options' => $numbers,
    '#description' => t('Reduce the number of items to prevent timeouts and memory errors while indexing.', array(
      '@cron' => url('admin/reports/status'),
    )),
  );
  $options = array(
    'apachesolr:show_error' => t('Show error message'),
  );
  foreach (module_implements('search_info') as $module) {

    // Don't allow apachesolr to return results on failure of apachesolr.
    if ($module == 'apachesolr_search') {
      continue;
    }
    $options[$module] = t('Show @name search results', array(
      '@name' => $module,
    ));
  }
  $options['apachesolr:show_no_results'] = t('Show no results');
  $form['advanced']['apachesolr_failure'] = array(
    '#type' => 'select',
    '#title' => t('On failure'),
    '#options' => $options,
    '#default_value' => variable_get('apachesolr_failure', 'apachesolr:show_error'),
  );
  return system_settings_form($form);
}

/**
 * Gets information about the fields already in solr index.
 */
function apachesolr_status_page($environment = NULL) {
  if (empty($environment)) {
    $env_id = apachesolr_default_environment();
    $environment = apachesolr_environment_load($env_id);
  }
  else {
    $env_id = $environment['env_id'];
  }

  // Check for availability
  $service_class_info = isset($environment['conf']['service_class_info']) ? $environment['conf']['service_class_info'] : NULL;
  $service_class = isset($environment['service_class']) ? $environment['service_class'] : NULL;
  if (!apachesolr_server_status($environment['url'], $service_class, $service_class_info)) {
    drupal_set_message(t('The server seems to be unavailable. Please verify the server settings at the <a href="!settings_page">settings page</a>', array(
      '!settings_page' => url("admin/settings/apachesolr/settings/{$environment['env_id']}/edit", array(
        'query' => drupal_get_destination(),
      )),
    )), 'warning');
    return '';
  }
  try {
    $solr = apachesolr_get_solr($environment["env_id"]);
    $solr
      ->clearCache();
    $data = $solr
      ->getLuke();
  } catch (Exception $e) {
    watchdog('Apache Solr', nl2br(check_plain($e
      ->getMessage())), NULL, WATCHDOG_ERROR);
    drupal_set_message(nl2br(check_plain($e
      ->getMessage())), "warning");
    $data = new stdClass();
    $data->fields = array();
  }
  $messages = array();
  if (isset($data->index->numDocs)) {
    try {

      // Collect the stats
      $stats_summary = $solr
        ->getStatsSummary();
      module_load_include('inc', 'apachesolr', 'apachesolr.index');
      $status = apachesolr_index_status($environment["env_id"]);

      // We need a schema version greater than beta3. This is mostly to catch
      // people using the Drupal 6 schema.
      if (preg_match('/^drupal-[13]/', $stats_summary['@schema_version'])) {
        $minimum = 'drupal-3.0-beta4';
        if (version_compare($stats_summary['@schema_version'], $minimum, '<')) {
          drupal_set_message(t('Your schema.xml version is too old. You must update it to at least %minimum and re-index your content.', array(
            '%minimum' => $minimum,
          )), 'error');
        }
      }
      $pending_msg = $stats_summary['@pending_docs'] ? t('(@pending_docs sent but not yet processed)', $stats_summary) : '';
      $index_msg = $stats_summary['@index_size'] ? t('(@index_size on disk)', $stats_summary) : '';
      $indexed_message = t('@num Items !pending !index_msg', array(
        '@num' => $data->index->numDocs,
        '!pending' => $pending_msg,
        '!index_msg' => $index_msg,
      ));
      $messages[] = array(
        t('Indexed'),
        $indexed_message,
      );
      $remaining_message = t('@items (@percentage% has been sent to the server)', array(
        '@items' => format_plural($status['remaining'], t('1 item'), t('@count items')),
        '@percentage' => (int) min(100, 100 * ($status['total'] - $status['remaining']) / max(1, $status['total'])),
      ));
      $messages[] = array(
        t('Remaining'),
        $remaining_message,
      );
      $messages[] = array(
        t('Schema'),
        t('@schema_version', $stats_summary),
      );
      if (!empty($stats_summary['@core_name'])) {
        $messages[] = array(
          t('Solr Core Name'),
          t('@core_name', $stats_summary),
        );
      }
      $messages[] = array(
        t('Delay'),
        t('@autocommit_time before updates are processed.', $stats_summary),
      );
      $messages[] = array(
        t('Pending Deletions'),
        t('@deletes_total', $stats_summary),
      );
    } catch (Exception $e) {
      watchdog('Apache Solr', nl2br(check_plain($e
        ->getMessage())), NULL, WATCHDOG_ERROR);
    }
  }
  if (empty($messages)) {
    $messages[] = array(
      t('Error'),
      t('No data was returned from the server. Check your log messages.'),
    );
  }

  // Initializes output with information about which server's setting we are
  // editing, as it is otherwise not transparent to the end user.
  $output['apachesolr_index_action_status_prefix'] = '<h3>' . t('@environment: Search Index Content', array(
    '@environment' => $environment['name'],
  )) . '</h3>';
  $output['apachesolr_index_action_status'] = theme('table', array(
    t('Type'),
    t('Value'),
  ), $messages);
  $output['viewmore'] = l(t('View more details on the search index contents'), 'admin/reports/apachesolr');
  $write_status = apachesolr_environment_variable_get($env_id, 'apachesolr_read_only', APACHESOLR_READ_WRITE);
  if ($write_status == APACHESOLR_READ_WRITE) {
    $output['index_action_form'] = drupal_get_form('apachesolr_index_action_form', $env_id);
    $output['index_config_form'] = drupal_get_form('apachesolr_index_config_form', $env_id);
  }
  else {
    drupal_set_message(t('Options for deleting and re-indexing are not available because the index is read-only. This can be changed on the <a href="!settings_page">settings page</a>', array(
      '!settings_page' => url('admin/settings/apachesolr/settings/' . $env_id . '/edit', array(
        'query' => drupal_get_destination(),
      )),
    )), 'warning');
  }

  // Print in a similar way as the Drupal 7 version
  $output_print = NULL;
  foreach ($output as $print) {
    $output_print .= $print;
  }
  return $output_print;
}
function apachesolr_index_report($environment = NULL) {
  if (!$environment) {
    $env_id = apachesolr_default_environment();
    drupal_goto('admin/reports/apachesolr/' . $env_id);
  }
  $environments = apachesolr_load_all_environments();
  $environments_list = array();
  foreach ($environments as $env) {
    $var_status = array(
      '@name' => $env['name'],
    );
    $environments_list[] = l(t('Statistics for @name', $var_status), 'admin/reports/apachesolr/' . $env['env_id']);
  }
  $output['environments_list'] = theme('item_list', $environments_list);
  try {
    $solr = apachesolr_get_solr($environment['env_id']);
    $solr
      ->clearCache();
    $data = $solr
      ->getLuke();
  } catch (Exception $e) {
    watchdog('Apache Solr', nl2br(check_plain($e
      ->getMessage())), NULL, WATCHDOG_ERROR);
    drupal_set_message(nl2br(check_plain($e
      ->getMessage())), "warning");
    return $output;
  }
  $messages = array();
  $messages[] = array(
    t('Number of documents in index'),
    $data->index->numDocs,
  );
  $limit = variable_get('apachesolr_luke_limit', 20000);
  if (isset($data->index->numDocs) && $data->index->numDocs > $limit) {
    $messages[] = array(
      t('Limit'),
      t('You have more than @limit documents, so term frequencies are being omitted for performance reasons.', array(
        '@limit' => $limit,
      )),
    );
    $not_found = t('<em>Omitted</em>');
  }
  elseif (isset($data->index->numDocs)) {
    $not_found = t('Not indexed');
    try {
      $solr = apachesolr_get_solr($environment['env_id']);

      // Note: we use 2 since 1 fails on Ubuntu Hardy.
      $data = $solr
        ->getLuke(2);
      if (isset($data->index->numTerms)) {
        $messages[] = array(
          t('# of terms in index'),
          $data->index->numTerms,
        );
      }
    } catch (Exception $e) {
      watchdog('Apache Solr', nl2br(check_plain($e
        ->getMessage())), NULL, WATCHDOG_ERROR);
      $data->fields = array();
    }
  }

  // Initializes output with information about which server's setting we are
  // editing, as it is otherwise not transparent to the end user.
  $fields = (array) $data->fields;
  if ($fields) {
    $messages[] = array(
      t('# of fields in index'),
      count($fields),
    );
  }

  // Output the messages we have for this page
  $output['apachesolr_index_report'] = theme('table', array(
    t('Type'),
    t('Value'),
  ), array_values($messages));
  if ($fields) {

    // Initializes table header.
    $header = array(
      t('Field name'),
      t('Index type'),
      t('Distinct terms'),
    );

    // Builds table rows.
    $rows = array();
    foreach ($fields as $name => $field) {

      // TODO: try to map the name to something more meaningful.
      $rows[$name] = array(
        'name' => $name,
        'type' => $field->type,
        'terms' => isset($field->distinct) ? $field->distinct : $not_found,
      );
    }
    ksort($rows);

    // Output the fields we found for this environment
    $output['field_table'] = theme('table', $header, array_values($rows));
  }
  else {
    $output['field_table'] = '<p>' . t('No data on indexed fields.') . "</p>/n";
  }

  // Print in a similar way as the Drupal 7 version
  $output_print = NULL;
  foreach ($output as $print) {
    $output_print .= $print;
  }
  return $output_print;
}

/**
 * Page callback to show available conf files.
 */
function apachesolr_config_files_overview($environment = NULL) {
  if (!$environment) {
    $env_id = apachesolr_default_environment();
  }
  else {
    $env_id = $environment['env_id'];
  }
  $xml = NULL;
  $output = NULL;
  try {
    $solr = apachesolr_get_solr($env_id);
    $response = $solr
      ->makeServletRequest('admin/file', array(
      'wt' => 'xml',
    ));
    $xml = simplexml_load_string($response->data);
  } catch (Exception $e) {
    watchdog('Apache Solr', nl2br(check_plain($e
      ->getMessage())), NULL, WATCHDOG_ERROR);
    drupal_set_message(nl2br(check_plain($e
      ->getMessage())), "warning");
  }
  if ($xml) {

    // Retrieve our items from the xml using xpath
    $items = $xml
      ->xpath('//lst[@name="files"]/lst');

    // Add all the data of the file in a files array
    $files = array();
    foreach ($items as $item_id => $item) {

      // Do not list directories. Always a bool
      if (isset($item->bool)) {
        break;
      }

      // Get data from the files.
      $name = (string) $item
        ->attributes() ? (string) $item
        ->attributes() : t('No name found');
      $files[$item_id]['name'] = l($name, 'admin/reports/apachesolr/' . $env_id . '/conf/' . $name);

      // Retrieve the date attribute
      if (isset($item->date)) {
        $modified = (string) $item->date
          ->attributes() == 'modified' ? (string) $item->date : t('No date found');
        $files[$item_id]['modified'] = format_date(strtotime($modified));
      }

      // Retrieve the size attribute
      if (isset($item->long)) {
        $size = (string) $item->long
          ->attributes() == 'size' ? (string) $item->long : t('No size found');
        $files[$item_id]['size'] = t('Size (bytes): @bytes', array(
          '@bytes' => $size,
        ));
      }
    }

    // Sort our files alphabetically
    ksort($files);

    // Initializes table header.
    $header = array(
      t('File name'),
      t('Modified'),
      t('Size'),
    );

    // Display the table of field names, index types, and term counts.
    $output .= theme('table', $header, $files);
  }
  else {
    $output = '<p>' . t('No data about any file found.') . "</p>\n";
  }
  return $output;
}

/**
 * Page callback to show one conf file.
 */
function apachesolr_config_file($name, $environment = NULL) {
  if (!$environment) {
    $env_id = apachesolr_default_environment();
  }
  else {
    $env_id = $environment['env_id'];
  }
  $output = '';
  try {
    $solr = apachesolr_get_solr($env_id);
    $response = $solr
      ->makeServletRequest('admin/file', array(
      'file' => $name,
    ));
    $raw_file = $response->data;
    $output = '<pre>' . check_plain($raw_file) . '</pre>';
    drupal_set_title(check_plain($name));
  } catch (Exception $e) {
    watchdog('Apache Solr', nl2br(check_plain($e
      ->getMessage())), NULL, WATCHDOG_ERROR);
    drupal_set_message(nl2br(check_plain($e
      ->getMessage())), "warning");
  }
  return $output;
}

/**
 * Form builder for the Apachesolr Indexer actions form.
 *
 * @see apachesolr_index_action_form_delete_submit().
 */
function apachesolr_index_action_form(&$form_state, $env_id) {
  $form = array();
  $form['action'] = array(
    '#type' => 'fieldset',
    '#title' => t('Actions'),
    '#collapsible' => TRUE,
  );
  $form['action']['env_id'] = array(
    '#type' => 'value',
    '#value' => $env_id,
  );
  $form['action']['cron'] = array(
    '#prefix' => '<div>',
    '#type' => 'submit',
    '#value' => t('Index queued content (!amount)', array(
      '!amount' => variable_get('apachesolr_cron_limit', 50),
    )),
    '#submit' => array(
      'apachesolr_index_action_form_cron_submit',
    ),
  );
  $form['action']['cron_description'] = array(
    '#type' => 'markup',
    '#prefix' => '<span>',
    '#suffix' => '</span></div>',
    '#value' => t('Indexes just as many items as 1 cron run would do.'),
  );
  $form['action']['remaining'] = array(
    '#prefix' => '<div>',
    '#type' => 'submit',
    '#value' => t('Index all queued content'),
    '#submit' => array(
      'apachesolr_index_action_form_remaining_submit',
    ),
  );
  $form['action']['remaining_description'] = array(
    '#type' => 'markup',
    '#prefix' => '<span>',
    '#suffix' => '</span></div>',
    '#value' => t('Could take time and could put an increased load on your server.'),
  );
  $form['action']['reset'] = array(
    '#prefix' => '<div>',
    '#suffix' => '</div>',
    '#type' => 'submit',
    '#value' => t('Queue all content for reindexing'),
    '#submit' => array(
      'apachesolr_index_action_form_reset_submit',
    ),
  );
  $form['action']['delete'] = array(
    '#prefix' => '<div>',
    '#type' => 'submit',
    '#value' => t('Delete the Search & Solr index'),
    '#submit' => array(
      'apachesolr_index_action_form_delete_submit',
    ),
  );
  $form['action']['delete_description'] = array(
    '#type' => 'markup',
    '#prefix' => '<span>',
    '#suffix' => '</span></div>',
    '#value' => t('Useful with a corrupt index or a new schema.xml.'),
  );
  return $form;
}

/**
 * Submit handler for the Indexer actions form, delete button.
 */
function apachesolr_index_action_form_remaining_submit($form, &$form_state) {
  $destination = array();
  if (isset($_GET['destination'])) {
    $destination = drupal_get_destination();
    unset($_GET['destination']);
  }
  $env_id = $form_state['values']['env_id'];

  // $form_state['storage'] must be unset for redirection to occur. Otherwise
  // $form_state['rebuild'] is automatically set and this form will be
  // rebuilt.
  unset($form_state['storage']);
  $form_state['redirect'] = array(
    'admin/settings/apachesolr/settings/' . $env_id . '/index/remaining',
    array(
      'query' => $destination,
    ),
  );
}

/**
 * Submit handler for the Indexer actions form, delete button.
 */
function apachesolr_index_action_form_delete_submit($form, &$form_state) {
  $destination = array();
  if (isset($_GET['destination'])) {
    $destination = drupal_get_destination();
    unset($_GET['destination']);
  }
  $env_id = $form_state['values']['env_id'];

  // $form_state['storage'] must be unset for redirection to occur. Otherwise
  // $form_state['rebuild'] is automatically set and this form will be
  // rebuilt.
  unset($form_state['storage']);
  $form_state['redirect'] = array(
    'admin/settings/apachesolr/settings/' . $env_id . '/index/delete',
    array(
      'query' => $destination,
    ),
  );
}

/**
 * Submit handler for the Indexer actions form, delete button.
 */
function apachesolr_index_action_form_reset_submit($form, &$form_state) {
  $destination = array();
  if (isset($_GET['destination'])) {
    $destination = drupal_get_destination();
    unset($_GET['destination']);
  }
  $env_id = $form_state['values']['env_id'];

  // $form_state['storage'] must be unset for redirection to occur. Otherwise
  // $form_state['rebuild'] is automatically set and this form will be
  // rebuilt.
  unset($form_state['storage']);
  $form_state['redirect'] = array(
    'admin/settings/apachesolr/settings/' . $env_id . '/index/reset',
    array(
      'query' => $destination,
    ),
  );
}

/**
 * Submit handler for the deletion form.
 */
function apachesolr_index_action_form_cron_submit($form, &$form_state) {
  if (!empty($form_state['build_info']['args'][0])) {
    $env_id = $form_state['build_info']['args'][0];
    $form_state['redirect'] = 'admin/settings/apachesolr/settings/' . $env_id . '/index';
  }
  else {
    $env_id = apachesolr_default_environment();
    $form_state['redirect'] = 'admin/settings/apachesolr';
  }

  // $form_state['storage'] must be unset for redirection to occur. Otherwise
  // $form_state['rebuild'] is automatically set and this form will be
  // rebuilt.
  unset($form_state['storage']);
  apachesolr_cron($env_id);
  drupal_set_message(t('Apachesolr cron successfully executed'));
}

/**
 * Form builder for to reindex the remaining.
 *
 * @see apachesolr_index_action_form_delete_confirm_submit().
 */
function apachesolr_index_action_form_remaining_confirm(&$form_state, $environment) {
  $form = array();
  return confirm_form($form, t('Are you sure you want index all remaining content?'), 'admin/settings/apachesolr/settings/' . $environment['env_id'] . '/index', NULL, t('Index all remaining'));
}

/**
 * Submit handler for the deletion form.
 */
function apachesolr_index_action_form_remaining_confirm_submit($form, &$form_state) {
  if (!empty($form_state['build_info']['args'][0]['env_id'])) {
    $env_id = $form_state['build_info']['args'][0]['env_id'];
    $form_state['redirect'] = 'admin/settings/apachesolr/settings/' . $env_id . '/index';
  }
  else {
    $env_id = apachesolr_default_environment();
    $form_state['redirect'] = 'admin/settings/apachesolr';
  }

  // $form_state['storage'] must be unset for redirection to occur. Otherwise
  // $form_state['rebuild'] is automatically set and this form will be
  // rebuilt.
  unset($form_state['storage']);
  apachesolr_index_batch_index_remaining($env_id);
}

/**
 * Form builder for the index re-enqueue form.
 *
 * @see apachesolr_index_action_form_reset_confirm_submit().
 */
function apachesolr_index_action_form_reset_confirm(&$form_state, $environment) {
  $form = array();
  return confirm_form($form, t('Are you sure you want to queue content for reindexing?'), 'admin/settings/apachesolr/settings/' . $environment['env_id'] . '/index', t('All content on the site will be queued for indexing. The documents currently in the Solr index will remain searchable.'), t('Queue all content'));
}

/**
 * Submit handler for the deletion form.
 */
function apachesolr_index_action_form_reset_confirm_submit($form, &$form_state) {
  if (!empty($form_state['build_info']['args'][0]['env_id'])) {
    $env_id = $form_state['build_info']['args'][0]['env_id'];
    $form_state['redirect'] = 'admin/settings/apachesolr/settings/' . $env_id . '/index';
  }
  else {
    $env_id = apachesolr_default_environment();
    $form_state['redirect'] = 'admin/settings/apachesolr';
  }
  module_load_include('inc', 'apachesolr', 'apachesolr.index');
  apachesolr_index_mark_for_reindex($env_id);
  drupal_set_message(t('All the content on your site is queued for indexing. You can wait for it to be indexed during cron runs, or you can manually reindex it.'));

  // $form_state['storage'] must be unset for redirection to occur. Otherwise
  // $form_state['rebuild'] is automatically set and this form will be
  // rebuilt.
  unset($form_state['storage']);
}

/**
 * Form builder for the index delete/clear form.
 *
 * @see apachesolr_index_action_form_delete_confirm_submit().
 */
function apachesolr_index_action_form_delete_confirm(&$form_state, $environment) {
  $form = array();
  return confirm_form($form, t('Are you sure you want to clear your index?'), 'admin/settings/apachesolr/settings/' . $environment['env_id'] . '/index', t('This will remove all data from your index and all search results will be incomplete until your site is reindexed.'), t('Delete index'));
}

/**
 * Submit handler for the deletion form.
 */
function apachesolr_index_action_form_delete_confirm_submit($form, &$form_state) {
  if (!empty($form_state['build_info']['args'][0]['env_id'])) {
    $env_id = $form_state['build_info']['args'][0]['env_id'];
    $form_state['redirect'] = 'admin/settings/apachesolr/settings/' . $env_id . '/index';
  }
  else {
    $env_id = apachesolr_default_environment();
    $form_state['redirect'] = 'admin/settings/apachesolr';
  }

  // Rebuild our tracking table.
  module_load_include('inc', 'apachesolr', 'apachesolr.index');
  apachesolr_index_delete_index($env_id);
  drupal_set_message(t('The index has been deleted.'));

  // $form_state['storage'] must be unset for redirection to occur. Otherwise
  // $form_state['rebuild'] is automatically set and this form will be
  // rebuilt.
  unset($form_state['storage']);
}

/**
 * Submit a batch job to index the remaining, unindexed content.
 */
function apachesolr_index_batch_index_remaining($env_id, $total_limit = null) {
  $batch = array(
    'operations' => array(
      array(
        'apachesolr_index_batch_index_entities',
        array(
          $env_id,
          $total_limit,
        ),
      ),
    ),
    'finished' => 'apachesolr_index_batch_index_finished',
    'title' => t('Indexing'),
    'init_message' => t('Preparing to submit content to Solr for indexing...'),
    'progress_message' => t('Submitting content to Solr...'),
    'error_message' => t('Solr indexing has encountered an error.'),
    'file' => drupal_get_path('module', 'apachesolr') . '/apachesolr.admin.inc',
  );
  drupal_alter('apachesolr_index_batch', $batch);
  batch_set($batch);
}

/**
 * Batch Operation Callback
 *
 * @param $env_id
 *   The machine name of the environment
 * @param $total_limit
 *   The total number of items to index across all batches
 * @param $context
 */
function apachesolr_index_batch_index_entities($env_id, $total_limit = NULL, &$context) {
  module_load_include('inc', 'apachesolr', 'apachesolr.index');
  if (empty($context['sandbox'])) {
    try {

      // Get the $solr object
      $solr = apachesolr_get_solr($env_id);

      // If there is no server available, don't continue.
      if (!$solr
        ->ping()) {
        throw new Exception(t('No Solr instance available during indexing.'));
      }
    } catch (Exception $e) {
      watchdog('Apache Solr', $e
        ->getMessage(), NULL, WATCHDOG_ERROR);
      return FALSE;
    }
    $status = apachesolr_index_status($env_id);
    $context['sandbox']['progress'] = 0;
    $context['sandbox']['submitted'] = 0;

    // How many items do we want to index? All or a limited set of items
    if (empty($total_limit)) {
      $context['sandbox']['max'] = $status['remaining'];
    }
    else {
      $context['sandbox']['max'] = $total_limit;
    }
  }

  // We can safely process the apachesolr_cron_limit nodes at a time without a
  // timeout or out of memory error.
  $limit = variable_get('apachesolr_cron_limit', 50);

  // Reduce the limit for our final batch if we would be processing more than had been requested
  if ($limit + $context['sandbox']['progress'] > $context['sandbox']['max']) {
    $limit = $context['sandbox']['max'] - $context['sandbox']['progress'];
  }
  if ($context['sandbox']['max'] >= $context['sandbox']['progress'] + $limit) {
    $context['sandbox']['progress'] += $limit;
  }
  else {
    $context['sandbox']['progress'] = $context['sandbox']['max'];
  }
  $context['sandbox']['submitted'] += apachesolr_index_entities($env_id, $limit);
  $arguments = array(
    '@current' => $context['sandbox']['progress'],
    '@total' => $context['sandbox']['max'],
    '@submitted' => $context['sandbox']['submitted'],
  );
  $context['message'] = t('Inspected @current of @total entities. Submitted @submitted documents to Solr', $arguments);

  // Inform the batch engine that we are not finished, and provide an
  // estimation of the completion level we reached.
  $context['finished'] = empty($context['sandbox']['max']) ? 1 : $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'];
    $context['results']['submitted'] = $context['sandbox']['submitted'];
  }
}

/**
 * Batch 'finished' callback
 */
function apachesolr_index_batch_index_finished($success, $results, $operations) {
  $message = '';

  // $results['count'] will not be set if Solr is unavailable.
  if (isset($results['count'])) {
    $message .= format_plural($results['count'], '1 item processed successfully. ', '@count items successfully processed. ');
  }
  if (isset($results['submitted'])) {
    $message .= format_plural($results['submitted'], '1 document successfully sent to Solr.', '@count documents successfully sent to Solr.');
  }
  if ($success) {
    $type = 'status';
  }
  else {

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

/**
 * Form builder for the bundle configuration form.
 *
 * @see apachesolr_index_config_form_submit().
 */
function apachesolr_index_config_form(&$form_state, $env_id) {
  $form = array();
  $form['config'] = array(
    '#type' => 'fieldset',
    '#title' => t('Configuration'),
    '#collapsible' => TRUE,
  );
  $form['config']['bundles'] = array(
    '#type' => 'markup',
    '#value' => t('Select the content types that should be indexed.'),
  );

  // For future extensibility, when we have multiple cores.
  $form['config']['env_id'] = array(
    '#type' => 'value',
    '#value' => $env_id,
  );
  $options = array();
  foreach (content_types() as $key => $info) {
    $options[$key] = $info['name'];
  }
  asort($options);
  $form['config']['entities']['#tree'] = TRUE;
  $form['config']['entities']['node'] = array(
    '#type' => 'checkboxes',
    '#title' => 'node',
    '#options' => $options,
    '#default_value' => apachesolr_get_index_bundles($env_id, 'node'),
  );
  $form['config']['submit'] = array(
    '#type' => 'submit',
    '#value' => t('Save'),
  );
  return $form;
}

/**
 * Submit handler for the bundle configuration form.
 */
function apachesolr_index_config_form_submit($form, &$form_state) {
  module_load_include('inc', 'apachesolr', 'apachesolr.index');
  $form_values = $form_state['values'];
  $env_id = $form_values['env_id'];
  $entity_count = 0;
  foreach ($form_values['entities'] as $entity_type => $bundles) {
    $existing_bundles = apachesolr_get_index_bundles($env_id, $entity_type);
    $all_bundles = array_keys($bundles);
    $new_bundles = array_values(array_filter($bundles));
    apachesolr_index_set_bundles($env_id, $entity_type, $new_bundles);

    // Remove all excluded bundles - this happens on form submit
    // even if there is no change so the admin can remove
    // bundles if there was an error.
    $excluded_bundles = array_diff($all_bundles, $new_bundles);
    if (apachesolr_index_delete_bundles($env_id, $entity_type, $excluded_bundles)) {
      $callback = apachesolr_entity_get_callback($entity_type, 'bundles changed callback');
      if (!empty($callback)) {
        call_user_func($callback, $env_id, $existing_bundles, $new_bundles);
      }
    }
    else {
      drupal_set_message(t('Search is temporarily unavailable. If the problem persists, please contact the site administrator.'), 'error');
    }
  }

  // Clear the content type cache, since we will be changing its data.
  content_clear_type_cache();
  cache_clear_all('apachesolr:environments', 'cache_apachesolr');
  drupal_set_message(t('Your settings have been saved.'));
}

/**
 * Callback for node/%node/devel/apachesolr
 */
function apachesolr_devel($node) {
  $item = new stdClass();
  $item->entity_type = 'node';
  $item->entity_id = $node->nid;
  $item->bundle = '';
  module_load_include('inc', 'apachesolr', 'apachesolr.index');
  $output = '';
  foreach (apachesolr_load_all_environments() as $env_id => $environment) {
    $documents = apachesolr_index_entity_to_documents($item, $env_id);
    $output .= '<h1>' . t('Environment %name (%env_id)', array(
      '%name' => $environment['name'],
      '%env_id' => $env_id,
    )) . '</h1>';
    foreach ($documents as $document) {
      $debug_data = array();
      foreach ($document as $key => $value) {
        $debug_data[$key] = $value;
      }
      $output .= kdevel_print_object($debug_data);
    }
  }
  return $output;
}

Functions

Namesort descending Description
apachesolr_check_facetapi Check to see if the facetapi module is installed, and if not put up a message.
apachesolr_config_file Page callback to show one conf file.
apachesolr_config_files_overview Page callback to show available conf files.
apachesolr_devel Callback for node/%node/devel/apachesolr
apachesolr_environment_clone_form
apachesolr_environment_clone_form_submit
apachesolr_environment_clone_submit
apachesolr_environment_delete_form Form to delete a search environment
apachesolr_environment_delete_form_submit
apachesolr_environment_edit_delete_submit
apachesolr_environment_edit_form Form builder for adding/editing a Solr environment used as a menu callback.
apachesolr_environment_edit_submit
apachesolr_environment_edit_test_submit
apachesolr_environment_edit_validate
apachesolr_environment_settings_page
apachesolr_index_action_form Form builder for the Apachesolr Indexer actions form.
apachesolr_index_action_form_cron_submit Submit handler for the deletion form.
apachesolr_index_action_form_delete_confirm Form builder for the index delete/clear form.
apachesolr_index_action_form_delete_confirm_submit Submit handler for the deletion form.
apachesolr_index_action_form_delete_submit Submit handler for the Indexer actions form, delete button.
apachesolr_index_action_form_remaining_confirm Form builder for to reindex the remaining.
apachesolr_index_action_form_remaining_confirm_submit Submit handler for the deletion form.
apachesolr_index_action_form_remaining_submit Submit handler for the Indexer actions form, delete button.
apachesolr_index_action_form_reset_confirm Form builder for the index re-enqueue form.
apachesolr_index_action_form_reset_confirm_submit Submit handler for the deletion form.
apachesolr_index_action_form_reset_submit Submit handler for the Indexer actions form, delete button.
apachesolr_index_batch_index_entities Batch Operation Callback
apachesolr_index_batch_index_finished Batch 'finished' callback
apachesolr_index_batch_index_remaining Submit a batch job to index the remaining, unindexed content.
apachesolr_index_config_form Form builder for the bundle configuration form.
apachesolr_index_config_form_submit Submit handler for the bundle configuration form.
apachesolr_index_report
apachesolr_settings Form builder for general settings used as a menu callback.
apachesolr_status_page Gets information about the fields already in solr index.