finder.module in Finder 7
Same filename and directory in other branches
The finder module.
Finder allows Drupal site administrators to create flexible search forms to find objects such as Nodes or Users based on the values of a field.
File
finder.moduleView source
<?php
// $Id: finder.module,v 1.7.2.58 2011/02/12 06:55:18 danielb Exp $
/**
* @file
* The finder module.
*
* Finder allows Drupal site administrators to create flexible search forms to
* find objects such as Nodes or Users based on the values of a field.
*/
/**
* Implements hook_menu().
*
* @see hook_menu()
*/
function finder_menu() {
$items = array();
$items['finder_ajax'] = array(
'title' => 'Finder Ajax',
'page callback' => 'finder_ajax',
'access arguments' => array(
'use finder',
),
'type' => MENU_CALLBACK,
);
$finders = finder_load_multiple(NULL, array(), TRUE);
if (is_array($finders)) {
foreach ($finders as $finder) {
$items[$finder->path] = array(
'title' => $finder->title,
'page callback' => 'finder_page',
'page arguments' => array(
$finder->finder_id,
),
'access arguments' => array(
'use finder',
),
'type' => MENU_CALLBACK,
'description' => $finder->description,
);
}
}
$admin_item = array(
'file' => 'finder.admin.inc',
'file path' => finder_inc_path(),
);
$items['admin/structure/finder'] = $admin_item + array(
'title' => 'Finder',
'page callback' => 'finder_admin_list',
'access arguments' => array(
'administer finder',
),
'weight' => 0,
'type' => MENU_NORMAL_ITEM,
'description' => 'Finders are configurable forms to allow users to find objects in the system.',
);
$items['admin/structure/finder/list'] = $admin_item + array(
'title' => 'Finder list',
'weight' => 1,
'type' => MENU_DEFAULT_LOCAL_TASK,
);
$items['admin/structure/finder/import'] = $admin_item + array(
'title' => 'Import finder',
'page callback' => 'finder_admin_import',
'access arguments' => array(
'administer finder',
),
'weight' => 2,
'type' => MENU_LOCAL_TASK,
);
$items['admin/structure/finder/%finder'] = $admin_item + array(
'title' => 'Edit finder',
'page callback' => 'drupal_get_form',
'page arguments' => array(
'finder_admin_edit',
3,
),
'access arguments' => array(
'administer finder',
),
'weight' => 3,
'type' => MENU_LOCAL_TASK,
);
$items['admin/structure/finder/%finder/edit'] = $admin_item + array(
'title' => 'Finder settings',
'weight' => 3,
'type' => MENU_DEFAULT_LOCAL_TASK,
);
$items['admin/structure/finder/%finder/delete'] = $admin_item + array(
'title' => 'Delete finder',
'page callback' => 'drupal_get_form',
'page arguments' => array(
'finder_admin_delete',
3,
),
'access arguments' => array(
'administer finder',
),
'weight' => 4,
'type' => MENU_CALLBACK,
);
$items['admin/structure/finder/%finder/export'] = $admin_item + array(
'title' => 'Export finder',
'page callback' => 'finder_admin_export',
'page arguments' => array(
3,
),
'access arguments' => array(
'administer finder',
),
'weight' => 5,
'type' => MENU_LOCAL_TASK,
);
$items['admin/structure/finder/%finder/%finder_arg_optional'] = $admin_item + array(
'title' => 'Finder element settings',
'page callback' => 'drupal_get_form',
'page arguments' => array(
'finder_admin_element_edit',
3,
4,
),
'access arguments' => array(
4,
),
'access callback' => 'finder_menu_element_allowed',
'weight' => 6,
'type' => MENU_LOCAL_TASK,
);
$items['admin/structure/finder/%finder/%finder_arg_optional/delete'] = $admin_item + array(
'title' => 'Delete element',
'page callback' => 'drupal_get_form',
'page arguments' => array(
'finder_admin_element_delete',
3,
4,
),
'access arguments' => array(
'administer finder',
),
'weight' => 7,
'type' => MENU_CALLBACK,
);
$items['admin/structure/finder/custom_matching'] = $admin_item + array(
'title' => 'Finder custom matching',
'page callback' => 'drupal_get_form',
'page arguments' => array(
'finder_admin_custom_matching',
),
'access arguments' => array(
'administer finder',
),
'weight' => 8,
'type' => MENU_LOCAL_TASK,
);
return $items;
}
/**
* A to_arg() function is used to provide a default for the arg in the
* menu wildcard.
*
* @param $arg
* The arg (URL fragment) to be tested.
*/
function finder_arg_optional_to_arg($arg) {
return empty($arg) ? '' : $arg;
}
/**
* Menu item access callback to hide the 'Finder element settings' tab when it
* shouldn't be shown (when the element ID arg isn't present).
*
* @param $arg
* The arg (URL fragment) to be tested.
*/
function finder_menu_element_allowed($arg) {
return !empty($arg) && user_access('administer finder');
}
/**
* Implements hook_permission().
*
* @see hook_permission()
*/
function finder_permission() {
return array(
'administer finder' => array(
'title' => t('Administer finder'),
'description' => t('Create and edit finders.'),
),
'administer finder PHP settings' => array(
'title' => t('Administer finder PHP settings'),
'description' => t('Allow access to PHP settings when administering finders.'),
),
'use finder' => array(
'title' => t('Use finder'),
'description' => t('Use finder forms.'),
),
);
}
/**
* Implements hook_theme().
*
* @see hook_theme()
*/
function finder_theme() {
return array(
'finder_admin_edit_elements_table' => array(
'file' => 'finder.theme.inc',
'path' => finder_inc_path(),
'variables' => array(
'form' => NULL,
),
),
'finder_admin_links' => array(
'file' => 'finder.theme.inc',
'path' => finder_inc_path(),
'variables' => array(
'finder' => NULL,
),
),
'finder_links' => array(
'file' => 'finder.theme.inc',
'path' => finder_inc_path(),
'variables' => array(
'finder' => NULL,
),
),
'finder_page' => array(
'file' => 'finder.theme.inc',
'path' => finder_inc_path(),
'variables' => array(
'finder' => NULL,
),
),
'finder_block' => array(
'file' => 'finder.theme.inc',
'path' => finder_inc_path(),
'variables' => array(
'finder' => NULL,
),
),
'finder_view' => array(
'file' => 'finder.theme.inc',
'path' => finder_inc_path(),
'variables' => array(
'finder' => NULL,
'display' => NULL,
'output_array' => NULL,
),
),
'finder_results' => array(
'file' => 'finder.theme.inc',
'path' => finder_inc_path(),
'variables' => array(
'results' => NULL,
'finder' => NULL,
'keywords' => NULL,
'pager' => NULL,
'params' => NULL,
'form_state' => NULL,
'no_results' => NULL,
),
),
);
}
/**
* Implements hook_forms().
*
* @see hook_forms()
*/
function finder_forms($form_id, $args) {
if (strpos($form_id, 'finder_form_') === 0) {
$forms[$form_id] = array(
'callback' => 'finder_form',
'callback arguments' => $args,
);
return $forms;
}
}
/**
* Invoke hook_finderapi().
*
* @param &$object
* The finder or finder flement.
* @param $op
* The operation, indicates where/when this is being invoked.
* @param $a3, $a4
* Arguments to pass to the hook implementation.
* @return
* The returned value of the invoked hooks.
*/
function finder_invoke_finderapi(&$object, $op, $a3 = NULL, $a4 = NULL) {
$return = array();
foreach (module_implements('finderapi') as $name) {
$function = $name . '_finderapi';
$result = $function($object, $op, $a3, $a4);
if (isset($result) && is_array($result)) {
$return = array_merge($return, $result);
}
elseif (isset($result)) {
$return[] = $result;
}
}
return $return;
}
/**
* Load objects from the database.
*
* This is intended as a reverse 'drupal_write_record' based on the code from
* node_load_multiple() in Drupal 7. It can be used to read records from any
* table assuming they are keyed by a serial field named {table}_id. Also
* supports serialized fields.
*
* @param $load
* The name of the table, and thus the type of object to load.
* @param $ids
* An array of IDs, if selecting by ID.
* @param $conditions
* An array of conditions on the table in the form 'field' => $value.
* @param $reset
* Whether to reset the internal cache for this type of $load object.
* @return
* An array of loaded objects indexed by ID.
*/
function finder_load_objects($load, $ids = NULL, $conditions = array(), $reset = FALSE) {
static $object_cache = array();
if ($reset) {
$object_cache[$load] = array();
}
if (!isset($object_cache[$load])) {
$object_cache[$load] = array();
}
$objects = array();
$id_key = $load . '_id';
// Create a new variable which is either a prepared version of the $ids
// array for later comparison with the finder cache, or FALSE if no $ids were
// passed. The $ids array is reduced as items are loaded from cache, and we
// need to know if it's empty for this reason to avoid querying the database
// when all requested objects are loaded from cache.
$passed_ids = !empty($ids) ? array_flip($ids) : FALSE;
// Load any available objects from the internal cache.
if ($object_cache[$load]) {
if (!empty($ids)) {
$objects += array_intersect_key($object_cache[$load], $passed_ids);
// If any objects were loaded, remove them from the $ids still to load.
$ids = array_keys(array_diff_key($passed_ids, $objects));
}
elseif ($conditions) {
$objects = $object_cache[$load];
}
}
// Exclude any objects loaded from cache if they don't match $conditions.
// This ensures the same behavior whether loading from memory or database.
if (!empty($conditions)) {
foreach ($objects as $object) {
$object_values = (array) $object;
if (array_diff_assoc($conditions, $object_values)) {
unset($objects[$object->{$id_key}]);
}
}
}
// Load objects from the database. This is the case if there are
// any $ids left to load, if $conditions was passed without $ids,
// or if $ids and $conditions were intentionally left blank.
if (!empty($ids) || $conditions && !$passed_ids || $ids === NULL && $conditions === array()) {
$query = array();
// build query
$query = db_select($load)
->fields($load);
if (!empty($ids)) {
$query
->condition($id_key, $ids, 'IN');
}
if (!empty($conditions)) {
foreach ($conditions as $field => $value) {
$query
->condition($field, $value);
}
}
if ($load == 'finder_element') {
$query
->orderBy('weight');
}
$queried_objects = $query
->execute()
->fetchAll();
}
// Pass all objects loaded from the database through the finder type specific
// callbacks and hook_finderapi(), then add them to the internal cache.
if (!empty($queried_objects)) {
foreach ($queried_objects as $q_key => $q_object) {
// unserialize settings
if (isset($q_object->settings)) {
$queried_objects[$q_key]->settings = (array) unserialize($q_object->settings);
}
// add elements if object is a finder.
// this code is here in lieu of a hook_finderapi ($op='finder_load') implementation.
if ($load == 'finder') {
$queried_objects[$q_key]->elements = finder_element_load_multiple(array(), array(
$id_key => $q_object->{$id_key},
));
if (!empty($queried_objects[$q_key]->elements)) {
foreach ($queried_objects[$q_key]->elements as $position => $element) {
$queried_objects[$q_key]->elements_index[$element->finder_element_id] = $position;
}
}
finder_load_base_handler($queried_objects[$q_key]);
finder_load_element_handler($queried_objects[$q_key]);
finder_load_links($queried_objects[$q_key]);
}
// invoke finderapi so modules can make changes
finder_invoke_finderapi($queried_objects[$q_key], $load . '_load');
}
$objects += $queried_objects;
// Add objects to the cache.
foreach ($queried_objects as $queried_object) {
$object_cache[$load][$queried_object->{$id_key}] = $queried_object;
}
}
// Ensure that the returned array is ordered the same as the original $ids
// array if this was passed in and remove any invalid ids.
if ($passed_ids) {
// Remove any invalid ids from the array.
$passed_ids = array_intersect_key($passed_ids, $objects);
foreach ($objects as $object) {
$passed_ids[$object->{$id_key}] = $object;
}
$objects = $passed_ids;
}
return $objects;
}
/**
* Load a finder object from the database.
*
* @param $finder_id
* The finder ID.
* @param $reset
* Whether to reset the internal cache for finder objects.
* @return
* The loaded finder object, or FALSE on failure.
*/
function finder_load($finder_id, $reset = FALSE) {
$finders = finder_load_multiple(array(
$finder_id,
), array(), $reset);
return $finders ? $finders[$finder_id] : FALSE;
}
/**
* Load finder objects from the database.
*
* @param $ids
* An array of finder IDs if selecting by IDs.
* @param $conditions
* An array of conditions on the table in the form 'field' => $value.
* @param $reset
* Whether to reset the internal cache for finder objects.
* @return
* An array of loaded finder objects indexed by ID.
*/
function finder_load_multiple($ids = NULL, $conditions = array(), $reset = FALSE) {
return finder_load_objects('finder', $ids, $conditions, $reset);
}
/**
* Save changes to a finder or add a new finder.
*
* @param &$finder
* The finder object.
*/
function finder_save(&$finder) {
finder_invoke_finderapi($finder, 'finder_presave');
$update = array();
$op = 'finder_insert';
if (!empty($finder->finder_id)) {
$update[] = 'finder_id';
$op = 'finder_update';
}
drupal_write_record('finder', $finder, $update);
finder_invoke_finderapi($finder, $op);
}
/**
* Delete a finder and it's finder elements.
*
* @param $finder_id
* The finder ID.
*/
function finder_delete($finder_id) {
$finder = finder_load($finder_id);
db_delete('finder_element')
->condition('finder_id', $finder_id)
->execute();
db_delete('finder')
->condition('finder_id', $finder_id)
->execute();
finder_invoke_finderapi($finder, 'finder_delete');
watchdog('finder', 'Finder %title deleted.', array(
'%title' => $finder->title,
));
drupal_set_message(t('Finder %title has been deleted.', array(
'%title' => $finder->title,
)));
}
/**
* Load a finder element object from the database.
*
* @param $finder_element_id
* The finder element ID.
* @param $reset
* Whether to reset the internal cache for finder element objects.
* @return
* The loaded finder element object, or FALSE on failure.
*/
function finder_element_load($finder_element_id, $reset = FALSE) {
$finder_elements = finder_element_load_multiple(array(
$finder_element_id,
), array(), $reset);
return $finder_elements ? $finder_elements[$finder_element_id] : FALSE;
}
/**
* Load finder element objects from the database.
*
* @param $ids
* An array of finder element IDs if selecting by IDs.
* @param $conditions
* An array of conditions on the table in the form 'field' => $value.
* @param $reset
* Whether to reset the internal cache for finder element objects.
* @return
* An array of loaded finder element objects indexed by ID.
*/
function finder_element_load_multiple($ids = NULL, $conditions = array(), $reset = FALSE) {
return finder_load_objects('finder_element', $ids, $conditions, $reset);
}
/**
* Save changes to a finder element or add a new finder element.
*
* @param &$finder_element
* The finder element object.
*/
function finder_element_save(&$finder_element) {
finder_invoke_finderapi($finder_element, 'finder_element_presave');
$update = array();
$op = 'finder_element_insert';
if (!empty($finder_element->finder_element_id)) {
$update[] = 'finder_element_id';
$op = 'finder_element_update';
}
drupal_write_record('finder_element', $finder_element, $update);
finder_invoke_finderapi($finder_element, $op);
}
/**
* Delete a finder element.
*
* @param $finder_element_id
* The finder element ID.
*/
function finder_element_delete($finder_element_id) {
$finder_element = finder_element_load($finder_element_id);
db_delete('finder_element')
->condition('finder_element_id', $finder_element_id)
->execute();
finder_invoke_finderapi($finder_element, 'finder_element_delete');
watchdog('finder', 'Finder element %title deleted.', array(
'%title' => $finder_element->title,
));
drupal_set_message(t('Finder element %title has been deleted.', array(
'%title' => $finder_element->title,
)));
}
/**
* Write a finder into the database as a new finder.
*
* @param $old_finder
* The finder object to clone.
* @return
* The new finder object.
*/
function finder_clone($old_finder) {
$finder = clone $old_finder;
finder_invoke_finderapi($finder, 'finder_clone');
unset($finder->finder_id);
finder_save($finder);
foreach ($finder->elements as $key => $finder_element) {
unset($finder_element->finder_element_id);
$finder_element->finder_id = $finder->finder_id;
finder_element_save($finder_element);
$finder->elements[$key] = $finder_element;
}
return $finder;
}
/**
* Menu callback; view a finder page.
*
* @param $finder_id
* The finder ID.
* @return
* Themed output of a finder page.
*/
function finder_page($finder_id) {
$finder = finder_load($finder_id);
finder_invoke_finderapi($finder, 'finder_page');
return theme('finder_page', array(
'finder' => $finder,
));
}
/**
* Generate display of a given finder.
*
* @param $finder
* The finder object to generate the output for.
* @param $display
* The type of display ('page', or 'block').
* @param $ajax
* Whether in the context of an ajax request.
* @return
* Themed output of a finder.
*
* @todo
* Take better advantage of Drupal 7's render array.
*/
function finder_view($finder, $display, $ajax = FALSE) {
finder_inc('form');
finder_invoke_finderapi($finder, 'finder_view', $display);
$output_array = array();
$finder->finder_view_build_id = 'finder-' . $display . '-' . $finder->finder_id . '-wrapper';
$finder->finder_view_build_display = $display;
// Always get the form in order to populate the form_state in case there are results we need to present.
// The form building function will not spend resources building elements if it doesn't need to.
// to do: this non form_on_page get_form may only be needed when hiding url args?
$form = drupal_get_form('finder_form_' . $finder->finder_id, $finder);
if ($finder->settings['advanced']['show_admin_links'] && user_access('administer finder')) {
$output['admin_links'] = theme('finder_admin_links', array(
'finder' => $finder,
));
}
if ($display != 'page' || $display == 'page' && $finder->settings['form_on_page']) {
$output['form'] = drupal_render($form);
}
if ($finder->settings['advanced']['show_links']) {
$output['links'] = theme('finder_links', array(
'finder' => $finder,
));
}
if ($display != 'block' || $display == 'block' && $ajax) {
$output['results'] = finder_results($finder);
}
$rendered = '';
$rendered .= $ajax ? '' : '<div id="' . $finder->finder_view_build_id . '" class="finder-view-wrapper">';
$rendered .= theme('finder_view', array(
'finder' => $finder,
'display' => $display,
'output_array' => $output,
));
$rendered .= $ajax ? '' : '</div>';
return $rendered;
}
/**
* Menu callback; get finder ajax output.
*
* @param $finder_id
* The finder ID.
* @param $display
* The type of display ('page', or 'block).
* @param $path
* Any number of additional params to pass to $_GET['q'].
* @return
* Finder ajax output.
*/
function finder_ajax($finder_id, $display) {
if ($finder_id) {
$finder = finder_load($finder_id);
if ($finder) {
// fix the path for any scripts that might call $_GET['q']
$path = func_get_args();
array_shift($path);
array_shift($path);
$_GET['q'] = implode('/', $path);
// force the json'd finder output to hide_args
$finder->settings['advanced']['hide_args'] = 1;
drupal_json_output(array(
'status' => TRUE,
'data' => finder_view($finder, $display, TRUE),
));
exit;
}
}
drupal_json_output(array(
'data' => '',
));
exit;
}
/**
* Create finder results output.
*
* @param $finder
* The finder object.
* @return
* Themed output of finder results.
*/
function finder_results($finder) {
$output = '';
$finder_form_state = finder_form_state($finder->finder_id);
finder_invoke_finderapi($finder, 'finder_results', $finder_form_state);
if ($finder_form_state && $finder_form_state['storage']['finished'] || $finder->settings['advanced']['filter']) {
// I can't remember what this is for...
foreach ($_REQUEST as $k => $v) {
unset($_REQUEST[$k]);
}
$keywords = array();
$pager =& $finder->settings['advanced']['pager'];
foreach ($finder->elements as $element) {
$match =& $element->settings['advanced']['match'];
$keyword = array();
if (!is_null($finder_form_state['values'][$element->finder_element_id])) {
$keyword = (array) $finder_form_state['values'][$element->finder_element_id];
if (!empty($element->settings['advanced']['delimit'])) {
// This enables escaped characters to work in delimiters.
$delimiter = stripcslashes($element->settings['advanced']['delimit']);
foreach ($keyword as $k => $v) {
unset($keyword[$k]);
$exploded = explode($delimiter, $v);
foreach ($exploded as $e) {
$keyword[] = trim($e);
}
}
}
}
$keywords[$element->finder_element_id] = $keyword;
}
$goto =& $finder->settings['advanced']['goto'];
if (isset($_GET['go']) && $_GET['go'] || isset($finder_form_state['clicked_button']) && $finder_form_state['clicked_button']['#name'] == 'go' && $finder_form_state['storage']['finished'] || $goto == 'always') {
$finder->go = TRUE;
}
$result = finder_find($finder, $keywords, 'results', $match, $pager);
if ($finder->settings['advanced']['hide_args']) {
if ($pager && !isset($finder_form_state['storage']['pager_token'])) {
$token = drupal_get_token();
$finder_form_state['storage']['pager_token'] = $token;
$_SESSION['finder'][$token] = $finder_form_state;
}
}
$base_module =& $finder->base_handler['#module'];
if (isset($finder->go) && $finder->go && count($result) || $goto == 'best' && count($result) === 1 && empty($_GET['page'])) {
drupal_alter('finder_goto', $result, $finder);
$current_result = current($result);
module_invoke($base_module, 'finder_goto', $finder, $current_result);
}
$results = module_invoke($base_module, 'finder_result', $finder, $keywords, $result, $finder_form_state);
$params = array();
if (!empty($finder_form_state['storage']['pager_token'])) {
$params['finder'] = $finder_form_state['storage']['pager_token'];
}
if (!empty($finder->settings['advanced']['no_results']['no_results'])) {
$variables = array(
'finder' => $finder,
'keywords' => $keywords,
'form_state' => $finder_form_state,
);
$no_results = finder_eval($finder->settings['advanced']['no_results']['no_results'], $variables);
}
else {
// Default text.
$no_results = t('There are no results to display');
}
$output .= theme('finder_results', array(
'results' => $results,
'finder' => $finder,
'keywords' => $keywords,
'pager' => $pager,
'params' => $params,
'form_state' => $finder_form_state,
'no_results' => $no_results,
));
}
return $output;
}
/**
* Implements hook_block_info().
*
* @see hook_block_info()
*/
function finder_block_info() {
$finders = finder_load_multiple(NULL, array(
'block' => 1,
));
$blocks = array();
foreach ($finders as $finder) {
$blocks['finder_' . $finder->finder_id] = array(
'info' => t('Finder') . ': ' . check_plain($finder->title),
'cache' => 'BLOCK_NO_CACHE',
);
}
return $blocks;
}
/**
* Implements hook_block_view().
*
* @see hook_block_view()
*/
function finder_block_view($delta = '') {
if (user_access('use finder')) {
$finder_id = str_replace('finder_', '', $delta);
$finder = finder_load($finder_id);
if ($finder) {
finder_invoke_finderapi($finder, 'finder_block');
$block = array(
'subject' => check_plain($finder->title),
'content' => array(
'#theme' => 'finder_block',
'#finder' => $finder,
),
);
return $block;
}
}
}
/**
* Get a list of choices for form or results.
*
* This will invoke the base handler module's hook_finder_find()
* implementation. It also allows for hooks to alter the keywords, finder, and
* choices/results.
*
* @param $finder
* The finder object.
* @param $keywords
* An array keyed by finder_element_id, where the values are any
* str/num/bool/null or an array of such values to be OR'd together.
* @param $mode
* 'choices' or 'results' depending on what we are fetching.
* @param $match
* The match method, see finder_condition_args().
* @param $pager
* Used to limit choices or results per page.
* @param $finder_element_id
* If $mode is 'choices', this is the finder element id to get choices for.
* @param $reset
* Reset the cached return value for this set of parameters.
* @return
* An array of choices/results.
* @see hook_finder_find()
*/
function finder_find($finder, $keywords, $mode = 'choices', $match = 'e', $pager = 0, $finder_element_id = NULL, $reset = FALSE) {
static $finder_find_cache = array();
// For a 'choices' find we need a main element to focus our query around
// normally calling finder_find() you would not specify the $finder_element_id
// but it would be interpreted as the index of the last $keywords element
// though some modules may need to specify a main element other than the last
// so that parameter is available. This value is best left 'NULL' for 'results'.
if ($mode == 'choices' && is_null($finder_element_id)) {
// no $finder_element_id was passed as the current element we're doing
// so let's assume the last array key in $keywords is current finder_element_id
$finder_element_id = end(array_keys($keywords));
}
// Create an ID using the function params so we can cache the return value.
$id = ($mode == 'choices' ? 'e' . $finder_element_id : 'f' . $finder->finder_id) . '|';
$id_length = strlen($id);
$cache_id = ($match != 'e' ? $match : '') . ($pager ? $pager . (isset($_GET['page']) ? '.' . $_GET['page'] : '') : '') . serialize($keywords);
$cache_id_length = strlen($cache_id);
if ($cache_id_length + $id_length > 255) {
// For ID's that (will) exceed 255 we will try to represent them a unique way and pray for the best :/
$cache_id = md5($cache_id) . $cache_id_length . substr($cache_id, 0, 223 - strlen($cache_id_length) - $id_length);
// 223 = 255 - 32
}
$cache_id = $id . $cache_id;
if (isset($finder_find_cache[$cache_id])) {
// Use the static data.
$options = $finder_find_cache[$cache_id];
}
elseif ($finder->settings['advanced']['cache_finder_find'] && !$reset && ($cache = cache_get($cache_id, 'cache_finder_find')) && !empty($cache->data)) {
// Use the cached data.
$options = $cache->data;
}
else {
// Calculate the values from the database.
// Figure out which module is the base module (the module that tells us the options)
$module =& $finder->base_handler['#module'];
// Allow other modules to intefere with the keywords.
drupal_alter('finder_find_keywords', $keywords, $finder, $finder_element_id, $mode, $match, $pager);
// Allow other modules to react to and alter the finder.
finder_invoke_finderapi($finder, 'finder_find', $mode, $finder_element_id);
$options = module_invoke($module, 'finder_find', $finder, $finder_element_id, $keywords, $mode, $match, $pager);
// If mode is choices, we want to conduct some extra processing on this list.
if ($mode == 'choices') {
$options = finder_find_choices($finder, $finder_element_id, $options, $keywords[$finder_element_id], $match);
}
// Allow other modules to intefere with the options.
drupal_alter('finder_find_options', $options, $finder, $finder_element_id, $keywords, $mode, $match, $pager);
// Add the resulting $options to the drupal cache.
if ($finder->settings['advanced']['cache_finder_find']) {
cache_set($cache_id, $options, 'cache_finder_find', REQUEST_TIME + $finder->settings['advanced']['cache_finder_find']);
}
// Add the resulting $options to the static internal load cache.
$finder_find_cache[$cache_id] = $options;
}
return $options;
}
/**
* Build basic finder query arrays.
*
* This is a helper function that can be leveraged by basic hook_finder_find()
* implementations. It takes care of the common tasks that need to be done to
* build the $query array correctly, and allows the base handler modules to
* focus on their specialties.
*
* @param $query
* The query array to modify.
* @param $finder
* The finder object.
* @param $finder_element_id
* If $mode is 'choices', this is the finder element id to get choices for.
* @param $keywords
* An array keyed by finder_element_id, where the values are any
* str/num/bool/null or an array of such values to be OR'd together.
* @param $mode
* 'choices' or 'results' depending on what we are fetching.
* @param $match
* The match method, see finder_condition_args().
* @param $pager
* Used to limit choices or results per page.
* @param $join_ons
* Join information in the form of an array where the key is the table string
* as returned when a value returned from hook_finder_fields() is put through
* finder_split_info() and the value is an array where the keys are the names
* of real tables to join, and the values are the 'ON condition' that follows
* the ON keyword in SQL.
* @param $base_table
* The name of the base table to query.
* @param $base_field
* The primary key of the base table.
* @return
* The modified query array.
*/
function finder_find_query($prequery, $finder, $finder_element_id, $keywords, $mode, $match, $pager, $join_ons, $base_table, $base_field) {
$options = array();
$query = db_select($base_table);
if ($pager) {
$query = $query
->extend('PagerDefault')
->limit($pager);
}
if ($mode == 'results') {
$base = $query
->addField($base_table, $base_field);
$query
->addExpression("'" . $base_field . "'", 'base_field');
$query
->addExpression("'" . $base_table . "'", 'base_table');
$query
->groupBy($base);
}
foreach ($keywords as $feid => $keyword_array) {
$element =& finder_element($finder, $feid);
$fields[$feid] =& $element->settings['choices']['field'];
foreach ($fields[$feid] as $key => $field) {
$field_info[$feid][$key] = finder_split_field($field);
}
$sort[$feid] =& $element->settings['choices']['sort'];
if ($mode == 'choices' && $feid == $finder_element_id && $element->settings['choices']['per_result']) {
$base = $query
->addField($base_table, $base_field);
$query
->addExpression('"' . $base_field . '"', 'base_field');
$query
->addExpression('"' . $base_table . '"', 'base_table');
$query
->groupBy($base);
}
foreach ($fields[$feid] as $key => $field) {
$field_alias = finder_field_alias($feid, $field_info[$feid][$key]['table'], $field_info[$feid][$key]['field']);
$field_alias = $query
->addField($field_info[$feid][$key]['table'], $field_info[$feid][$key]['field'], $field_alias);
if ($mode == 'choices' && $feid == $finder_element_id) {
$query
->groupBy($field_alias);
}
}
// join tables if needed
foreach ($fields[$feid] as $key => $field) {
if (in_array($field_info[$feid][$key]['table'], array_keys($join_ons))) {
$join_on = $join_ons[$field_info[$feid][$key]['table']];
foreach ($join_on as $table => $on) {
$query
->innerJoin($table, $table, $on);
}
}
}
$field_operator = $element->settings['advanced']['field_combination'] ? 'AND' : 'OR';
$value_operator = $element->settings['advanced']['value_combination'] ? 'AND' : 'OR';
$nesting_order =& $element->settings['advanced']['nesting_order'];
$outer_operator = $nesting_order ? $field_operator : $value_operator;
$inner_operator = $nesting_order ? $value_operator : $field_operator;
// Restrict by keywords on field.
$keyword_array = (array) $keyword_array;
$keyword_array = array_filter($keyword_array, 'finder_empty_keyword');
if (!empty($keyword_array)) {
$prequery['conditions']['outer']['#operator'] = $outer_operator;
foreach ($keyword_array as $keyword_index => $keyword) {
if ($feid == $finder_element_id) {
foreach ($fields[$feid] as $key => $field) {
$inner_key = $nesting_order ? $key : $keyword_index;
if (!isset($prequery['conditions']['outer']['inner' . $inner_key]['#operator'])) {
$prequery['conditions']['outer']['inner' . $inner_key]['#operator'] = $inner_operator;
}
$prequery['conditions']['outer']['inner' . $inner_key][] = finder_condition_args($field, $keyword, $match);
}
}
else {
$prequery['conditions']['restrictions'][$feid]['outer']['#operator'] = $outer_operator;
foreach ($fields[$feid] as $key => $field) {
$inner_key = $nesting_order ? $key : $keyword_index;
if (!isset($prequery['conditions']['restrictions'][$feid]['outer']['inner' . $inner_key]['#operator'])) {
$prequery['conditions']['restrictions'][$feid]['outer']['inner' . $inner_key]['#operator'] = $inner_operator;
}
$prequery['conditions']['restrictions'][$feid]['outer']['inner' . $inner_key][] = finder_condition_args($field, $keyword, $element->settings['advanced']['match']);
}
}
}
}
}
// for additional elements wheres group
if (!empty($prequery['conditions']['restrictions'])) {
$prequery['conditions']['restrictions']['#operator'] = $finder->settings['advanced']['element_combination'] ? 'OR' : 'AND';
}
// if this is a choices list add a sort if there is only one field
if ($mode == 'choices' && $sort[$finder_element_id] && count($fields[$finder_element_id]) === 1) {
$query
->orderBy(end($fields[$finder_element_id]));
}
// Restrict to one result if $finder->go is TRUE.
if ($mode == 'results' && isset($finder->go) && $finder->go) {
$query
->range(0, 1);
}
if (!empty($prequery['conditions'])) {
$conditions = finder_wheres($prequery['conditions']);
$query
->where($conditions['sql'], $conditions['args']);
}
if (!empty($prequery['joins'])) {
foreach ($prequery['joins'] as $join_func => $joins) {
foreach ($joins as $join) {
call_user_func_array(array(
$query,
$join_func,
), $join);
}
}
}
return $query
->execute()
->fetchAll();
}
/*
* Recursively process SQL 'wheres'.
*/
function finder_wheres($wheres) {
static $placeholders = 0;
$return = array(
'sql' => '',
'args' => array(),
);
$operator = isset($wheres['#operator']) ? $wheres['#operator'] : 'AND';
unset($wheres['#operator']);
foreach ($wheres as $key => $where) {
if (is_array($where)) {
$finder_wheres = finder_wheres($where);
if ($finder_wheres['sql']) {
$wheres[$key] = '(' . $finder_wheres['sql'] . ')';
if ($finder_wheres['args']) {
$return['args'] = array_merge($return['args'], $finder_wheres['args']);
}
}
else {
unset($wheres[$key]);
}
}
elseif (is_object($where)) {
$placeholder = ':finder_' . $placeholders++;
$return['args'][$placeholder] = $where->value;
if (is_array($where->value)) {
$placeholder = '(' . $placeholder . ')';
}
$wheres[$key] = $where->field . ' ' . $where->match . ' ' . $placeholder;
}
}
if (!empty($wheres)) {
$return['sql'] = implode(' ' . $operator . ' ', $wheres);
}
return $return;
}
/**
* Get a list of possible element types.
*
* @return
* An array of element handlers from hook implementations.
* @see hook_finder_element_handlers()
*/
function finder_element_handlers() {
static $element_handlers;
if (empty($element_handlers)) {
$element_handlers = module_invoke_all('finder_element_handlers');
}
return $element_handlers;
}
/**
* Attach element handler data to the finder.
*
* @param &$finder
* The finder object.
*/
function finder_load_element_handler(&$finder) {
$element_handlers = finder_element_handlers();
if (!empty($finder->elements)) {
foreach ($finder->elements as $key => $element) {
$finder->elements[$key]->element_handler = $element_handlers[$element->element];
}
}
}
/**
* Get a list of findable Drupal objects.
*
* @return
* An array of base handlers from hook implementations.
* @see hook_finder_base_handlers()
*/
function finder_base_handlers() {
static $base_handlers;
if (empty($base_handlers)) {
$base_handlers = module_invoke_all('finder_base_handlers');
}
return $base_handlers;
}
/**
* Attach base handler data to the finder.
*
* @param &$finder
* The finder object.
*/
function finder_load_base_handler(&$finder) {
$base_handlers = finder_base_handlers();
$finder->base_handler = $base_handlers[$finder->base];
}
/**
* Load a module include file according to finder's naming convention.
*
* Finder's naming convention suggests includes be put in a directory called
* 'includes' within the module's directory, and named like so:
* module-name.inc-string.inc
*
* @param $inc_string
* If the file is finder.foo.inc then the $inc_string to specify is 'foo'.
* @param $module
* The name of the module.
*/
function finder_inc($inc_string, $module = 'finder') {
return module_load_include('inc', $module, 'includes/' . $module . '.' . $inc_string);
}
/**
* Returns the path to a module's includes directory according to finder's
* naming convention.
*
* Finder's naming convention suggests includes be put in a directory called
* 'includes' within the module's directory.
*
* @param $module
* The name of the module.
*/
function finder_inc_path($module = 'finder') {
static $inc_path;
if (empty($inc_path[$module])) {
$inc_path[$module] = drupal_get_path('module', $module) . '/includes';
}
return $inc_path[$module];
}
/**
* Return info about a field.
*
* Finder stores information about fields as "table-name.field-name", this is
* just a simple function to split the field string into the two parts for
* convenience.
*
* @param $field
* A field value as given in the array keys returned from
* hook_finder_fields() implementations.
* @return
* An array with keys 'field' and 'table'.
*/
function finder_split_field($field) {
$field_parts = explode('.', $field);
$field_info['field'] = $field_parts[1];
$field_info['table'] = $field_parts[0];
return $field_info;
}
/**
* Get data about finder match methods.
*
* @param $field
* The field to match.
* @param $value
* The value to match.
* @param $match
* The match method according to finder's settings.
* @return
* An object cast from an array with keys field/value/match if $match is set.
* Otherwise returns the full array of data about all match methods including
* operators and descriptions.
*/
function finder_condition_args($field = NULL, $value = NULL, $match = NULL) {
static $operators;
if (empty($operators)) {
// Operators use abbreviated key names because they need to be tiny in cache ID's.
$operators = finder_condition_args_default();
$operators = array_merge($operators, variable_get('finder_custom_matching', array()));
drupal_alter('finder_condition_args', $operators);
}
if (!is_null($match)) {
if ($operators[$match]['value_prefix']) {
$value = $operators[$match]['value_prefix'] . $value;
}
if ($operators[$match]['value_suffix']) {
$value = $value . $operators[$match]['value_suffix'];
}
return (object) array(
'field' => $field,
'value' => $value,
'match' => $operators[$match]['operator'],
);
}
return $operators;
}
/**
* Get preconfigured condition match methods.
*/
function finder_condition_args_default() {
return array(
'c' => array(
'name' => t('Contains'),
'description' => t('!matches <em>contain</em> the !keywords.'),
'operator' => 'LIKE',
'value_prefix' => '%',
'value_suffix' => '%',
),
'cw' => array(
'name' => t('Contains word'),
'description' => t('!matches <em>contain</em> the !keywords as whole words.'),
'operator' => 'REGEXP',
'value_prefix' => '[[:<:]]',
'value_suffix' => '[[:>:]]',
),
'e' => array(
'name' => t('Equals'),
'description' => t('!matches must match the !keywords <em>exactly</em>.'),
'operator' => '=',
'value_prefix' => '',
'value_suffix' => '',
),
'sw' => array(
'name' => t('Starts with'),
'description' => t('!matches must <em>start with</em> the !keywords.'),
'operator' => 'LIKE',
'value_prefix' => '%',
'value_suffix' => '',
),
'ew' => array(
'name' => t('Ends with'),
'description' => t('!matches must <em>end with</em> the !keywords.'),
'operator' => 'LIKE',
'value_prefix' => '',
'value_suffix' => '%',
),
'lt' => array(
'name' => t('Less than'),
'description' => t('!matches must be <em>less than</em> the !keywords.'),
'operator' => '<',
'value_prefix' => '',
'value_suffix' => '',
),
'lte' => array(
'name' => t('Less than or equals'),
'description' => t('!matches must be <em>less than or equal to</em> the !keywords.'),
'operator' => '<=',
'value_prefix' => '',
'value_suffix' => '',
),
'gt' => array(
'name' => t('Greater than'),
'description' => t('!matches must be <em>greater than</em> the !keywords.'),
'operator' => '>',
'value_prefix' => '',
'value_suffix' => '',
),
'gte' => array(
'name' => t('Greater than or equals'),
'description' => t('!matches must be <em>greater than or equal to</em> the !keywords.'),
'operator' => '>=',
'value_prefix' => '',
'value_suffix' => '',
),
'nc' => array(
'name' => t("Doesn't contain"),
'description' => t("!matches <em>don't contain</em> the !keywords."),
'operator' => 'NOT LIKE',
'value_prefix' => '%',
'value_suffix' => '%',
),
'ncw' => array(
'name' => t("Doesn't Contain word"),
'description' => t("!matches <em>don't contain</em> the !keywords as whole words."),
'operator' => 'NOT REGEXP',
'value_prefix' => '[[:<:]]',
'value_suffix' => '[[:>:]]',
),
'ne' => array(
'name' => t('Not equals'),
'description' => t('!matches must <em>not</em> match the !keywords.'),
'operator' => '<>',
'value_prefix' => '',
'value_suffix' => '',
),
);
}
/**
* Get label for a condition args entry.
*
* @param $entry
* A condition args entry.
* @param $matches
* The translated string to replace !matches with.
* @param $keywords
* The translated string to replace !keywords with.
* @return
* The formatted label.
*/
function finder_condition_args_label($entry, $matches, $keywords) {
return drupal_ucfirst($entry['name'] . ' - ' . strtr($entry['description'], array(
'!matches' => $matches,
'!keywords' => $keywords,
)));
}
/**
* Get an element from a finder.
*
* Finder stores it's elements in an indexed array, as well as tracking an
* array that maps finder element IDs to the index position. This can be
* awkward to use when dealing with a particular element's settings especially
* when the element variable needs to be a reference to an element in the
* finder variable. This function conveniently allows us to pull a finder
* element into a reference. If called by reference, for example,
* $element = &finder_element($finder, $finder_element_id);
* this function will return a reference to a finder element from the supplied
* finder as identified by the supplied finder element id, if not called by
* reference it will return a copy of the element.
*
* @param &$finder
* The finder object from which to get the element.
* @param &$finder_element_id
* The ID of the finder element required.
* @return
* The finder element, or reference to the finder element.
*/
function &finder_element(&$finder, &$finder_element_id) {
$key =& $finder->elements_index[$finder_element_id];
return $finder->elements[$key];
}
/**
* Attach 'links' data and 'admin links' data to the finder.
*
* @param &$finder
* The finder object.
*/
function finder_load_links(&$finder) {
// create admin links
$finder->admin_links = array();
$finder->admin_links[$finder->path] = t('View "Path"');
if (!isset($finder->settings['programmatic']) || !$finder->settings['programmatic']) {
$finder->admin_links['admin/structure/finder/' . $finder->finder_id . '/edit'] = t('Edit');
}
// create links
$finder->links = array();
}
/**
* Build finder code string recursively.
*/
function finder_export($var, $iteration = 0) {
$tab = '';
for ($i = 0; $i < $iteration; $i++) {
$tab = $tab . ' ';
}
$iteration++;
if (is_object($var)) {
$var = (array) $var;
$var['#_finder_object'] = '1';
}
if (is_array($var)) {
$empty = empty($var);
$code = "array(" . ($empty ? '' : "\n");
foreach ($var as $key => $value) {
$out = $tab . " '" . $key . "' => " . finder_export($value, $iteration) . ",\n";
drupal_alter('finder_export', $out, $tab, $key, $value, $iteration);
$code .= $out;
}
$code .= ($empty ? '' : $tab) . ")";
return $code;
}
else {
if (is_string($var)) {
return "'" . addslashes($var) . "'";
}
elseif (is_numeric($var)) {
return $var;
}
elseif (is_bool($var)) {
return $var ? 'TRUE' : 'FALSE';
}
else {
return 'NULL';
}
}
}
/**
* Evaluate and return decoded string.
*/
function finder_import($string) {
$array = eval('return ' . $string . ';');
$return = finder_import_objects($array);
return $return;
}
/**
* Recursively converts arrays back into objects.
*/
function finder_import_objects($array) {
foreach ($array as $k => $v) {
if (is_array($v)) {
$array[$k] = finder_import_objects($v);
}
if (is_string($v)) {
$array[$k] = stripslashes($v);
}
}
if ($array['#_finder_object']) {
unset($array['#_finder_object']);
$array = (object) $array;
}
return $array;
}
/**
* Postprocessing for returned finder_find options when mode is choices.
*
* TO DO: some of the added properties here could probably just be "selected"
* in the query.
*/
function finder_find_choices($finder, $finder_element_id, $options, $keywords, $match) {
if ($options) {
// If there are options, fetch some info about the field names.
$element =& finder_element($finder, $finder_element_id);
$fields =& $element->settings['choices']['field'];
foreach ($fields as $key => $field) {
$field_info[$key] = finder_split_field($field);
$field_names[$key] = finder_field_alias($finder_element_id, $field_info[$key]['table'], $field_info[$key]['field']);
}
// Create a new array where we will put the options to return.
$new_options = array();
// Iterate through the options.
foreach ($options as $option) {
// Simply case - there is only one field.
if (count($fields) === 1) {
// Add the field name to the option.
$option->field_name = end($field_names);
// Add the field name to the display_field property of the option too.
$option->display_field = $option->field_name;
// If doing "per_result" then switch the field name to the base field.
if (isset($element->settings['choices']['per_result']) && $element->settings['choices']['per_result']) {
$option->field_name = $option->base_field;
}
// Append this option to the array.
$new_options[] = $option;
}
elseif (count($fields) > 1) {
if (is_null($keywords)) {
$matching_names = $field_names;
}
else {
$operator = str_replace('%%', '%', str_replace("'", "", finder_condition_args($match)));
$matching_names = array();
foreach ($field_names as $field_name) {
$expression = str_replace('%s', $keywords, $operator);
if (strpos($expression, 'LIKE') !== FALSE) {
$expression = str_replace(' LIKE ', '', $expression);
// Added strtolower() because some people were reporting
// case-sensitivity even with the i modifier.
$like_matches = preg_grep("/^" . str_replace('%', '(.*?)', preg_quote(drupal_strtolower($expression))) . "\$/si", array(
drupal_strtolower($option->{$field_name}),
));
if (!empty($like_matches)) {
$matching_names[] = $field_name;
}
}
elseif (eval('return ' . $option->{$field_name} . $expression . ';')) {
$matching_names[] = $field_name;
}
}
}
if (count($matching_names) === 1) {
$option->field_name = end($matching_names);
$option->display_field = $option->field_name;
$new_options[] = $option;
}
elseif (!empty($matching_names)) {
foreach ($matching_names as $matching_name) {
$new_option = clone $option;
$new_option->field_name = $matching_name;
$new_option->display_field = $new_option->field_name;
$new_options[] = $new_option;
}
}
}
}
return $new_options;
}
return $options;
}
/**
* Evaluate a string of PHP code.
*
* This is a wrapper around PHP's eval(). It uses output buffering to capture
* both returned value and printed text. Allows to use variables with the given
* code.
* Using this wrapper also ensures that the PHP code which is evaluated can not
* overwrite any variables in the calling code, unlike a regular eval() call.
* In other words, we evaluate the code with independent variable scope.
*
* @param $code
* The code to evaluate.
* @param $variables
* Variables to import to local variable scope.
* @return
* A string containing the printed output of the code, followed by the
* returned output of the code.
*/
function finder_eval($code, $variables = array()) {
global $theme_path, $theme_info, $conf;
// Store current theme path.
$old_theme_path = $theme_path;
// Restore theme_path to the theme, as long as drupal_eval() executes,
// so code evaluted will not see the caller module as the current theme.
// If theme info is not initialized get the path from theme_default.
if (!isset($theme_info)) {
$theme_path = drupal_get_path('theme', $conf['theme_default']);
}
else {
$theme_path = dirname($theme_info->filename);
}
extract($variables);
ob_start();
print eval('?>' . $code);
$output = ob_get_contents();
ob_end_clean();
// Recover original theme path.
$theme_path = $old_theme_path;
return $output;
}
/**
* Modify a PHP setting element.
*
* Used for security reasons to prevent an unauthorized user editing the
* field. Also makes variables available for the PHP input.
*
* @param $element
* The original array for the element.
* @param $variables
* Array where keys are variable names (without the $) to make available in
* the PHP, and the values are descriptions already passed through t().
*/
function finder_php_setting($element, $variables = array()) {
if (user_access('administer finder PHP settings')) {
$var_list = array();
foreach ($variables as $variable => $description) {
$var_list[] = '<em>$' . $variable . '</em> - ' . $description;
}
if (!empty($var_list)) {
$element['#description'] = (isset($element['#description']) ? $element['#description'] : '') . '<div>' . t('Available variables') . ':' . theme('item_list', array(
'items' => $var_list,
)) . '</div>';
}
}
else {
$element['#disabled'] = TRUE;
$element['#prefix'] = '<div class="messages warning">' . t("You don't have permission to modify <em>!setting</em>.", array(
'!setting' => $element['#title'],
)) . '</div>' . (isset($element['#prefix']) ? $element['#prefix'] : '');
$element['#value'] = $element['#default_value'];
}
return $element;
}
/**
* Callback for array_filter to remove empty keywords.
*/
function finder_empty_keyword($keyword) {
if ($keyword !== '' && $keyword !== NULL) {
return TRUE;
}
return FALSE;
}
/**
* Build table alias in consistent manner.
*
* Deprecated. Use finder_alias('table', ...) instead.
*/
function finder_table_alias($feid, $table = NULL, $field = NULL, $delta = NULL) {
return finder_alias('table', $feid, $table, $field, $delta);
}
/**
* Build field alias in consistent manner.
*
* Deprecated. Use finder_alias('field', ...) instead.
*/
function finder_field_alias($feid, $table = NULL, $field = NULL, $delta = NULL) {
return finder_alias('field', $feid, $table, $field, $delta);
}
/**
* Build aliases in consistent manner.
*
* Only the $type and $feid are required, but you can supply as many params as
* is needed, as long as your usage is consistent between places where it needs
* to be.
*
* @param $type
* The type of alias ('table', or 'field').
* @param $feid
* The finder element id.
* @param $table
* The raw table name.
* @param $field
* The raw field name.
* @param $delta
* A meaningful extra value.
*/
function finder_alias($type, $feid, $table = NULL, $field = NULL, $delta = NULL) {
static $aliases = array();
static $alias_count = 0;
$alias =& $aliases[$type][$feid][$table][$field][$delta];
if (empty($alias)) {
$alias = ++$alias_count;
}
return 'finder_' . $type . '_' . $alias;
}
/**
* Callback for array filter, removes NULL/FALSE but keeps 0.
*
* @todo: Deprecated?
*/
function finder_alias_filter($var) {
return $var !== FALSE && $var !== NULL;
}
// TO DO: add multifield element sorts into finder_find_choices.
// TO DO: reduce duplicate choices returned from queries - distinct doesn't
// work because nid's are selected too. - but it doesn't hurt to add
// it to prevent superfluous rows??
// TO DO: theme functions all over this project are incorrectly documented in
// doxygen comments. Need to reassess how themes are used in general.
Functions
Name | Description |
---|---|
finder_ajax | Menu callback; get finder ajax output. |
finder_alias | Build aliases in consistent manner. |
finder_alias_filter | Callback for array filter, removes NULL/FALSE but keeps 0. |
finder_arg_optional_to_arg | A to_arg() function is used to provide a default for the arg in the menu wildcard. |
finder_base_handlers | Get a list of findable Drupal objects. |
finder_block_info | Implements hook_block_info(). |
finder_block_view | Implements hook_block_view(). |
finder_clone | Write a finder into the database as a new finder. |
finder_condition_args | Get data about finder match methods. |
finder_condition_args_default | Get preconfigured condition match methods. |
finder_condition_args_label | Get label for a condition args entry. |
finder_delete | Delete a finder and it's finder elements. |
finder_element | Get an element from a finder. |
finder_element_delete | Delete a finder element. |
finder_element_handlers | Get a list of possible element types. |
finder_element_load | Load a finder element object from the database. |
finder_element_load_multiple | Load finder element objects from the database. |
finder_element_save | Save changes to a finder element or add a new finder element. |
finder_empty_keyword | Callback for array_filter to remove empty keywords. |
finder_eval | Evaluate a string of PHP code. |
finder_export | Build finder code string recursively. |
finder_field_alias | Build field alias in consistent manner. |
finder_find | Get a list of choices for form or results. |
finder_find_choices | Postprocessing for returned finder_find options when mode is choices. |
finder_find_query | Build basic finder query arrays. |
finder_forms | Implements hook_forms(). |
finder_import | Evaluate and return decoded string. |
finder_import_objects | Recursively converts arrays back into objects. |
finder_inc | Load a module include file according to finder's naming convention. |
finder_inc_path | Returns the path to a module's includes directory according to finder's naming convention. |
finder_invoke_finderapi | Invoke hook_finderapi(). |
finder_load | Load a finder object from the database. |
finder_load_base_handler | Attach base handler data to the finder. |
finder_load_element_handler | Attach element handler data to the finder. |
finder_load_links | Attach 'links' data and 'admin links' data to the finder. |
finder_load_multiple | Load finder objects from the database. |
finder_load_objects | Load objects from the database. |
finder_menu | Implements hook_menu(). |
finder_menu_element_allowed | Menu item access callback to hide the 'Finder element settings' tab when it shouldn't be shown (when the element ID arg isn't present). |
finder_page | Menu callback; view a finder page. |
finder_permission | Implements hook_permission(). |
finder_php_setting | Modify a PHP setting element. |
finder_results | Create finder results output. |
finder_save | Save changes to a finder or add a new finder. |
finder_split_field | Return info about a field. |
finder_table_alias | Build table alias in consistent manner. |
finder_theme | Implements hook_theme(). |
finder_view | Generate display of a given finder. |
finder_wheres |