finder.module
Version 1.7.2.58 (checked in on 2011/02/12 at 06:55:18 by danielb)
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.
Functions & methods
| Name | Description |
|---|---|
| finder_ajax | Menu callback; get finder ajax output. |
| 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 |
View 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();
$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));
}
// If loading objects only by conditions, fetch all available objects from
// the cache. objects which don't match are removed later.
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', 'block', or 'ajax').
* @return
* Themed output of a finder.
*
* @todo
* Take better advantage of Drupal 7's render array.
*/
function finder_view($finder, $display) {
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') {
$output['results'] = finder_results($finder);
}
$rendered = '';
$rendered .= ($display == '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 .= ($display == 'ajax') ? '' : '</div>';
return $rendered;
}
/**
* Menu callback; get finder ajax output.
*
* @param $finder_id
* The finder ID.
* @param $path
* URL encoded path substitute.
* @return
* Finder ajax output.
*/
function finder_ajax($finder_id, $path) {
if ($finder_id) {
$finder = finder_load($finder_id);
if ($finder) {
// fix the path for any scripts that might call $_GET['q']
$_GET['q'] = urldecode($path);
// force the json'd finder output to hide_args
$finder->settings['advanced']['hide_args'] = 1;
//D6 drupal_json(array('status' => TRUE, 'data' => finder_view($finder, 'ajax')));
return finder_view($finder, 'ajax');
exit;
}
}
//D6 drupal_json(array('data' => ''));
return '';
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 ($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]));
}
if ($pager) {
$query->extend('PagerDefault');
}
// 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;
$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;
}
// Complex case - more than one field.
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.
*
* @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_table_alias($feid, $table = NULL, $field = NULL, $delta = NULL) {
$alias_bits = array('finder_element', $feid, $table, $field, $delta);
return implode('_', array_filter($alias_bits, 'finder_alias_filter'));
}
/**
* Build field alias in consistent manner.
*
* Only the $feid is 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 $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_field_alias($feid, $table = NULL, $field = NULL, $delta = NULL) {
$alias_bits = array(
finder_table_alias($feid, $table, $field, $delta),
$field,
);
return implode('_', array_filter($alias_bits, 'finder_alias_filter'));
}
/**
* Callback for array filter, removes NULL/FALSE but keeps 0.
*/
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.