'admin/settings/field_indexer', 'title' => t('Field indexer'), 'access' => user_access('administer search'), 'description' => t('Enable fields for use by field searches.'), 'callback' => 'drupal_get_form', 'callback arguments' => array('field_indexer_admin'), ); } return $items; } /** * Menu callback for configuring the module. */ function field_indexer_admin() { // Find all the fields available for indexing. $fields = field_indexer_find_fields(); if (empty($fields)) { $form['message'] = array( '#type' => 'markup', '#value' => t('No fields available for indexing.'), ); return $form; } // Find which fields were already configured. $known_fields = field_indexer_load_fields(); // Assign fiid and status to fields. foreach ($fields as $key => $field) { if (isset($known_fields[$key])) { // Preserve the known fiid and status. $fields[$key]['fiid'] = $known_fields[$key]['fiid']; $fields[$key]['status'] = $known_fields[$key]['status']; // Remove from array so at the end we have a list of fields that no longer // exist on the system. unset($known_fields[$key]); } } // Build the list of options. $options = array(); $defaults = array(); foreach ($fields as $key => $field) { // TODO: Format field label and help in a nice table instead. $help = check_plain($field['namespace']) . (empty($field['help']) ? '' : ' / '. check_plain($field['help'])); $options[$key] = check_plain($field['label']) .' ('. $help .')'; if ($field['status']) { $defaults[] = $key; } } // Build the settings form. $form['selector'] = array( '#type' => 'checkboxes', '#title' => t('Fields to index'), '#description' => t('Check the fields that need to be indexed.'), '#options' => $options, '#default_value' => $defaults, ); $form['fields'] = array( '#type' => 'value', '#value' => $fields, ); $form['lost_fields'] = array( '#type' => 'value', '#value' => $known_fields, ); $form['submit'] = array( '#type' => 'submit', '#value' => t('Save configuration'), ); return $form; } /** * Submit handler for the settings. */ function field_indexer_admin_submit($form_id, $form_values) { $enabled = FALSE; // Update or insert field selection. foreach ($form_values['fields'] as $key => $field) { $status = $form_values['selector'][$key] ? 1 : 0; if (isset($field['fiid'])) { // Update existing field if its status or label has changed. db_query("UPDATE {field_indexer_map} SET status = %d, label = '%s' WHERE fiid = %d AND (status != %d OR label != '%s')", $status, $field['label'], $field['fiid'], $status, $field['label']); if (db_affected_rows()) { if ($status) { $enabled = TRUE; } else { // Field is disabled, delete its index entries. field_indexer_wipe($field['fiid']); } } } else { // Insert newly discovered field. db_query("INSERT INTO {field_indexer_map} (fiid, namespace, name, extra_name, status, label) VALUES (%d, '%s', '%s', '%s', %d, '%s')", db_next_id('{field_indexer_map}_fiid'), $field['namespace'], $field['name'], $field['extra_name'], $status, $field['label']); if ($status) { $enabled = TRUE; } } } // Delete obsolete fields. foreach ($form_values['lost_fields'] as $field) { // TODO!!! test this! // Delete obsolete field data. db_query('DELETE FROM {field_indexer_map} WHERE fiid = %d', $field['fiid']); // Delete index entries for the field. field_indexer_wipe($field['fiid']); } if ($enabled) { field_indexer_reindex(); // Re-index all nodes. drupal_set_message(t('All nodes have been marked for re-indexing to ensure the indexing of the newly enabled fields. Indexing will start on the next cron run.', array('@cron' => url('admin/logs/status/run-cron')))); } drupal_set_message(t('The configuration options have been saved.')); } /** * Update the full-text search index for a particular field value. * * @param $nid * Id of the node containing the value. * @param $fiid * Id of the field containing the value. * @param $text * The content of the field. Must be a piece of HTML text. */ function field_indexer_index($nid, $fiid, $text) { search_index($nid, field_indexer_type($fiid), $text); } /** * Mark all nodes for re-indexing. */ function field_indexer_reindex() { node_search('reset'); } /** * Delete index entries for the specified field. */ function field_indexer_wipe($fiid) { $type = field_indexer_type($fiid); db_query("DELETE FROM {search_dataset} WHERE type = '%s'", $type); db_query("DELETE FROM {search_index} WHERE type = '%s'", $type); } /** * Retrieve a fresh list of fields that are exposed by modules for indexing. */ function field_indexer_find_fields() { $fields = array(); foreach (module_implements('field_indexer_list') as $module) { $module_fields = module_invoke($module, 'field_indexer_list'); foreach ($module_fields as $field) { // Add the field. $fields[_field_indexer_key($field)] = $field; } } return $fields; } /** * Retrieve the fields settings from the database. * * @param $status * Optional status of the fields to be returned (TRUE for fields that are * enabled for indexing, FALSE for fields that are not being indexed). When * set, only fields with the given status are returned. When not set, all * indexable fields are returned. * @param $namespace * Optional namespace of whose fields we are interested in. When not * specified, all indexable fields are returned. * @return * An array describing the fields. */ function field_indexer_load_fields($status = NULL, $namespace = NULL) { $where = array(); $args = array(); if (isset($status)) { $where[] = 'status = %d'; $args[] = $status ? 1 : 0; } if (isset($namespace)) { $where[] = "namespace = '%s'"; $args[] = $namespace; } if (count($where)) { $where = ' WHERE '. implode(' AND ', $where); } else { $where = ''; } $fields = array(); $results = db_query('SELECT * FROM {field_indexer_map}'. $where, $args); while ($field = db_fetch_array($results)) { $fields[_field_indexer_key($field)] = $field; } return $fields; } /** * Return the index type of the specified field. */ function field_indexer_type($fiid) { return 'field_'. $fiid; } /** * Return a unique key for a field. This is useful when collecting fields whose * fiid have not yet been assigned. */ function _field_indexer_key($field) { return $field['namespace'] .'_'. $field['name'] .'_'. $field['extra_name']; }