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