View source
<?php
define('SEARCH_API_DEFAULT_CRON_LIMIT', 50);
function search_api_menu() {
$pre = 'admin/config/search/search_api';
$items[$pre] = array(
'title' => 'Search API',
'description' => 'Create and configure search engines.',
'page callback' => 'search_api_admin_overview',
'access arguments' => array(
'administer search_api',
),
'file' => 'search_api.admin.inc',
);
$items[$pre . '/overview'] = array(
'title' => 'Overview',
'type' => MENU_DEFAULT_LOCAL_TASK,
'weight' => -10,
);
$items[$pre . '/add_server'] = array(
'title' => 'Add server',
'description' => 'Create a new search server.',
'page callback' => 'drupal_get_form',
'page arguments' => array(
'search_api_admin_add_server',
),
'access arguments' => array(
'administer search_api',
),
'file' => 'search_api.admin.inc',
'weight' => -1,
'type' => MENU_LOCAL_ACTION,
);
$items[$pre . '/add_index'] = array(
'title' => 'Add index',
'description' => 'Create a new search index.',
'page callback' => 'drupal_get_form',
'page arguments' => array(
'search_api_admin_add_index',
),
'access arguments' => array(
'administer search_api',
),
'file' => 'search_api.admin.inc',
'type' => MENU_LOCAL_ACTION,
);
$items[$pre . '/server/%search_api_server'] = array(
'title' => 'View server',
'title callback' => 'search_api_admin_item_title',
'title arguments' => array(
5,
),
'description' => 'View server details.',
'page callback' => 'search_api_admin_server_view',
'page arguments' => array(
5,
),
'access arguments' => array(
'administer search_api',
),
'file' => 'search_api.admin.inc',
);
$items[$pre . '/server/%search_api_server/view'] = array(
'title' => 'View',
'type' => MENU_DEFAULT_LOCAL_TASK,
'weight' => -10,
);
$items[$pre . '/server/%search_api_server/edit'] = array(
'title' => 'Edit',
'description' => 'Edit server details.',
'page callback' => 'drupal_get_form',
'page arguments' => array(
'search_api_admin_server_edit',
5,
),
'access arguments' => array(
'administer search_api',
),
'file' => 'search_api.admin.inc',
'weight' => -1,
'type' => MENU_LOCAL_TASK,
'context' => MENU_CONTEXT_INLINE | MENU_CONTEXT_PAGE,
);
$items[$pre . '/server/%search_api_server/execute-tasks'] = array(
'title' => 'Execute pending tasks',
'description' => 'Attempt to process pending tasks for a given server.',
'page callback' => 'search_api_execute_pending_tasks',
'page arguments' => array(
5,
),
'access callback' => 'search_api_access_execute_tasks_batch',
'access arguments' => array(
5,
),
'type' => MENU_CALLBACK,
);
$items[$pre . '/server/%search_api_server/disable'] = array(
'title' => 'Disable',
'description' => 'Disable index.',
'page callback' => 'search_api_admin_server_view',
'page arguments' => array(
5,
6,
),
'access callback' => 'search_api_access_disable_page',
'access arguments' => array(
5,
),
'file' => 'search_api.admin.inc',
'type' => MENU_LOCAL_TASK,
'context' => MENU_CONTEXT_INLINE,
'weight' => 8,
);
$items[$pre . '/server/%search_api_server/delete'] = array(
'title' => 'Delete',
'title callback' => 'search_api_title_delete_page',
'title arguments' => array(
5,
),
'description' => 'Delete server.',
'page callback' => 'drupal_get_form',
'page arguments' => array(
'search_api_admin_confirm',
'server',
'delete',
5,
),
'access callback' => 'search_api_access_delete_page',
'access arguments' => array(
5,
),
'file' => 'search_api.admin.inc',
'type' => MENU_LOCAL_TASK,
'context' => MENU_CONTEXT_INLINE,
'weight' => 10,
);
$items[$pre . '/execute-tasks'] = array(
'title' => 'Execute pending tasks',
'description' => 'Attempt to process pending server tasks.',
'page callback' => 'search_api_execute_pending_tasks',
'access callback' => 'search_api_access_execute_tasks_batch',
'type' => MENU_LOCAL_ACTION,
);
$items[$pre . '/index/%search_api_index'] = array(
'title' => 'View index',
'title callback' => 'search_api_admin_item_title',
'title arguments' => array(
5,
),
'description' => 'View index details.',
'page callback' => 'search_api_admin_index_view',
'page arguments' => array(
5,
),
'access arguments' => array(
'administer search_api',
),
'file' => 'search_api.admin.inc',
);
$items[$pre . '/index/%search_api_index/view'] = array(
'title' => 'View',
'type' => MENU_DEFAULT_LOCAL_TASK,
'weight' => -10,
);
$items[$pre . '/index/%search_api_index/edit'] = array(
'title' => 'Edit',
'description' => 'Edit index settings.',
'page callback' => 'drupal_get_form',
'page arguments' => array(
'search_api_admin_index_edit',
5,
),
'access arguments' => array(
'administer search_api',
),
'file' => 'search_api.admin.inc',
'type' => MENU_LOCAL_TASK,
'context' => MENU_CONTEXT_INLINE | MENU_CONTEXT_PAGE,
'weight' => -6,
);
$items[$pre . '/index/%search_api_index/fields'] = array(
'title' => 'Fields',
'description' => 'Select indexed fields.',
'page callback' => 'drupal_get_form',
'page arguments' => array(
'search_api_admin_index_fields',
5,
),
'access arguments' => array(
'administer search_api',
),
'file' => 'search_api.admin.inc',
'type' => MENU_LOCAL_TASK,
'context' => MENU_CONTEXT_INLINE | MENU_CONTEXT_PAGE,
'weight' => -4,
);
$items[$pre . '/index/%search_api_index/workflow'] = array(
'title' => 'Filters',
'description' => 'Edit indexing workflow.',
'page callback' => 'drupal_get_form',
'page arguments' => array(
'search_api_admin_index_workflow',
5,
),
'access arguments' => array(
'administer search_api',
),
'file' => 'search_api.admin.inc',
'type' => MENU_LOCAL_TASK,
'context' => MENU_CONTEXT_INLINE | MENU_CONTEXT_PAGE,
'weight' => -2,
);
$items[$pre . '/index/%search_api_index/disable'] = array(
'title' => 'Disable',
'description' => 'Disable index.',
'page callback' => 'search_api_admin_index_view',
'page arguments' => array(
5,
6,
),
'access callback' => 'search_api_access_disable_page',
'access arguments' => array(
5,
),
'file' => 'search_api.admin.inc',
'type' => MENU_LOCAL_TASK,
'context' => MENU_CONTEXT_INLINE,
'weight' => 8,
);
$items[$pre . '/index/%search_api_index/delete'] = array(
'title' => 'Delete',
'title callback' => 'search_api_title_delete_page',
'title arguments' => array(
5,
),
'description' => 'Delete index.',
'page callback' => 'drupal_get_form',
'page arguments' => array(
'search_api_admin_confirm',
'index',
'delete',
5,
),
'access callback' => 'search_api_access_delete_page',
'access arguments' => array(
5,
),
'file' => 'search_api.admin.inc',
'type' => MENU_LOCAL_TASK,
'context' => MENU_CONTEXT_INLINE,
'weight' => 10,
);
return $items;
}
function search_api_help($path) {
switch ($path) {
case 'admin/help#search_api':
$classes = array();
foreach (search_api_get_service_info() as $id => $info) {
$id = drupal_clean_css_identifier($id);
$name = check_plain($info['name']);
$description = isset($info['description']) ? $info['description'] : '';
$classes[] = "<h2 id=\"{$id}\">{$name}</h2>\n{$description}";
}
$output = '';
if ($classes) {
$output .= '<p>' . t('The following service classes are available for creating a search server.') . "</p>\n";
$output .= implode("\n\n", $classes);
}
return $output;
case 'admin/config/search/search_api':
return '<p>' . t('A search server and search index are used to execute searches. Several indexes can exist per server.<br />You need at least one server and one index to create searches on your site.') . '</p>';
}
}
function search_api_hook_info() {
$hook_info = array(
'group' => 'search_api',
);
return array(
'search_api_service_info' => $hook_info,
'search_api_service_info_alter' => $hook_info,
'search_api_item_type_info' => $hook_info,
'search_api_item_type_info_alter' => $hook_info,
'search_api_data_type_info' => $hook_info,
'search_api_data_type_info_alter' => $hook_info,
'search_api_alter_callback_info' => $hook_info,
'search_api_alter_callback_info_alter' => $hook_info,
'search_api_processor_info' => $hook_info,
'search_api_processor_info_alter' => $hook_info,
'search_api_index_items_alter' => $hook_info,
'search_api_items_indexed' => $hook_info,
'search_api_query_alter' => $hook_info,
'search_api_results_alter' => $hook_info,
'search_api_server_load' => $hook_info,
'search_api_server_insert' => $hook_info,
'search_api_server_update' => $hook_info,
'search_api_server_delete' => $hook_info,
'default_search_api_server' => $hook_info,
'default_search_api_server_alter' => $hook_info,
'search_api_index_load' => $hook_info,
'search_api_index_insert' => $hook_info,
'search_api_index_update' => $hook_info,
'search_api_index_reindex' => $hook_info,
'search_api_index_delete' => $hook_info,
'default_search_api_index' => $hook_info,
'default_search_api_index_alter' => $hook_info,
);
}
function search_api_theme() {
$themes['search_api_dropbutton'] = array(
'variables' => array(
'links' => array(),
),
'file' => 'search_api.admin.inc',
);
$themes['search_api_server'] = array(
'variables' => array(
'id' => NULL,
'name' => '',
'machine_name' => '',
'description' => NULL,
'enabled' => NULL,
'class_id' => NULL,
'class_name' => NULL,
'class_description' => NULL,
'indexes' => array(),
'options' => array(),
'status' => ENTITY_CUSTOM,
'extra' => array(),
),
'file' => 'search_api.admin.inc',
);
$themes['search_api_index'] = array(
'variables' => array(
'id' => NULL,
'name' => '',
'machine_name' => '',
'description' => NULL,
'item_type' => NULL,
'datasource_config' => NULL,
'enabled' => NULL,
'server' => NULL,
'options' => array(),
'fields' => array(),
'indexed_items' => 0,
'on_server' => NULL,
'total_items' => 0,
'status' => ENTITY_CUSTOM,
'read_only' => 0,
),
'file' => 'search_api.admin.inc',
);
$themes['search_api_admin_item_order'] = array(
'render element' => 'element',
'file' => 'search_api.admin.inc',
);
$themes['search_api_admin_fields_table'] = array(
'render element' => 'element',
'file' => 'search_api.admin.inc',
);
return $themes;
}
function search_api_permission() {
return array(
'administer search_api' => array(
'title' => t('Administer Search API'),
'description' => t('Create and configure Search API servers and indexes.'),
),
);
}
function search_api_cron() {
search_api_server_tasks_check();
$conditions = array(
'enabled' => TRUE,
'read_only' => 0,
);
$indexes = search_api_index_load_multiple(FALSE, $conditions);
if (!$indexes) {
return;
}
$ignored_servers = array();
$end = time() + variable_get('search_api_index_worker_callback_runtime', 15);
$first_pass = TRUE;
while (TRUE) {
if (!$indexes) {
break;
}
foreach ($indexes as $id => $index) {
if (!$first_pass && time() >= $end) {
break 2;
}
if (!empty($ignored_servers[$index->server])) {
continue;
}
$limit = isset($index->options['cron_limit']) ? $index->options['cron_limit'] : SEARCH_API_DEFAULT_CRON_LIMIT;
$num = 0;
if ($limit) {
try {
$num = search_api_index_items($index, $limit);
if ($num) {
$variables = array(
'@num' => $num,
'%name' => $index->name,
);
watchdog('search_api', 'Indexed @num items for index %name.', $variables, WATCHDOG_INFO);
}
} catch (SearchApiException $e) {
$ignored_servers[$index->server] = TRUE;
$vars['%index'] = $index->name;
watchdog_exception('search_api', $e, '%type while trying to index items on %index: !message in %function (line %line of %file).', $vars);
}
}
if (!$num) {
unset($indexes[$id]);
}
}
$first_pass = FALSE;
}
}
function search_api_entity_info() {
$info['search_api_server'] = array(
'label' => t('Search server'),
'controller class' => 'EntityAPIControllerExportable',
'metadata controller class' => FALSE,
'entity class' => 'SearchApiServer',
'base table' => 'search_api_server',
'uri callback' => 'search_api_server_url',
'access callback' => 'search_api_entity_access',
'module' => 'search_api',
'exportable' => TRUE,
'entity keys' => array(
'id' => 'id',
'label' => 'name',
'name' => 'machine_name',
),
);
$info['search_api_index'] = array(
'label' => t('Search index'),
'controller class' => 'EntityAPIControllerExportable',
'metadata controller class' => FALSE,
'entity class' => 'SearchApiIndex',
'base table' => 'search_api_index',
'uri callback' => 'search_api_index_url',
'access callback' => 'search_api_entity_access',
'module' => 'search_api',
'exportable' => TRUE,
'entity keys' => array(
'id' => 'id',
'label' => 'name',
'name' => 'machine_name',
),
);
return $info;
}
function search_api_entity_property_info() {
$info['search_api_server']['properties'] = array(
'id' => array(
'label' => t('ID'),
'type' => 'integer',
'description' => t('The primary identifier for a server.'),
'schema field' => 'id',
'validation callback' => 'entity_metadata_validate_integer_positive',
),
'name' => array(
'label' => t('Name'),
'type' => 'text',
'description' => t('The displayed name for a server.'),
'schema field' => 'name',
'required' => TRUE,
),
'machine_name' => array(
'label' => t('Machine name'),
'type' => 'token',
'description' => t('The internally used machine name for a server.'),
'schema field' => 'machine_name',
'required' => TRUE,
),
'description' => array(
'label' => t('Description'),
'type' => 'text',
'description' => t('The displayed description for a server.'),
'schema field' => 'description',
'sanitize' => 'filter_xss',
),
'class' => array(
'label' => t('Service class'),
'type' => 'text',
'description' => t('The ID of the service class to use for this server.'),
'schema field' => 'class',
'required' => TRUE,
),
'enabled' => array(
'label' => t('Enabled'),
'type' => 'boolean',
'description' => t('A flag indicating whether the server is enabled.'),
'schema field' => 'enabled',
),
'status' => array(
'label' => t('Status'),
'type' => 'integer',
'description' => t('Search API server status property'),
'schema field' => 'status',
'options list' => 'search_api_status_options_list',
),
'module' => array(
'label' => t('Module'),
'type' => 'text',
'description' => t('The name of the module from which this server originates.'),
'schema field' => 'module',
),
);
$info['search_api_index']['properties'] = array(
'id' => array(
'label' => t('ID'),
'type' => 'integer',
'description' => t('An integer identifying the index.'),
'schema field' => 'id',
'validation callback' => 'entity_metadata_validate_integer_positive',
),
'name' => array(
'label' => t('Name'),
'type' => 'text',
'description' => t('A name to be displayed for the index.'),
'schema field' => 'name',
'required' => TRUE,
),
'machine_name' => array(
'label' => t('Machine name'),
'type' => 'token',
'description' => t('The internally used machine name for an index.'),
'schema field' => 'machine_name',
'required' => TRUE,
),
'description' => array(
'label' => t('Description'),
'type' => 'text',
'description' => t("A string describing the index' use to users."),
'schema field' => 'description',
'sanitize' => 'filter_xss',
),
'server' => array(
'label' => t('Server ID'),
'type' => 'token',
'description' => t('The machine name of the search_api_server with which data should be indexed.'),
'schema field' => 'server',
),
'server_entity' => array(
'label' => t('Server'),
'type' => 'search_api_server',
'description' => t('The search_api_server with which data should be indexed.'),
'getter callback' => 'search_api_index_get_server',
),
'item_type' => array(
'label' => t('Item type'),
'type' => 'token',
'description' => t('The type of items stored in this index.'),
'schema field' => 'item_type',
'required' => TRUE,
),
'enabled' => array(
'label' => t('Enabled'),
'type' => 'boolean',
'description' => t('A flag indicating whether the index is enabled.'),
'schema field' => 'enabled',
),
'read_only' => array(
'label' => t('Read only'),
'type' => 'boolean',
'description' => t('A flag indicating whether the index is read-only.'),
'schema field' => 'read_only',
),
'status' => array(
'label' => t('Status'),
'type' => 'integer',
'description' => t('Search API index status property'),
'schema field' => 'status',
'options list' => 'search_api_status_options_list',
),
'module' => array(
'label' => t('Module'),
'type' => 'text',
'description' => t('The name of the module from which this index originates.'),
'schema field' => 'module',
),
);
return $info;
}
function search_api_search_api_server_insert(SearchApiServer $server) {
$reverts =& drupal_static('search_api_search_api_server_delete', array());
if (isset($reverts[$server->machine_name])) {
$server->original = $reverts[$server->machine_name];
unset($reverts[$server->machine_name]);
search_api_search_api_server_update($server);
unset($server->original);
return;
}
$server
->postCreate();
}
function search_api_search_api_server_update(SearchApiServer $server) {
if ($server
->postUpdate()) {
foreach (search_api_index_load_multiple(FALSE, array(
'server' => $server->machine_name,
)) as $index) {
$index
->reindex();
}
}
if (!empty($server->original) && $server->enabled != $server->original->enabled) {
if ($server->enabled) {
search_api_server_tasks_check($server);
}
else {
foreach (search_api_index_load_multiple(FALSE, array(
'server' => $server->machine_name,
)) as $index) {
$index
->update(array(
'enabled' => 0,
'server' => NULL,
));
}
}
}
}
function search_api_search_api_server_delete(SearchApiServer $server) {
if ($server
->hasStatus(ENTITY_IN_CODE)) {
$reverts =& drupal_static(__FUNCTION__, array());
$reverts[$server->machine_name] = $server;
return;
}
$server
->preDelete();
foreach (search_api_index_load_multiple(FALSE, array(
'server' => $server->machine_name,
)) as $index) {
$index
->update(array(
'server' => NULL,
'enabled' => FALSE,
));
}
search_api_server_tasks_delete(NULL, $server);
}
function search_api_search_api_index_insert(SearchApiIndex $index) {
$reverts =& drupal_static('search_api_search_api_index_delete', array());
if (isset($reverts[$index->machine_name])) {
$index->original = $reverts[$index->machine_name];
unset($reverts[$index->machine_name]);
search_api_search_api_index_update($index);
unset($index->original);
return;
}
$index
->postCreate();
}
function search_api_search_api_index_update(SearchApiIndex $index) {
search_api_index_update_datasource($index, 'search_api_item');
search_api_index_update_datasource($index, 'search_api_item_string_id');
if ($index->server != $index->original->server) {
if ($index->original->server) {
$old_server = search_api_server_load($index->original->server);
if ($old_server) {
$old_server
->removeIndex($index);
}
}
if ($index->server) {
try {
$new_server = $index
->server(TRUE);
$new_server
->addIndex($index);
} catch (SearchApiException $e) {
watchdog_exception('search_api', $e);
$index->server = NULL;
$index
->save();
}
}
_search_api_index_reindex($index);
}
$old_fields = $index->original->options + array(
'fields' => array(),
);
$old_fields = $old_fields['fields'];
$new_fields = $index->options + array(
'fields' => array(),
);
$new_fields = $new_fields['fields'];
if ($old_fields != $new_fields) {
if ($index->server) {
$index
->server()
->fieldsUpdated($index);
}
}
if (!$index->read_only && $index->enabled != $index->original->enabled) {
if ($index->enabled) {
$index
->queueItems();
}
else {
$index
->dequeueItems();
}
}
elseif ($index->read_only != $index->original->read_only) {
if ($index->read_only) {
$index
->dequeueItems();
}
else {
$index
->queueItems();
}
}
}
function search_api_search_api_index_delete(SearchApiIndex $index) {
if ($index
->hasStatus(ENTITY_IN_CODE)) {
$reverts =& drupal_static(__FUNCTION__, array());
$reverts[$index->machine_name] = $index;
return;
}
cache_clear_all($index
->getCacheId(''), 'cache', TRUE);
$index
->postDelete();
}
function search_api_features_export_alter(&$export) {
if (isset($export['features']['search_api_server'])) {
$hook = 'search_api_service_info';
$classes = array();
foreach (module_implements('search_api_service_info') as $module) {
$function = $module . '_' . $hook;
$engines = $function();
foreach ($engines as $service => $specs) {
$classes[$service] = $module;
}
}
foreach ($export['features']['search_api_server'] as $server_name) {
$server = search_api_server_load($server_name);
$module = $classes[$server->class];
if (!isset($export['dependencies'][$module])) {
$export['dependencies'][$module] = $module;
}
}
ksort($export['dependencies']);
}
}
function search_api_system_info_alter(&$info, $file, $type) {
if ($type != 'module' || $file->name == 'search_api' || !module_exists($file->name)) {
return;
}
if (module_hook($file->name, 'search_api_item_type_info')) {
$types = array();
foreach (search_api_get_item_type_info() as $type => $type_info) {
if ($type_info['module'] == $file->name) {
$types[] = $type;
}
}
if ($types) {
$sql = 'SELECT machine_name, name FROM {search_api_index} WHERE item_type IN (:types)';
$indexes = db_query($sql, array(
':types' => $types,
))
->fetchAllKeyed();
if ($indexes) {
$info['required'] = TRUE;
$links = array();
foreach ($indexes as $id => $name) {
$url = url("admin/config/search/search_api/index/{$id}");
$links[] = '<a href="' . check_plain($url) . '">' . check_plain($name) . '</a>';
}
$args = array(
'!indexes' => implode(', ', $links),
);
$info['explanation'] = format_plural(count($indexes), 'Item type in use by the following index: !indexes.', 'Item type(s) in use by the following indexes: !indexes.', $args);
}
}
}
if (module_hook($file->name, 'search_api_service_info')) {
$classes = array();
foreach (search_api_get_service_info() as $class => $class_info) {
if ($class_info['module'] == $file->name) {
$classes[] = $class;
}
}
if ($classes) {
$sql = 'SELECT machine_name, name FROM {search_api_server} WHERE class IN (:classes)';
$servers = db_query($sql, array(
':classes' => $classes,
))
->fetchAllKeyed();
if ($servers) {
$info['required'] = TRUE;
$links = array();
foreach ($servers as $id => $name) {
$url = url("admin/config/search/search_api/server/{$id}");
$links[] = '<a href="' . check_plain($url) . '">' . check_plain($name) . '</a>';
}
$args = array(
'!servers' => implode(', ', $links),
);
$explanation = format_plural(count($servers), 'Service class in use by the following server: !servers.', 'Service class(es) in use by the following servers: !servers.', $args);
$info['explanation'] = (!empty($info['explanation']) ? $info['explanation'] . ' ' : '') . $explanation;
}
}
}
}
function search_api_entity_insert($entity, $type) {
if ($type == 'search_api_index' || !entity_get_property_info($type)) {
return;
}
list($id) = entity_extract_ids($type, $entity);
if (isset($id)) {
search_api_track_item_insert($type, array(
$id,
));
$combined_id = $type . '/' . $id;
search_api_track_item_insert('multiple', array(
$combined_id,
));
}
}
function search_api_entity_update($entity, $type) {
if (!entity_get_property_info($type)) {
return;
}
list($id, , $new_bundle) = entity_extract_ids($type, $entity);
if (!empty($entity->original)) {
list(, , $old_bundle) = entity_extract_ids($type, $entity->original);
if ($new_bundle != $old_bundle) {
_search_api_entity_datasource_bundle_change($type, $id, $old_bundle, $new_bundle);
}
}
if (isset($id)) {
search_api_track_item_change($type, array(
$id,
));
$combined_id = $type . '/' . $id;
search_api_track_item_change('multiple', array(
$combined_id,
));
}
}
function search_api_entity_delete($entity, $type) {
if (!entity_get_property_info($type)) {
return;
}
list($id) = entity_extract_ids($type, $entity);
if (isset($id)) {
search_api_track_item_delete($type, array(
$id,
));
$combined_id = $type . '/' . $id;
search_api_track_item_delete('multiple', array(
$combined_id,
));
}
}
function search_api_node_access_records_alter(&$grants, $node) {
$conditions = array(
'enabled' => 1,
'read_only' => 0,
);
$indexes = search_api_index_load_multiple(FALSE, $conditions);
foreach ($indexes as $index) {
$item_ids = array();
if (!empty($index->options['data_alter_callbacks']['search_api_alter_node_access']['status'])) {
$item_id = $index
->datasource()
->getItemId($node);
if ($item_id !== NULL) {
$item_ids = array(
$item_id,
);
}
}
elseif (!empty($index->options['data_alter_callbacks']['search_api_alter_comment_access']['status'])) {
if (!isset($comments)) {
$comments = comment_load_multiple(FALSE, array(
'nid' => $node->nid,
));
}
foreach ($comments as $comment) {
$item_ids[] = $index
->datasource()
->getItemId($comment);
}
}
if ($item_ids) {
search_api_track_item_change_for_indexes($index->item_type, $item_ids, array(
$index->machine_name => $index,
));
}
}
}
function search_api_field_attach_rename_bundle($entity_type, $bundle_old, $bundle_new) {
foreach (search_api_index_load_multiple(FALSE, array(
'item_type' => $entity_type,
)) as $index) {
$bundles =& $index->options['datasource']['bundles'];
if (isset($bundles) && ($pos = array_search($bundle_old, $bundles)) !== FALSE) {
$bundles[$pos] = $bundle_new;
$index
->save();
$index
->resetCaches();
drupal_static_reset('search_api_get_datasource_controller');
}
}
}
function search_api_field_update_field($field, $prior_field) {
$before = $prior_field['cardinality'];
$after = $field['cardinality'];
if ($before != $after && ($before == 1 || $after == 1)) {
drupal_register_shutdown_function('search_api_index_recalculate_fields');
}
}
function search_api_flush_caches() {
search_api_index_recalculate_fields();
}
function search_api_search_api_item_type_info() {
$types = array();
foreach (search_api_entity_type_options_list() as $type => $label) {
$types[$type] = array(
'name' => $label,
'datasource controller' => 'SearchApiEntityDataSourceController',
'entity_type' => $type,
);
}
$types['multiple'] = array(
'name' => t('Multiple types'),
'datasource controller' => 'SearchApiCombinedEntityDataSourceController',
);
return $types;
}
function search_api_module_implements_alter(array &$implementations, $hook) {
switch ($hook) {
case 'modules_enabled':
$group = $implementations['search_api'];
unset($implementations['search_api']);
$implementations = array(
'search_api' => $group,
) + $implementations;
break;
case 'modules_disabled':
$group = $implementations['search_api'];
unset($implementations['search_api']);
$implementations['search_api'] = $group;
break;
}
}
function search_api_modules_enabled() {
drupal_static_reset('search_api_get_item_type_info');
drupal_static_reset('search_api_get_service_info');
}
function search_api_modules_disabled() {
drupal_static_reset('search_api_get_item_type_info');
drupal_static_reset('search_api_get_service_info');
}
function search_api_search_api_alter_callback_info() {
$callbacks['search_api_alter_bundle_filter'] = array(
'name' => t('Bundle filter'),
'description' => t('Exclude items from indexing based on their bundle (content type, vocabulary, …).'),
'class' => 'SearchApiAlterBundleFilter',
'weight' => -10,
);
$callbacks['search_api_alter_role_filter'] = array(
'name' => t('Role filter'),
'description' => t('Exclude users from indexing based on their role.'),
'class' => 'SearchApiAlterRoleFilter',
'weight' => -10,
);
$callbacks['search_api_alter_add_url'] = array(
'name' => t('URL field'),
'description' => t("Adds the item's URL to the indexed data."),
'class' => 'SearchApiAlterAddUrl',
);
$callbacks['search_api_alter_add_aggregation'] = array(
'name' => t('Aggregated fields'),
'description' => t('Gives you the ability to define additional fields, containing data from one or more other fields.'),
'class' => 'SearchApiAlterAddAggregation',
);
$callbacks['search_api_alter_add_viewed_entity'] = array(
'name' => t('Complete entity view'),
'description' => t('Adds an additional field containing the whole HTML content of the entity when viewed.'),
'class' => 'SearchApiAlterAddViewedEntity',
);
$callbacks['search_api_alter_add_hierarchy'] = array(
'name' => t('Index hierarchy'),
'description' => t('Allows to index hierarchical fields along with all their ancestors.'),
'class' => 'SearchApiAlterAddHierarchy',
);
$callbacks['search_api_alter_file_entity_public'] = array(
'name' => t('Exclude private files'),
'description' => t('Exclude file entities in the private files folder from being indexed. <strong>Caution:</strong> This only affects the indexed file entities themselves. If an indexed entity has references to file entities in the private folder, those will still be indexed (or displayed) normally.'),
'class' => 'SearchApiAlterFileEntityPublic',
);
$callbacks['search_api_alter_language_control'] = array(
'name' => t('Language control'),
'description' => t('Lets you determine the language of items in the index.'),
'class' => 'SearchApiAlterLanguageControl',
);
$callbacks['search_api_alter_node_access'] = array(
'name' => t('Node access'),
'description' => t('Add node access information to the index. <strong>Caution:</strong> This only affects the indexed nodes themselves, not any node reference fields that are indexed with them, or displayed in search results.'),
'class' => 'SearchApiAlterNodeAccess',
);
$callbacks['search_api_alter_comment_access'] = array(
'name' => t('Access check'),
'description' => t('Add node access information to the index. <strong>Caution:</strong> This only affects the indexed nodes themselves, not any node reference fields that are indexed with them, or displayed in search results.'),
'class' => 'SearchApiAlterCommentAccess',
);
$callbacks['search_api_alter_node_status'] = array(
'name' => t('Exclude unpublished nodes'),
'description' => t('Exclude unpublished nodes from the index. <strong>Caution:</strong> This only affects the indexed nodes themselves. If an enabled node has references to disabled nodes, those will still be indexed (or displayed) normally.'),
'class' => 'SearchApiAlterNodeStatus',
);
$callbacks['search_api_alter_user_content'] = array(
'name' => t('Add user content'),
'description' => t('Allows indexing of nodes (and their fields) created by the indexed user. (Caution: This might lead to performance problems, or even errors during indexing, on larger sites.)'),
'class' => 'SearchApiAlterAddUserContent',
);
$callbacks['search_api_alter_user_status'] = array(
'name' => t('Exclude blocked users'),
'description' => t('Exclude blocked users from the index. <strong>Caution:</strong> This only affects the indexed users themselves. If an active user account includes a reference to a disabled user, that reference will still be indexed (or displayed) normally.'),
'class' => 'SearchApiAlterUserStatus',
);
return $callbacks;
}
function search_api_search_api_processor_info() {
$processors['search_api_case_ignore'] = array(
'name' => t('Ignore case'),
'description' => t('This processor will make searches case-insensitive for fulltext or string fields.'),
'class' => 'SearchApiIgnoreCase',
);
$processors['search_api_html_filter'] = array(
'name' => t('HTML filter'),
'description' => t('Strips HTML tags from fulltext fields and decodes HTML entities. ' . 'Use this processor when indexing HTML data, e.g., node bodies for certain text formats.<br />' . 'The processor also allows to boost (or ignore) the contents of specific elements.'),
'class' => 'SearchApiHtmlFilter',
'weight' => 10,
);
if (module_exists('transliteration')) {
$processors['search_api_transliteration'] = array(
'name' => t('Transliteration'),
'description' => t('This processor will make searches insensitive to accents and other non-ASCII characters.'),
'class' => 'SearchApiTransliteration',
'weight' => 15,
);
}
$processors['search_api_tokenizer'] = array(
'name' => t('Tokenizer'),
'description' => t('Tokenizes fulltext data by stripping whitespace. ' . 'This processor allows you to specify which characters make up words and which characters should be ignored, using regular expression syntax. ' . 'Otherwise it is up to the search server implementation to decide how to split indexed fulltext data.'),
'class' => 'SearchApiTokenizer',
'weight' => 20,
);
$processors['search_api_stopwords'] = array(
'name' => t('Stopwords'),
'description' => t('This processor prevents certain words from being indexed and removes them from search terms. ' . 'For best results, it should only be executed after tokenizing.'),
'class' => 'SearchApiStopWords',
'weight' => 30,
);
$processors['search_api_porter_stemmer'] = array(
'name' => t('Stem words'),
'description' => t('This processor reduces words to a stem (e.g., "talking" to "talk"). For best results, it should only be executed after tokenizing.'),
'class' => 'SearchApiPorterStemmer',
'weight' => 35,
);
$processors['search_api_highlighting'] = array(
'name' => t('Highlighting'),
'description' => t('Adds highlighting for search results.'),
'class' => 'SearchApiHighlight',
'weight' => 40,
);
return $processors;
}
function search_api_track_item_insert($type, array $item_ids) {
$conditions = array(
'enabled' => 1,
'item_type' => $type,
'read_only' => 0,
);
$indexes = search_api_index_load_multiple(FALSE, $conditions);
if (!$indexes) {
return;
}
try {
$returned_indexes = search_api_get_datasource_controller($type)
->trackItemInsert($item_ids, $indexes);
if (isset($returned_indexes)) {
$indexes = $returned_indexes;
}
} catch (SearchApiException $e) {
$vars['%item_type'] = $type;
watchdog_exception('search_api', $e, '%type while inserting items of type %item_type: !message in %function (line %line of %file).', $vars);
return;
}
foreach ($indexes as $index) {
if (!empty($index->options['index_directly'])) {
search_api_index_specific_items_delayed($index, $item_ids);
}
}
}
function search_api_track_item_change($type, array $item_ids) {
$conditions = array(
'enabled' => 1,
'item_type' => $type,
'read_only' => 0,
);
$indexes = search_api_index_load_multiple(FALSE, $conditions);
if (!$indexes) {
return;
}
search_api_track_item_change_for_indexes($type, $item_ids, $indexes);
}
function search_api_track_item_change_for_indexes($type, array $item_ids, $indexes) {
try {
$returned_indexes = search_api_get_datasource_controller($type)
->trackItemChange($item_ids, $indexes);
if (isset($returned_indexes)) {
$indexes = $returned_indexes;
}
foreach ($indexes as $index) {
if (!empty($index->options['index_directly'])) {
try {
search_api_index_specific_items_delayed($index, $item_ids);
} catch (SearchApiException $e) {
watchdog_exception('search_api', $e);
}
}
}
} catch (SearchApiException $e) {
$vars['%item_type'] = $type;
watchdog_exception('search_api', $e, '%type while updating items of type %item_type: !message in %function (line %line of %file).', $vars);
}
}
function search_api_track_item_queued(SearchApiIndex $index, array $item_ids) {
try {
$index
->datasource()
->trackItemQueued($item_ids, $index);
} catch (SearchApiException $e) {
watchdog_exception('search_api', $e);
}
}
function search_api_track_item_indexed(SearchApiIndex $index, array $item_ids) {
$index
->datasource()
->trackItemIndexed($item_ids, $index);
module_invoke_all('search_api_items_indexed', $index, $item_ids);
}
function search_api_track_item_delete($type, array $item_ids) {
$conditions = array(
'enabled' => 1,
'item_type' => $type,
'read_only' => 0,
);
$indexes = search_api_index_load_multiple(FALSE, $conditions);
if ($indexes) {
try {
$changed_indexes = search_api_get_datasource_controller($type)
->trackItemDelete($item_ids, $indexes);
if (isset($changed_indexes)) {
$indexes = $changed_indexes;
}
} catch (SearchApiException $e) {
$vars['%item_type'] = $type;
watchdog_exception('search_api', $e, '%type while deleting items of type %item_type: !message in %function (line %line of %file).', $vars);
}
}
$conditions['enabled'] = 0;
$indexes = array_merge($indexes, search_api_index_load_multiple(FALSE, $conditions));
foreach ($indexes as $index) {
try {
if ($server = $index
->server()) {
$server
->deleteItems($item_ids, $index);
}
} catch (Exception $e) {
$vars['%item_type'] = $type;
watchdog_exception('search_api', $e, '%type while deleting items of type %item_type: !message in %function (line %line of %file).', $vars);
}
}
}
function search_api_server_tasks_check(SearchApiServer $server = NULL) {
$select = db_select('search_api_task', 't')
->fields('t')
->condition('t.type', array(
'addIndex',
'fieldsUpdated',
'removeIndex',
'deleteItems',
));
if ($server) {
$select
->condition('t.server_id', $server->machine_name);
}
else {
$select
->innerJoin('search_api_server', 's', 't.server_id = s.machine_name AND s.enabled = 1');
$select
->orderBy('t.server_id');
}
$count_query = $select
->countQuery();
$select
->orderBy('t.id');
$select
->range(0, 100);
$tasks = $select
->execute();
$executed_tasks = array();
foreach ($tasks as $task) {
if (!$server || $server->machine_name != $task->server_id) {
$server = search_api_server_load($task->server_id);
if (!$server) {
continue;
}
}
switch ($task->type) {
case 'addIndex':
$index = search_api_index_load($task->index_id);
if ($index) {
$server
->addIndex($index);
}
break;
case 'fieldsUpdated':
$index = search_api_index_load($task->index_id);
if ($index) {
if ($task->data) {
$index->original = unserialize($task->data);
}
$server
->fieldsUpdated($index);
}
break;
case 'removeIndex':
$index = search_api_index_load($task->index_id);
if ($index) {
$server
->removeIndex($index ? $index : $task->index_id);
}
break;
case 'deleteItems':
$ids = $task->data ? unserialize($task->data) : 'all';
$index = $task->index_id ? search_api_index_load($task->index_id) : NULL;
$index = $index ? $index : NULL;
$server
->deleteItems($ids, $index);
break;
default:
continue 2;
}
$executed_tasks[] = $task->id;
}
if (!$executed_tasks) {
return TRUE;
}
search_api_server_tasks_delete($executed_tasks);
return $count_query
->execute()
->fetchField() === 0;
}
function search_api_execute_pending_tasks(SearchApiServer $server = NULL) {
batch_set(array(
'title' => t('Processing pending tasks'),
'operations' => array(
array(
'search_api_execute_pending_tasks_batch',
array(
$server,
),
),
),
'finished' => 'search_api_execute_pending_tasks_finished',
));
if ($server) {
$path = 'admin/config/search/search_api/server/' . $server->machine_name;
}
else {
$path = 'admin/config/search/search_api';
}
if (function_exists('drush_backend_batch_process')) {
drush_backend_batch_process();
}
else {
batch_process($path);
}
}
function search_api_execute_pending_tasks_batch(SearchApiServer $server = NULL, &$context) {
if (!isset($context['results']['total'])) {
$context['results']['total'] = search_api_server_tasks_count($server);
}
$total = $context['results']['total'];
search_api_server_tasks_check($server);
$remaining = search_api_server_tasks_count($server);
$executed = max($total - $remaining, 0);
$args['@remaining'] = $remaining;
$context['message'] = format_plural($executed, 'Successfully executed @count task, @remaining remaining.', 'Successfully executed @count tasks, @remaining remaining.', $args);
$context['finished'] = $executed / $total;
}
function search_api_execute_pending_tasks_finished($success, $results, $operations) {
if ($success) {
drupal_get_messages('warning');
drupal_set_message(format_plural($results['total'], 'Successfully executed @count task.', 'Successfully executed @count tasks.'));
}
}
function search_api_server_tasks_count(SearchApiServer $server = NULL) {
$query = db_select('search_api_task', 't')
->fields('t');
if ($server) {
$query
->condition('server_id', $server->machine_name);
}
else {
$query
->join('search_api_server', 's', 's.machine_name = t.server_id');
$query
->condition('s.enabled', 1);
}
return $query
->countQuery()
->execute()
->fetchField();
}
function search_api_access_execute_tasks_batch(SearchApiServer $server = NULL) {
return user_access('administer search_api') && search_api_server_tasks_count($server) && (!$server || $server->enabled);
}
function search_api_server_tasks_add(SearchApiServer $server, $type, $index = NULL, $data = NULL) {
db_insert('search_api_task')
->fields(array(
'server_id' => $server->machine_name,
'type' => $type,
'index_id' => $index ? is_object($index) ? $index->machine_name : $index : NULL,
'data' => isset($data) ? serialize($data) : NULL,
))
->execute();
}
function search_api_server_tasks_delete(array $ids = NULL, SearchApiServer $server = NULL, $index = NULL) {
$delete = db_delete('search_api_task');
if ($ids) {
$delete
->condition('id', $ids);
}
if ($server) {
$delete
->condition('server_id', $server->machine_name);
}
if ($index) {
$delete
->condition('index_id', $index->machine_name);
}
$delete
->execute();
}
function search_api_index_recalculate_fields($indexes = FALSE) {
if (!is_array($indexes)) {
$indexes = search_api_index_load_multiple(FALSE);
}
$stored_keys = drupal_map_assoc(array(
'type',
'entity_type',
'real_type',
'boost',
));
foreach ($indexes as $index) {
if (empty($index->options['fields'])) {
continue;
}
cache_clear_all($index
->getCacheId() . '-1-0', 'cache');
$index
->resetCaches();
$fields = $index
->getFields();
foreach ($fields as $key => $field) {
$fields[$key] = array_intersect_key($field, $stored_keys);
if (isset($fields[$key]['boost']) && $fields[$key]['boost'] == '1.0') {
unset($fields[$key]['boost']);
}
}
if ($fields != $index->options['fields']) {
$options = $index->options;
$options['fields'] = $fields;
$index
->update(array(
'options' => $options,
));
}
}
}
function _search_api_settings_equals($setting1, $setting2) {
if (!is_array($setting1) || !is_array($setting2)) {
return $setting1 == $setting2;
}
foreach ($setting1 as $key => $value) {
if (!array_key_exists($key, $setting2)) {
return FALSE;
}
if (!_search_api_settings_equals($value, $setting2[$key])) {
return FALSE;
}
unset($setting2[$key]);
}
return !$setting2;
}
function search_api_index_items(SearchApiIndex $index, $limit = -1) {
if ($index->read_only) {
return 0;
}
$ids = search_api_get_items_to_index($index, $limit);
return $ids ? count(search_api_index_specific_items($index, $ids)) : 0;
}
function search_api_index_specific_items(SearchApiIndex $index, array $ids) {
if (!search_api_server_tasks_check($index
->server())) {
throw new SearchApiException(t('Could not index items since important pending server tasks could not be performed.'));
}
$items = $index
->loadItems($ids);
$cloned_items = array();
foreach ($items as $id => $item) {
if (is_object($item)) {
$cloned_items[$id] = clone $item;
}
else {
$type = search_api_get_item_type_info($index->item_type);
$type = $type ? $type['name'] : $index->item_type;
watchdog('search_api', "Error during indexing: invalid item loaded for @type with ID @id.", array(
'@id' => $id,
'@type' => $type,
), WATCHDOG_WARNING);
}
}
$indexed = $items ? $index
->index($cloned_items) : array();
if ($indexed) {
search_api_track_item_indexed($index, $indexed);
if (count($indexed) < count($ids)) {
$diff = array_keys(array_diff_key(array_flip($ids), array_flip($indexed)));
$index
->datasource()
->trackItemIndexed($diff, $index);
$index
->datasource()
->trackItemChange($diff, array(
$index,
));
}
}
return $indexed;
}
function &search_api_index_specific_items_delayed(SearchApiIndex $index = NULL, array $ids = array()) {
static $queue = array();
static $registered = FALSE;
if (empty($registered)) {
drupal_register_shutdown_function('_search_api_index_queued_items');
$registered = TRUE;
}
if ($index && $ids) {
$index_id = $index->machine_name;
$queue += array(
$index_id => array(),
);
$queue[$index_id] += drupal_map_assoc($ids);
}
return $queue;
}
function search_api_get_items_to_index(SearchApiIndex $index, $limit = -1) {
if ($limit == 0) {
return array();
}
return $index
->datasource()
->getChangedItems($index, $limit);
}
function search_api_query($id, array $options = array()) {
$index = search_api_index_load($id);
if (!$index) {
throw new SearchApiException(t('Unknown index with ID @id.', array(
'@id' => $id,
)));
}
return $index
->query($options);
}
function search_api_current_search($search_id = NULL, SearchApiQuery $query = NULL, array $results = array()) {
$searches =& drupal_static(__FUNCTION__, array());
if (isset($query)) {
if (!isset($search_id)) {
$search_id = $query
->getOption('search id');
}
$base = $search_id;
$i = 0;
while (isset($searches[$search_id])) {
$search_id = $base . '-' . ++$i;
}
$searches[$search_id] = array(
$query,
$results,
);
}
if (isset($search_id)) {
return isset($searches[$search_id]) ? $searches[$search_id] : NULL;
}
return $searches;
}
function search_api_field_types() {
$types = search_api_default_field_types();
foreach (search_api_get_data_type_info() as $id => $type) {
$types[$id] = $type['name'];
}
return $types;
}
function search_api_default_field_types() {
return array(
'text' => t('Fulltext'),
'string' => t('String'),
'integer' => t('Integer'),
'decimal' => t('Decimal'),
'date' => t('Date'),
'duration' => t('Duration'),
'boolean' => t('Boolean'),
'uri' => t('URI'),
);
}
function search_api_get_data_type_info($type = NULL) {
$types =& drupal_static(__FUNCTION__);
if (!isset($types)) {
$default_types = search_api_default_field_types();
$types = module_invoke_all('search_api_data_type_info');
$types = $types ? $types : array();
foreach ($types as &$type_info) {
if (!isset($type_info['fallback']) || !isset($default_types[$type_info['fallback']])) {
$type_info['fallback'] = 'string';
}
}
drupal_alter('search_api_data_type_info', $types);
}
if (isset($type)) {
return isset($types[$type]) ? $types[$type] : NULL;
}
return $types;
}
function search_api_get_service_info($id = NULL) {
$services =& drupal_static(__FUNCTION__);
if (!isset($services)) {
$services = array();
foreach (module_implements('search_api_service_info') as $module) {
$function = $module . '_search_api_service_info';
if (function_exists($function)) {
$new_services = $function();
if (isset($new_services) && is_array($new_services)) {
foreach ($new_services as $service => $info) {
$new_services[$service] += array(
'module' => $module,
);
}
}
$services += $new_services;
}
}
foreach (module_implements('search_api_service_info_alter') as $module) {
$function = $module . '_search_api_service_info_alter';
if (function_exists($function)) {
$old = $services;
$function($services);
if ($new_services = array_diff_key($services, $old)) {
foreach ($new_services as $service => $info) {
$services[$service] += array(
'module' => $module,
);
}
}
}
}
}
if (isset($id)) {
return isset($services[$id]) ? $services[$id] : NULL;
}
return $services;
}
function search_api_get_item_type_info($type = NULL) {
$types =& drupal_static(__FUNCTION__);
if (!isset($types)) {
$types = array();
foreach (module_implements('search_api_item_type_info') as $module) {
$function = $module . '_search_api_item_type_info';
if (function_exists($function)) {
$new_types = $function();
if (isset($new_types) && is_array($new_types)) {
foreach ($new_types as $id => $info) {
$new_types[$id] += array(
'module' => $module,
);
}
}
$types += $new_types;
}
}
foreach (module_implements('search_api_item_type_info_alter') as $module) {
$function = $module . '_search_api_item_type_info_alter';
if (function_exists($function)) {
$old = $types;
$function($types);
if ($new_types = array_diff_key($types, $old)) {
foreach ($new_types as $id => $info) {
$types[$id] += array(
'module' => $module,
);
}
}
}
}
}
if (isset($type)) {
return isset($types[$type]) ? $types[$type] : NULL;
}
return $types;
}
function search_api_get_datasource_controller($type) {
$datasources =& drupal_static(__FUNCTION__, array());
if (empty($datasources[$type])) {
$info = search_api_get_item_type_info($type);
if (isset($info['datasource controller']) && class_exists($info['datasource controller'])) {
$datasources[$type] = new $info['datasource controller']($type);
}
if (empty($datasources[$type]) || !$datasources[$type] instanceof SearchApiDataSourceControllerInterface) {
unset($datasources[$type]);
throw new SearchApiException(t('Unknown or invalid item type @type.', array(
'@type' => $type,
)));
}
}
return $datasources[$type];
}
function search_api_get_alter_callbacks() {
$callbacks =& drupal_static(__FUNCTION__);
if (!isset($callbacks)) {
$callbacks = module_invoke_all('search_api_alter_callback_info');
foreach ($callbacks as $id => $callback) {
$callbacks[$id] += array(
'weight' => 0,
);
}
drupal_alter('search_api_alter_callback_info', $callbacks);
}
return $callbacks;
}
function search_api_get_processors() {
$processors =& drupal_static(__FUNCTION__);
if (!isset($processors)) {
$processors = module_invoke_all('search_api_processor_info');
foreach ($processors as $id => $processor) {
$processors[$id] += array(
'weight' => 0,
);
}
drupal_alter('search_api_processor_info', $processors);
}
return $processors;
}
function search_api_search_api_query_alter(SearchApiQueryInterface $query) {
global $user;
$index = $query
->getIndex();
$type = $index
->getEntityType();
if (!empty($index->options['data_alter_callbacks']["search_api_alter_{$type}_access"]['status']) && !$query
->getOption('search_api_bypass_access')) {
$account = $query
->getOption('search_api_access_account', $user);
if (is_numeric($account)) {
$account = user_load($account);
}
if (is_object($account)) {
try {
_search_api_query_add_node_access($account, $query, $type);
} catch (SearchApiException $e) {
watchdog_exception('search_api', $e);
}
}
else {
$account = $query
->getOption('search_api_access_account', '(' . t('none') . ')');
if (is_object($account)) {
$account = $account->uid;
}
if (!is_scalar($account)) {
$account = var_export($account, TRUE);
}
watchdog('search_api', 'An illegal user UID was given for node access: @uid.', array(
'@uid' => $account,
), WATCHDOG_WARNING);
}
}
}
function _search_api_query_add_node_access($account, SearchApiQueryInterface $query, $type = 'node') {
if (user_access('bypass node access', $account)) {
return;
}
$is_comment = $type == 'comment';
$fields = $query
->getIndex()->options['fields'];
$required = array(
'search_api_access_node',
'status',
);
if (!$is_comment) {
$required[] = 'author';
}
foreach ($required as $field) {
if (empty($fields[$field])) {
$vars['@field'] = $field;
$vars['@index'] = $query
->getIndex()->name;
throw new SearchApiException(t('Required field @field not indexed on index @index. Could not perform access checks.', $vars));
}
}
if (!user_access('access content', $account) || $is_comment && !user_access('access comments', $account)) {
$query
->condition('status', 0);
$query
->condition('status', 1);
watchdog('search_api', 'User @name tried to execute a search, but cannot access content.', array(
'@name' => theme('username', array(
'account' => $account,
)),
), WATCHDOG_NOTICE);
return;
}
$published = $is_comment ? COMMENT_PUBLISHED : NODE_PUBLISHED;
if (!$is_comment && user_access('view own unpublished content')) {
$filter = $query
->createFilter('OR');
$filter
->condition('status', $published);
$filter
->condition('author', $account->uid);
$query
->filter($filter);
}
else {
$query
->condition('status', $published);
}
$filter = $query
->createFilter('OR');
$grants = node_access_grants('view', $account);
foreach ($grants as $realm => $gids) {
foreach ($gids as $gid) {
$filter
->condition('search_api_access_node', "node_access_{$realm}:{$gid}");
}
}
$filter
->condition('search_api_access_node', 'node_access__all');
$query
->filter($filter);
}
function search_api_is_text_type($type, array $allowed = array(
'text',
)) {
return array_search(search_api_extract_inner_type($type), $allowed) !== FALSE;
}
function search_api_is_list_type($type) {
return substr($type, 0, 5) == 'list<';
}
function search_api_list_nesting_level($type) {
$level = 0;
while (search_api_is_list_type($type)) {
$type = substr($type, 5, -1);
++$level;
}
return $level;
}
function search_api_nest_type($type, $nested_type) {
while (search_api_is_list_type($nested_type)) {
$nested_type = substr($nested_type, 5, -1);
$type = "list<{$type}>";
}
return $type;
}
function search_api_extract_inner_type($type) {
while (search_api_is_list_type($type)) {
$type = substr($type, 5, -1);
}
return $type;
}
function search_api_index_update_datasource(SearchApiIndex $index, $table, $column = 'index_id') {
if ($index->id != $index->original->id) {
db_update($table)
->fields(array(
$column => $index->id,
))
->condition($column, $index->original->id)
->execute();
}
}
function search_api_extract_fields(EntityMetadataWrapper $wrapper, array $fields, array $value_options = array()) {
$value_options += array(
'identifier' => TRUE,
);
$wrapper_info = $wrapper
->info();
if (search_api_is_list_type($wrapper_info['type'])) {
foreach ($fields as &$info) {
$info['value'] = array();
$info['original_type'] = $info['type'];
}
unset($info);
try {
foreach ($wrapper as $w) {
$nested_fields = search_api_extract_fields($w, $fields, $value_options);
foreach ($nested_fields as $field => $info) {
if (isset($info['value'])) {
$fields[$field]['value'][] = $info['value'];
}
if (isset($info['original_type'])) {
$fields[$field]['original_type'] = $info['original_type'];
}
}
}
} catch (EntityMetadataWrapperException $e) {
}
return $fields;
}
$nested = array();
$entity_infos = entity_get_info();
foreach ($fields as $field => &$info) {
$pos = strpos($field, ':');
if ($pos === FALSE) {
$info['value'] = NULL;
$info['original_type'] = $info['type'];
if (isset($wrapper->{$field})) {
try {
$property_info = $wrapper->{$field}
->info();
$info['original_type'] = $property_info['type'];
$info['value'] = $wrapper->{$field}
->value($value_options);
$is_entity = isset($entity_infos[search_api_extract_inner_type($property_info['type'])]);
if ($is_entity && $info['value'] === FALSE) {
$info['value'] = NULL;
}
if (search_api_is_text_type($info['type']) && isset($info['value'])) {
if ($wrapper->{$field}
->optionsList('view')) {
_search_api_add_option_values($info['value'], $wrapper->{$field}
->optionsList('view'));
}
elseif ($is_entity) {
$info['value'] = _search_api_extract_entity_value($wrapper->{$field}, TRUE);
}
}
} catch (EntityMetadataWrapperException $e) {
}
}
}
else {
list($prefix, $key) = explode(':', $field, 2);
$nested[$prefix][$key] = $info;
}
}
unset($info);
foreach ($nested as $prefix => $nested_fields) {
if (isset($wrapper->{$prefix})) {
$nested_fields = search_api_extract_fields($wrapper->{$prefix}, $nested_fields, $value_options);
foreach ($nested_fields as $field => $info) {
$fields["{$prefix}:{$field}"] = $info;
}
}
else {
foreach ($nested_fields as &$info) {
$info['value'] = NULL;
$info['original_type'] = $info['type'];
}
}
}
return $fields;
}
function _search_api_add_option_values(&$value, array $options) {
if (is_array($value)) {
foreach ($value as &$v) {
_search_api_add_option_values($v, $options);
}
return;
}
if (is_scalar($value) && isset($options[$value])) {
$value .= ' ' . $options[$value];
}
}
function _search_api_extract_entity_value(EntityMetadataWrapper $wrapper, $fulltext = FALSE) {
$v = $wrapper
->value();
if (is_array($v)) {
$ret = array();
foreach ($wrapper as $item) {
$values = _search_api_extract_entity_value($item, $fulltext);
if ($values) {
$ret[] = $values;
}
}
return $ret;
}
if ($v) {
$ret = $wrapper
->getIdentifier();
if ($fulltext && ($label = $wrapper
->label())) {
$ret .= ' ' . $label;
}
return $ret;
}
return NULL;
}
function search_api_server_load($id, $reset = FALSE) {
$ret = search_api_server_load_multiple(array(
$id,
), array(), $reset);
return $ret ? reset($ret) : FALSE;
}
function search_api_server_load_multiple($ids = array(), $conditions = array(), $reset = FALSE) {
$servers = entity_load('search_api_server', $ids, $conditions, $reset);
return entity_key_array_by_property($servers, 'machine_name');
}
function search_api_server_url(SearchApiServer $server) {
return array(
'path' => 'admin/config/search/search_api/server/' . $server->machine_name,
'options' => array(),
);
}
function search_api_admin_item_title($object) {
return $object->name;
}
function search_api_title_delete_page(Entity $entity) {
return $entity
->hasStatus(ENTITY_OVERRIDDEN) ? t('Revert') : t('Delete');
}
function search_api_access_disable_page(Entity $entity) {
return user_access('administer search_api') && !empty($entity->enabled);
}
function search_api_access_delete_page(Entity $entity) {
return user_access('administer search_api') && $entity
->hasStatus(ENTITY_CUSTOM);
}
function search_api_entity_access() {
return user_access('administer search_api');
}
function search_api_server_insert(array $values) {
$server = entity_create('search_api_server', $values);
$server->is_new = TRUE;
$server
->save();
return $server->id;
}
function search_api_server_edit($id, array $fields) {
$server = search_api_server_load($id, TRUE);
$ret = $server
->update($fields);
return $ret ? 1 : $ret;
}
function search_api_server_enable($id) {
$server = search_api_server_load($id, TRUE);
$ret = $server
->update(array(
'enabled' => 1,
));
return $ret ? 1 : $ret;
}
function search_api_server_disable($id) {
$server = search_api_server_load($id, TRUE);
$ret = $server
->update(array(
'enabled' => 0,
));
return $ret ? 1 : $ret;
}
function search_api_server_clear($id) {
$server = search_api_server_load($id);
$success = TRUE;
foreach (search_api_index_load_multiple(FALSE, array(
'server' => $server->machine_name,
)) as $index) {
$success &= $index
->reindex();
}
if ($success) {
$server
->deleteItems();
}
return $success;
}
function search_api_server_delete($id) {
$server = search_api_server_load($id, TRUE);
$server
->delete();
return 1;
}
function search_api_index_load($id, $reset = FALSE) {
$ret = search_api_index_load_multiple(array(
$id,
), array(), $reset);
return reset($ret);
}
function search_api_index_load_multiple($ids = array(), $conditions = array(), $reset = FALSE) {
new SearchApiIndex();
$indexes = entity_load('search_api_index', $ids, $conditions, $reset);
return entity_key_array_by_property($indexes, 'machine_name');
}
function search_api_index_status(SearchApiIndex $index) {
return $index
->datasource()
->getIndexStatus($index);
}
function search_api_index_url(SearchApiIndex $index) {
return array(
'path' => 'admin/config/search/search_api/index/' . $index->machine_name,
'options' => array(),
);
}
function search_api_index_get_server(SearchApiIndex $index) {
try {
return $index
->server();
} catch (SearchApiException $e) {
watchdog_exception('search_api', $e);
return NULL;
}
}
function search_api_status_options_list() {
return array(
ENTITY_CUSTOM => t('Custom'),
ENTITY_IN_CODE => t('Default'),
ENTITY_OVERRIDDEN => t('Overridden'),
ENTITY_FIXED => t('Fixed'),
);
}
function search_api_index_insert(array $values) {
$index = entity_create('search_api_index', $values);
$index->is_new = TRUE;
$index
->save();
return $index->id;
}
function search_api_index_edit($id, array $fields) {
$index = search_api_index_load($id, TRUE);
$ret = $index
->update($fields);
return $ret ? 1 : $ret;
}
function search_api_index_edit_fields($id, array $fields) {
$index = search_api_index_load($id, TRUE);
$options = $index->options;
$options['fields'] = $fields;
$ret = $index
->update(array(
'options' => $options,
));
return $ret ? 1 : $ret;
}
function search_api_index_enable($id) {
$index = search_api_index_load($id, TRUE);
$ret = $index
->update(array(
'enabled' => 1,
));
return $ret ? 1 : $ret;
}
function search_api_index_disable($id) {
$index = search_api_index_load($id, TRUE);
$ret = $index
->update(array(
'enabled' => 0,
));
return $ret ? 1 : $ret;
}
function search_api_index_reindex($id) {
$index = search_api_index_load($id);
return $index
->reindex();
}
function _search_api_index_reindex(SearchApiIndex $index) {
$index
->datasource()
->trackItemChange(FALSE, array(
$index,
), TRUE);
}
function search_api_index_clear($id) {
$index = search_api_index_load($id);
return $index
->clear();
}
function search_api_index_delete($id) {
$index = search_api_index_load($id);
if (!$index) {
return FALSE;
}
$index
->delete();
return TRUE;
}
function search_api_get_sanitized_field_values(array $values) {
foreach ($values as $field_id => $field_value) {
if (is_array($field_value) && isset($field_value['#sanitize_callback']) && ($field_value['#sanitize_callback'] === FALSE || is_callable($field_value['#sanitize_callback'])) && array_key_exists('#value', $field_value)) {
$sanitize_callback = $field_value['#sanitize_callback'];
$field_value = $field_value['#value'];
}
else {
$sanitize_callback = 'check_plain';
}
if ($sanitize_callback !== FALSE) {
$field_value = search_api_sanitize_field_value($field_value, $sanitize_callback);
}
$values[$field_id] = $field_value;
}
return $values;
}
function search_api_sanitize_field_value($field_value, $sanitize_callback = 'check_plain') {
if ($field_value === NULL) {
return $field_value;
}
if (is_scalar($field_value)) {
return call_user_func($sanitize_callback, $field_value);
}
foreach ($field_value as &$nested_value) {
$nested_value = search_api_sanitize_field_value($nested_value, $sanitize_callback);
}
return $field_value;
}
function search_api_index_options_list() {
$ret = array(
NULL => '- ' . t('All') . ' -',
);
foreach (search_api_index_load_multiple(FALSE) as $id => $index) {
$ret[$id] = $index->name;
}
return $ret;
}
function search_api_entity_type_options_list() {
$types = array();
foreach (array_keys(entity_get_property_info()) as $type) {
$info = entity_get_info($type);
if ($info) {
$types[$type] = $info['label'];
}
}
return $types;
}
function search_api_combined_bundle_options_list() {
$types = array();
foreach (array_keys(entity_get_property_info()) as $type) {
$info = entity_get_info($type);
if (!empty($info['bundles'])) {
foreach ($info['bundles'] as $bundle => $bundle_info) {
$types["{$type}:{$bundle}"] = $bundle_info['label'];
}
}
}
return $types;
}
function search_api_get_multi_type_item_label($item) {
$label = entity_label($item->item_type, $item->{$item->item_type});
return $label ? $label : NULL;
}
function _search_api_index_queued_items() {
$queue =& search_api_index_specific_items_delayed();
try {
if ($queue) {
$indexes = search_api_index_load_multiple(array_keys($queue));
foreach ($indexes as $index_id => $index) {
if ($index->enabled && !$index->read_only) {
search_api_index_specific_items($index, $queue[$index_id]);
}
}
}
$queue = array();
} catch (SearchApiException $e) {
watchdog_exception('search_api', $e);
}
}
function _search_api_wrapper_add_all_properties(EntityMetadataWrapper $wrapper, array $property_info) {
if ($properties = entity_get_all_property_info($wrapper
->type())) {
$property_info['properties'] = $properties;
}
return $property_info;
}
function _search_api_convert_custom_type($callback, $value, $original_type, $type, $nesting_level) {
if ($nesting_level == 0) {
return call_user_func($callback, $value, $original_type, $type);
}
if (!is_array($value)) {
return NULL;
}
--$nesting_level;
$values = array();
foreach ($value as $v) {
$v = _search_api_convert_custom_type($callback, $v, $original_type, $type, $nesting_level);
if (isset($v) && !(is_array($v) && !$v)) {
$values[] = $v;
}
}
return $values;
}
function _search_api_get_items_on_server(SearchApiIndex $index) {
if (!$index->enabled) {
return 0;
}
$query = $index
->query()
->fields(array())
->range(0, 0);
$response = $index
->server()
->search($query);
return $response['result count'];
}
function _search_api_deep_copy(array $array) {
$copy = array();
foreach ($array as $k => $v) {
if (is_array($v)) {
$copy[$k] = _search_api_deep_copy($v);
}
elseif (is_object($v)) {
$copy[$k] = clone $v;
}
elseif ($v) {
$copy[$k] = $v;
}
}
return $copy;
}
function _search_api_entity_datasource_bundle_change($type, $id, $old_bundle, $new_bundle) {
$controller = search_api_get_datasource_controller($type);
$conditions = array(
'enabled' => 1,
'item_type' => $type,
'read_only' => 0,
);
foreach (search_api_index_load_multiple(FALSE, $conditions) as $index) {
if (!empty($index->options['datasource']['bundles'])) {
$bundles = drupal_map_assoc($index->options['datasource']['bundles']);
if (empty($bundles[$new_bundle]) != empty($bundles[$old_bundle])) {
if (empty($bundles[$new_bundle])) {
$controller
->trackItemDelete(array(
$id,
), array(
$index,
));
}
else {
$controller
->trackItemInsert(array(
$id,
), array(
$index,
));
}
}
}
}
}
function _search_api_batch_indexing_create(SearchApiIndex $index, $batch_size, $limit, $remaining, $drush = FALSE) {
if ($limit !== 0 && $batch_size !== 0) {
$t = !empty($drush) ? 'dt' : 't';
if ($limit < 0 || $limit > $remaining) {
$limit = $remaining;
}
if ($batch_size < 0) {
$batch_size = $remaining;
}
$batch = array(
'title' => $t('Indexing items'),
'operations' => array(
array(
'_search_api_batch_indexing_callback',
array(
$index,
$batch_size,
$limit,
$drush,
),
),
),
'progress_message' => $t('Completed about @percentage% of the indexing operation.'),
'finished' => '_search_api_batch_indexing_finished',
'file' => drupal_get_path('module', 'search_api') . '/search_api.module',
);
batch_set($batch);
return TRUE;
}
return FALSE;
}
function _search_api_batch_indexing_callback(SearchApiIndex $index, $batch_size, $limit, $drush, &$context) {
if (!isset($context['sandbox']['limit'])) {
$context['sandbox']['limit'] = $limit;
$context['sandbox']['batch_size'] = $batch_size;
$context['sandbox']['progress'] = 0;
}
if (!isset($context['results']['indexed'])) {
$context['results']['indexed'] = 0;
$context['results']['not indexed'] = 0;
$context['results']['drush'] = $drush;
}
$to_index = min($context['sandbox']['limit'] - $context['sandbox']['progress'], $context['sandbox']['batch_size']);
try {
$indexed = search_api_index_items($index, $to_index);
$context['results']['indexed'] += $indexed;
} catch (SearchApiException $e) {
watchdog_exception('search_api', $e);
$vars['@message'] = $e
->getMessage();
$context['message'] = t('An error occurred during indexing: @message.', $vars);
$context['finished'] = 1;
$context['results']['not indexed'] += $context['sandbox']['limit'] - $context['sandbox']['progress'];
return;
}
if ($indexed > 0) {
$format_plural = $context['results']['drush'] === TRUE ? '_search_api_drush_format_plural' : 'format_plural';
$context['message'] = $format_plural($context['results']['indexed'], 'Successfully indexed 1 item.', 'Successfully indexed @count items.');
}
if ($indexed !== $to_index) {
$context['results']['not indexed'] += $to_index - $indexed;
}
$context['sandbox']['progress'] += $to_index;
if ($indexed === 0 || $context['sandbox']['progress'] >= $context['sandbox']['limit']) {
$context['finished'] = 1;
}
else {
$context['finished'] = $context['sandbox']['progress'] / $context['sandbox']['limit'];
}
}
function _search_api_batch_indexing_finished($success, $results) {
if (!empty($results['drush'])) {
$drupal_set_message = 'drush_log';
$format_plural = '_search_api_drush_format_plural';
$t = 'dt';
$success_message = 'success';
}
else {
$drupal_set_message = 'drupal_set_message';
$format_plural = 'format_plural';
$t = 't';
$success_message = 'status';
}
if ($success) {
if (!empty($results['indexed'])) {
$drupal_set_message($format_plural($results['indexed'], 'Successfully indexed 1 item.', 'Successfully indexed @count items.'), $success_message);
if (!empty($results['not indexed'])) {
$drupal_set_message($format_plural($results['not indexed'], '1 item could not be indexed. Check the logs for details.', '@count items could not be indexed. Check the logs for details.'), 'warning');
}
}
else {
$drupal_set_message($t("Couldn't index items. Check the logs for details."), 'error');
}
}
else {
$drupal_set_message($t("An error occurred while trying to index items. Check the logs for details."), 'error');
}
}