annotate modules/search/search.api.php @ 0:ff03f76ab3fe

initial version
author danieleb <danielebarchiesi@me.com>
date Wed, 21 Aug 2013 18:51:11 +0100
parents
children
rev   line source
danielebarchiesi@0 1 <?php
danielebarchiesi@0 2
danielebarchiesi@0 3 /**
danielebarchiesi@0 4 * @file
danielebarchiesi@0 5 * Hooks provided by the Search module.
danielebarchiesi@0 6 */
danielebarchiesi@0 7
danielebarchiesi@0 8 /**
danielebarchiesi@0 9 * @addtogroup hooks
danielebarchiesi@0 10 * @{
danielebarchiesi@0 11 */
danielebarchiesi@0 12
danielebarchiesi@0 13 /**
danielebarchiesi@0 14 * Define a custom search type.
danielebarchiesi@0 15 *
danielebarchiesi@0 16 * This hook allows a module to tell search.module that it wishes to perform
danielebarchiesi@0 17 * searches on content it defines (custom node types, users, or comments for
danielebarchiesi@0 18 * example) when a site search is performed.
danielebarchiesi@0 19 *
danielebarchiesi@0 20 * In order for the search to do anything, your module must also implement
danielebarchiesi@0 21 * hook_search_execute(), which is called when someone requests a search
danielebarchiesi@0 22 * on your module's type of content. If you want to have your content
danielebarchiesi@0 23 * indexed in the standard search index, your module should also implement
danielebarchiesi@0 24 * hook_update_index(). If your search type has settings, you can implement
danielebarchiesi@0 25 * hook_search_admin() to add them to the search settings page. You can use
danielebarchiesi@0 26 * hook_form_FORM_ID_alter(), with FORM_ID set to 'search_form', to add fields
danielebarchiesi@0 27 * to the search form (see node_form_search_form_alter() for an example).
danielebarchiesi@0 28 * You can use hook_search_access() to limit access to searching,
danielebarchiesi@0 29 * and hook_search_page() to override how search results are displayed.
danielebarchiesi@0 30 *
danielebarchiesi@0 31 * @return
danielebarchiesi@0 32 * Array with optional keys:
danielebarchiesi@0 33 * - title: Title for the tab on the search page for this module. Defaults
danielebarchiesi@0 34 * to the module name if not given.
danielebarchiesi@0 35 * - path: Path component after 'search/' for searching with this module.
danielebarchiesi@0 36 * Defaults to the module name if not given.
danielebarchiesi@0 37 * - conditions_callback: An implementation of callback_search_conditions().
danielebarchiesi@0 38 *
danielebarchiesi@0 39 * @ingroup search
danielebarchiesi@0 40 */
danielebarchiesi@0 41 function hook_search_info() {
danielebarchiesi@0 42 return array(
danielebarchiesi@0 43 'title' => 'Content',
danielebarchiesi@0 44 'path' => 'node',
danielebarchiesi@0 45 'conditions_callback' => 'callback_search_conditions',
danielebarchiesi@0 46 );
danielebarchiesi@0 47 }
danielebarchiesi@0 48
danielebarchiesi@0 49 /**
danielebarchiesi@0 50 * Define access to a custom search routine.
danielebarchiesi@0 51 *
danielebarchiesi@0 52 * This hook allows a module to define permissions for a search tab.
danielebarchiesi@0 53 *
danielebarchiesi@0 54 * @ingroup search
danielebarchiesi@0 55 */
danielebarchiesi@0 56 function hook_search_access() {
danielebarchiesi@0 57 return user_access('access content');
danielebarchiesi@0 58 }
danielebarchiesi@0 59
danielebarchiesi@0 60 /**
danielebarchiesi@0 61 * Take action when the search index is going to be rebuilt.
danielebarchiesi@0 62 *
danielebarchiesi@0 63 * Modules that use hook_update_index() should update their indexing
danielebarchiesi@0 64 * bookkeeping so that it starts from scratch the next time
danielebarchiesi@0 65 * hook_update_index() is called.
danielebarchiesi@0 66 *
danielebarchiesi@0 67 * @ingroup search
danielebarchiesi@0 68 */
danielebarchiesi@0 69 function hook_search_reset() {
danielebarchiesi@0 70 db_update('search_dataset')
danielebarchiesi@0 71 ->fields(array('reindex' => REQUEST_TIME))
danielebarchiesi@0 72 ->condition('type', 'node')
danielebarchiesi@0 73 ->execute();
danielebarchiesi@0 74 }
danielebarchiesi@0 75
danielebarchiesi@0 76 /**
danielebarchiesi@0 77 * Report the status of indexing.
danielebarchiesi@0 78 *
danielebarchiesi@0 79 * The core search module only invokes this hook on active modules.
danielebarchiesi@0 80 * Implementing modules do not need to check whether they are active when
danielebarchiesi@0 81 * calculating their return values.
danielebarchiesi@0 82 *
danielebarchiesi@0 83 * @return
danielebarchiesi@0 84 * An associative array with the key-value pairs:
danielebarchiesi@0 85 * - 'remaining': The number of items left to index.
danielebarchiesi@0 86 * - 'total': The total number of items to index.
danielebarchiesi@0 87 *
danielebarchiesi@0 88 * @ingroup search
danielebarchiesi@0 89 */
danielebarchiesi@0 90 function hook_search_status() {
danielebarchiesi@0 91 $total = db_query('SELECT COUNT(*) FROM {node} WHERE status = 1')->fetchField();
danielebarchiesi@0 92 $remaining = db_query("SELECT COUNT(*) FROM {node} n LEFT JOIN {search_dataset} d ON d.type = 'node' AND d.sid = n.nid WHERE n.status = 1 AND d.sid IS NULL OR d.reindex <> 0")->fetchField();
danielebarchiesi@0 93 return array('remaining' => $remaining, 'total' => $total);
danielebarchiesi@0 94 }
danielebarchiesi@0 95
danielebarchiesi@0 96 /**
danielebarchiesi@0 97 * Add elements to the search settings form.
danielebarchiesi@0 98 *
danielebarchiesi@0 99 * @return
danielebarchiesi@0 100 * Form array for the Search settings page at admin/config/search/settings.
danielebarchiesi@0 101 *
danielebarchiesi@0 102 * @ingroup search
danielebarchiesi@0 103 */
danielebarchiesi@0 104 function hook_search_admin() {
danielebarchiesi@0 105 // Output form for defining rank factor weights.
danielebarchiesi@0 106 $form['content_ranking'] = array(
danielebarchiesi@0 107 '#type' => 'fieldset',
danielebarchiesi@0 108 '#title' => t('Content ranking'),
danielebarchiesi@0 109 );
danielebarchiesi@0 110 $form['content_ranking']['#theme'] = 'node_search_admin';
danielebarchiesi@0 111 $form['content_ranking']['info'] = array(
danielebarchiesi@0 112 '#value' => '<em>' . t('The following numbers control which properties the content search should favor when ordering the results. Higher numbers mean more influence, zero means the property is ignored. Changing these numbers does not require the search index to be rebuilt. Changes take effect immediately.') . '</em>'
danielebarchiesi@0 113 );
danielebarchiesi@0 114
danielebarchiesi@0 115 // Note: reversed to reflect that higher number = higher ranking.
danielebarchiesi@0 116 $options = drupal_map_assoc(range(0, 10));
danielebarchiesi@0 117 foreach (module_invoke_all('ranking') as $var => $values) {
danielebarchiesi@0 118 $form['content_ranking']['factors']['node_rank_' . $var] = array(
danielebarchiesi@0 119 '#title' => $values['title'],
danielebarchiesi@0 120 '#type' => 'select',
danielebarchiesi@0 121 '#options' => $options,
danielebarchiesi@0 122 '#default_value' => variable_get('node_rank_' . $var, 0),
danielebarchiesi@0 123 );
danielebarchiesi@0 124 }
danielebarchiesi@0 125 return $form;
danielebarchiesi@0 126 }
danielebarchiesi@0 127
danielebarchiesi@0 128 /**
danielebarchiesi@0 129 * Execute a search for a set of key words.
danielebarchiesi@0 130 *
danielebarchiesi@0 131 * Use database API with the 'PagerDefault' query extension to perform your
danielebarchiesi@0 132 * search.
danielebarchiesi@0 133 *
danielebarchiesi@0 134 * If your module uses hook_update_index() and search_index() to index its
danielebarchiesi@0 135 * items, use table 'search_index' aliased to 'i' as the main table in your
danielebarchiesi@0 136 * query, with the 'SearchQuery' extension. You can join to your module's table
danielebarchiesi@0 137 * using the 'i.sid' field, which will contain the $sid values you provided to
danielebarchiesi@0 138 * search_index(). Add the main keywords to the query by using method
danielebarchiesi@0 139 * searchExpression(). The functions search_expression_extract() and
danielebarchiesi@0 140 * search_expression_insert() may also be helpful for adding custom search
danielebarchiesi@0 141 * parameters to the search expression.
danielebarchiesi@0 142 *
danielebarchiesi@0 143 * See node_search_execute() for an example of a module that uses the search
danielebarchiesi@0 144 * index, and user_search_execute() for an example that doesn't use the search
danielebarchiesi@0 145 * index.
danielebarchiesi@0 146 *
danielebarchiesi@0 147 * @param $keys
danielebarchiesi@0 148 * The search keywords as entered by the user.
danielebarchiesi@0 149 * @param $conditions
danielebarchiesi@0 150 * An optional array of additional conditions, such as filters.
danielebarchiesi@0 151 *
danielebarchiesi@0 152 * @return
danielebarchiesi@0 153 * An array of search results. To use the default search result
danielebarchiesi@0 154 * display, each item should have the following keys':
danielebarchiesi@0 155 * - 'link': Required. The URL of the found item.
danielebarchiesi@0 156 * - 'type': The type of item (such as the content type).
danielebarchiesi@0 157 * - 'title': Required. The name of the item.
danielebarchiesi@0 158 * - 'user': The author of the item.
danielebarchiesi@0 159 * - 'date': A timestamp when the item was last modified.
danielebarchiesi@0 160 * - 'extra': An array of optional extra information items.
danielebarchiesi@0 161 * - 'snippet': An excerpt or preview to show with the result (can be
danielebarchiesi@0 162 * generated with search_excerpt()).
danielebarchiesi@0 163 * - 'language': Language code for the item (usually two characters).
danielebarchiesi@0 164 *
danielebarchiesi@0 165 * @ingroup search
danielebarchiesi@0 166 */
danielebarchiesi@0 167 function hook_search_execute($keys = NULL, $conditions = NULL) {
danielebarchiesi@0 168 // Build matching conditions
danielebarchiesi@0 169 $query = db_select('search_index', 'i', array('target' => 'slave'))->extend('SearchQuery')->extend('PagerDefault');
danielebarchiesi@0 170 $query->join('node', 'n', 'n.nid = i.sid');
danielebarchiesi@0 171 $query
danielebarchiesi@0 172 ->condition('n.status', 1)
danielebarchiesi@0 173 ->addTag('node_access')
danielebarchiesi@0 174 ->searchExpression($keys, 'node');
danielebarchiesi@0 175
danielebarchiesi@0 176 // Insert special keywords.
danielebarchiesi@0 177 $query->setOption('type', 'n.type');
danielebarchiesi@0 178 $query->setOption('language', 'n.language');
danielebarchiesi@0 179 if ($query->setOption('term', 'ti.tid')) {
danielebarchiesi@0 180 $query->join('taxonomy_index', 'ti', 'n.nid = ti.nid');
danielebarchiesi@0 181 }
danielebarchiesi@0 182 // Only continue if the first pass query matches.
danielebarchiesi@0 183 if (!$query->executeFirstPass()) {
danielebarchiesi@0 184 return array();
danielebarchiesi@0 185 }
danielebarchiesi@0 186
danielebarchiesi@0 187 // Add the ranking expressions.
danielebarchiesi@0 188 _node_rankings($query);
danielebarchiesi@0 189
danielebarchiesi@0 190 // Load results.
danielebarchiesi@0 191 $find = $query
danielebarchiesi@0 192 ->limit(10)
danielebarchiesi@0 193 ->execute();
danielebarchiesi@0 194 $results = array();
danielebarchiesi@0 195 foreach ($find as $item) {
danielebarchiesi@0 196 // Build the node body.
danielebarchiesi@0 197 $node = node_load($item->sid);
danielebarchiesi@0 198 node_build_content($node, 'search_result');
danielebarchiesi@0 199 $node->body = drupal_render($node->content);
danielebarchiesi@0 200
danielebarchiesi@0 201 // Fetch comments for snippet.
danielebarchiesi@0 202 $node->rendered .= ' ' . module_invoke('comment', 'node_update_index', $node);
danielebarchiesi@0 203 // Fetch terms for snippet.
danielebarchiesi@0 204 $node->rendered .= ' ' . module_invoke('taxonomy', 'node_update_index', $node);
danielebarchiesi@0 205
danielebarchiesi@0 206 $extra = module_invoke_all('node_search_result', $node);
danielebarchiesi@0 207
danielebarchiesi@0 208 $results[] = array(
danielebarchiesi@0 209 'link' => url('node/' . $item->sid, array('absolute' => TRUE)),
danielebarchiesi@0 210 'type' => check_plain(node_type_get_name($node)),
danielebarchiesi@0 211 'title' => $node->title,
danielebarchiesi@0 212 'user' => theme('username', array('account' => $node)),
danielebarchiesi@0 213 'date' => $node->changed,
danielebarchiesi@0 214 'node' => $node,
danielebarchiesi@0 215 'extra' => $extra,
danielebarchiesi@0 216 'score' => $item->calculated_score,
danielebarchiesi@0 217 'snippet' => search_excerpt($keys, $node->body),
danielebarchiesi@0 218 );
danielebarchiesi@0 219 }
danielebarchiesi@0 220 return $results;
danielebarchiesi@0 221 }
danielebarchiesi@0 222
danielebarchiesi@0 223 /**
danielebarchiesi@0 224 * Override the rendering of search results.
danielebarchiesi@0 225 *
danielebarchiesi@0 226 * A module that implements hook_search_info() to define a type of search may
danielebarchiesi@0 227 * implement this hook in order to override the default theming of its search
danielebarchiesi@0 228 * results, which is otherwise themed using theme('search_results').
danielebarchiesi@0 229 *
danielebarchiesi@0 230 * Note that by default, theme('search_results') and theme('search_result')
danielebarchiesi@0 231 * work together to create an ordered list (OL). So your hook_search_page()
danielebarchiesi@0 232 * implementation should probably do this as well.
danielebarchiesi@0 233 *
danielebarchiesi@0 234 * @param $results
danielebarchiesi@0 235 * An array of search results.
danielebarchiesi@0 236 *
danielebarchiesi@0 237 * @return
danielebarchiesi@0 238 * A renderable array, which will render the formatted search results with a
danielebarchiesi@0 239 * pager included.
danielebarchiesi@0 240 *
danielebarchiesi@0 241 * @see search-result.tpl.php
danielebarchiesi@0 242 * @see search-results.tpl.php
danielebarchiesi@0 243 */
danielebarchiesi@0 244 function hook_search_page($results) {
danielebarchiesi@0 245 $output['prefix']['#markup'] = '<ol class="search-results">';
danielebarchiesi@0 246
danielebarchiesi@0 247 foreach ($results as $entry) {
danielebarchiesi@0 248 $output[] = array(
danielebarchiesi@0 249 '#theme' => 'search_result',
danielebarchiesi@0 250 '#result' => $entry,
danielebarchiesi@0 251 '#module' => 'my_module_name',
danielebarchiesi@0 252 );
danielebarchiesi@0 253 }
danielebarchiesi@0 254 $output['suffix']['#markup'] = '</ol>' . theme('pager');
danielebarchiesi@0 255
danielebarchiesi@0 256 return $output;
danielebarchiesi@0 257 }
danielebarchiesi@0 258
danielebarchiesi@0 259 /**
danielebarchiesi@0 260 * Preprocess text for search.
danielebarchiesi@0 261 *
danielebarchiesi@0 262 * This hook is called to preprocess both the text added to the search index and
danielebarchiesi@0 263 * the keywords users have submitted for searching.
danielebarchiesi@0 264 *
danielebarchiesi@0 265 * Possible uses:
danielebarchiesi@0 266 * - Adding spaces between words of Chinese or Japanese text.
danielebarchiesi@0 267 * - Stemming words down to their root words to allow matches between, for
danielebarchiesi@0 268 * instance, walk, walked, walking, and walks in searching.
danielebarchiesi@0 269 * - Expanding abbreviations and acronymns that occur in text.
danielebarchiesi@0 270 *
danielebarchiesi@0 271 * @param $text
danielebarchiesi@0 272 * The text to preprocess. This is a single piece of plain text extracted
danielebarchiesi@0 273 * from between two HTML tags or from the search query. It will not contain
danielebarchiesi@0 274 * any HTML entities or HTML tags.
danielebarchiesi@0 275 *
danielebarchiesi@0 276 * @return
danielebarchiesi@0 277 * The text after preprocessing. Note that if your module decides not to alter
danielebarchiesi@0 278 * the text, it should return the original text. Also, after preprocessing,
danielebarchiesi@0 279 * words in the text should be separated by a space.
danielebarchiesi@0 280 *
danielebarchiesi@0 281 * @ingroup search
danielebarchiesi@0 282 */
danielebarchiesi@0 283 function hook_search_preprocess($text) {
danielebarchiesi@0 284 // Do processing on $text
danielebarchiesi@0 285 return $text;
danielebarchiesi@0 286 }
danielebarchiesi@0 287
danielebarchiesi@0 288 /**
danielebarchiesi@0 289 * Update the search index for this module.
danielebarchiesi@0 290 *
danielebarchiesi@0 291 * This hook is called every cron run if search.module is enabled, your
danielebarchiesi@0 292 * module has implemented hook_search_info(), and your module has been set as
danielebarchiesi@0 293 * an active search module on the Search settings page
danielebarchiesi@0 294 * (admin/config/search/settings). It allows your module to add items to the
danielebarchiesi@0 295 * built-in search index using search_index(), or to add them to your module's
danielebarchiesi@0 296 * own indexing mechanism.
danielebarchiesi@0 297 *
danielebarchiesi@0 298 * When implementing this hook, your module should index content items that
danielebarchiesi@0 299 * were modified or added since the last run. PHP has a time limit
danielebarchiesi@0 300 * for cron, though, so it is advisable to limit how many items you index
danielebarchiesi@0 301 * per run using variable_get('search_cron_limit') (see example below). Also,
danielebarchiesi@0 302 * since the cron run could time out and abort in the middle of your run, you
danielebarchiesi@0 303 * should update your module's internal bookkeeping on when items have last
danielebarchiesi@0 304 * been indexed as you go rather than waiting to the end of indexing.
danielebarchiesi@0 305 *
danielebarchiesi@0 306 * @ingroup search
danielebarchiesi@0 307 */
danielebarchiesi@0 308 function hook_update_index() {
danielebarchiesi@0 309 $limit = (int)variable_get('search_cron_limit', 100);
danielebarchiesi@0 310
danielebarchiesi@0 311 $result = db_query_range("SELECT n.nid FROM {node} n LEFT JOIN {search_dataset} d ON d.type = 'node' AND d.sid = n.nid WHERE d.sid IS NULL OR d.reindex <> 0 ORDER BY d.reindex ASC, n.nid ASC", 0, $limit);
danielebarchiesi@0 312
danielebarchiesi@0 313 foreach ($result as $node) {
danielebarchiesi@0 314 $node = node_load($node->nid);
danielebarchiesi@0 315
danielebarchiesi@0 316 // Save the changed time of the most recent indexed node, for the search
danielebarchiesi@0 317 // results half-life calculation.
danielebarchiesi@0 318 variable_set('node_cron_last', $node->changed);
danielebarchiesi@0 319
danielebarchiesi@0 320 // Render the node.
danielebarchiesi@0 321 node_build_content($node, 'search_index');
danielebarchiesi@0 322 $node->rendered = drupal_render($node->content);
danielebarchiesi@0 323
danielebarchiesi@0 324 $text = '<h1>' . check_plain($node->title) . '</h1>' . $node->rendered;
danielebarchiesi@0 325
danielebarchiesi@0 326 // Fetch extra data normally not visible
danielebarchiesi@0 327 $extra = module_invoke_all('node_update_index', $node);
danielebarchiesi@0 328 foreach ($extra as $t) {
danielebarchiesi@0 329 $text .= $t;
danielebarchiesi@0 330 }
danielebarchiesi@0 331
danielebarchiesi@0 332 // Update index
danielebarchiesi@0 333 search_index($node->nid, 'node', $text);
danielebarchiesi@0 334 }
danielebarchiesi@0 335 }
danielebarchiesi@0 336 /**
danielebarchiesi@0 337 * @} End of "addtogroup hooks".
danielebarchiesi@0 338 */
danielebarchiesi@0 339
danielebarchiesi@0 340 /**
danielebarchiesi@0 341 * Provide search query conditions.
danielebarchiesi@0 342 *
danielebarchiesi@0 343 * Callback for hook_search_info().
danielebarchiesi@0 344 *
danielebarchiesi@0 345 * This callback is invoked by search_view() to get an array of additional
danielebarchiesi@0 346 * search conditions to pass to search_data(). For example, a search module
danielebarchiesi@0 347 * may get additional keywords, filters, or modifiers for the search from
danielebarchiesi@0 348 * the query string.
danielebarchiesi@0 349 *
danielebarchiesi@0 350 * This example pulls additional search keywords out of the $_REQUEST variable,
danielebarchiesi@0 351 * (i.e. from the query string of the request). The conditions may also be
danielebarchiesi@0 352 * generated internally - for example based on a module's settings.
danielebarchiesi@0 353 *
danielebarchiesi@0 354 * @param $keys
danielebarchiesi@0 355 * The search keywords string.
danielebarchiesi@0 356 *
danielebarchiesi@0 357 * @return
danielebarchiesi@0 358 * An array of additional conditions, such as filters.
danielebarchiesi@0 359 *
danielebarchiesi@0 360 * @ingroup callbacks
danielebarchiesi@0 361 * @ingroup search
danielebarchiesi@0 362 */
danielebarchiesi@0 363 function callback_search_conditions($keys) {
danielebarchiesi@0 364 $conditions = array();
danielebarchiesi@0 365
danielebarchiesi@0 366 if (!empty($_REQUEST['keys'])) {
danielebarchiesi@0 367 $conditions['keys'] = $_REQUEST['keys'];
danielebarchiesi@0 368 }
danielebarchiesi@0 369 if (!empty($_REQUEST['sample_search_keys'])) {
danielebarchiesi@0 370 $conditions['sample_search_keys'] = $_REQUEST['sample_search_keys'];
danielebarchiesi@0 371 }
danielebarchiesi@0 372 if ($force_keys = config('sample_search.settings')->get('force_keywords')) {
danielebarchiesi@0 373 $conditions['sample_search_force_keywords'] = $force_keys;
danielebarchiesi@0 374 }
danielebarchiesi@0 375 return $conditions;
danielebarchiesi@0 376 }