Relevanssi – A Better Search - Version 3.5.8

Version Description

  • Did you mean function had a XSS vulnerability, which is now removed.
  • Minimum word length wasn't applied to titles in indexing. It is now fixed. If you think this is a problem, rebuild the index.
  • TablePress compatibility has been improved.
  • Meta query handling has been improved, thanks to Maxime Culea.
  • Improved WP_Query parameter support: setting query variable sentence to 1 forces phrase search.
  • Improved ACF compatibility.
Download this release

Release Info

Developer msaari
Plugin Icon 128x128 Relevanssi – A Better Search
Version 3.5.8
Comparing to
See all releases

Code changes from version 3.5.7.1 to 3.5.8

Files changed (7) hide show
  1. lib/common.php +81 -103
  2. lib/excerpts-highlights.php +7 -19
  3. lib/indexing.php +8 -3
  4. lib/interface.php +14 -8
  5. lib/search.php +113 -34
  6. readme.txt +74 -182
  7. relevanssi.php +6 -40
lib/common.php CHANGED
@@ -55,63 +55,46 @@ function relevanssi_wpml_filter($data) {
55
  * Function by Matthew Hood http://my.php.net/manual/en/function.sort.php#75036
56
  */
57
  function relevanssi_object_sort(&$data, $key, $dir = 'desc') {
58
- if ('title' == $key) $key = 'post_title';
59
- if ('date' == $key) $key = 'post_date';
60
- if (!isset($data[0]->$key)) return; // trying to sort by a non-existent key
61
- $dir = strtolower($dir);
 
62
  for ($i = count($data) - 1; $i >= 0; $i--) {
63
- $swapped = false;
64
- for ($j = 0; $j < $i; $j++) {
65
- if (function_exists('mb_strtolower')) {
66
- $key1 = "";
67
- $key2 = "";
68
- if (isset($data[$j]->$key)) {
69
- $key1 = mb_strtolower($data[$j]->$key);
70
- }
71
- else {
72
- $key1 = apply_filters('relevanssi_missing_sort_key', $key1, $key);
73
- }
74
- if (isset($data[$j + 1]->$key)) {
75
- $key2 = mb_strtolower($data[$j + 1]->$key);
76
- }
77
- else {
78
- $key2 = apply_filters('relevanssi_missing_sort_key', $key2, $key);
79
- }
80
- }
81
- else {
82
- $key1 = "";
83
- $key2 = "";
84
- if (isset($data[$j]->$key)) {
85
- $key1 = strtolower($data[$j]->$key);
86
- }
87
- else {
88
- $key1 = apply_filters('relevanssi_missing_sort_key', $key1, $key);
89
- }
90
- if (isset($data[$j + 1]->$key)) {
91
- $key2 = strtolower($data[$j + 1]->$key);
92
- }
93
- else {
94
- $key2 = apply_filters('relevanssi_missing_sort_key', $key2, $key);
95
- }
96
- }
97
- if ('asc' == $dir) {
98
- if ($key1 > $key2) {
99
- $tmp = $data[$j];
100
- $data[$j] = $data[$j + 1];
101
- $data[$j + 1] = $tmp;
102
- $swapped = true;
103
- }
104
- }
105
- else {
106
- if ($key1 < $key2) {
107
- $tmp = $data[$j];
108
- $data[$j] = $data[$j + 1];
109
- $data[$j + 1] = $tmp;
110
- $swapped = true;
111
- }
112
- }
113
- }
114
- if (!$swapped) return;
115
  }
116
  }
117
 
@@ -293,29 +276,22 @@ function relevanssi_get_term_taxonomy($id) {
293
  * Returns an array of phrases
294
  */
295
  function relevanssi_extract_phrases($q) {
296
- if ( function_exists( 'mb_strpos' ) )
297
- $pos = mb_strpos($q, '"');
298
- else
299
- $pos = strpos($q, '"');
300
 
301
  $phrases = array();
302
  while ($pos !== false) {
303
  $start = $pos;
304
- if ( function_exists( 'mb_strpos' ) )
305
- $end = mb_strpos($q, '"', $start + 1);
306
- else
307
- $end = strpos($q, '"', $start + 1);
308
 
309
  if ($end === false) {
310
  // just one " in the query
311
  $pos = $end;
312
  continue;
313
  }
314
- if ( function_exists( 'mb_substr' ) )
315
- $phrase = mb_substr($q, $start + 1, $end - $start - 1);
316
- else
317
- $phrase = substr($q, $start + 1, $end - $start - 1);
318
-
319
  $phrase = trim($phrase);
320
 
321
  if (!empty($phrase)) $phrases[] = $phrase;
@@ -424,40 +400,42 @@ function relevanssi_mb_trim($string) {
424
  }
425
 
426
  function relevanssi_remove_punct($a) {
427
- $a = preg_replace ('/<[^>]*>/', ' ', $a);
428
-
429
- $a = str_replace("\r", '', $a); // --- replace with empty space
430
- $a = str_replace("\n", ' ', $a); // --- replace with space
431
- $a = str_replace("\t", ' ', $a); // --- replace with space
432
-
433
- $a = stripslashes($a);
434
-
435
- $a = str_replace('ß', 'ss', $a);
436
-
437
- $a = str_replace("·", '', $a);
438
- $a = str_replace("…", '', $a);
439
- $a = str_replace("", '', $a);
440
- $a = str_replace("&shy;", '', $a);
441
-
442
- $a = str_replace(chr(194) . chr(160), ' ', $a);
443
- $a = str_replace("&nbsp;", ' ', $a);
444
- $a = str_replace('&#8217;', ' ', $a);
445
- $a = str_replace("'", ' ', $a);
446
- $a = str_replace("’", ' ', $a);
447
- $a = str_replace("", ' ', $a);
448
- $a = str_replace("", ' ', $a);
449
- $a = str_replace("", ' ', $a);
450
- $a = str_replace("", ' ', $a);
451
- $a = str_replace("´", ' ', $a);
452
- $a = str_replace("", ' ', $a);
453
- $a = str_replace("", ' ', $a);
454
- $a = str_replace("×", ' ', $a);
455
- $a = preg_replace('/[[:punct:]]+/u', ' ', $a);
456
-
457
- $a = preg_replace('/[[:space:]]+/', ' ', $a);
458
- $a = trim($a);
459
-
460
- return $a;
 
 
461
  }
462
 
463
 
55
  * Function by Matthew Hood http://my.php.net/manual/en/function.sort.php#75036
56
  */
57
  function relevanssi_object_sort(&$data, $key, $dir = 'desc') {
58
+ if ('title' == $key) $key = 'post_title';
59
+ if ('date' == $key) $key = 'post_date';
60
+ if (!isset($data[0]->$key)) return; // trying to sort by a non-existent key
61
+ $dir = strtolower($dir);
62
+ function_exists('mb_strtolower') ? $strtolower = 'mb_strtolower' : $strtolower = 'strtolower';
63
  for ($i = count($data) - 1; $i >= 0; $i--) {
64
+ $swapped = false;
65
+ for ($j = 0; $j < $i; $j++) {
66
+ $key1 = "";
67
+ $key2 = "";
68
+ if (isset($data[$j]->$key)) {
69
+ $key1 = call_user_func($strtolower, $data[$j]->$key);
70
+ }
71
+ else {
72
+ $key1 = apply_filters('relevanssi_missing_sort_key', $key1, $key);
73
+ }
74
+ if (isset($data[$j + 1]->$key)) {
75
+ $key2 = call_user_func($strtolower, $data[$j + 1]->$key);
76
+ }
77
+ else {
78
+ $key2 = apply_filters('relevanssi_missing_sort_key', $key2, $key);
79
+ }
80
+ if ('asc' == $dir) {
81
+ if ($key1 > $key2) {
82
+ $tmp = $data[$j];
83
+ $data[$j] = $data[$j + 1];
84
+ $data[$j + 1] = $tmp;
85
+ $swapped = true;
86
+ }
87
+ }
88
+ else {
89
+ if ($key1 < $key2) {
90
+ $tmp = $data[$j];
91
+ $data[$j] = $data[$j + 1];
92
+ $data[$j + 1] = $tmp;
93
+ $swapped = true;
94
+ }
95
+ }
96
+ }
97
+ if (!$swapped) return;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
98
  }
99
  }
100
 
276
  * Returns an array of phrases
277
  */
278
  function relevanssi_extract_phrases($q) {
279
+ function_exists( 'mb_strpos' ) ? $strpos_function = "mb_strpos" : $strpos_function = "strpos";
280
+ function_exists( 'mb_substr' ) ? $substr_function = "mb_substr" : $substr_function = "substr";
281
+
282
+ $pos = call_user_func($strpos_function, $q, '"');
283
 
284
  $phrases = array();
285
  while ($pos !== false) {
286
  $start = $pos;
287
+ $end = call_user_func($strpos_function, $q, '"', $start + 1);
 
 
 
288
 
289
  if ($end === false) {
290
  // just one " in the query
291
  $pos = $end;
292
  continue;
293
  }
294
+ $phrase = call_user_func($substr_function, $q, $start + 1, $end - $start - 1);
 
 
 
 
295
  $phrase = trim($phrase);
296
 
297
  if (!empty($phrase)) $phrases[] = $phrase;
400
  }
401
 
402
  function relevanssi_remove_punct($a) {
403
+ if (!is_string($a)) return ""; // In case something sends a non-string here.
404
+
405
+ $a = preg_replace ('/<[^>]*>/', ' ', $a);
406
+
407
+ $a = str_replace("\r", '', $a); // --- replace with empty space
408
+ $a = str_replace("\n", ' ', $a); // --- replace with space
409
+ $a = str_replace("\t", ' ', $a); // --- replace with space
410
+
411
+ $a = stripslashes($a);
412
+
413
+ $a = str_replace('ß', 'ss', $a);
414
+
415
+ $a = str_replace("·", '', $a);
416
+ $a = str_replace("", '', $a);
417
+ $a = str_replace("€", '', $a);
418
+ $a = str_replace("&shy;", '', $a);
419
+
420
+ $a = str_replace(chr(194) . chr(160), ' ', $a);
421
+ $a = str_replace("&nbsp;", ' ', $a);
422
+ $a = str_replace('&#8217;', ' ', $a);
423
+ $a = str_replace("'", ' ', $a);
424
+ $a = str_replace("", ' ', $a);
425
+ $a = str_replace("", ' ', $a);
426
+ $a = str_replace("", ' ', $a);
427
+ $a = str_replace("", ' ', $a);
428
+ $a = str_replace("", ' ', $a);
429
+ $a = str_replace("´", ' ', $a);
430
+ $a = str_replace("", ' ', $a);
431
+ $a = str_replace("–", ' ', $a);
432
+ $a = str_replace("×", ' ', $a);
433
+ $a = preg_replace('/[[:punct:]]+/u', ' ', $a);
434
+
435
+ $a = preg_replace('/[[:space:]]+/', ' ', $a);
436
+ $a = trim($a);
437
+
438
+ return $a;
439
  }
440
 
441
 
lib/excerpts-highlights.php CHANGED
@@ -482,14 +482,9 @@ function relevanssi_determine_snip_location($locations, $prevcount) {
482
  // 1/6 ratio on prevcount tends to work pretty well and puts the terms
483
  // in the middle of the extract
484
  function relevanssi_extract_relevant($words, $fulltext, $rellength=300, $prevcount=50) {
 
485
 
486
- if (function_exists('mb_strlen')) {
487
- $textlength = mb_strlen($fulltext);
488
- }
489
- else {
490
- $textlength = strlen($fulltext);
491
- }
492
- if($textlength <= $rellength) {
493
  return array($fulltext, 1, 0);
494
  }
495
 
@@ -501,21 +496,14 @@ function relevanssi_extract_relevant($words, $fulltext, $rellength=300, $prevcou
501
  $startpos = $startpos - ($textlength-$startpos)/2;
502
  }
503
 
504
- if (function_exists('mb_substr')) {
505
- $reltext = mb_substr($fulltext, $startpos, $rellength);
506
- }
507
- else {
508
- $reltext = substr($fulltext, $startpos, $rellength);
509
- }
510
 
511
  // check to ensure we dont snip the last word if thats the match
512
  if( $startpos + $rellength < $textlength) {
513
- if (function_exists('mb_substr') && function_exists('mb_strrpos')) {
514
- $reltext = mb_substr($reltext, 0, mb_strrpos($reltext, " ")); // remove last word
515
- }
516
- else {
517
- $reltext = substr($reltext, 0, strrpos($reltext, " ")); // remove last word
518
- }
519
  }
520
 
521
  $start = false;
482
  // 1/6 ratio on prevcount tends to work pretty well and puts the terms
483
  // in the middle of the extract
484
  function relevanssi_extract_relevant($words, $fulltext, $rellength=300, $prevcount=50) {
485
+ $textlength = relevanssi_strlen($fulltext);
486
 
487
+ if($textlength <= $rellength) {
 
 
 
 
 
 
488
  return array($fulltext, 1, 0);
489
  }
490
 
496
  $startpos = $startpos - ($textlength-$startpos)/2;
497
  }
498
 
499
+ function_exists('mb_substr') ? $substr = 'mb_substr' : $substr = 'substr';
500
+ function_exists('mb_strrpos') ? $strrpos = 'mb_strrpos' : $strrpos = 'strrpos';
501
+
502
+ $reltext = call_user_func($substr, $fulltext, $startpos, $rellength);
 
 
503
 
504
  // check to ensure we dont snip the last word if thats the match
505
  if( $startpos + $rellength < $textlength) {
506
+ $reltext = call_user_func($substr, $reltext, 0, call_user_func($strrpos, $reltext, " ")); // remove last word
 
 
 
 
 
507
  }
508
 
509
  $start = false;
lib/indexing.php CHANGED
@@ -289,7 +289,7 @@ function relevanssi_index_doc($indexpost, $remove_first = false, $custom_fields
289
  if ("" == $values) continue;
290
  foreach ($values as $value) {
291
  // Quick hack : allow indexing of PODS relationship custom fields // TMV
292
- if (isset($value['post_title'])) $value = $value['post_title'];
293
  relevanssi_index_acf($insert_data, $post->ID, $field, $value);
294
  $value_tokens = relevanssi_tokenize($value, true, $min_word_length);
295
  foreach ($value_tokens as $token => $count) {
@@ -318,7 +318,7 @@ function relevanssi_index_doc($indexpost, $remove_first = false, $custom_fields
318
  if (!empty($post->post_title)) {
319
  if (apply_filters('relevanssi_index_titles', $index_titles)) {
320
  $filtered_title = apply_filters('relevanssi_post_title_before_tokenize', $post->post_title, $post);
321
- $titles = relevanssi_tokenize(apply_filters('the_title', $filtered_title, $post->ID), apply_filters('relevanssi_remove_stopwords_in_titles', true));
322
 
323
  if (count($titles) > 0) {
324
  foreach ($titles as $title => $count) {
@@ -350,7 +350,12 @@ function relevanssi_index_doc($indexpost, $remove_first = false, $custom_fields
350
  $My_WP_Table_Reloaded = new WP_Table_Reloaded_Controller_Frontend();
351
  }
352
  // TablePress support
353
- if ( defined( 'TABLEPRESS_ABSPATH' ) ) {
 
 
 
 
 
354
  $My_TablePress_Controller = TablePress::load_controller( 'frontend' );
355
  $My_TablePress_Controller->init_shortcodes();
356
  }
289
  if ("" == $values) continue;
290
  foreach ($values as $value) {
291
  // Quick hack : allow indexing of PODS relationship custom fields // TMV
292
+ if (is_array($value) && isset($value['post_title'])) $value = $value['post_title'];
293
  relevanssi_index_acf($insert_data, $post->ID, $field, $value);
294
  $value_tokens = relevanssi_tokenize($value, true, $min_word_length);
295
  foreach ($value_tokens as $token => $count) {
318
  if (!empty($post->post_title)) {
319
  if (apply_filters('relevanssi_index_titles', $index_titles)) {
320
  $filtered_title = apply_filters('relevanssi_post_title_before_tokenize', $post->post_title, $post);
321
+ $titles = relevanssi_tokenize(apply_filters('the_title', $filtered_title, $post->ID), apply_filters('relevanssi_remove_stopwords_in_titles', true), $min_word_length);
322
 
323
  if (count($titles) > 0) {
324
  foreach ($titles as $title => $count) {
350
  $My_WP_Table_Reloaded = new WP_Table_Reloaded_Controller_Frontend();
351
  }
352
  // TablePress support
353
+ if (defined('TABLEPRESS_ABSPATH')) {
354
+ if (!isset(TablePress::$model_options)) {
355
+ include_once(TABLEPRESS_ABSPATH . 'classes/class-model.php');
356
+ include_once(TABLEPRESS_ABSPATH . 'models/model-options.php');
357
+ TablePress::$model_options = new TablePress_Options_Model();
358
+ }
359
  $My_TablePress_Controller = TablePress::load_controller( 'frontend' );
360
  $My_TablePress_Controller->init_shortcodes();
361
  }
lib/interface.php CHANGED
@@ -376,10 +376,14 @@ function relevanssi_remove_stopword($term) {
376
  $success = $wpdb->query($q);
377
 
378
  if ($success) {
379
- printf(__("<div id='message' class='updated fade'><p>Term '%s' removed from stopwords! Re-index to get it back to index.</p></div>", "relevanssi"), stripslashes($term));
 
 
380
  }
381
  else {
382
- printf(__("<div id='message' class='updated fade'><p>Couldn't remove term '%s' from stopwords!</p></div>", "relevanssi"), stripslashes($term));
 
 
383
  }
384
  }
385
 
@@ -1208,7 +1212,7 @@ function relevanssi_options_form() {
1208
  else {
1209
  $checked = '';
1210
  }
1211
- $label = sprintf(__("%s", 'relevanssi'), $type);
1212
  in_array($type, $public_types) ? $public = __('yes', 'relevanssi') : $public = __('no', 'relevanssi');
1213
 
1214
  echo <<<EOH
@@ -1262,7 +1266,7 @@ EOH;
1262
  else {
1263
  $checked = '';
1264
  }
1265
- $label = sprintf(__("%s", 'relevanssi'), $taxonomy->name);
1266
  $taxonomy->public ? $public = __('yes', 'relevanssi') : $public = __('no', 'relevanssi');
1267
  $type = $taxonomy->name;
1268
 
@@ -1370,13 +1374,15 @@ function relevanssi_show_stopwords() {
1370
 
1371
  RELEVANSSI_PREMIUM ? $plugin = 'relevanssi-premium' : $plugin = 'relevanssi';
1372
 
1373
- _e("<p>Enter a word here to add it to the list of stopwords. The word will automatically be removed from the index, so re-indexing is not necessary. You can enter many words at the same time, separate words with commas.</p>", 'relevanssi');
 
 
1374
 
1375
  ?><label for="addstopword"><p><?php _e("Stopword(s) to add: ", 'relevanssi'); ?><textarea name="addstopword" id="addstopword" rows="2" cols="40"></textarea>
1376
  <input type="submit" value="<?php esc_attr_e("Add", 'relevanssi'); ?>" class='button' /></p></label>
1377
- <?php
1378
 
1379
- _e("<p>Here's a list of stopwords in the database. Click a word to remove it from stopwords. Removing stopwords won't automatically return them to index, so you need to re-index all posts after removing stopwords to get those words back to index.", 'relevanssi');
1380
 
1381
  if (function_exists("plugins_url")) {
1382
  if (version_compare($wp_version, '2.8dev', '>' )) {
@@ -1391,7 +1397,7 @@ function relevanssi_show_stopwords() {
1391
  $src = '/wp-content/plugins/' . $plugin . '/delete.png';
1392
  }
1393
 
1394
- echo "<ul>";
1395
  $results = $wpdb->get_results("SELECT * FROM " . $relevanssi_variables['stopword_table']);
1396
  $exportlist = array();
1397
  foreach ($results as $stopword) {
376
  $success = $wpdb->query($q);
377
 
378
  if ($success) {
379
+ echo "<div id='message' class='updated fade'><p>";
380
+ printf(__("Term '%s' removed from stopwords! Re-index to get it back to index.", "relevanssi"), stripslashes($term));
381
+ echo "</p></div>";
382
  }
383
  else {
384
+ echo "<div id='message' class='updated fade'><p>";
385
+ printf(__("Couldn't remove term '%s' from stopwords!", "relevanssi"), stripslashes($term));
386
+ echo "</p></div>";
387
  }
388
  }
389
 
1212
  else {
1213
  $checked = '';
1214
  }
1215
+ $label = sprintf("%s", $type);
1216
  in_array($type, $public_types) ? $public = __('yes', 'relevanssi') : $public = __('no', 'relevanssi');
1217
 
1218
  echo <<<EOH
1266
  else {
1267
  $checked = '';
1268
  }
1269
+ $label = sprintf("%s", $taxonomy->name);
1270
  $taxonomy->public ? $public = __('yes', 'relevanssi') : $public = __('no', 'relevanssi');
1271
  $type = $taxonomy->name;
1272
 
1374
 
1375
  RELEVANSSI_PREMIUM ? $plugin = 'relevanssi-premium' : $plugin = 'relevanssi';
1376
 
1377
+ echo "<p>";
1378
+ _e("Enter a word here to add it to the list of stopwords. The word will automatically be removed from the index, so re-indexing is not necessary. You can enter many words at the same time, separate words with commas.", 'relevanssi');
1379
+ echo "</p>";
1380
 
1381
  ?><label for="addstopword"><p><?php _e("Stopword(s) to add: ", 'relevanssi'); ?><textarea name="addstopword" id="addstopword" rows="2" cols="40"></textarea>
1382
  <input type="submit" value="<?php esc_attr_e("Add", 'relevanssi'); ?>" class='button' /></p></label>
1383
+ <p><?php
1384
 
1385
+ _e("Here's a list of stopwords in the database. Click a word to remove it from stopwords. Removing stopwords won't automatically return them to index, so you need to re-index all posts after removing stopwords to get those words back to index.", 'relevanssi');
1386
 
1387
  if (function_exists("plugins_url")) {
1388
  if (version_compare($wp_version, '2.8dev', '>' )) {
1397
  $src = '/wp-content/plugins/' . $plugin . '/delete.png';
1398
  }
1399
 
1400
+ echo "</p><ul>";
1401
  $results = $wpdb->get_results("SELECT * FROM " . $relevanssi_variables['stopword_table']);
1402
  $exportlist = array();
1403
  foreach ($results as $stopword) {
lib/search.php CHANGED
@@ -74,6 +74,7 @@ function relevanssi_search($args) {
74
  $orderby = $filtered_args['orderby'];
75
  $order = $filtered_args['order'];
76
  $fields = $filtered_args['fields'];
 
77
 
78
  $hits = array();
79
 
@@ -377,6 +378,11 @@ function relevanssi_search($args) {
377
 
378
  $remove_stopwords = apply_filters('relevanssi_remove_stopwords_in_titles', true);
379
  if (function_exists('wp_encode_emoji')) $q = wp_encode_emoji($q);
 
 
 
 
 
380
  $phrases = relevanssi_recognize_phrases($q);
381
 
382
  if (function_exists('relevanssi_recognize_negatives')) {
@@ -867,22 +873,57 @@ function relevanssi_do_query(&$query) {
867
  $multi_args['operator'] = $operator;
868
 
869
  $meta_query = array();
870
- if (!empty($query->query_vars["meta_query"])) {
871
- $meta_query = $query->query_vars["meta_query"];
872
- }
873
- if (isset($query->query_vars["customfield_key"])) {
874
- isset($query->query_vars["customfield_value"]) ? $value = $query->query_vars["customfield_value"] : $value = null;
875
- $meta_query[] = array('key' => $query->query_vars["customfield_key"], 'value' => $value, 'compare' => '=');
876
- }
877
- if (!empty($query->query_vars["meta_key"]) ||
878
- !empty($query->query_vars["meta_value"]) ||
879
- !empty($query->query_vars["meta_value_num"])) {
880
- $value = null;
881
- if (!empty($query->query_vars["meta_value"])) $value = $query->query_vars["meta_value"];
882
- if (!empty($query->query_vars["meta_value_num"])) $value = $query->query_vars["meta_value_num"];
883
- !empty($query->query_vars["meta_compare"]) ? $compare = $query->query_vars["meta_compare"] : $compare = '=';
884
- $meta_query[] = array('key' => $query->query_vars["meta_key"], 'value' => $value, 'compare' => $compare);
885
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
886
 
887
  $multi_args['meta_query'] = $meta_query;
888
  if (function_exists('relevanssi_search_multi')) {
@@ -1046,25 +1087,57 @@ function relevanssi_do_query(&$query) {
1046
  $parent_query = array('parent not in' => $query->query_vars['post_parent__not_in']);
1047
  }
1048
 
 
1049
  $meta_query = array();
1050
- $meta_query_relation = apply_filters('relevanssi_default_meta_query_relation', 'AND');
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1051
 
1052
- if (!empty($query->query_vars["meta_query"])) {
1053
- $meta_query = $query->query_vars["meta_query"];
1054
- }
1055
- if (isset($query->query_vars["customfield_key"])) {
1056
- isset($query->query_vars["customfield_value"]) ? $value = $query->query_vars["customfield_value"] : $value = null;
1057
- $meta_query[] = array('key' => $query->query_vars["customfield_key"], 'value' => $value, 'compare' => '=');
1058
- }
1059
- if (!empty($query->query_vars["meta_key"]) ||
1060
- !empty($query->query_vars["meta_value"]) ||
1061
- !empty($query->query_vars["meta_value_num"])) {
1062
- $value = null;
1063
- if (!empty($query->query_vars["meta_value"])) $value = $query->query_vars["meta_value"];
1064
- if (!empty($query->query_vars["meta_value_num"])) $value = $query->query_vars["meta_value_num"];
1065
- !empty($query->query_vars["meta_compare"]) ? $compare = $query->query_vars["meta_compare"] : $compare = '=';
1066
- $meta_query[] = array('key' => $query->query_vars["meta_key"], 'value' => $value, 'compare' => $compare);
1067
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1068
 
1069
  $date_query = false;
1070
  if (!empty($query->date_query)) {
@@ -1114,6 +1187,11 @@ function relevanssi_do_query(&$query) {
1114
  isset($query->query_vars['orderby']) ? $orderby = $query->query_vars['orderby'] : $orderby = null;
1115
  isset($query->query_vars['order']) ? $order = $query->query_vars['order'] : $order = null;
1116
 
 
 
 
 
 
1117
  $fields = "";
1118
  if (!empty($query->query_vars['fields'])) {
1119
  if ($query->query_vars['fields'] == 'ids') {
@@ -1147,7 +1225,8 @@ function relevanssi_do_query(&$query) {
1147
  'author' => $author,
1148
  'orderby' => $orderby,
1149
  'order' => $order,
1150
- 'fields' => $fields);
 
1151
 
1152
  $return = relevanssi_search($search_params);
1153
  }
74
  $orderby = $filtered_args['orderby'];
75
  $order = $filtered_args['order'];
76
  $fields = $filtered_args['fields'];
77
+ $sentence = $filtered_args['sentence'];
78
 
79
  $hits = array();
80
 
378
 
379
  $remove_stopwords = apply_filters('relevanssi_remove_stopwords_in_titles', true);
380
  if (function_exists('wp_encode_emoji')) $q = wp_encode_emoji($q);
381
+ if ($sentence) {
382
+ $q = str_replace('"', '', $q);
383
+ $q = '"' . $q . '"';
384
+ }
385
+
386
  $phrases = relevanssi_recognize_phrases($q);
387
 
388
  if (function_exists('relevanssi_recognize_negatives')) {
873
  $multi_args['operator'] = $operator;
874
 
875
  $meta_query = array();
876
+ if ( ! empty( $query->query_vars["meta_query"] ) ) {
877
+ $meta_query = $query->query_vars["meta_query"];
878
+ }
879
+
880
+ if ( isset( $query->query_vars["customfield_key"] ) ) {
881
+ $build_meta_query = array();
882
+
883
+ // Use meta key
884
+ $build_meta_query['key'] = $query->query_vars["customfield_key"];
885
+
886
+ /**
887
+ * Check the value is not empty for ordering purpose,
888
+ * Set it or not for the current meta query
889
+ */
890
+ if ( ! empty( $query->query_vars["customfield_value"] ) ) {
891
+ $build_meta_query['value'] = $query->query_vars["customfield_value"];
892
+ }
893
+
894
+ // Set the compare
895
+ $build_meta_query['compare'] = '=';
896
+
897
+ $meta_query[] = $build_meta_query;
898
+ }
899
+
900
+ if ( ! empty($query->query_vars["meta_key"] ) || ! empty($query->query_vars["meta_value"] ) || ! empty( $query->query_vars["meta_value_num"] ) ) {
901
+
902
+ $build_meta_query = array();
903
+
904
+ // Use meta key
905
+ $build_meta_query['key'] = $query->query_vars["meta_key"];
906
+
907
+ $value = null;
908
+ if ( ! empty( $query->query_vars["meta_value"] ) ) {
909
+ $value = $query->query_vars["meta_value"];
910
+ } elseif ( ! empty( $query->query_vars["meta_value_num"] ) ) {
911
+ $value = $query->query_vars["meta_value_num"];
912
+ }
913
+
914
+ /**
915
+ * Check the meta value, as it could be not set for ordering purpose
916
+ * set it or not for the current meta query
917
+ */
918
+ if ( ! empty( $value ) ) {
919
+ $build_meta_query['value'] = $value;
920
+ }
921
+
922
+ // Set meta compare
923
+ $build_meta_query['compare'] = ! empty( $query->query_vars["meta_compare"] ) ? $query->query_vars["meta_compare"] : '=';
924
+
925
+ $meta_query[] = $build_meta_query;
926
+ }
927
 
928
  $multi_args['meta_query'] = $meta_query;
929
  if (function_exists('relevanssi_search_multi')) {
1087
  $parent_query = array('parent not in' => $query->query_vars['post_parent__not_in']);
1088
  }
1089
 
1090
+ $meta_query_relation = apply_filters('relevanssi_default_meta_query_relation', 'AND');
1091
  $meta_query = array();
1092
+ if ( ! empty( $query->query_vars["meta_query"] ) ) {
1093
+ $meta_query = $query->query_vars["meta_query"];
1094
+ }
1095
+
1096
+ if ( isset( $query->query_vars["customfield_key"] ) ) {
1097
+ $build_meta_query = array();
1098
+
1099
+ // Use meta key
1100
+ $build_meta_query['key'] = $query->query_vars["customfield_key"];
1101
+
1102
+ /**
1103
+ * Check the value is not empty for ordering purpose,
1104
+ * Set it or not for the current meta query
1105
+ */
1106
+ if ( ! empty( $query->query_vars["customfield_value"] ) ) {
1107
+ $build_meta_query['value'] = $query->query_vars["customfield_value"];
1108
+ }
1109
 
1110
+ // Set the compare
1111
+ $build_meta_query['compare'] = '=';
1112
+ $meta_query[] = $build_meta_query;
1113
+ }
1114
+
1115
+ if ( ! empty($query->query_vars["meta_key"] ) || ! empty($query->query_vars["meta_value"] ) || ! empty( $query->query_vars["meta_value_num"] ) ) {
1116
+ $build_meta_query = array();
1117
+
1118
+ // Use meta key
1119
+ $build_meta_query['key'] = $query->query_vars["meta_key"];
1120
+
1121
+ $value = null;
1122
+ if ( ! empty( $query->query_vars["meta_value"] ) ) {
1123
+ $value = $query->query_vars["meta_value"];
1124
+ } elseif ( ! empty( $query->query_vars["meta_value_num"] ) ) {
1125
+ $value = $query->query_vars["meta_value_num"];
1126
+ }
1127
+
1128
+ /**
1129
+ * Check the meta value, as it could be not set for ordering purpose
1130
+ * set it or not for the current meta query
1131
+ */
1132
+ if ( ! empty( $value ) ) {
1133
+ $build_meta_query['value'] = $value;
1134
+ }
1135
+
1136
+ // Set meta compare
1137
+ $build_meta_query['compare'] = ! empty( $query->query_vars["meta_compare"] ) ? $query->query_vars["meta_compare"] : '=';
1138
+
1139
+ $meta_query[] = $build_meta_query;
1140
+ }
1141
 
1142
  $date_query = false;
1143
  if (!empty($query->date_query)) {
1187
  isset($query->query_vars['orderby']) ? $orderby = $query->query_vars['orderby'] : $orderby = null;
1188
  isset($query->query_vars['order']) ? $order = $query->query_vars['order'] : $order = null;
1189
 
1190
+ $sentence = false;
1191
+ if (isset($query->query_vars['sentence']) && !empty($query->query_vars['sentence'])) {
1192
+ $sentence = true;
1193
+ }
1194
+
1195
  $fields = "";
1196
  if (!empty($query->query_vars['fields'])) {
1197
  if ($query->query_vars['fields'] == 'ids') {
1225
  'author' => $author,
1226
  'orderby' => $orderby,
1227
  'order' => $order,
1228
+ 'fields' => $fields,
1229
+ 'sentence' => $sentence);
1230
 
1231
  $return = relevanssi_search($search_params);
1232
  }
readme.txt CHANGED
@@ -3,8 +3,8 @@ Contributors: msaari
3
  Donate link: http://www.relevanssi.com/buy-premium/
4
  Tags: search, relevance, better search
5
  Requires at least: 4.0
6
- Tested up to: 4.7.1
7
- Stable tag: 3.5.7.1
8
  License: GPLv2 or later
9
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
10
 
@@ -12,13 +12,9 @@ Relevanssi replaces the default search with a partial-match search that sorts re
12
 
13
  == Description ==
14
 
15
- Relevanssi replaces the standard WordPress search with a better search engine, with lots of features
16
- and configurable options. You'll get better results, better presentation of results - your users
17
- will thank you.
18
 
19
- This is the free version of Relevanssi. There's also Relevanssi Premium, which has added features,
20
- including Multisite support. This free version does not work properly on Multisite. For more
21
- information about Premium, see [Relevanssi.com](http://www.relevanssi.com/).
22
 
23
  = Key features =
24
  * Search results sorted in the order of relevance, not by date.
@@ -42,11 +38,7 @@ information about Premium, see [Relevanssi.com](http://www.relevanssi.com/).
42
  * Search result throttling to improve performance on large databases.
43
  * Disable indexing of post content and post titles with a simple filter hook.
44
 
45
- Relevanssi is available in two versions, regular and Premium. Regular Relevanssi is and will remain
46
- free to download and use. Relevanssi Premium comes with a cost, but will get all the new features.
47
- Standard Relevanssi will be updated to fix bugs, but new features will mostly appear in Premium.
48
- Also, support for standard Relevanssi depends very much on my mood and available time. Premium
49
- pricing includes support.
50
 
51
  = Premium features (only in Relevanssi Premium) =
52
  * Improved spelling correction in "Did you mean?" suggestions.
@@ -61,16 +53,12 @@ pricing includes support.
61
  * Export and import settings.
62
 
63
  = Relevanssi in Facebook =
64
- You can find [Relevanssi in Facebook](http://www.facebook.com/relevanssi).
65
- Become a fan to follow the development of the plugin, I'll post updates on bugs, new features and
66
- new versions to the Facebook page.
67
 
68
  = Other search plugins =
69
- Relevanssi owes a lot to [wpSearch](http://wordpress.org/extend/plugins/wpsearch/) by Kenny
70
- Katzgrau. Relevanssi was built to replace wpSearch, when it started to fail.
71
 
72
- Search Unleashed is a popular search plugin, but it hasn't been updated since 2010. Relevanssi
73
- is in active development and does what Search Unleashed does.
74
 
75
 
76
 
@@ -81,75 +69,44 @@ is in active development and does what Search Unleashed does.
81
  1. Activate the plugin through the 'Plugins' menu in WordPress.
82
  1. Go to the plugin settings and build the index following the instructions there.
83
 
84
- To update your installation, simply overwrite the old files with the new, activate the new
85
- version and if the new version has changes in the indexing, rebuild the index.
86
 
87
  = Note on updates =
88
- If it seems the plugin doesn't work after an update, the first thing to try is deactivating and
89
- reactivating the plugin. If there are changes in the database structure, those changes do not happen
90
- without a deactivation, for some reason.
91
 
92
  = Changes to templates =
93
- None necessary! Relevanssi uses the standard search form and doesn't usually need any changes in
94
- the search results template.
95
 
96
- If the search does not bring any results, your theme probably has a query_posts() call in the
97
- search results template. That throws Relevanssi off. For more information, see [The most
98
- important Relevanssi debugging trick](http://www.relevanssi.com/knowledge-base/query_posts/).
99
 
100
  = How to index =
101
- Check the options to make sure they're to your liking, then click "Save indexing options and
102
- build the index". If everything's fine, you'll see the Relevanssi options screen again with a
103
- message "Indexing successful!"
104
-
105
- If something fails, usually the result is a blank screen. The most common problem is a timeout:
106
- server ran out of time while indexing. The solution to that is simple: just return to Relevanssi
107
- screen (do not just try to reload the blank page) and click "Continue indexing". Indexing will
108
- continue. Most databases will get indexed in just few clicks of "Continue indexing". You can
109
- follow the process in the "State of the Index": if the amount of documents is growing, the
110
- indexing is moving along.
111
-
112
- If the indexing gets stuck, something's wrong. I've had trouble with some plugins, for example
113
- Flowplayer video player stopped indexing. I had to disable the plugin, index and then activate
114
- the plugin again. Try disabling plugins, especially those that use shortcodes, to see if that
115
- helps. Relevanssi shows the highest post ID in the index - start troubleshooting from the post
116
- or page with the next highest ID. Server error logs may be useful, too.
117
 
118
  = Using custom search results =
119
- If you want to use the custom search results, make sure your search results template uses `the_excerpt()`
120
- to display the entries, because the plugin creates the custom snippet by replacing the post excerpt.
121
 
122
- If you're using a plugin that affects excerpts (like Advanced Excerpt), you may run into some
123
- problems. For those cases, I've included the function `relevanssi_the_excerpt()`, which you can
124
- use instead of `the_excerpt()`. It prints out the excerpt, but doesn't apply `wp_trim_excerpt()`
125
- filters (it does apply `the_content()`, `the_excerpt()`, and `get_the_excerpt()` filters).
126
 
127
  To avoid trouble, use the function like this:
128
 
129
  `<?php if (function_exists('relevanssi_the_excerpt')) { relevanssi_the_excerpt(); }; ?>`
130
 
131
- See Frequently Asked Questions for more instructions on what you can do with
132
- Relevanssi.
133
 
134
  = The advanced hacker option =
135
- If you're doing something unusual with your search and Relevanssi doesn't work, try
136
- using `relevanssi_do_query()`. See [Knowledge Base](http://www.relevanssi.com/knowledge-base/relevanssi_do_query/).
137
 
138
  = Uninstalling =
139
- To uninstall the plugin remove the plugin using the normal WordPress plugin management tools
140
- (from the Plugins page, first Deactivate, then Delete). If you remove the plugin files manually,
141
- the database tables and options will remain.
142
 
143
  = Combining with other plugins =
144
- Relevanssi doesn't work with plugins that rely on standard WP search. Those plugins want to
145
- access the MySQL queries, for example. That won't do with Relevanssi. [Search Light](http://wordpress.org/extend/plugins/search-light/),
146
- for example, won't work with Relevanssi.
147
 
148
- Some plugins cause problems when indexing documents. These are generally plugins that use shortcodes
149
- to do something somewhat complicated. One such plugin is [MapPress Easy Google Maps](http://wordpress.org/extend/plugins/mappress-google-maps-for-wordpress/).
150
- When indexing, you'll get a white screen. To fix the problem, disable either the offending plugin
151
- or shortcode expansion in Relevanssi while indexing. After indexing, you can activate the plugin
152
- again.
153
 
154
  == Frequently Asked Questions ==
155
 
@@ -157,146 +114,95 @@ again.
157
  You can find solutions and answers at the [Relevanssi Knowledge Base](http://www.relevanssi.com/category/knowledge-base/).
158
 
159
  = Relevanssi doesn't work =
160
- If you the results don't change after installing and activating Relevanssi, the most likely
161
- reason is that you have a call to `query_posts()` on your search results template. This confuses
162
- Relevanssi. Try removing the query_posts call and see what happens.
163
 
164
  = Searching for words with ampersands or hyphens doesn't work =
165
- Please read [Words with punctuation can't be found](http://www.relevanssi.com/knowledge-base/words-ampersands-cant-found/).
166
- This is a Relevanssi feature, but you can circumvent it with a simple filter function.
167
 
168
  = Where are the user search logs? =
169
- See the top of the admin menu. There's 'User searches'. There. If the logs are empty, please note
170
- showing the results needs at least MySQL 5.
171
 
172
  = Displaying the number of search results found =
173
 
174
- The typical solution to showing the number of search results found does not work with Relevanssi.
175
- However, there's a solution that's much easier: the number of search results is stored in a
176
- variable within $wp_query. Just add the following code to your search results template:
177
 
178
  `<?php echo 'Relevanssi found ' . $wp_query->found_posts . ' hits'; ?>`
179
 
180
  = Advanced search result filtering =
181
 
182
- If you want to add extra filters to the search results, you can add them using a hook.
183
- Relevanssi searches for results in the _relevanssi table, where terms and post_ids are listed.
184
- The various filtering methods work by listing either allowed or forbidden post ids in the
185
- query WHERE clause. Using the `relevanssi_where` hook you can add your own restrictions to
186
- the WHERE clause.
187
 
188
  These restrictions must be in the general format of
189
  ` AND doc IN (' . {a list of post ids, which could be a subquery} . ')`
190
 
191
- For more details, see where the filter is applied in the `relevanssi_search()` function. This
192
- is stricly an advanced hacker option for those people who're used to using filters and MySQL
193
- WHERE clauses and it is possible to break the search results completely by doing something wrong
194
- here.
195
 
196
- There's another filter hook, `relevanssi_hits_filter`, which lets you modify the hits directly.
197
- The filter passes an array, where index 0 gives the list of hits in the form of an array of
198
- post objects and index 1 has the search query as a string. The filter expects you to return an
199
- array containing the array of post objects in index 0 (`return array($your_processed_hit_array)`).
200
 
201
  = Direct access to query engine =
202
- Relevanssi can't be used in any situation, because it checks the presence of search with
203
- the `is_search()` function. This causes some unfortunate limitations and reduces the general usability
204
- of the plugin.
205
 
206
- You can now access the query engine directly. There's a new function `relevanssi_do_query()`,
207
- which can be used to do search queries just about anywhere. The function takes a WP_Query object
208
- as a parameter, so you need to store all the search parameters in the object (for example, put the
209
- search terms in `$your_query_object->query_vars['s']`). Then just pass the WP_Query object to
210
- Relevanssi with `relevanssi_do_query($your_wp_query_object);`.
211
 
212
- Relevanssi will process the query and insert the found posts as `$your_query_object->posts`. The
213
- query object is passed as reference and modified directly, so there's no return value. The posts
214
- array will contain all results that are found.
215
 
216
  = Sorting search results =
217
- If you want something else than relevancy ranking, you can use orderby and order parameters. Orderby
218
- accepts $post variable attributes and order can be "asc" or "desc". The most relevant attributes
219
- here are most likely "post_date" and "comment_count".
220
 
221
- If you want to give your users the ability to sort search results by date, you can just add a link
222
- to http://www.yourblogdomain.com/?s=search-term&orderby=post_date&order=desc to your search result
223
- page.
224
 
225
  Order by relevance is either orderby=relevance or no orderby parameter at all.
226
 
227
  = Filtering results by date =
228
- You can specify date limits on searches with `by_date` search parameter. You can use it your
229
- search result page like this: http://www.yourblogdomain.com/?s=search-term&by_date=1d to offer
230
- your visitor the ability to restrict their search to certain time limit (see
231
- [RAPLIQ](http://www.rapliq.org/) for a working example).
232
 
233
- The date range is always back from the current date and time. Possible units are hour (h), day (d),
234
- week (w), month (m) and year (y). So, to see only posts from past week, you could use by_date=7d
235
- or by_date=1w.
236
 
237
- Using wrong letters for units or impossible date ranges will lead to either defaulting to date
238
- or no results at all, depending on case.
239
 
240
  Thanks to Charles St-Pierre for the idea.
241
 
242
  = Displaying the relevance score =
243
- Relevanssi stores the relevance score it uses to sort results in the $post variable. Just add
244
- something like
245
 
246
  `echo $post->relevance_score`
247
 
248
  to your search results template inside a PHP code block to display the relevance score.
249
 
250
  = Did you mean? suggestions =
251
- To use Google-style "did you mean?" suggestions, first enable search query logging. The
252
- suggestions are based on logged queries, so without good base of logged queries, the
253
- suggestions will be odd and not very useful.
254
 
255
- To use the suggestions, add the following line to your search result template, preferably
256
- before the have_posts() check:
257
 
258
  `<?php if (function_exists('relevanssi_didyoumean')) { relevanssi_didyoumean(get_search_query(), "<p>Did you mean: ", "?</p>", 5); }?>`
259
 
260
- The first parameter passes the search term, the second is the text before the result,
261
- the third is the text after the result and the number is the amount of search results
262
- necessary to not show suggestions. With the default value of 5, suggestions are not
263
- shown if the search returns more than 5 hits.
264
 
265
  = Search shortcode =
266
- Relevanssi also adds a shortcode to help making links to search results. That way users
267
- can easily find more information about a given subject from your blog. The syntax is
268
- simple:
269
 
270
  `[search]John Doe[/search]`
271
 
272
- This will make the text John Doe a link to search results for John Doe. In case you
273
- want to link to some other search term than the anchor text (necessary in languages
274
- like Finnish), you can use:
275
 
276
  `[search term="John Doe"]Mr. John Doe[/search]`
277
 
278
  Now the search will be for John Doe, but the anchor says Mr. John Doe.
279
 
280
- One more parameter: setting `[search phrase="on"]` will wrap the search term in
281
- quotation marks, making it a phrase. This can be useful in some cases.
282
 
283
  = Restricting searches to categories and tags =
284
- Relevanssi supports the hidden input field `cat` to restrict searches to certain categories (or
285
- tags, since those are pretty much the same). Just add a hidden input field named `cat` in your
286
- search form and list the desired category or tag IDs in the `value` field - positive numbers
287
- include those categories and tags, negative numbers exclude them.
288
 
289
- This input field can only take one category or tag id (a restriction caused by WordPress, not
290
- Relevanssi). If you need more, use `cats` and use a comma-separated list of category IDs.
291
 
292
  The same works with post types. The input fields are called `post_type` and `post_types`.
293
 
294
- You can also set the restriction from general plugin settings (and then override it in individual
295
- search forms with the special field). This works with custom taxonomies as well, just replace `cat`
296
- with the name of your taxonomy.
297
 
298
- If you want to restrict the search to categories using a dropdown box on the search form, use
299
- a code like this:
300
 
301
  `<form method="get" action="<?php bloginfo('url'); ?>">
302
  <div><label class="screen-reader-text" for="s">Search</label>
@@ -308,49 +214,31 @@ a code like this:
308
  </div>
309
  </form>`
310
 
311
- This produces a search form with a dropdown box for categories. Do note that this code won't work
312
- when placed in a Text widget: either place it directly in the template or use a PHP widget plugin
313
- to get a widget that can execute PHP code.
314
 
315
  = Restricting searches with taxonomies =
316
 
317
- You can use taxonomies to restrict search results to posts and pages tagged with a certain
318
- taxonomy term. If you have a custom taxonomy of "People" and want to search entries tagged
319
- "John" in this taxonomy, just use `?s=keyword&people=John` in the URL. You should be able to use
320
- an input field in the search form to do this, as well - just name the input field with the name
321
- of the taxonomy you want to use.
322
 
323
- It's also possible to do a dropdown for custom taxonomies, using the same function. Just adjust
324
- the arguments like this:
325
 
326
  `wp_dropdown_categories(array('show_option_all' => 'All people', 'name' => 'people', 'taxonomy' => 'people'));`
327
 
328
- This would do a dropdown box for the "People" taxonomy. The 'name' must be the keyword used in
329
- the URL, while 'taxonomy' has the name of the taxonomy.
330
 
331
  = Automatic indexing =
332
- Relevanssi indexes changes in documents as soon as they happen. However, changes in shortcoded
333
- content won't be registered automatically. If you use lots of shortcodes and dynamic content, you
334
- may want to add extra indexing. Here's how to do it:
335
 
336
  `if (!wp_next_scheduled('relevanssi_build_index')) {
337
  wp_schedule_event( time(), 'daily', 'relevanssi_build_index' );
338
  }`
339
 
340
- Add the code above in your theme functions.php file so it gets executed. This will cause
341
- WordPress to build the index once a day. This is an untested and unsupported feature that may
342
- cause trouble and corrupt index if your database is large, so use at your own risk. This was
343
- presented at [forum](http://wordpress.org/support/topic/plugin-relevanssi-a-better-search-relevanssi-chron-indexing?replies=2).
344
 
345
  = Highlighting terms =
346
- Relevanssi search term highlighting can be used outside search results. You can access the search
347
- term highlighting function directly. This can be used for example to highlight search terms in
348
- structured search result data that comes from custom fields and isn't normally highlighted by
349
- Relevanssi.
350
 
351
- Just pass the content you want highlighted through `relevanssi_highlight_terms()` function. The
352
- content to highlight is the first parameter, the search query the second. The content with
353
- highlights is then returned by the function. Use it like this:
354
 
355
  `if (function_exists('relevanssi_highlight_terms')) {
356
  echo relevanssi_highlight_terms($content, get_search_query());
@@ -359,20 +247,13 @@ else { echo $content; }`
359
 
360
  = What is tf * idf weighing? =
361
 
362
- It's the basic weighing scheme used in information retrieval. Tf stands for *term frequency*
363
- while idf is *inverted document frequency*. Term frequency is simply the number of times the term
364
- appears in a document, while document frequency is the number of documents in the database where
365
- the term appears.
366
 
367
- Thus, the weight of the word for a document increases the more often it appears in the document and
368
- the less often it appears in other documents.
369
 
370
  = What are stop words? =
371
 
372
- Each document database is full of useless words. All the little words that appear in just about
373
- every document are completely useless for information retrieval purposes. Basically, their
374
- inverted document frequency is really low, so they never have much power in matching. Also,
375
- removing those words helps to make the index smaller and searching faster.
376
 
377
  == Known issues and To-do's ==
378
  * Known issue: In general, multiple Loops on the search page may cause surprising results. Please make sure the actual search results are the first loop.
@@ -386,6 +267,14 @@ removing those words helps to make the index smaller and searching faster.
386
 
387
  == Changelog ==
388
 
 
 
 
 
 
 
 
 
389
  = 3.5.7.1 =
390
  * Small fix for a bug that broke the settings page.
391
 
@@ -1146,6 +1035,9 @@ removing those words helps to make the index smaller and searching faster.
1146
 
1147
  == Upgrade notice ==
1148
 
 
 
 
1149
  = 3.5.7.1 =
1150
  * Fix for the broken settings page.
1151
 
3
  Donate link: http://www.relevanssi.com/buy-premium/
4
  Tags: search, relevance, better search
5
  Requires at least: 4.0
6
+ Tested up to: 4.7.3
7
+ Stable tag: 3.5.8
8
  License: GPLv2 or later
9
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
10
 
12
 
13
  == Description ==
14
 
15
+ Relevanssi replaces the standard WordPress search with a better search engine, with lots of features and configurable options. You'll get better results, better presentation of results - your users will thank you.
 
 
16
 
17
+ This is the free version of Relevanssi. There's also Relevanssi Premium, which has added features, including Multisite support. This free version does not work properly on Multisite. For more information about Premium, see [Relevanssi.com](http://www.relevanssi.com/).
 
 
18
 
19
  = Key features =
20
  * Search results sorted in the order of relevance, not by date.
38
  * Search result throttling to improve performance on large databases.
39
  * Disable indexing of post content and post titles with a simple filter hook.
40
 
41
+ Relevanssi is available in two versions, regular and Premium. Regular Relevanssi is and will remain free to download and use. Relevanssi Premium comes with a cost, but will get all the new features. Standard Relevanssi will be updated to fix bugs, but new features will mostly appear in Premium. Also, support for standard Relevanssi depends very much on my mood and available time. Premium pricing includes support.
 
 
 
 
42
 
43
  = Premium features (only in Relevanssi Premium) =
44
  * Improved spelling correction in "Did you mean?" suggestions.
53
  * Export and import settings.
54
 
55
  = Relevanssi in Facebook =
56
+ You can find [Relevanssi in Facebook](http://www.facebook.com/relevanssi). Become a fan to follow the development of the plugin, I'll post updates on bugs, new features and new versions to the Facebook page.
 
 
57
 
58
  = Other search plugins =
59
+ Relevanssi owes a lot to [wpSearch](http://wordpress.org/extend/plugins/wpsearch/) by Kenny Katzgrau. Relevanssi was built to replace wpSearch, when it started to fail.
 
60
 
61
+ Search Unleashed is a popular search plugin, but it hasn't been updated since 2010. Relevanssi is in active development and does what Search Unleashed does.
 
62
 
63
 
64
 
69
  1. Activate the plugin through the 'Plugins' menu in WordPress.
70
  1. Go to the plugin settings and build the index following the instructions there.
71
 
72
+ To update your installation, simply overwrite the old files with the new, activate the new version and if the new version has changes in the indexing, rebuild the index.
 
73
 
74
  = Note on updates =
75
+ If it seems the plugin doesn't work after an update, the first thing to try is deactivating and reactivating the plugin. If there are changes in the database structure, those changes do not happen without a deactivation, for some reason.
 
 
76
 
77
  = Changes to templates =
78
+ None necessary! Relevanssi uses the standard search form and doesn't usually need any changes in the search results template.
 
79
 
80
+ If the search does not bring any results, your theme probably has a query_posts() call in the search results template. That throws Relevanssi off. For more information, see [The most important Relevanssi debugging trick](http://www.relevanssi.com/knowledge-base/query_posts/).
 
 
81
 
82
  = How to index =
83
+ Check the options to make sure they're to your liking, then click "Save indexing options and build the index". If everything's fine, you'll see the Relevanssi options screen again with a message "Indexing successful!"
84
+
85
+ If something fails, usually the result is a blank screen. The most common problem is a timeout: server ran out of time while indexing. The solution to that is simple: just return to Relevanssi screen (do not just try to reload the blank page) and click "Continue indexing". Indexing will continue. Most databases will get indexed in just few clicks of "Continue indexing". You can follow the process in the "State of the Index": if the amount of documents is growing, the indexing is moving along.
86
+
87
+ If the indexing gets stuck, something's wrong. I've had trouble with some plugins, for example Flowplayer video player stopped indexing. I had to disable the plugin, index and then activate the plugin again. Try disabling plugins, especially those that use shortcodes, to see if that helps. Relevanssi shows the highest post ID in the index - start troubleshooting from the post or page with the next highest ID. Server error logs may be useful, too.
 
 
 
 
 
 
 
 
 
 
 
88
 
89
  = Using custom search results =
90
+ If you want to use the custom search results, make sure your search results template uses `the_excerpt()` to display the entries, because the plugin creates the custom snippet by replacing the post excerpt.
 
91
 
92
+ If you're using a plugin that affects excerpts (like Advanced Excerpt), you may run into some problems. For those cases, I've included the function `relevanssi_the_excerpt()`, which you can use instead of `the_excerpt()`. It prints out the excerpt, but doesn't apply `wp_trim_excerpt()` filters (it does apply `the_content()`, `the_excerpt()`, and `get_the_excerpt()` filters).
 
 
 
93
 
94
  To avoid trouble, use the function like this:
95
 
96
  `<?php if (function_exists('relevanssi_the_excerpt')) { relevanssi_the_excerpt(); }; ?>`
97
 
98
+ See Frequently Asked Questions for more instructions on what you can do with Relevanssi.
 
99
 
100
  = The advanced hacker option =
101
+ If you're doing something unusual with your search and Relevanssi doesn't work, try using `relevanssi_do_query()`. See [Knowledge Base](http://www.relevanssi.com/knowledge-base/relevanssi_do_query/).
 
102
 
103
  = Uninstalling =
104
+ To uninstall the plugin remove the plugin using the normal WordPress plugin management tools (from the Plugins page, first Deactivate, then Delete). If you remove the plugin files manually, the database tables and options will remain.
 
 
105
 
106
  = Combining with other plugins =
107
+ Relevanssi doesn't work with plugins that rely on standard WP search. Those plugins want to access the MySQL queries, for example. That won't do with Relevanssi. [Search Light](http://wordpress.org/extend/plugins/search-light/), for example, won't work with Relevanssi.
 
 
108
 
109
+ Some plugins cause problems when indexing documents. These are generally plugins that use shortcodes to do something somewhat complicated. One such plugin is [MapPress Easy Google Maps](http://wordpress.org/extend/plugins/mappress-google-maps-for-wordpress/). When indexing, you'll get a white screen. To fix the problem, disable either the offending plugin or shortcode expansion in Relevanssi while indexing. After indexing, you can activate the plugin again.
 
 
 
 
110
 
111
  == Frequently Asked Questions ==
112
 
114
  You can find solutions and answers at the [Relevanssi Knowledge Base](http://www.relevanssi.com/category/knowledge-base/).
115
 
116
  = Relevanssi doesn't work =
117
+ If you the results don't change after installing and activating Relevanssi, the most likely reason is that you have a call to `query_posts()` on your search results template. This confuses Relevanssi. Try removing the query_posts call and see what happens.
 
 
118
 
119
  = Searching for words with ampersands or hyphens doesn't work =
120
+ Please read [Words with punctuation can't be found](http://www.relevanssi.com/knowledge-base/words-ampersands-cant-found/). This is a Relevanssi feature, but you can circumvent it with a simple filter function.
 
121
 
122
  = Where are the user search logs? =
123
+ See the top of the admin menu. There's 'User searches'. There. If the logs are empty, please note showing the results needs at least MySQL 5.
 
124
 
125
  = Displaying the number of search results found =
126
 
127
+ The typical solution to showing the number of search results found does not work with Relevanssi. However, there's a solution that's much easier: the number of search results is stored in a variable within $wp_query. Just add the following code to your search results template:
 
 
128
 
129
  `<?php echo 'Relevanssi found ' . $wp_query->found_posts . ' hits'; ?>`
130
 
131
  = Advanced search result filtering =
132
 
133
+ If you want to add extra filters to the search results, you can add them using a hook. Relevanssi searches for results in the _relevanssi table, where terms and post_ids are listed. The various filtering methods work by listing either allowed or forbidden post ids in the query WHERE clause. Using the `relevanssi_where` hook you can add your own restrictions to the WHERE clause.
 
 
 
 
134
 
135
  These restrictions must be in the general format of
136
  ` AND doc IN (' . {a list of post ids, which could be a subquery} . ')`
137
 
138
+ For more details, see where the filter is applied in the `relevanssi_search()` function. This is stricly an advanced hacker option for those people who're used to using filters and MySQL WHERE clauses and it is possible to break the search results completely by doing something wrong here.
 
 
 
139
 
140
+ There's another filter hook, `relevanssi_hits_filter`, which lets you modify the hits directly. The filter passes an array, where index 0 gives the list of hits in the form of an array of post objects and index 1 has the search query as a string. The filter expects you to return an array containing the array of post objects in index 0 (`return array($your_processed_hit_array)`).
 
 
 
141
 
142
  = Direct access to query engine =
143
+ Relevanssi can't be used in any situation, because it checks the presence of search with the `is_search()` function. This causes some unfortunate limitations and reduces the general usability of the plugin.
 
 
144
 
145
+ You can now access the query engine directly. There's a new function `relevanssi_do_query()`, which can be used to do search queries just about anywhere. The function takes a WP_Query object as a parameter, so you need to store all the search parameters in the object (for example, put the search terms in `$your_query_object->query_vars['s']`). Then just pass the WP_Query object to Relevanssi with `relevanssi_do_query($your_wp_query_object);`.
 
 
 
 
146
 
147
+ Relevanssi will process the query and insert the found posts as `$your_query_object->posts`. The query object is passed as reference and modified directly, so there's no return value. The posts array will contain all results that are found.
 
 
148
 
149
  = Sorting search results =
150
+ If you want something else than relevancy ranking, you can use orderby and order parameters. Orderby accepts $post variable attributes and order can be "asc" or "desc". The most relevant attributes here are most likely "post_date" and "comment_count".
 
 
151
 
152
+ If you want to give your users the ability to sort search results by date, you can just add a link to http://www.yourblogdomain.com/?s=search-term&orderby=post_date&order=desc to your search result page.
 
 
153
 
154
  Order by relevance is either orderby=relevance or no orderby parameter at all.
155
 
156
  = Filtering results by date =
157
+ You can specify date limits on searches with `by_date` search parameter. You can use it your search result page like this: http://www.yourblogdomain.com/?s=search-term&by_date=1d to offer your visitor the ability to restrict their search to certain time limit (see [RAPLIQ](http://www.rapliq.org/) for a working example).
 
 
 
158
 
159
+ The date range is always back from the current date and time. Possible units are hour (h), day (d), week (w), month (m) and year (y). So, to see only posts from past week, you could use by_date=7d or by_date=1w.
 
 
160
 
161
+ Using wrong letters for units or impossible date ranges will lead to either defaulting to date or no results at all, depending on case.
 
162
 
163
  Thanks to Charles St-Pierre for the idea.
164
 
165
  = Displaying the relevance score =
166
+ Relevanssi stores the relevance score it uses to sort results in the $post variable. Just add something like
 
167
 
168
  `echo $post->relevance_score`
169
 
170
  to your search results template inside a PHP code block to display the relevance score.
171
 
172
  = Did you mean? suggestions =
173
+ To use Google-style "did you mean?" suggestions, first enable search query logging. The suggestions are based on logged queries, so without good base of logged queries, the suggestions will be odd and not very useful.
 
 
174
 
175
+ To use the suggestions, add the following line to your search result template, preferably before the have_posts() check:
 
176
 
177
  `<?php if (function_exists('relevanssi_didyoumean')) { relevanssi_didyoumean(get_search_query(), "<p>Did you mean: ", "?</p>", 5); }?>`
178
 
179
+ The first parameter passes the search term, the second is the text before the result, the third is the text after the result and the number is the amount of search results necessary to not show suggestions. With the default value of 5, suggestions are not shown if the search returns more than 5 hits.
180
+
181
+ Relevanssi Premium has a much better version of this feature.
 
182
 
183
  = Search shortcode =
184
+ Relevanssi also adds a shortcode to help making links to search results. That way users can easily find more information about a given subject from your blog. The syntax is simple:
 
 
185
 
186
  `[search]John Doe[/search]`
187
 
188
+ This will make the text John Doe a link to search results for John Doe. In case you want to link to some other search term than the anchor text (necessary in languages like Finnish), you can use:
 
 
189
 
190
  `[search term="John Doe"]Mr. John Doe[/search]`
191
 
192
  Now the search will be for John Doe, but the anchor says Mr. John Doe.
193
 
194
+ One more parameter: setting `[search phrase="on"]` will wrap the search term in quotation marks, making it a phrase. This can be useful in some cases.
 
195
 
196
  = Restricting searches to categories and tags =
197
+ Relevanssi supports the hidden input field `cat` to restrict searches to certain categories (or tags, since those are pretty much the same). Just add a hidden input field named `cat` in your search form and list the desired category or tag IDs in the `value` field - positive numbers include those categories and tags, negative numbers exclude them.
 
 
 
198
 
199
+ This input field can only take one category or tag id (a restriction caused by WordPress, not Relevanssi). If you need more, use `cats` and use a comma-separated list of category IDs.
 
200
 
201
  The same works with post types. The input fields are called `post_type` and `post_types`.
202
 
203
+ You can also set the restriction from general plugin settings (and then override it in individual search forms with the special field). This works with custom taxonomies as well, just replace `cat` with the name of your taxonomy.
 
 
204
 
205
+ If you want to restrict the search to categories using a dropdown box on the search form, use a code like this:
 
206
 
207
  `<form method="get" action="<?php bloginfo('url'); ?>">
208
  <div><label class="screen-reader-text" for="s">Search</label>
214
  </div>
215
  </form>`
216
 
217
+ This produces a search form with a dropdown box for categories. Do note that this code won't work when placed in a Text widget: either place it directly in the template or use a PHP widget plugin to get a widget that can execute PHP code.
 
 
218
 
219
  = Restricting searches with taxonomies =
220
 
221
+ You can use taxonomies to restrict search results to posts and pages tagged with a certain taxonomy term. If you have a custom taxonomy of "People" and want to search entries tagged "John" in this taxonomy, just use `?s=keyword&people=John` in the URL. You should be able to use an input field in the search form to do this, as well - just name the input field with the name of the taxonomy you want to use.
 
 
 
 
222
 
223
+ It's also possible to do a dropdown for custom taxonomies, using the same function. Just adjust the arguments like this:
 
224
 
225
  `wp_dropdown_categories(array('show_option_all' => 'All people', 'name' => 'people', 'taxonomy' => 'people'));`
226
 
227
+ This would do a dropdown box for the "People" taxonomy. The 'name' must be the keyword used in the URL, while 'taxonomy' has the name of the taxonomy.
 
228
 
229
  = Automatic indexing =
230
+ Relevanssi indexes changes in documents as soon as they happen. However, changes in shortcoded content won't be registered automatically. If you use lots of shortcodes and dynamic content, you may want to add extra indexing. Here's how to do it:
 
 
231
 
232
  `if (!wp_next_scheduled('relevanssi_build_index')) {
233
  wp_schedule_event( time(), 'daily', 'relevanssi_build_index' );
234
  }`
235
 
236
+ Add the code above in your theme functions.php file so it gets executed. This will cause WordPress to build the index once a day. This is an untested and unsupported feature that may cause trouble and corrupt index if your database is large, so use at your own risk. This was presented at [forum](http://wordpress.org/support/topic/plugin-relevanssi-a-better-search-relevanssi-chron-indexing?replies=2).
 
 
 
237
 
238
  = Highlighting terms =
239
+ Relevanssi search term highlighting can be used outside search results. You can access the search term highlighting function directly. This can be used for example to highlight search terms in structured search result data that comes from custom fields and isn't normally highlighted by Relevanssi.
 
 
 
240
 
241
+ Just pass the content you want highlighted through `relevanssi_highlight_terms()` function. The content to highlight is the first parameter, the search query the second. The content with highlights is then returned by the function. Use it like this:
 
 
242
 
243
  `if (function_exists('relevanssi_highlight_terms')) {
244
  echo relevanssi_highlight_terms($content, get_search_query());
247
 
248
  = What is tf * idf weighing? =
249
 
250
+ It's the basic weighing scheme used in information retrieval. Tf stands for *term frequency* while idf is *inverted document frequency*. Term frequency is simply the number of times the term appears in a document, while document frequency is the number of documents in the database where the term appears.
 
 
 
251
 
252
+ Thus, the weight of the word for a document increases the more often it appears in the document and the less often it appears in other documents.
 
253
 
254
  = What are stop words? =
255
 
256
+ Each document database is full of useless words. All the little words that appear in just about every document are completely useless for information retrieval purposes. Basically, their inverted document frequency is really low, so they never have much power in matching. Also, removing those words helps to make the index smaller and searching faster.
 
 
 
257
 
258
  == Known issues and To-do's ==
259
  * Known issue: In general, multiple Loops on the search page may cause surprising results. Please make sure the actual search results are the first loop.
267
 
268
  == Changelog ==
269
 
270
+ = 3.5.8 =
271
+ * Did you mean function had a XSS vulnerability, which is now removed.
272
+ * Minimum word length wasn't applied to titles in indexing. It is now fixed. If you think this is a problem, rebuild the index.
273
+ * TablePress compatibility has been improved.
274
+ * Meta query handling has been improved, thanks to Maxime Culea.
275
+ * Improved WP_Query parameter support: setting query variable `sentence` to 1 forces phrase search.
276
+ * Improved ACF compatibility.
277
+
278
  = 3.5.7.1 =
279
  * Small fix for a bug that broke the settings page.
280
 
1035
 
1036
  == Upgrade notice ==
1037
 
1038
+ = 3.5.8 =
1039
+ * Fix for a XSS vulnerability.
1040
+
1041
  = 3.5.7.1 =
1042
  * Fix for the broken settings page.
1043
 
relevanssi.php CHANGED
@@ -3,7 +3,7 @@
3
  Plugin Name: Relevanssi
4
  Plugin URI: http://www.relevanssi.com/
5
  Description: This plugin replaces WordPress search with a relevance-sorting search.
6
- Version: 3.5.7.1
7
  Author: Mikko Saari
8
  Author URI: http://www.mikkosaari.fi/
9
  */
@@ -69,7 +69,7 @@ function relevanssi_didyoumean($query, $pre, $post, $n = 5, $echo = true) {
69
  $q = apply_filters('relevanssi_didyoumean_query', $q);
70
 
71
  $data = $wpdb->get_results($q);
72
-
73
  $distance = -1;
74
  $closest = "";
75
 
@@ -94,6 +94,7 @@ function relevanssi_didyoumean($query, $pre, $post, $n = 5, $echo = true) {
94
 
95
  ), $url ));
96
  $url = apply_filters('relevanssi_didyoumean_url', $url, $query, $closest);
 
97
  $result = apply_filters('relevanssi_didyoumean_suggestion', "$pre<a href='$url'>$closest</a>$post");
98
  if ($echo) echo $result;
99
  }
@@ -378,50 +379,15 @@ better search experience for your users?</p>
378
  comparison</a> and <a href="http://www.relevanssi.com/buy-premium/?utm_source=plugin&utm_medium=link&utm_campaign=license">license prices</a>.</p>
379
 
380
  <p><strong><a href="http://www.relevanssi.com/buy-premium/?utm_source=plugin&utm_medium=link&utm_campaign=license">Buy Premium now &raquo;</a></strong></p>
381
- </div>
382
- </div>
383
- </div>
384
 
385
- <div class="meta-box-sortables" style="min-height: 0">
386
- <div id="relevanssi_list" class="postbox">
387
- <!-- Begin MailChimp Signup Form -->
388
- <div id="mc_embed_signup">
389
- <form action="//painavasana.us11.list-manage.com/subscribe/post?u=33d7be02c521d776357962ad2&amp;id=ef7d31c98a" method="post" id="mc-embedded-subscribe-form" name="mc-embedded-subscribe-form" class="validate" target="_blank" novalidate>
390
- <div id="mc_embed_signup_scroll">
391
- <h3 class="hndle"><span>Subscribe to our mailing list</span></h3>
392
-
393
- <div class="inside">
394
-
395
- <div class="mc-field-group">
396
- <label for="mce-EMAIL">Email Address
397
- </label>
398
- <input type="email" value="" name="EMAIL" class="required email" id="mce-EMAIL">
399
- </div>
400
- <div id="mce-responses" class="clear">
401
- <div class="response" id="mce-error-response" style="display:none"></div>
402
- <div class="response" id="mce-success-response" style="display:none"></div>
403
- </div> <!-- real people should not fill this in and expect good things - do not remove this or risk form bot signups-->
404
- <div style="position: absolute; left: -5000px;"><input type="text" name="b_33d7be02c521d776357962ad2_ef7d31c98a" tabindex="-1" value=""></div>
405
- <div class="clear"><input type="submit" value="Subscribe" name="subscribe" id="mc-embedded-subscribe" class="button-primary"></div>
406
-
407
- <p>Subscribe to our mailing list to get updates on Relevanssi development. As a thank you for subscribing, you'll
408
- get a <strong>20% discount</strong> for Relevanssi Premium.</p>
409
-
410
- </div>
411
-
412
- </div>
413
-
414
- </form>
415
-
416
- </div>
417
-
418
- <!--End mc_embed_signup-->
419
  </div>
420
  </div>
 
421
 
422
  <div class="meta-box-sortables" style="min-height: 0">
423
  <div id="relevanssi_premium" class="postbox">
424
- <h3 class="hndle"><span>Sample Premium features</span></h3>
425
  <div class="inside">
426
  <p>With Relevanssi Premium, you would have more options:</p>
427
 
3
  Plugin Name: Relevanssi
4
  Plugin URI: http://www.relevanssi.com/
5
  Description: This plugin replaces WordPress search with a relevance-sorting search.
6
+ Version: 3.5.8
7
  Author: Mikko Saari
8
  Author URI: http://www.mikkosaari.fi/
9
  */
69
  $q = apply_filters('relevanssi_didyoumean_query', $q);
70
 
71
  $data = $wpdb->get_results($q);
72
+
73
  $distance = -1;
74
  $closest = "";
75
 
94
 
95
  ), $url ));
96
  $url = apply_filters('relevanssi_didyoumean_url', $url, $query, $closest);
97
+ $closest = htmlspecialchars($closest);
98
  $result = apply_filters('relevanssi_didyoumean_suggestion', "$pre<a href='$url'>$closest</a>$post");
99
  if ($echo) echo $result;
100
  }
379
  comparison</a> and <a href="http://www.relevanssi.com/buy-premium/?utm_source=plugin&utm_medium=link&utm_campaign=license">license prices</a>.</p>
380
 
381
  <p><strong><a href="http://www.relevanssi.com/buy-premium/?utm_source=plugin&utm_medium=link&utm_campaign=license">Buy Premium now &raquo;</a></strong></p>
 
 
 
382
 
383
+ <p>Use the coupon <strong>FREE2017</strong> to get 20% off the price (valid through 2017).</p>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
384
  </div>
385
  </div>
386
+ </div>
387
 
388
  <div class="meta-box-sortables" style="min-height: 0">
389
  <div id="relevanssi_premium" class="postbox">
390
+ <h3 class="hndle"><span>Some Premium features</span></h3>
391
  <div class="inside">
392
  <p>With Relevanssi Premium, you would have more options:</p>
393