Version Description
- New feature: Relevanssi now supports multilingual synonyms and stopwords. Relevanssi now has a different set of synonyms and stopwords for each language. This feature is compatible with WPML and Polylang.
- New feature: SEO by Rank Math compatibility is added: posts marked as 'noindex' with Rank Math are not indexed by Relevanssi.
- Minor fix: With keyword matching set to 'whole words' and the 'expand highlights' disabled, words that ended with an 's' weren't highlighted correctly.
- Minor fix: The 'Post exclusion' setting didn't work correctly. It has been fixed.
- Minor fix: It's now impossible to set negative weights in searching settings. They did not work as expected anyway.
- Minor fix: Relevanssi had an unnecessary index on the
doc
column in thewp_relevanssi
database table. It is now removed to save space. Thanks to Matthew Wang. - Minor fix: Improved Oxygen Builder support makes sure
ct_builder_shortcodes
custom field is always indexed.
Download this release
Release Info
Developer | msaari |
Plugin | Relevanssi – A Better Search |
Version | 4.10.0 |
Comparing to | |
See all releases |
Code changes from version 4.9.1 to 4.10.0
- changelog.txt +20 -0
- lib/common.php +20 -1000
- lib/compatibility/oxygen.php +15 -1
- lib/compatibility/polylang.php +32 -0
- lib/compatibility/rankmath.php +59 -0
- lib/didyoumean.php +196 -0
- lib/excerpts-highlights.php +9 -44
- lib/indexing.php +3 -3
- lib/init.php +27 -8
- lib/install.php +2 -2
- lib/interface.php +0 -474
- lib/options.php +322 -0
- lib/phrases.php +277 -0
- lib/search.php +7 -4
- lib/stopwords.php +131 -40
- lib/tabs/searching-tab.php +2 -2
- lib/tabs/stopwords-tab.php +23 -11
- lib/tabs/synonyms-tab.php +3 -1
- lib/uninstall.php +1 -0
- lib/utils.php +867 -0
- readme.txt +15 -35
- relevanssi.php +8 -3
- stopwords/stopword.zh_TW +769 -0
changelog.txt
CHANGED
@@ -1,3 +1,23 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
= 4.5.0 =
|
2 |
* New feature: New filter hook `relevanssi_disable_stopwords` can be used to disable stopwords completely. Just add a filter function that returns `true`.
|
3 |
* Changed behaviour: Stopwords are no longer automatically restored if emptied. It's now possible to empty the stopword list. If you want to restore the stopwords from the file (or from the database, if you're upgrading from an earlier version of Relevanssi and find your stopwords missing), just click the button on the stopwords settings page that restores the stopwords.
|
1 |
+
= 4.7.2 =
|
2 |
+
* Minor fix: Media Library searches failed if Relevanssi was enabled in the WP admin, but the `attachment` post type wasn't indexed. Relevanssi will no longer block the default Media Library search in these cases.
|
3 |
+
* Minor fix: Adds more backwards compatibility for the `relevanssi_indexing_restriction` change, there's now an alert on indexing tab if there's a problem.
|
4 |
+
|
5 |
+
= 4.7.1 =
|
6 |
+
* New feature: New filter hook `relevanssi_post_content_after_shortcodes` filters the post content after shortcodes have been processed but before the HTML tags are stripped.
|
7 |
+
* Minor fix: Adds more backwards compatibility for the `relevanssi_indexing_restriction` change.
|
8 |
+
|
9 |
+
= 4.7.0 =
|
10 |
+
* New feature: New filter hook `relevanssi_admin_search_blocked_post_types` makes it easy to block Relevanssi from searching a specific post type in the admin dashboard. There's built-in support for Reusable Content Blocks `rc_blocks` post type, for example.
|
11 |
+
* New feature: The reason why a post is not indexed is now stored in the `_relevanssi_noindex_reason` custom field.
|
12 |
+
* Changed behaviour: The `relevanssi_indexing_restriction` filter hook has a changed format. Instead of a string value, the filter now expects an array with the MySQL query in the index 'mysql' and a reason in string format in 'reason'. There's some temporary backwards compatibility for this.
|
13 |
+
* Changed behaviour: Relevanssi now applies minimum word length when tokenizing search query terms.
|
14 |
+
* Changed behaviour: Content stopwords are removed from the search queries when doing excerpts and highlights. When Relevanssi uses the untokenized search terms for excerpt-building, stopwords are removed from those words. This should lead to better excerpts.
|
15 |
+
* Minor fix: Improves handling of emoji in indexing. If the database supports emoji, they are allowed, otherwise they are encoded.
|
16 |
+
|
17 |
+
= 4.6.0 =
|
18 |
+
* Changed behaviour: Phrases in OR search are now less restrictive. A search for 'foo "bar baz"' used to only return posts with the "bar baz" phrase, but now also posts with just the word 'foo' will be returned.
|
19 |
+
* Minor fix: User Access Manager showed drafts in search results for all users. This is now fixed.
|
20 |
+
|
21 |
= 4.5.0 =
|
22 |
* New feature: New filter hook `relevanssi_disable_stopwords` can be used to disable stopwords completely. Just add a filter function that returns `true`.
|
23 |
* Changed behaviour: Stopwords are no longer automatically restored if emptied. It's now possible to empty the stopword list. If you want to restore the stopwords from the file (or from the database, if you're upgrading from an earlier version of Relevanssi and find your stopwords missing), just click the button on the stopwords settings page that restores the stopwords.
|
lib/common.php
CHANGED
@@ -8,72 +8,6 @@
|
|
8 |
* @see https://www.relevanssi.com/
|
9 |
*/
|
10 |
|
11 |
-
/**
|
12 |
-
* Multibyte friendly case-insensitive string comparison.
|
13 |
-
*
|
14 |
-
* If multibyte string functions are available, do strcmp() after using
|
15 |
-
* mb_strtoupper() to both strings. Otherwise use strcasecmp().
|
16 |
-
*
|
17 |
-
* @param string $str1 First string to compare.
|
18 |
-
* @param string $str2 Second string to compare.
|
19 |
-
* @param string $encoding The encoding to use. Defaults to mb_internal_encoding().
|
20 |
-
*
|
21 |
-
* @return int $val Returns < 0 if str1 is less than str2; > 0 if str1 is greater
|
22 |
-
* than str2, and 0 if they are equal.
|
23 |
-
*/
|
24 |
-
function relevanssi_mb_strcasecmp( $str1, $str2, $encoding = null ) {
|
25 |
-
if ( ! function_exists( 'mb_internal_encoding' ) ) {
|
26 |
-
return strnatcasecmp( $str1, $str2 );
|
27 |
-
} else {
|
28 |
-
if ( null === $encoding ) {
|
29 |
-
$encoding = mb_internal_encoding();
|
30 |
-
}
|
31 |
-
return strnatcmp( mb_strtoupper( $str1, $encoding ), mb_strtoupper( $str2, $encoding ) );
|
32 |
-
}
|
33 |
-
}
|
34 |
-
|
35 |
-
/**
|
36 |
-
* Multibyte friendly strtolower.
|
37 |
-
*
|
38 |
-
* If multibyte string functions are available, returns mb_strtolower() and falls
|
39 |
-
* back to strtolower() if multibyte functions are not available.
|
40 |
-
*
|
41 |
-
* @param string $string The string to lowercase.
|
42 |
-
*
|
43 |
-
* @return string $string The string in lowercase.
|
44 |
-
*/
|
45 |
-
function relevanssi_strtolower( $string ) {
|
46 |
-
if ( ! function_exists( 'mb_strtolower' ) ) {
|
47 |
-
return strtolower( $string );
|
48 |
-
} else {
|
49 |
-
return mb_strtolower( $string );
|
50 |
-
}
|
51 |
-
}
|
52 |
-
|
53 |
-
/**
|
54 |
-
* Multibyte friendly substr.
|
55 |
-
*
|
56 |
-
* If multibyte string functions are available, returns mb_substr() and falls
|
57 |
-
* back to substr() if multibyte functions are not available.
|
58 |
-
*
|
59 |
-
* @param string $string The source string.
|
60 |
-
* @param int $start If start is non-negative, the returned string will
|
61 |
-
* start at the start'th position in str, counting from zero. If start is
|
62 |
-
* negative, the returned string will start at the start'th character from the
|
63 |
-
* end of string.
|
64 |
-
* @param int $length Maximum number of characters to use from string. If
|
65 |
-
* omitted or null is passed, extract all characters to the end of the string.
|
66 |
-
*
|
67 |
-
* @return string $string The string in lowercase.
|
68 |
-
*/
|
69 |
-
function relevanssi_substr( $string, $start, $length = null ) {
|
70 |
-
if ( ! function_exists( 'mb_substr' ) ) {
|
71 |
-
return substr( $string, $start, $length );
|
72 |
-
} else {
|
73 |
-
return mb_substr( $string, $start, $length );
|
74 |
-
}
|
75 |
-
}
|
76 |
-
|
77 |
/**
|
78 |
* Adds the search result match breakdown to the post object.
|
79 |
*
|
@@ -318,319 +252,6 @@ function relevanssi_populate_array( $matches ) {
|
|
318 |
wp_suspend_cache_addition( false );
|
319 |
}
|
320 |
|
321 |
-
/**
|
322 |
-
* Fetches the taxonomy based on term ID.
|
323 |
-
*
|
324 |
-
* Fetches the taxonomy from wp_term_taxonomy based on term_id.
|
325 |
-
*
|
326 |
-
* @global object $wpdb The WordPress database interface.
|
327 |
-
* @param int $term_id The term ID.
|
328 |
-
* @deprecated Will be removed in future versions.
|
329 |
-
* @return string $taxonomy The term taxonomy.
|
330 |
-
*/
|
331 |
-
function relevanssi_get_term_taxonomy( $term_id ) {
|
332 |
-
global $wpdb;
|
333 |
-
|
334 |
-
$taxonomy = $wpdb->get_var( $wpdb->prepare( "SELECT taxonomy FROM $wpdb->term_taxonomy WHERE term_id = %d", $term_id ) ); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
|
335 |
-
return $taxonomy;
|
336 |
-
}
|
337 |
-
|
338 |
-
/**
|
339 |
-
* Extracts phrases from the search query.
|
340 |
-
*
|
341 |
-
* Finds all phrases wrapped in quotes from the search query.
|
342 |
-
*
|
343 |
-
* @param string $query The query.
|
344 |
-
*
|
345 |
-
* @return array An array of phrases (strings).
|
346 |
-
*/
|
347 |
-
function relevanssi_extract_phrases( $query ) {
|
348 |
-
$strpos_function = 'strpos';
|
349 |
-
if ( function_exists( 'mb_strpos' ) ) {
|
350 |
-
$strpos_function = 'mb_strpos';
|
351 |
-
}
|
352 |
-
$substr_function = 'substr';
|
353 |
-
if ( function_exists( 'mb_substr' ) ) {
|
354 |
-
$substr_function = 'mb_substr';
|
355 |
-
}
|
356 |
-
|
357 |
-
// iOS uses “” as the default quotes, so Relevanssi needs to understand that as
|
358 |
-
// well.
|
359 |
-
$normalized_query = str_replace( array( '”', '“' ), '"', $query );
|
360 |
-
$pos = call_user_func( $strpos_function, $normalized_query, '"' );
|
361 |
-
|
362 |
-
$phrases = array();
|
363 |
-
while ( false !== $pos ) {
|
364 |
-
if ( $pos + 2 > relevanssi_strlen( $normalized_query ) ) {
|
365 |
-
$pos = false;
|
366 |
-
continue;
|
367 |
-
}
|
368 |
-
$start = call_user_func( $strpos_function, $normalized_query, '"', $pos );
|
369 |
-
$end = false;
|
370 |
-
if ( false !== $start ) {
|
371 |
-
$end = call_user_func( $strpos_function, $normalized_query, '"', $start + 2 );
|
372 |
-
}
|
373 |
-
if ( false === $end ) {
|
374 |
-
// Just one " in the query.
|
375 |
-
$pos = $end;
|
376 |
-
continue;
|
377 |
-
}
|
378 |
-
$phrase = call_user_func( $substr_function, $normalized_query, $start + 1, $end - $start - 1 );
|
379 |
-
$phrase = trim( $phrase );
|
380 |
-
|
381 |
-
// Do not count single-word phrases as phrases.
|
382 |
-
if ( ! empty( $phrase ) && count( explode( ' ', $phrase ) ) > 1 ) {
|
383 |
-
$phrases[] = $phrase;
|
384 |
-
}
|
385 |
-
$pos = $end + 1;
|
386 |
-
}
|
387 |
-
|
388 |
-
return $phrases;
|
389 |
-
}
|
390 |
-
|
391 |
-
/**
|
392 |
-
* Generates the MySQL code for restricting the search to phrase hits.
|
393 |
-
*
|
394 |
-
* This function uses relevanssi_extract_phrases() to figure out the phrases in
|
395 |
-
* the search query, then generates MySQL queries to restrict the search to the
|
396 |
-
* posts containing those phrases in the title, content, taxonomy terms or meta
|
397 |
-
* fields.
|
398 |
-
*
|
399 |
-
* @global array $relevanssi_variables The global Relevanssi variables.
|
400 |
-
*
|
401 |
-
* @param string $search_query The search query.
|
402 |
-
* @param string $operator The search operator (AND or OR).
|
403 |
-
*
|
404 |
-
* @return string $queries If not phrase hits are found, an empty string;
|
405 |
-
* otherwise MySQL queries to restrict the search.
|
406 |
-
*/
|
407 |
-
function relevanssi_recognize_phrases( $search_query, $operator = 'AND' ) {
|
408 |
-
global $relevanssi_variables;
|
409 |
-
|
410 |
-
$phrases = relevanssi_extract_phrases( $search_query );
|
411 |
-
|
412 |
-
$all_queries = array();
|
413 |
-
if ( 0 === count( $phrases ) ) {
|
414 |
-
return $all_queries;
|
415 |
-
}
|
416 |
-
|
417 |
-
$custom_fields = relevanssi_get_custom_fields();
|
418 |
-
$taxonomies = get_option( 'relevanssi_index_taxonomies_list', array() );
|
419 |
-
$excerpts = get_option( 'relevanssi_index_excerpt', 'off' );
|
420 |
-
$index_pdf_parent = get_option( 'relevanssi_index_pdf_parent' );
|
421 |
-
|
422 |
-
$phrase_queries = array();
|
423 |
-
$queries = array();
|
424 |
-
|
425 |
-
if (
|
426 |
-
isset( $relevanssi_variables['phrase_targets'] ) &&
|
427 |
-
is_array( $relevanssi_variables['phrase_targets'] )
|
428 |
-
) {
|
429 |
-
$non_targeted_phrases = array();
|
430 |
-
foreach ( $phrases as $phrase ) {
|
431 |
-
if (
|
432 |
-
isset( $relevanssi_variables['phrase_targets'][ $phrase ] ) &&
|
433 |
-
function_exists( 'relevanssi_targeted_phrases' )
|
434 |
-
) {
|
435 |
-
$queries = relevanssi_targeted_phrases( $phrase );
|
436 |
-
} else {
|
437 |
-
$non_targeted_phrases[] = $phrase;
|
438 |
-
}
|
439 |
-
}
|
440 |
-
$phrases = $non_targeted_phrases;
|
441 |
-
}
|
442 |
-
|
443 |
-
$queries = array_merge(
|
444 |
-
$queries,
|
445 |
-
relevanssi_generate_phrase_queries(
|
446 |
-
$phrases,
|
447 |
-
$taxonomies,
|
448 |
-
$custom_fields,
|
449 |
-
$excerpts,
|
450 |
-
$index_pdf_parent
|
451 |
-
)
|
452 |
-
);
|
453 |
-
|
454 |
-
$phrase_queries = array();
|
455 |
-
|
456 |
-
foreach ( $queries as $phrase => $p_queries ) {
|
457 |
-
$p_queries = implode( ' OR relevanssi.doc IN ', $p_queries );
|
458 |
-
$p_queries = "(relevanssi.doc IN $p_queries)";
|
459 |
-
$all_queries[] = $p_queries;
|
460 |
-
|
461 |
-
$phrase_queries[ $phrase ] = $p_queries;
|
462 |
-
}
|
463 |
-
|
464 |
-
$operator = strtoupper( $operator );
|
465 |
-
if ( 'AND' !== $operator && 'OR' !== $operator ) {
|
466 |
-
$operator = 'AND';
|
467 |
-
}
|
468 |
-
|
469 |
-
if ( ! empty( $all_queries ) ) {
|
470 |
-
$all_queries = ' AND ( ' . implode( ' ' . $operator . ' ', $all_queries ) . ' ) ';
|
471 |
-
}
|
472 |
-
|
473 |
-
return array(
|
474 |
-
'and' => $all_queries,
|
475 |
-
'or' => $phrase_queries,
|
476 |
-
);
|
477 |
-
}
|
478 |
-
|
479 |
-
/**
|
480 |
-
* Generates the phrase queries from phrases.
|
481 |
-
*
|
482 |
-
* Takes in phrases and a bunch of parameters and generates the MySQL queries
|
483 |
-
* that restrict the main search query to only posts that have the phrase.
|
484 |
-
*
|
485 |
-
* @param array $phrases A list of phrases to handle.
|
486 |
-
* @param array $taxonomies An array of taxonomy names to use.
|
487 |
-
* @param array $custom_fields A list of custom field names to use.
|
488 |
-
* @param string $excerpts If 'on', include excerpts.
|
489 |
-
* @param string $index_pdf_parent If 'on', include PDF parent.
|
490 |
-
*
|
491 |
-
* @global object $wpdb The WordPress database interface.
|
492 |
-
*
|
493 |
-
* @return array An array of queries sorted by phrase.
|
494 |
-
*/
|
495 |
-
function relevanssi_generate_phrase_queries( $phrases, $taxonomies, $custom_fields, $excerpts, $index_pdf_parent ) {
|
496 |
-
global $wpdb;
|
497 |
-
|
498 |
-
$status = relevanssi_valid_status_array();
|
499 |
-
|
500 |
-
// Add "inherit" to the list of allowed statuses to include attachments.
|
501 |
-
if ( ! strstr( $status, 'inherit' ) ) {
|
502 |
-
$status .= ",'inherit'";
|
503 |
-
}
|
504 |
-
|
505 |
-
$phrase_queries = array();
|
506 |
-
|
507 |
-
foreach ( $phrases as $phrase ) {
|
508 |
-
$queries = array();
|
509 |
-
$phrase = $wpdb->esc_like( $phrase );
|
510 |
-
$phrase = str_replace( array( '‘', '’', "'", '"', '”', '“', '“', '„', '´' ), '_', $phrase );
|
511 |
-
$phrase = htmlspecialchars( $phrase );
|
512 |
-
$phrase = esc_sql( $phrase );
|
513 |
-
|
514 |
-
$excerpt = '';
|
515 |
-
if ( 'on' === $excerpts ) {
|
516 |
-
$excerpt = "OR post_excerpt LIKE '%$phrase%'";
|
517 |
-
}
|
518 |
-
|
519 |
-
$query = "(SELECT ID FROM $wpdb->posts
|
520 |
-
WHERE (post_content LIKE '%$phrase%' OR post_title LIKE '%$phrase%' $excerpt)
|
521 |
-
AND post_status IN ($status))";
|
522 |
-
|
523 |
-
$queries[] = $query;
|
524 |
-
|
525 |
-
if ( $taxonomies ) {
|
526 |
-
$taxonomies_escaped = implode( "','", array_map( 'esc_sql', $taxonomies ) );
|
527 |
-
$taxonomies_sql = "AND s.taxonomy IN ('$taxonomies_escaped')";
|
528 |
-
|
529 |
-
$query = "(SELECT ID FROM $wpdb->posts as p, $wpdb->term_relationships as r, $wpdb->term_taxonomy as s, $wpdb->terms as t
|
530 |
-
WHERE r.term_taxonomy_id = s.term_taxonomy_id AND s.term_id = t.term_id AND p.ID = r.object_id
|
531 |
-
$taxonomies_sql
|
532 |
-
AND t.name LIKE '%$phrase%' AND p.post_status IN ($status))";
|
533 |
-
|
534 |
-
$queries[] = $query;
|
535 |
-
}
|
536 |
-
|
537 |
-
if ( $custom_fields ) {
|
538 |
-
$keys = '';
|
539 |
-
|
540 |
-
if ( is_array( $custom_fields ) ) {
|
541 |
-
if ( ! in_array( '_relevanssi_pdf_content', $custom_fields, true ) ) {
|
542 |
-
array_push( $custom_fields, '_relevanssi_pdf_content' );
|
543 |
-
}
|
544 |
-
|
545 |
-
if ( strpos( implode( ' ', $custom_fields ), '%' ) ) {
|
546 |
-
// ACF repeater fields involved.
|
547 |
-
$custom_fields_regexp = str_replace( '%', '.+', implode( '|', $custom_fields ) );
|
548 |
-
$keys = "AND m.meta_key REGEXP ('$custom_fields_regexp')";
|
549 |
-
} else {
|
550 |
-
$custom_fields_escaped = implode(
|
551 |
-
"','",
|
552 |
-
array_map(
|
553 |
-
'esc_sql',
|
554 |
-
$custom_fields
|
555 |
-
)
|
556 |
-
);
|
557 |
-
$keys = "AND m.meta_key IN ('$custom_fields_escaped')";
|
558 |
-
}
|
559 |
-
}
|
560 |
-
|
561 |
-
if ( 'visible' === $custom_fields ) {
|
562 |
-
$keys = "AND (m.meta_key NOT LIKE '\_%' OR m.meta_key = '_relevanssi_pdf_content')";
|
563 |
-
}
|
564 |
-
|
565 |
-
$query = "(SELECT ID
|
566 |
-
FROM $wpdb->posts AS p, $wpdb->postmeta AS m
|
567 |
-
WHERE p.ID = m.post_id
|
568 |
-
$keys
|
569 |
-
AND m.meta_value LIKE '%$phrase%'
|
570 |
-
AND p.post_status IN ($status))";
|
571 |
-
|
572 |
-
$queries[] = $query;
|
573 |
-
} elseif ( RELEVANSSI_PREMIUM ) {
|
574 |
-
$index_post_types = get_option( 'relevanssi_index_post_types', array() );
|
575 |
-
if ( in_array( 'attachment', $index_post_types, true ) ) {
|
576 |
-
$query = "(SELECT ID
|
577 |
-
FROM $wpdb->posts AS p, $wpdb->postmeta AS m
|
578 |
-
WHERE p.ID = m.post_id
|
579 |
-
AND m.meta_key = '_relevanssi_pdf_content'
|
580 |
-
AND m.meta_value LIKE '%$phrase%'
|
581 |
-
AND p.post_status IN ($status))";
|
582 |
-
|
583 |
-
$queries[] = $query;
|
584 |
-
}
|
585 |
-
}
|
586 |
-
|
587 |
-
if ( 'on' === $index_pdf_parent ) {
|
588 |
-
$query = "(SELECT parent.ID
|
589 |
-
FROM $wpdb->posts AS p, $wpdb->postmeta AS m, $wpdb->posts AS parent
|
590 |
-
WHERE p.ID = m.post_id
|
591 |
-
AND p.post_parent = parent.ID
|
592 |
-
AND m.meta_key = '_relevanssi_pdf_content'
|
593 |
-
AND m.meta_value LIKE '%$phrase%'
|
594 |
-
AND p.post_status = 'inherit')";
|
595 |
-
|
596 |
-
$queries[] = $query;
|
597 |
-
}
|
598 |
-
|
599 |
-
$phrase_queries[ $phrase ] = $queries;
|
600 |
-
}
|
601 |
-
|
602 |
-
return $phrase_queries;
|
603 |
-
}
|
604 |
-
|
605 |
-
/**
|
606 |
-
* Strips invisible elements from text.
|
607 |
-
*
|
608 |
-
* Strips <style>, <script>, <object>, <embed>, <applet>, <noscript>, <noembed>,
|
609 |
-
* <iframe>, and <del> tags and their contents from the text.
|
610 |
-
*
|
611 |
-
* @param string $text The source text.
|
612 |
-
*
|
613 |
-
* @return string The processed text.
|
614 |
-
*/
|
615 |
-
function relevanssi_strip_invisibles( $text ) {
|
616 |
-
$text = preg_replace(
|
617 |
-
array(
|
618 |
-
'@<style[^>]*?>.*?</style>@siu',
|
619 |
-
'@<script[^>]*?.*?</script>@siu',
|
620 |
-
'@<object[^>]*?.*?</object>@siu',
|
621 |
-
'@<embed[^>]*?.*?</embed>@siu',
|
622 |
-
'@<applet[^>]*?.*?</applet>@siu',
|
623 |
-
'@<noscript[^>]*?.*?</noscript>@siu',
|
624 |
-
'@<noembed[^>]*?.*?</noembed>@siu',
|
625 |
-
'@<iframe[^>]*?.*?</iframe>@siu',
|
626 |
-
'@<del[^>]*?.*?</del>@siu',
|
627 |
-
),
|
628 |
-
' ',
|
629 |
-
$text
|
630 |
-
);
|
631 |
-
return $text;
|
632 |
-
}
|
633 |
-
|
634 |
/**
|
635 |
* Returns the custom fields to index.
|
636 |
*
|
@@ -659,36 +280,6 @@ function relevanssi_get_custom_fields() {
|
|
659 |
return $custom_fields;
|
660 |
}
|
661 |
|
662 |
-
/**
|
663 |
-
* Trims multibyte strings.
|
664 |
-
*
|
665 |
-
* Removes the 194+160 non-breakable spaces, removes null bytes and removes whitespace.
|
666 |
-
*
|
667 |
-
* @param string $string The source string.
|
668 |
-
*
|
669 |
-
* @return string Trimmed string.
|
670 |
-
*/
|
671 |
-
function relevanssi_mb_trim( $string ) {
|
672 |
-
$string = str_replace( chr( 194 ) . chr( 160 ), '', $string );
|
673 |
-
$string = str_replace( "\0", '', $string );
|
674 |
-
$string = preg_replace( '/(^\s+)|(\s+$)/us', '', $string );
|
675 |
-
return $string;
|
676 |
-
}
|
677 |
-
|
678 |
-
/**
|
679 |
-
* Wraps the relevanssi_mb_trim() function so that it can be used as a callback for
|
680 |
-
* array_walk().
|
681 |
-
*
|
682 |
-
* @since 2.1.4
|
683 |
-
*
|
684 |
-
* @see relevanssi_mb_trim.
|
685 |
-
*
|
686 |
-
* @param string $string String to trim.
|
687 |
-
*/
|
688 |
-
function relevanssi_array_walk_trim( &$string ) {
|
689 |
-
$string = relevanssi_mb_trim( $string );
|
690 |
-
}
|
691 |
-
|
692 |
/**
|
693 |
* Removes punctuation from a string.
|
694 |
*
|
@@ -1154,72 +745,6 @@ function relevanssi_get_post_type( $post_id ) {
|
|
1154 |
}
|
1155 |
}
|
1156 |
|
1157 |
-
/**
|
1158 |
-
* Prints out a list of tags for post.
|
1159 |
-
*
|
1160 |
-
* Replacement for the_tags() that does the same, but applies Relevanssi search term
|
1161 |
-
* highlighting on the results.
|
1162 |
-
*
|
1163 |
-
* @param string $before What is printed before the tags, default null.
|
1164 |
-
* @param string $separator The separator between items, default ', '.
|
1165 |
-
* @param string $after What is printed after the tags, default ''.
|
1166 |
-
* @param boolean $echo If true, echo, otherwise return the result. Default true.
|
1167 |
-
* @param int $post_id The post ID. Default current post ID (in the Loop).
|
1168 |
-
*/
|
1169 |
-
function relevanssi_the_tags( $before = null, $separator = ', ', $after = '', $echo = true, $post_id = null ) {
|
1170 |
-
$tag_list = get_the_tag_list( $before, $separator, $after, $post_id );
|
1171 |
-
$found = preg_match_all( '~<a href=".*?" rel="tag">(.*?)</a>~', $tag_list, $matches );
|
1172 |
-
if ( $found ) {
|
1173 |
-
$originals = $matches[0];
|
1174 |
-
$tag_names = $matches[1];
|
1175 |
-
$highlighted = array();
|
1176 |
-
|
1177 |
-
$count = count( $matches[0] );
|
1178 |
-
for ( $i = 0; $i < $count; $i++ ) {
|
1179 |
-
$highlighted_tag_name = relevanssi_highlight_terms( $tag_names[ $i ], get_search_query(), true );
|
1180 |
-
$highlighted[ $i ] = str_replace( '>' . $tag_names[ $i ] . '<', '>' . $highlighted_tag_name . '<', $originals[ $i ] );
|
1181 |
-
}
|
1182 |
-
|
1183 |
-
$tag_list = str_replace( $originals, $highlighted, $tag_list );
|
1184 |
-
}
|
1185 |
-
|
1186 |
-
if ( $echo ) {
|
1187 |
-
echo $tag_list; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
1188 |
-
} else {
|
1189 |
-
return $tag_list;
|
1190 |
-
}
|
1191 |
-
}
|
1192 |
-
|
1193 |
-
/**
|
1194 |
-
* Gets a list of tags for post.
|
1195 |
-
*
|
1196 |
-
* Replacement for get_the_tags() that does the same, but applies Relevanssi search term
|
1197 |
-
* highlighting on the results.
|
1198 |
-
*
|
1199 |
-
* @param string $before What is printed before the tags, default null.
|
1200 |
-
* @param string $separator The separator between items, default ', '.
|
1201 |
-
* @param string $after What is printed after the tags, default ''.
|
1202 |
-
* @param int $post_id The post ID. Default current post ID (in the Loop).
|
1203 |
-
*/
|
1204 |
-
function relevanssi_get_the_tags( $before = null, $separator = ', ', $after = '', $post_id = null ) {
|
1205 |
-
return relevanssi_the_tags( $before, $separator, $after, false, $post_id );
|
1206 |
-
}
|
1207 |
-
|
1208 |
-
/**
|
1209 |
-
* Returns the term taxonomy ID for a term based on term ID.
|
1210 |
-
*
|
1211 |
-
* @global object $wpdb The WordPress database interface.
|
1212 |
-
*
|
1213 |
-
* @param int $term_id The term ID.
|
1214 |
-
* @param string $taxonomy The taxonomy.
|
1215 |
-
*
|
1216 |
-
* @return int Term taxonomy ID.
|
1217 |
-
*/
|
1218 |
-
function relevanssi_get_term_tax_id( $term_id, $taxonomy ) {
|
1219 |
-
global $wpdb;
|
1220 |
-
return $wpdb->get_var( $wpdb->prepare( "SELECT term_taxonomy_id FROM $wpdb->term_taxonomy WHERE term_id = %d AND taxonomy = %s", $term_id, $taxonomy ) );
|
1221 |
-
}
|
1222 |
-
|
1223 |
/**
|
1224 |
* Adds synonyms to a search query.
|
1225 |
*
|
@@ -1234,11 +759,13 @@ function relevanssi_add_synonyms( $query ) {
|
|
1234 |
return $query;
|
1235 |
}
|
1236 |
|
1237 |
-
$
|
1238 |
-
|
|
|
|
|
1239 |
$synonyms = array();
|
1240 |
-
$
|
1241 |
-
$pairs = explode( ';', $
|
1242 |
|
1243 |
foreach ( $pairs as $pair ) {
|
1244 |
if ( empty( $pair ) ) {
|
@@ -1292,105 +819,6 @@ function relevanssi_add_synonyms( $query ) {
|
|
1292 |
return $query;
|
1293 |
}
|
1294 |
|
1295 |
-
/**
|
1296 |
-
* Returns the position of substring in the string.
|
1297 |
-
*
|
1298 |
-
* Uses mb_stripos() if possible, falls back to mb_strpos() and mb_strtoupper() if
|
1299 |
-
* that cannot be found, and falls back to just strpos() if even that is not
|
1300 |
-
* possible.
|
1301 |
-
*
|
1302 |
-
* @param string $haystack String where to look.
|
1303 |
-
* @param string $needle The string to look for.
|
1304 |
-
* @param int $offset Where to start, default 0.
|
1305 |
-
*
|
1306 |
-
* @return mixed False, if no result or $offset outside the length of $haystack,
|
1307 |
-
* otherwise the position (which can be non-false 0!).
|
1308 |
-
*/
|
1309 |
-
function relevanssi_stripos( $haystack, $needle, $offset = 0 ) {
|
1310 |
-
if ( $offset > relevanssi_strlen( $haystack ) ) {
|
1311 |
-
return false;
|
1312 |
-
}
|
1313 |
-
|
1314 |
-
if ( preg_match( '/[\?\*]/', $needle ) ) {
|
1315 |
-
// There's a ? or an * in the string, which means it's a wildcard search
|
1316 |
-
// query (a Premium feature) and requires some extra steps.
|
1317 |
-
|
1318 |
-
$needle_regex = str_replace(
|
1319 |
-
array( '?', '*' ),
|
1320 |
-
array( '.', '.*' ),
|
1321 |
-
$needle
|
1322 |
-
);
|
1323 |
-
$pos_found = false;
|
1324 |
-
while ( ! $pos_found ) {
|
1325 |
-
preg_match(
|
1326 |
-
"/$needle_regex/i",
|
1327 |
-
$haystack,
|
1328 |
-
$matches,
|
1329 |
-
PREG_OFFSET_CAPTURE,
|
1330 |
-
$offset
|
1331 |
-
);
|
1332 |
-
/**
|
1333 |
-
* This trickery is necessary, because PREG_OFFSET_CAPTURE gives
|
1334 |
-
* wrong offsets for multibyte strings. The mb_strlen() gives the
|
1335 |
-
* correct offset, the rest of this is because the $offset received
|
1336 |
-
* as a parameter can be before the first $position, leading to an
|
1337 |
-
* infinite loop.
|
1338 |
-
*/
|
1339 |
-
$pos = isset( $matches[0][1] )
|
1340 |
-
? mb_strlen( substr( $haystack, 0, $matches[0][1] ) )
|
1341 |
-
: false;
|
1342 |
-
if ( $pos && $pos > $offset ) {
|
1343 |
-
$pos_found = true;
|
1344 |
-
} elseif ( $pos ) {
|
1345 |
-
$offset++;
|
1346 |
-
} else {
|
1347 |
-
$pos_found = true;
|
1348 |
-
}
|
1349 |
-
}
|
1350 |
-
} elseif ( function_exists( 'mb_stripos' ) ) {
|
1351 |
-
if ( '' === $haystack ) {
|
1352 |
-
$pos = false;
|
1353 |
-
} else {
|
1354 |
-
$pos = mb_stripos( $haystack, $needle, $offset );
|
1355 |
-
}
|
1356 |
-
} elseif ( function_exists( 'mb_strpos' ) && function_exists( 'mb_strtoupper' ) && function_exists( 'mb_substr' ) ) {
|
1357 |
-
$pos = mb_strpos( mb_strtoupper( $haystack ), mb_strtoupper( $needle ), $offset );
|
1358 |
-
} else {
|
1359 |
-
$pos = strpos( strtoupper( $haystack ), strtoupper( $needle ), $offset );
|
1360 |
-
}
|
1361 |
-
return $pos;
|
1362 |
-
}
|
1363 |
-
|
1364 |
-
/**
|
1365 |
-
* Closes tags in a bit of HTML code.
|
1366 |
-
*
|
1367 |
-
* Used to make sure no tags are left open in excerpts. This method is not foolproof,
|
1368 |
-
* but it's good enough for now.
|
1369 |
-
*
|
1370 |
-
* @param string $html The HTML code to analyze.
|
1371 |
-
*
|
1372 |
-
* @return string The HTML code, with tags closed.
|
1373 |
-
*/
|
1374 |
-
function relevanssi_close_tags( $html ) {
|
1375 |
-
$result = array();
|
1376 |
-
preg_match_all( '#<(?!meta|img|br|hr|input\b)\b([a-z]+)(?: .*)?(?<![/|/ ])>#iU', $html, $result );
|
1377 |
-
$opened_tags = $result[1];
|
1378 |
-
preg_match_all( '#</([a-z]+)>#iU', $html, $result );
|
1379 |
-
$closed_tags = $result[1];
|
1380 |
-
$len_opened = count( $opened_tags );
|
1381 |
-
if ( count( $closed_tags ) === $len_opened ) {
|
1382 |
-
return $html;
|
1383 |
-
}
|
1384 |
-
$opened_tags = array_reverse( $opened_tags );
|
1385 |
-
for ( $i = 0; $i < $len_opened; $i++ ) {
|
1386 |
-
if ( ! in_array( $opened_tags[ $i ], $closed_tags, true ) ) {
|
1387 |
-
$html .= '</' . $opened_tags[ $i ] . '>';
|
1388 |
-
} else {
|
1389 |
-
unset( $closed_tags[ array_search( $opened_tags[ $i ], $closed_tags, true ) ] );
|
1390 |
-
}
|
1391 |
-
}
|
1392 |
-
return $html;
|
1393 |
-
}
|
1394 |
|
1395 |
/**
|
1396 |
* Prints out post title with highlighting.
|
@@ -1463,49 +891,6 @@ function relevanssi_async_update_doc_count() {
|
|
1463 |
relevanssi_launch_ajax_action( 'relevanssi_update_counts' );
|
1464 |
}
|
1465 |
|
1466 |
-
/**
|
1467 |
-
* Returns the length of the string.
|
1468 |
-
*
|
1469 |
-
* Uses mb_strlen() if available, otherwise falls back to strlen().
|
1470 |
-
*
|
1471 |
-
* @param string $s The string to measure.
|
1472 |
-
*
|
1473 |
-
* @return int The length of the string.
|
1474 |
-
*/
|
1475 |
-
function relevanssi_strlen( $s ) {
|
1476 |
-
if ( function_exists( 'mb_strlen' ) ) {
|
1477 |
-
return mb_strlen( $s );
|
1478 |
-
}
|
1479 |
-
return strlen( $s );
|
1480 |
-
}
|
1481 |
-
|
1482 |
-
/**
|
1483 |
-
* Prints out debugging notices.
|
1484 |
-
*
|
1485 |
-
* If WP_CLI is available, prints out the debug notice as a WP_CLI::log(), otherwise
|
1486 |
-
* just echo.
|
1487 |
-
*
|
1488 |
-
* @param string $notice The notice to print out.
|
1489 |
-
*/
|
1490 |
-
function relevanssi_debug_echo( $notice ) {
|
1491 |
-
if ( defined( 'WP_CLI' ) && WP_CLI ) {
|
1492 |
-
WP_CLI::log( $notice );
|
1493 |
-
} else {
|
1494 |
-
echo esc_html( $notice ) . "\n";
|
1495 |
-
}
|
1496 |
-
}
|
1497 |
-
|
1498 |
-
/**
|
1499 |
-
* Returns a Relevanssi_Taxonomy_Walker instance.
|
1500 |
-
*
|
1501 |
-
* Requires the class file and generates a new Relevanssi_Taxonomy_Walker instance.
|
1502 |
-
*
|
1503 |
-
* @return object A new Relevanssi_Taxonomy_Walker instance.
|
1504 |
-
*/
|
1505 |
-
function get_relevanssi_taxonomy_walker() {
|
1506 |
-
require_once 'class-relevanssi-taxonomy-walker.php';
|
1507 |
-
return new Relevanssi_Taxonomy_Walker();
|
1508 |
-
}
|
1509 |
|
1510 |
/**
|
1511 |
* Adjusts Relevanssi variables when switch_blog() happens.
|
@@ -1574,34 +959,6 @@ function relevanssi_add_highlight( $permalink, $link_post = null ) {
|
|
1574 |
return $permalink;
|
1575 |
}
|
1576 |
|
1577 |
-
/**
|
1578 |
-
* Gets the permalink to the current post within Loop.
|
1579 |
-
*
|
1580 |
-
* Uses get_permalink() to get the permalink, then adds the 'highlight' parameter
|
1581 |
-
* if necessary using relevanssi_add_highlight().
|
1582 |
-
*
|
1583 |
-
* @return string The permalink.
|
1584 |
-
*/
|
1585 |
-
function relevanssi_get_permalink() {
|
1586 |
-
/**
|
1587 |
-
* Filters the permalink.
|
1588 |
-
*
|
1589 |
-
* @param string The permalink, generated by get_permalink().
|
1590 |
-
*/
|
1591 |
-
$permalink = apply_filters( 'relevanssi_permalink', get_permalink() );
|
1592 |
-
return $permalink;
|
1593 |
-
}
|
1594 |
-
|
1595 |
-
/**
|
1596 |
-
* Echoes out the permalink to the current post within Loop.
|
1597 |
-
*
|
1598 |
-
* Uses get_permalink() to get the permalink, then adds the 'highlight' parameter
|
1599 |
-
* if necessary using relevanssi_add_highlight(), then echoes it out.
|
1600 |
-
*/
|
1601 |
-
function relevanssi_the_permalink() {
|
1602 |
-
echo esc_url( relevanssi_get_permalink() );
|
1603 |
-
}
|
1604 |
-
|
1605 |
/**
|
1606 |
* Adjusts the permalink to use the Relevanssi-generated link.
|
1607 |
*
|
@@ -1637,178 +994,6 @@ function relevanssi_permalink( $link, $link_post = null ) {
|
|
1637 |
return $link;
|
1638 |
}
|
1639 |
|
1640 |
-
/**
|
1641 |
-
* Generates the Did you mean suggestions.
|
1642 |
-
*
|
1643 |
-
* A wrapper function that prints out the Did you mean suggestions. If Premium is
|
1644 |
-
* available, will use relevanssi_premium_didyoumean(), otherwise the
|
1645 |
-
* relevanssi_simple_didyoumean() is used.
|
1646 |
-
*
|
1647 |
-
* @param string $query The query.
|
1648 |
-
* @param string $pre Printed out before the suggestion.
|
1649 |
-
* @param string $post Printed out after the suggestion.
|
1650 |
-
* @param int $n Maximum number of search results found for the suggestions
|
1651 |
-
* to show up. Default 5.
|
1652 |
-
* @param boolean $echo If true, echo out. Default true.
|
1653 |
-
*
|
1654 |
-
* @return string The suggestion HTML element.
|
1655 |
-
*/
|
1656 |
-
function relevanssi_didyoumean( $query, $pre, $post, $n = 5, $echo = true ) {
|
1657 |
-
if ( function_exists( 'relevanssi_premium_didyoumean' ) ) {
|
1658 |
-
$result = relevanssi_premium_didyoumean( $query, $pre, $post, $n );
|
1659 |
-
} else {
|
1660 |
-
$result = relevanssi_simple_didyoumean( $query, $pre, $post, $n );
|
1661 |
-
}
|
1662 |
-
|
1663 |
-
if ( $echo ) {
|
1664 |
-
echo $result; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
1665 |
-
}
|
1666 |
-
|
1667 |
-
return $result;
|
1668 |
-
}
|
1669 |
-
|
1670 |
-
/**
|
1671 |
-
* Generates the Did you mean suggestions HTML code.
|
1672 |
-
*
|
1673 |
-
* Uses relevanssi_simple_generate_suggestion() to come up with a suggestion, then
|
1674 |
-
* wraps that up with HTML code.
|
1675 |
-
*
|
1676 |
-
* @global object $wpdb The WordPress database interface.
|
1677 |
-
* @global array $relevanssi_variables The Relevanssi global variables.
|
1678 |
-
* @global object $wp_query The WP_Query object.
|
1679 |
-
*
|
1680 |
-
* @param string $query The query.
|
1681 |
-
* @param string $pre Printed out before the suggestion.
|
1682 |
-
* @param string $post Printed out after the suggestion.
|
1683 |
-
* @param int $n Maximum number of search results found for the suggestions
|
1684 |
-
* to show up. Default 5.
|
1685 |
-
*
|
1686 |
-
* @return string The suggestion HTML code, null if nothing found.
|
1687 |
-
*/
|
1688 |
-
function relevanssi_simple_didyoumean( $query, $pre, $post, $n = 5 ) {
|
1689 |
-
global $wp_query;
|
1690 |
-
|
1691 |
-
$total_results = $wp_query->found_posts;
|
1692 |
-
|
1693 |
-
if ( $total_results > $n ) {
|
1694 |
-
return null;
|
1695 |
-
}
|
1696 |
-
|
1697 |
-
$suggestion = relevanssi_simple_generate_suggestion( $query );
|
1698 |
-
|
1699 |
-
$result = null;
|
1700 |
-
if ( $suggestion ) {
|
1701 |
-
$url = get_bloginfo( 'url' );
|
1702 |
-
$url = esc_attr( add_query_arg( array( 's' => rawurlencode( $suggestion ) ), $url ) );
|
1703 |
-
|
1704 |
-
/**
|
1705 |
-
* Filters the 'Did you mean' suggestion URL.
|
1706 |
-
*
|
1707 |
-
* @param string $url The URL for the suggested search query.
|
1708 |
-
* @param string $query The search query.
|
1709 |
-
* @param string $suggestion The suggestion.
|
1710 |
-
*/
|
1711 |
-
$url = apply_filters( 'relevanssi_didyoumean_url', $url, $query, $suggestion );
|
1712 |
-
|
1713 |
-
// Escape the suggestion to avoid XSS attacks.
|
1714 |
-
$suggestion = htmlspecialchars( $suggestion );
|
1715 |
-
|
1716 |
-
/**
|
1717 |
-
* Filters the complete 'Did you mean' suggestion.
|
1718 |
-
*
|
1719 |
-
* @param string The suggestion HTML code.
|
1720 |
-
*/
|
1721 |
-
$result = apply_filters( 'relevanssi_didyoumean_suggestion', "$pre<a href='$url'>$suggestion</a>$post" );
|
1722 |
-
}
|
1723 |
-
|
1724 |
-
return $result;
|
1725 |
-
}
|
1726 |
-
|
1727 |
-
/**
|
1728 |
-
* Generates the 'Did you mean' suggestions. Can be used to correct any queries.
|
1729 |
-
*
|
1730 |
-
* Uses the Relevanssi search logs as source material for corrections. If there are
|
1731 |
-
* no logged search queries, can't do anything.
|
1732 |
-
*
|
1733 |
-
* @global object $wpdb The WordPress database interface.
|
1734 |
-
* @global array $relevanssi_variables The Relevanssi global variables, used for
|
1735 |
-
* table names.
|
1736 |
-
*
|
1737 |
-
* @param string $query The query to correct.
|
1738 |
-
*
|
1739 |
-
* @return string Corrected query, empty if nothing found.
|
1740 |
-
*/
|
1741 |
-
function relevanssi_simple_generate_suggestion( $query ) {
|
1742 |
-
global $wpdb, $relevanssi_variables;
|
1743 |
-
|
1744 |
-
/**
|
1745 |
-
* The minimum limit of occurrances to include a word.
|
1746 |
-
*
|
1747 |
-
* To save resources, only words with more than this many occurrances are fed for
|
1748 |
-
* the spelling corrector. If there are problems with the spelling corrector,
|
1749 |
-
* increasing this value may fix those problems.
|
1750 |
-
*
|
1751 |
-
* @param int $number The number of occurrances must be more than this value,
|
1752 |
-
* default 2.
|
1753 |
-
*/
|
1754 |
-
$count = apply_filters( 'relevanssi_get_words_having', 2 );
|
1755 |
-
if ( ! is_numeric( $count ) ) {
|
1756 |
-
$count = 2;
|
1757 |
-
}
|
1758 |
-
$q = 'SELECT query, count(query) as c, AVG(hits) as a FROM ' . $relevanssi_variables['log_table'] . ' WHERE hits > ' . $count . ' GROUP BY query ORDER BY count(query) DESC';
|
1759 |
-
$q = apply_filters( 'relevanssi_didyoumean_query', $q );
|
1760 |
-
|
1761 |
-
$data = get_transient( 'relevanssi_didyoumean_query' );
|
1762 |
-
if ( empty( $data ) ) {
|
1763 |
-
$data = $wpdb->get_results( $q ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
|
1764 |
-
set_transient( 'relevanssi_didyoumean_query', $data, MONTH_IN_SECONDS );
|
1765 |
-
}
|
1766 |
-
|
1767 |
-
$query = htmlspecialchars_decode( $query, ENT_QUOTES );
|
1768 |
-
$tokens = relevanssi_tokenize( $query );
|
1769 |
-
$suggestions_made = false;
|
1770 |
-
$suggestion = '';
|
1771 |
-
|
1772 |
-
foreach ( $tokens as $token => $count ) {
|
1773 |
-
$closest = '';
|
1774 |
-
$distance = -1;
|
1775 |
-
foreach ( $data as $row ) {
|
1776 |
-
if ( $row->c < 2 ) {
|
1777 |
-
break;
|
1778 |
-
}
|
1779 |
-
|
1780 |
-
if ( $token === $row->query ) {
|
1781 |
-
$closest = '';
|
1782 |
-
break;
|
1783 |
-
} else {
|
1784 |
-
if ( relevanssi_strlen( $token ) < 255 ) {
|
1785 |
-
// The levenshtein() function has a max length of 255 characters.
|
1786 |
-
$lev = levenshtein( $token, $row->query );
|
1787 |
-
if ( $lev < 3 && ( $lev < $distance || $distance < 0 ) ) {
|
1788 |
-
if ( $row->a > 0 ) {
|
1789 |
-
$distance = $lev;
|
1790 |
-
$closest = $row->query;
|
1791 |
-
if ( $lev < 2 ) {
|
1792 |
-
break; // get the first with distance of 1 and go.
|
1793 |
-
}
|
1794 |
-
}
|
1795 |
-
}
|
1796 |
-
}
|
1797 |
-
}
|
1798 |
-
}
|
1799 |
-
if ( ! empty( $closest ) ) {
|
1800 |
-
$query = str_ireplace( $token, $closest, $query );
|
1801 |
-
$suggestions_made = true;
|
1802 |
-
}
|
1803 |
-
}
|
1804 |
-
|
1805 |
-
if ( $suggestions_made ) {
|
1806 |
-
$suggestion = $query;
|
1807 |
-
}
|
1808 |
-
|
1809 |
-
return $suggestion;
|
1810 |
-
}
|
1811 |
-
|
1812 |
/**
|
1813 |
* Instructs a multisite installation to drop the tables.
|
1814 |
*
|
@@ -1830,77 +1015,6 @@ function relevanssi_wpmu_drop( $tables ) {
|
|
1830 |
return $tables;
|
1831 |
}
|
1832 |
|
1833 |
-
/**
|
1834 |
-
* Replacement for get_post() that uses the Relevanssi post cache.
|
1835 |
-
*
|
1836 |
-
* Tries to fetch the post from the Relevanssi post cache. If that doesn't work, gets
|
1837 |
-
* the post using get_post().
|
1838 |
-
*
|
1839 |
-
* @param int $post_id The post ID.
|
1840 |
-
* @param int $blog_id The blog ID, default -1.
|
1841 |
-
*
|
1842 |
-
* @return object The post object.
|
1843 |
-
*/
|
1844 |
-
function relevanssi_get_post( $post_id, $blog_id = -1 ) {
|
1845 |
-
if ( function_exists( 'relevanssi_premium_get_post' ) ) {
|
1846 |
-
return relevanssi_premium_get_post( $post_id, $blog_id );
|
1847 |
-
}
|
1848 |
-
|
1849 |
-
global $relevanssi_post_array;
|
1850 |
-
|
1851 |
-
$post = null;
|
1852 |
-
if ( isset( $relevanssi_post_array[ $post_id ] ) ) {
|
1853 |
-
$post = $relevanssi_post_array[ $post_id ];
|
1854 |
-
}
|
1855 |
-
if ( ! $post ) {
|
1856 |
-
$post = get_post( $post_id );
|
1857 |
-
|
1858 |
-
$relevanssi_post_array[ $post_id ] = $post;
|
1859 |
-
}
|
1860 |
-
return $post;
|
1861 |
-
}
|
1862 |
-
|
1863 |
-
/**
|
1864 |
-
* Recursively flattens a multidimensional array to produce a string.
|
1865 |
-
*
|
1866 |
-
* @param array $array The source array.
|
1867 |
-
*
|
1868 |
-
* @return string The array contents as a string.
|
1869 |
-
*/
|
1870 |
-
function relevanssi_flatten_array( array $array ) {
|
1871 |
-
$return_value = '';
|
1872 |
-
foreach ( new RecursiveIteratorIterator( new RecursiveArrayIterator( $array ) ) as $value ) {
|
1873 |
-
$return_value .= ' ' . $value;
|
1874 |
-
}
|
1875 |
-
return trim( $return_value );
|
1876 |
-
}
|
1877 |
-
|
1878 |
-
/**
|
1879 |
-
* Sanitizes hex color strings.
|
1880 |
-
*
|
1881 |
-
* A copy of sanitize_hex_color(), because that isn't always available.
|
1882 |
-
*
|
1883 |
-
* @param string $color A hex color string to sanitize.
|
1884 |
-
*
|
1885 |
-
* @return string Sanitized hex string, or an empty string.
|
1886 |
-
*/
|
1887 |
-
function relevanssi_sanitize_hex_color( $color ) {
|
1888 |
-
if ( '' === $color ) {
|
1889 |
-
return '';
|
1890 |
-
}
|
1891 |
-
|
1892 |
-
if ( '#' !== substr( $color, 0, 1 ) ) {
|
1893 |
-
$color = '#' . $color;
|
1894 |
-
}
|
1895 |
-
|
1896 |
-
// 3 or 6 hex digits, or the empty string.
|
1897 |
-
if ( preg_match( '|^#([A-Fa-f0-9]{3}){1,2}$|', $color ) ) {
|
1898 |
-
return $color;
|
1899 |
-
}
|
1900 |
-
|
1901 |
-
return '';
|
1902 |
-
}
|
1903 |
-
|
1904 |
/**
|
1905 |
* Displays the list of most common words in the index.
|
1906 |
*
|
@@ -2009,7 +1123,7 @@ function relevanssi_get_forbidden_post_types() {
|
|
2009 |
'bigcommerce_task', // BigCommerce.
|
2010 |
'slides', // Qoda slides.
|
2011 |
'carousels', // Qoda carousels.
|
2012 |
-
|
2013 |
);
|
2014 |
}
|
2015 |
|
@@ -2031,17 +1145,6 @@ function relevanssi_get_forbidden_taxonomies() {
|
|
2031 |
);
|
2032 |
}
|
2033 |
|
2034 |
-
/**
|
2035 |
-
* Returns "off".
|
2036 |
-
*
|
2037 |
-
* Useful for returning "off" to filters easily.
|
2038 |
-
*
|
2039 |
-
* @return string A string with value "off".
|
2040 |
-
*/
|
2041 |
-
function relevanssi_return_off() {
|
2042 |
-
return 'off';
|
2043 |
-
}
|
2044 |
-
|
2045 |
/**
|
2046 |
* Filters out unwanted custom fields.
|
2047 |
*
|
@@ -2065,7 +1168,6 @@ function relevanssi_filter_custom_fields( $values, $field ) {
|
|
2065 |
return $values;
|
2066 |
}
|
2067 |
|
2068 |
-
|
2069 |
/**
|
2070 |
* Removes page builder short codes from content.
|
2071 |
*
|
@@ -2221,46 +1323,6 @@ EOH;
|
|
2221 |
return $notice;
|
2222 |
}
|
2223 |
|
2224 |
-
/**
|
2225 |
-
* Launches an asynchronous Ajax action.
|
2226 |
-
*
|
2227 |
-
* Makes a wp_remote_post() call with the specific action. Handles nonce
|
2228 |
-
* verification.
|
2229 |
-
*
|
2230 |
-
* @see wp_remove_post()
|
2231 |
-
* @see wp_create_nonce()
|
2232 |
-
*
|
2233 |
-
* @param string $action The action to trigger (also the name of the
|
2234 |
-
* nonce).
|
2235 |
-
* @param array $payload_args The parameters sent to the action. Defaults to
|
2236 |
-
* an empty array.
|
2237 |
-
*
|
2238 |
-
* @return WP_Error|array The wp_remote_post() response or WP_Error on failure.
|
2239 |
-
*/
|
2240 |
-
function relevanssi_launch_ajax_action( $action, $payload_args = array() ) {
|
2241 |
-
$cookies = array();
|
2242 |
-
foreach ( $_COOKIE as $name => $value ) {
|
2243 |
-
$cookies[] = "$name=" . rawurlencode(
|
2244 |
-
is_array( $value ) ? wp_json_encode( $value ) : $value
|
2245 |
-
);
|
2246 |
-
}
|
2247 |
-
$default_payload = array(
|
2248 |
-
'action' => $action,
|
2249 |
-
'_nonce' => wp_create_nonce( $action ),
|
2250 |
-
);
|
2251 |
-
$payload = array_merge( $default_payload, $payload_args );
|
2252 |
-
$args = array(
|
2253 |
-
'timeout' => 0.01,
|
2254 |
-
'blocking' => false,
|
2255 |
-
'body' => $payload,
|
2256 |
-
'headers' => array(
|
2257 |
-
'cookie' => implode( '; ', $cookies ),
|
2258 |
-
),
|
2259 |
-
);
|
2260 |
-
$url = admin_url( 'admin-ajax.php' );
|
2261 |
-
return wp_remote_post( $url, $args );
|
2262 |
-
}
|
2263 |
-
|
2264 |
/**
|
2265 |
* Fetches the data and generates the HTML for the "How Relevanssi sees this
|
2266 |
* post".
|
@@ -2431,61 +1493,6 @@ function relevanssi_fetch_sees_data( $post_id ) {
|
|
2431 |
);
|
2432 |
}
|
2433 |
|
2434 |
-
/**
|
2435 |
-
* Removes quotes from array keys. Does not keep array values.
|
2436 |
-
*
|
2437 |
-
* Used to remove phrase quotes from search term array, which have the format
|
2438 |
-
* of (term => hits). The number of hits is not needed, so this function
|
2439 |
-
* discards it as a side effect.
|
2440 |
-
*
|
2441 |
-
* @param array $array An array to process.
|
2442 |
-
*
|
2443 |
-
* @return array The same array with quotes removed from the keys.
|
2444 |
-
*/
|
2445 |
-
function relevanssi_remove_quotes_from_array_keys( $array ) {
|
2446 |
-
$array = array_keys( $array );
|
2447 |
-
array_walk(
|
2448 |
-
$array,
|
2449 |
-
function( &$key ) {
|
2450 |
-
$key = relevanssi_remove_quotes( $key );
|
2451 |
-
}
|
2452 |
-
);
|
2453 |
-
return array_flip( $array );
|
2454 |
-
}
|
2455 |
-
|
2456 |
-
/**
|
2457 |
-
* Removes quotes (", ”, “) from a string.
|
2458 |
-
*
|
2459 |
-
* @param string $string The string to clean.
|
2460 |
-
*
|
2461 |
-
* @return string The cleaned string.
|
2462 |
-
*/
|
2463 |
-
function relevanssi_remove_quotes( $string ) {
|
2464 |
-
return str_replace( array( '”', '“', '"' ), '', $string );
|
2465 |
-
}
|
2466 |
-
|
2467 |
-
/**
|
2468 |
-
* Strips tags from contents, keeping the allowed tags.
|
2469 |
-
*
|
2470 |
-
* The allowable tags are read from the relevanssi_excerpt_allowable_tags
|
2471 |
-
* option. Spaces are added between tags before removing the tags, so that
|
2472 |
-
* words don't get stuck together. The function also remove invisible content.
|
2473 |
-
*
|
2474 |
-
* @see relevanssi_strip_invisibles
|
2475 |
-
*
|
2476 |
-
* @param string $content The content.
|
2477 |
-
*
|
2478 |
-
* @return string The content without tags.
|
2479 |
-
*/
|
2480 |
-
function relevanssi_strip_tags( $content ) {
|
2481 |
-
$content = relevanssi_strip_invisibles( $content );
|
2482 |
-
$content = preg_replace( '/(<\/[^>]+?>)(<[^>\/][^>]*?>)/', '$1 $2', $content );
|
2483 |
-
return strip_tags(
|
2484 |
-
$content,
|
2485 |
-
get_option( 'relevanssi_excerpt_allowable_tags', '' )
|
2486 |
-
);
|
2487 |
-
}
|
2488 |
-
|
2489 |
/**
|
2490 |
* Generates a list of custom fields for a post.
|
2491 |
*
|
@@ -2544,3 +1551,16 @@ function relevanssi_generate_list_of_custom_fields( $post_id, $custom_fields = n
|
|
2544 |
|
2545 |
return $custom_fields;
|
2546 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
8 |
* @see https://www.relevanssi.com/
|
9 |
*/
|
10 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
11 |
/**
|
12 |
* Adds the search result match breakdown to the post object.
|
13 |
*
|
252 |
wp_suspend_cache_addition( false );
|
253 |
}
|
254 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
255 |
/**
|
256 |
* Returns the custom fields to index.
|
257 |
*
|
280 |
return $custom_fields;
|
281 |
}
|
282 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
283 |
/**
|
284 |
* Removes punctuation from a string.
|
285 |
*
|
745 |
}
|
746 |
}
|
747 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
748 |
/**
|
749 |
* Adds synonyms to a search query.
|
750 |
*
|
759 |
return $query;
|
760 |
}
|
761 |
|
762 |
+
$current_language = relevanssi_get_current_language();
|
763 |
+
$synonym_data = get_option( 'relevanssi_synonyms', array() );
|
764 |
+
$synonym_list = isset( $synonym_data[ $current_language ] ) ? $synonym_data[ $current_language ] : '';
|
765 |
+
if ( $synonym_list ) {
|
766 |
$synonyms = array();
|
767 |
+
$synonym_list = relevanssi_strtolower( $synonym_list );
|
768 |
+
$pairs = explode( ';', $synonym_list );
|
769 |
|
770 |
foreach ( $pairs as $pair ) {
|
771 |
if ( empty( $pair ) ) {
|
819 |
return $query;
|
820 |
}
|
821 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
822 |
|
823 |
/**
|
824 |
* Prints out post title with highlighting.
|
891 |
relevanssi_launch_ajax_action( 'relevanssi_update_counts' );
|
892 |
}
|
893 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
894 |
|
895 |
/**
|
896 |
* Adjusts Relevanssi variables when switch_blog() happens.
|
959 |
return $permalink;
|
960 |
}
|
961 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
962 |
/**
|
963 |
* Adjusts the permalink to use the Relevanssi-generated link.
|
964 |
*
|
994 |
return $link;
|
995 |
}
|
996 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
997 |
/**
|
998 |
* Instructs a multisite installation to drop the tables.
|
999 |
*
|
1015 |
return $tables;
|
1016 |
}
|
1017 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1018 |
/**
|
1019 |
* Displays the list of most common words in the index.
|
1020 |
*
|
1123 |
'bigcommerce_task', // BigCommerce.
|
1124 |
'slides', // Qoda slides.
|
1125 |
'carousels', // Qoda carousels.
|
1126 |
+
'pretty-link', // Pretty Links.
|
1127 |
);
|
1128 |
}
|
1129 |
|
1145 |
);
|
1146 |
}
|
1147 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1148 |
/**
|
1149 |
* Filters out unwanted custom fields.
|
1150 |
*
|
1168 |
return $values;
|
1169 |
}
|
1170 |
|
|
|
1171 |
/**
|
1172 |
* Removes page builder short codes from content.
|
1173 |
*
|
1323 |
return $notice;
|
1324 |
}
|
1325 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1326 |
/**
|
1327 |
* Fetches the data and generates the HTML for the "How Relevanssi sees this
|
1328 |
* post".
|
1493 |
);
|
1494 |
}
|
1495 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1496 |
/**
|
1497 |
* Generates a list of custom fields for a post.
|
1498 |
*
|
1551 |
|
1552 |
return $custom_fields;
|
1553 |
}
|
1554 |
+
|
1555 |
+
/**
|
1556 |
+
* Updates the relevanssi_synonyms setting from a simple string to an array
|
1557 |
+
* that is required for multilingual synonyms.
|
1558 |
+
*/
|
1559 |
+
function relevanssi_update_synonyms_setting() {
|
1560 |
+
$synonyms = get_option( 'relevanssi_synonyms' );
|
1561 |
+
|
1562 |
+
$current_language = relevanssi_get_current_language();
|
1563 |
+
|
1564 |
+
$array_synonyms[ $current_language ] = $synonyms;
|
1565 |
+
update_option( 'relevanssi_synonyms', $array_synonyms );
|
1566 |
+
}
|
lib/compatibility/oxygen.php
CHANGED
@@ -12,7 +12,7 @@
|
|
12 |
|
13 |
add_filter( 'relevanssi_custom_field_value', 'relevanssi_oxygen_compatibility', 10, 3 );
|
14 |
add_filter( 'relevanssi_index_custom_fields', 'relevanssi_add_oxygen' );
|
15 |
-
|
16 |
/**
|
17 |
* Cleans up the Oxygen Builder custom field for Relevanssi consumption.
|
18 |
*
|
@@ -120,3 +120,17 @@ function relevanssi_add_oxygen( $fields ) {
|
|
120 |
return $fields;
|
121 |
}
|
122 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
12 |
|
13 |
add_filter( 'relevanssi_custom_field_value', 'relevanssi_oxygen_compatibility', 10, 3 );
|
14 |
add_filter( 'relevanssi_index_custom_fields', 'relevanssi_add_oxygen' );
|
15 |
+
add_filter( 'pre_option_relevanssi_index_fields', 'relevanssi_oxygen_fix_none_setting' );
|
16 |
/**
|
17 |
* Cleans up the Oxygen Builder custom field for Relevanssi consumption.
|
18 |
*
|
120 |
return $fields;
|
121 |
}
|
122 |
|
123 |
+
/**
|
124 |
+
* Makes sure the Oxygen builder shortcode is included in the index, even when
|
125 |
+
* the custom field setting is set to 'none'.
|
126 |
+
*
|
127 |
+
* @param string $value The custom field indexing setting value.
|
128 |
+
*
|
129 |
+
* @return string If value is undefined, it's set to 'ct_builder_shortcodes'.
|
130 |
+
*/
|
131 |
+
function relevanssi_oxygen_fix_none_setting( $value ) {
|
132 |
+
if ( ! $value ) {
|
133 |
+
$value = 'ct_builder_shortcodes';
|
134 |
+
}
|
135 |
+
return $value;
|
136 |
+
}
|
lib/compatibility/polylang.php
CHANGED
@@ -171,3 +171,35 @@ function relevanssi_polylang_term_filter( $hits ) {
|
|
171 |
}
|
172 |
return $hits;
|
173 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
171 |
}
|
172 |
return $hits;
|
173 |
}
|
174 |
+
|
175 |
+
/**
|
176 |
+
* Returns the term_taxonomy_id matching the Polylang language based on locale.
|
177 |
+
*
|
178 |
+
* @param string $locale The locale string for the language.
|
179 |
+
*
|
180 |
+
* @return int The term_taxonomy_id for the language; 0 if nothing is found.
|
181 |
+
*/
|
182 |
+
function relevanssi_get_language_term_taxonomy_id( $locale ) {
|
183 |
+
global $wpdb, $relevanssi_language_term_ids;
|
184 |
+
|
185 |
+
if ( isset( $relevanssi_language_term_ids[ $locale ] ) ) {
|
186 |
+
return $relevanssi_language_term_ids[ $locale ];
|
187 |
+
}
|
188 |
+
|
189 |
+
$languages = $wpdb->get_results(
|
190 |
+
"SELECT term_taxonomy_id, description FROM $wpdb->term_taxonomy " .
|
191 |
+
"WHERE taxonomy = 'language'"
|
192 |
+
);
|
193 |
+
$term_id = 0;
|
194 |
+
foreach ( $languages as $row ) {
|
195 |
+
$description = unserialize( $row->description ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions
|
196 |
+
if ( $description['locale'] === $locale ) {
|
197 |
+
$term_id = $row->term_taxonomy_id;
|
198 |
+
break;
|
199 |
+
}
|
200 |
+
}
|
201 |
+
|
202 |
+
$relevanssi_language_term_ids[ $locale ] = $term_id;
|
203 |
+
|
204 |
+
return $term_id;
|
205 |
+
}
|
lib/compatibility/rankmath.php
ADDED
@@ -0,0 +1,59 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* /lib/compatibility/rankmath.php
|
4 |
+
*
|
5 |
+
* Rank Math noindex filtering function.
|
6 |
+
*
|
7 |
+
* @package Relevanssi
|
8 |
+
* @license https://wordpress.org/about/gpl/ GNU General Public License
|
9 |
+
* @see https://www.relevanssi.com/
|
10 |
+
*/
|
11 |
+
|
12 |
+
add_filter( 'relevanssi_do_not_index', 'relevanssi_rankmath_noindex', 10, 2 );
|
13 |
+
add_filter( 'relevanssi_indexing_restriction', 'relevanssi_rankmath_exclude' );
|
14 |
+
|
15 |
+
/**
|
16 |
+
* Blocks indexing of posts marked "noindex" in the Rank Math settings.
|
17 |
+
*
|
18 |
+
* Attaches to the 'relevanssi_do_not_index' filter hook.
|
19 |
+
*
|
20 |
+
* @param boolean $do_not_index True, if the post shouldn't be indexed.
|
21 |
+
* @param integer $post_id The post ID number.
|
22 |
+
*
|
23 |
+
* @return string|boolean If the post shouldn't be indexed, this returns
|
24 |
+
* 'RankMath'. The value may also be a boolean.
|
25 |
+
*/
|
26 |
+
function relevanssi_rankmath_noindex( $do_not_index, $post_id ) {
|
27 |
+
$noindex = get_post_meta( $post_id, 'rank_math_robots', true );
|
28 |
+
if ( in_array( 'noindex', $noindex, true ) ) {
|
29 |
+
$do_not_index = 'RankMath';
|
30 |
+
}
|
31 |
+
return $do_not_index;
|
32 |
+
}
|
33 |
+
|
34 |
+
/**
|
35 |
+
* Excludes the "noindex" posts from Relevanssi indexing.
|
36 |
+
*
|
37 |
+
* Adds a MySQL query restriction that blocks posts that have the Rank Math
|
38 |
+
* "rank_math_robots" setting set to something that includes "noindex".
|
39 |
+
*
|
40 |
+
* @param array $restriction An array with two values: 'mysql' for the MySQL
|
41 |
+
* query restriction to modify, 'reason' for the reason of restriction.
|
42 |
+
*/
|
43 |
+
function relevanssi_rankmath_exclude( $restriction ) {
|
44 |
+
global $wpdb;
|
45 |
+
|
46 |
+
// Backwards compatibility code for 2.8.0, remove at some point.
|
47 |
+
if ( is_string( $restriction ) ) {
|
48 |
+
$restriction = array(
|
49 |
+
'mysql' => $restriction,
|
50 |
+
'reason' => '',
|
51 |
+
);
|
52 |
+
}
|
53 |
+
|
54 |
+
$restriction['mysql'] .= " AND post.ID NOT IN (SELECT post_id FROM
|
55 |
+
$wpdb->postmeta WHERE meta_key = 'rank_math_robots'
|
56 |
+
AND meta_value LIKE '%noindex%' ) ";
|
57 |
+
$restriction['reason'] .= ' Rank Math';
|
58 |
+
return $restriction;
|
59 |
+
}
|
lib/didyoumean.php
ADDED
@@ -0,0 +1,196 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* /lib/didyoumean.php
|
4 |
+
*
|
5 |
+
* @package Relevanssi
|
6 |
+
* @author Mikko Saari
|
7 |
+
* @license https://wordpress.org/about/gpl/ GNU General Public License
|
8 |
+
* @see https://www.relevanssi.com/
|
9 |
+
*/
|
10 |
+
|
11 |
+
/**
|
12 |
+
* Generates the Did you mean suggestions.
|
13 |
+
*
|
14 |
+
* A wrapper function that prints out the Did you mean suggestions. If Premium
|
15 |
+
* is available, will use relevanssi_premium_didyoumean(), otherwise the
|
16 |
+
* relevanssi_simple_didyoumean() is used.
|
17 |
+
*
|
18 |
+
* @param string $query The query.
|
19 |
+
* @param string $pre Printed out before the suggestion.
|
20 |
+
* @param string $post Printed out after the suggestion.
|
21 |
+
* @param int $n Maximum number of search results found for the
|
22 |
+
* suggestions to show up. Default 5.
|
23 |
+
* @param boolean $echo If true, echo out. Default true.
|
24 |
+
*
|
25 |
+
* @return string|null The suggestion HTML element.
|
26 |
+
*/
|
27 |
+
function relevanssi_didyoumean( $query, $pre, $post, $n = 5, $echo = true ) {
|
28 |
+
if ( function_exists( 'relevanssi_premium_didyoumean' ) ) {
|
29 |
+
$result = relevanssi_premium_didyoumean( $query, $pre, $post, $n );
|
30 |
+
} else {
|
31 |
+
$result = relevanssi_simple_didyoumean( $query, $pre, $post, $n );
|
32 |
+
}
|
33 |
+
|
34 |
+
if ( $echo ) {
|
35 |
+
echo $result; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
36 |
+
}
|
37 |
+
|
38 |
+
return $result;
|
39 |
+
}
|
40 |
+
|
41 |
+
/**
|
42 |
+
* Generates the Did you mean suggestions HTML code.
|
43 |
+
*
|
44 |
+
* Uses relevanssi_simple_generate_suggestion() to come up with a suggestion,
|
45 |
+
* then wraps that up with HTML code.
|
46 |
+
*
|
47 |
+
* @global object $wpdb The WordPress database interface.
|
48 |
+
* @global array $relevanssi_variables The Relevanssi global variables.
|
49 |
+
* @global object $wp_query The WP_Query object.
|
50 |
+
*
|
51 |
+
* @param string $query The query.
|
52 |
+
* @param string $pre Printed out before the suggestion.
|
53 |
+
* @param string $post Printed out after the suggestion.
|
54 |
+
* @param int $n Maximum number of search results found for the
|
55 |
+
* suggestions to show up. Default 5.
|
56 |
+
*
|
57 |
+
* @return string|null The suggestion HTML code, null if nothing found.
|
58 |
+
*/
|
59 |
+
function relevanssi_simple_didyoumean( $query, $pre, $post, $n = 5 ) {
|
60 |
+
global $wp_query;
|
61 |
+
|
62 |
+
$total_results = $wp_query->found_posts;
|
63 |
+
|
64 |
+
if ( $total_results > $n ) {
|
65 |
+
return null;
|
66 |
+
}
|
67 |
+
|
68 |
+
$suggestion = relevanssi_simple_generate_suggestion( $query );
|
69 |
+
|
70 |
+
$result = null;
|
71 |
+
if ( $suggestion ) {
|
72 |
+
$url = get_bloginfo( 'url' );
|
73 |
+
$url = esc_attr(
|
74 |
+
add_query_arg(
|
75 |
+
array( 's' => rawurlencode( $suggestion ) ),
|
76 |
+
$url
|
77 |
+
)
|
78 |
+
);
|
79 |
+
|
80 |
+
/**
|
81 |
+
* Filters the 'Did you mean' suggestion URL.
|
82 |
+
*
|
83 |
+
* @param string $url The URL for the suggested search query.
|
84 |
+
* @param string $query The search query.
|
85 |
+
* @param string $suggestion The suggestion.
|
86 |
+
*/
|
87 |
+
$url = apply_filters(
|
88 |
+
'relevanssi_didyoumean_url',
|
89 |
+
$url,
|
90 |
+
$query,
|
91 |
+
$suggestion
|
92 |
+
);
|
93 |
+
|
94 |
+
// Escape the suggestion to avoid XSS attacks.
|
95 |
+
$suggestion = htmlspecialchars( $suggestion );
|
96 |
+
|
97 |
+
/**
|
98 |
+
* Filters the complete 'Did you mean' suggestion.
|
99 |
+
*
|
100 |
+
* @param string The suggestion HTML code.
|
101 |
+
*/
|
102 |
+
$result = apply_filters(
|
103 |
+
'relevanssi_didyoumean_suggestion',
|
104 |
+
"$pre<a href='$url'>$suggestion</a>$post"
|
105 |
+
);
|
106 |
+
}
|
107 |
+
|
108 |
+
return $result;
|
109 |
+
}
|
110 |
+
|
111 |
+
/**
|
112 |
+
* Generates the 'Did you mean' suggestions. Can be used to correct any queries.
|
113 |
+
*
|
114 |
+
* Uses the Relevanssi search logs as source material for corrections. If there
|
115 |
+
* are no logged search queries, can't do anything.
|
116 |
+
*
|
117 |
+
* @global object $wpdb The WordPress database interface.
|
118 |
+
* @global array $relevanssi_variables The Relevanssi global variables, used
|
119 |
+
* for table names.
|
120 |
+
*
|
121 |
+
* @param string $query The query to correct.
|
122 |
+
*
|
123 |
+
* @return string Corrected query, empty if nothing found.
|
124 |
+
*/
|
125 |
+
function relevanssi_simple_generate_suggestion( $query ) {
|
126 |
+
global $wpdb, $relevanssi_variables;
|
127 |
+
|
128 |
+
/**
|
129 |
+
* The minimum limit of occurrances to include a word.
|
130 |
+
*
|
131 |
+
* To save resources, only words with more than this many occurrances are
|
132 |
+
* fed for the spelling corrector. If there are problems with the spelling
|
133 |
+
* corrector, increasing this value may fix those problems.
|
134 |
+
*
|
135 |
+
* @param int $number The number of occurrances must be more than this
|
136 |
+
* value, default 2.
|
137 |
+
*/
|
138 |
+
$count = apply_filters( 'relevanssi_get_words_having', 2 );
|
139 |
+
if ( ! is_numeric( $count ) ) {
|
140 |
+
$count = 2;
|
141 |
+
}
|
142 |
+
$q = 'SELECT query, count(query) as c, AVG(hits) as a FROM '
|
143 |
+
. $relevanssi_variables['log_table'] . ' WHERE hits > ' . $count
|
144 |
+
. ' GROUP BY query ORDER BY count(query) DESC';
|
145 |
+
$q = apply_filters( 'relevanssi_didyoumean_query', $q );
|
146 |
+
|
147 |
+
$data = get_transient( 'relevanssi_didyoumean_query' );
|
148 |
+
if ( empty( $data ) ) {
|
149 |
+
$data = $wpdb->get_results( $q ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
|
150 |
+
set_transient( 'relevanssi_didyoumean_query', $data, MONTH_IN_SECONDS );
|
151 |
+
}
|
152 |
+
|
153 |
+
$query = htmlspecialchars_decode( $query, ENT_QUOTES );
|
154 |
+
$tokens = relevanssi_tokenize( $query );
|
155 |
+
$suggestions_made = false;
|
156 |
+
$suggestion = '';
|
157 |
+
|
158 |
+
foreach ( $tokens as $token => $count ) {
|
159 |
+
$closest = '';
|
160 |
+
$distance = -1;
|
161 |
+
foreach ( $data as $row ) {
|
162 |
+
if ( $row->c < 2 ) {
|
163 |
+
break;
|
164 |
+
}
|
165 |
+
|
166 |
+
if ( $token === $row->query ) {
|
167 |
+
$closest = '';
|
168 |
+
break;
|
169 |
+
} else {
|
170 |
+
if ( relevanssi_strlen( $token ) < 255 ) {
|
171 |
+
// The levenshtein() function has a max length of 255 characters.
|
172 |
+
$lev = levenshtein( $token, $row->query );
|
173 |
+
if ( $lev < 3 && ( $lev < $distance || $distance < 0 ) ) {
|
174 |
+
if ( $row->a > 0 ) {
|
175 |
+
$distance = $lev;
|
176 |
+
$closest = $row->query;
|
177 |
+
if ( $lev < 2 ) {
|
178 |
+
break; // get the first with distance of 1 and go.
|
179 |
+
}
|
180 |
+
}
|
181 |
+
}
|
182 |
+
}
|
183 |
+
}
|
184 |
+
}
|
185 |
+
if ( ! empty( $closest ) ) {
|
186 |
+
$query = str_ireplace( $token, $closest, $query );
|
187 |
+
$suggestions_made = true;
|
188 |
+
}
|
189 |
+
}
|
190 |
+
|
191 |
+
if ( $suggestions_made ) {
|
192 |
+
$suggestion = $query;
|
193 |
+
}
|
194 |
+
|
195 |
+
return $suggestion;
|
196 |
+
}
|
lib/excerpts-highlights.php
CHANGED
@@ -8,23 +8,6 @@
|
|
8 |
* @see https://www.relevanssi.com/
|
9 |
*/
|
10 |
|
11 |
-
/**
|
12 |
-
* Prints out the post excerpt.
|
13 |
-
*
|
14 |
-
* Prints out the post excerpt from $post->post_excerpt, unless the post is
|
15 |
-
* protected. Only works in the Loop.
|
16 |
-
*
|
17 |
-
* @global $post The global post object.
|
18 |
-
*/
|
19 |
-
function relevanssi_the_excerpt() {
|
20 |
-
global $post;
|
21 |
-
if ( ! post_password_required( $post ) ) {
|
22 |
-
echo '<p>' . $post->post_excerpt . '</p>'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
23 |
-
} else {
|
24 |
-
esc_html_e( 'There is no excerpt because this is a protected post.', 'relevanssi' );
|
25 |
-
}
|
26 |
-
}
|
27 |
-
|
28 |
/**
|
29 |
* Generates an excerpt for a post.
|
30 |
*
|
@@ -476,14 +459,15 @@ function relevanssi_highlight_in_docs( $content ) {
|
|
476 |
* you want to override the settings, 'pre_option_relevanssi_highlight' filter
|
477 |
* hook is your friend).
|
478 |
*
|
479 |
-
* @param string $content
|
480 |
-
* @param string|array $query
|
481 |
-
*
|
482 |
-
* @param boolean $
|
|
|
483 |
*
|
484 |
* @return string The $content with highlighting.
|
485 |
*/
|
486 |
-
function relevanssi_highlight_terms( $content, $query, $
|
487 |
$type = get_option( 'relevanssi_highlight' );
|
488 |
if ( 'none' === $type ) {
|
489 |
return $content;
|
@@ -707,7 +691,7 @@ function relevanssi_highlight_terms( $content, $query, $in_docs = false ) {
|
|
707 |
}
|
708 |
|
709 |
$content = relevanssi_remove_nested_highlights( $content, $start_emp_token, $end_emp_token );
|
710 |
-
$content = relevanssi_fix_entities( $content, $
|
711 |
|
712 |
/**
|
713 |
* Allows cleaning unwanted highlights.
|
@@ -881,25 +865,6 @@ function relevanssi_entities_inside( $content, $tag ) {
|
|
881 |
return $content;
|
882 |
}
|
883 |
|
884 |
-
/**
|
885 |
-
* Generates closing tags for an array of tags.
|
886 |
-
*
|
887 |
-
* @param array $tags Array of tag names.
|
888 |
-
*
|
889 |
-
* @return array $closing_tags Array of closing tags.
|
890 |
-
*/
|
891 |
-
function relevanssi_generate_closing_tags( $tags ) {
|
892 |
-
$closing_tags = array();
|
893 |
-
foreach ( $tags as $tag ) {
|
894 |
-
$a = str_replace( '<', '</', $tag );
|
895 |
-
$b = str_replace( '>', '/>', $tag );
|
896 |
-
|
897 |
-
$closing_tags[] = $a;
|
898 |
-
$closing_tags[] = $b;
|
899 |
-
}
|
900 |
-
return $closing_tags;
|
901 |
-
}
|
902 |
-
|
903 |
/**
|
904 |
* Removes nested highlights from a string.
|
905 |
*
|
@@ -1248,8 +1213,8 @@ function relevanssi_add_accent_variations( $word ) {
|
|
1248 |
array(
|
1249 |
'from' => array( 'a', 'c', 'e', 'i', 'o', 'u', 'n' ),
|
1250 |
'to' => array( '(?:a|á|à|â)', '(?:c|ç)', '(?:e|é|è|ê|ë)', '(?:i|í|ì|î|ï)', '(?:o|ó|ò|ô|õ)', '(?:u|ú|ù|ü|û)', '(?:n|ñ)' ),
|
1251 |
-
'from_re' => array( "/(s)('|’)?$/", "/[^\(
|
1252 |
-
'to_re' => array( "(('|’)?\\1|\\1('|’)?)", "?('|’)?" ),
|
1253 |
)
|
1254 |
);
|
1255 |
|
8 |
* @see https://www.relevanssi.com/
|
9 |
*/
|
10 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
11 |
/**
|
12 |
* Generates an excerpt for a post.
|
13 |
*
|
459 |
* you want to override the settings, 'pre_option_relevanssi_highlight' filter
|
460 |
* hook is your friend).
|
461 |
*
|
462 |
+
* @param string $content The content to highlight.
|
463 |
+
* @param string|array $query The search query (should be a string,
|
464 |
+
* can also be an array of string).
|
465 |
+
* @param boolean $convert_entities Are we highlighting post content?
|
466 |
+
* Default false.
|
467 |
*
|
468 |
* @return string The $content with highlighting.
|
469 |
*/
|
470 |
+
function relevanssi_highlight_terms( $content, $query, $convert_entities = false ) {
|
471 |
$type = get_option( 'relevanssi_highlight' );
|
472 |
if ( 'none' === $type ) {
|
473 |
return $content;
|
691 |
}
|
692 |
|
693 |
$content = relevanssi_remove_nested_highlights( $content, $start_emp_token, $end_emp_token );
|
694 |
+
$content = relevanssi_fix_entities( $content, $convert_entities );
|
695 |
|
696 |
/**
|
697 |
* Allows cleaning unwanted highlights.
|
865 |
return $content;
|
866 |
}
|
867 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
868 |
/**
|
869 |
* Removes nested highlights from a string.
|
870 |
*
|
1213 |
array(
|
1214 |
'from' => array( 'a', 'c', 'e', 'i', 'o', 'u', 'n' ),
|
1215 |
'to' => array( '(?:a|á|à|â)', '(?:c|ç)', '(?:e|é|è|ê|ë)', '(?:i|í|ì|î|ï)', '(?:o|ó|ò|ô|õ)', '(?:u|ú|ù|ü|û)', '(?:n|ñ)' ),
|
1216 |
+
'from_re' => array( "/(s)('|’)?$/", "/[^\(\|:]('|’)/" ),
|
1217 |
+
'to_re' => array( "(?:(?:'|’)?\\1|\\1(?:'|’)?)", "?('|’)?" ),
|
1218 |
)
|
1219 |
);
|
1220 |
|
lib/indexing.php
CHANGED
@@ -356,9 +356,6 @@ function relevanssi_build_index( $extend_offset = false, $verbose = null, $post_
|
|
356 |
// @codeCoverageIgnoreEnd
|
357 |
}
|
358 |
|
359 |
-
// To prevent empty indices.
|
360 |
-
$wpdb->query( "ANALYZE TABLE $relevanssi_table" ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared,WordPress.DB.PreparedSQL.InterpolatedNotPrepared
|
361 |
-
|
362 |
$complete = false;
|
363 |
$size = $indexing_query_args['size'];
|
364 |
|
@@ -366,6 +363,9 @@ function relevanssi_build_index( $extend_offset = false, $verbose = null, $post_
|
|
366 |
$complete = true;
|
367 |
update_option( 'relevanssi_indexed', 'done', false );
|
368 |
|
|
|
|
|
|
|
369 |
// Update the document count variable.
|
370 |
relevanssi_async_update_doc_count();
|
371 |
}
|
356 |
// @codeCoverageIgnoreEnd
|
357 |
}
|
358 |
|
|
|
|
|
|
|
359 |
$complete = false;
|
360 |
$size = $indexing_query_args['size'];
|
361 |
|
363 |
$complete = true;
|
364 |
update_option( 'relevanssi_indexed', 'done', false );
|
365 |
|
366 |
+
// To prevent empty indices.
|
367 |
+
$wpdb->query( "ANALYZE TABLE $relevanssi_table" ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared,WordPress.DB.PreparedSQL.InterpolatedNotPrepared
|
368 |
+
|
369 |
// Update the document count variable.
|
370 |
relevanssi_async_update_doc_count();
|
371 |
}
|
lib/init.php
CHANGED
@@ -35,7 +35,7 @@ add_action( 'deleted_comment', 'relevanssi_index_comment' );
|
|
35 |
|
36 |
// Attachment indexing.
|
37 |
add_action( 'delete_attachment', 'relevanssi_remove_doc' );
|
38 |
-
add_action( 'add_attachment', '
|
39 |
add_action( 'edit_attachment', 'relevanssi_insert_edit' );
|
40 |
|
41 |
// When a post status changes, check child posts that inherit their status from parent.
|
@@ -186,6 +186,10 @@ function relevanssi_init() {
|
|
186 |
require_once 'compatibility/seoframework.php';
|
187 |
}
|
188 |
|
|
|
|
|
|
|
|
|
189 |
if ( function_exists( 'members_content_permissions_enabled' ) ) {
|
190 |
require_once 'compatibility/members.php';
|
191 |
}
|
@@ -235,6 +239,20 @@ function relevanssi_init() {
|
|
235 |
if ( defined( 'CT_VERSION' ) ) {
|
236 |
require_once 'compatibility/oxygen.php';
|
237 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
238 |
}
|
239 |
|
240 |
/**
|
@@ -391,7 +409,7 @@ function relevanssi_create_database_tables( $relevanssi_db_version ) {
|
|
391 |
$relevanssi_term_reverse_idx_exists = false;
|
392 |
$docs_exists = false;
|
393 |
$typeitem_exists = false;
|
394 |
-
$doctermitem_exists
|
395 |
foreach ( $indices as $index ) {
|
396 |
if ( 'terms' === $index->Key_name ) { // phpcs:ignore WordPress.NamingConventions.ValidVariableName
|
397 |
$terms_exists = true;
|
@@ -420,11 +438,6 @@ function relevanssi_create_database_tables( $relevanssi_db_version ) {
|
|
420 |
$wpdb->query( $sql ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.DirectDatabaseQuery
|
421 |
}
|
422 |
|
423 |
-
if ( ! $docs_exists ) {
|
424 |
-
$sql = "CREATE INDEX docs ON $relevanssi_table (doc)";
|
425 |
-
$wpdb->query( $sql ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.DirectDatabaseQuery
|
426 |
-
}
|
427 |
-
|
428 |
if ( ! $typeitem_exists ) {
|
429 |
$sql = "CREATE INDEX typeitem ON $relevanssi_table (type(190), item)";
|
430 |
$wpdb->query( $sql ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.DirectDatabaseQuery
|
@@ -435,6 +448,11 @@ function relevanssi_create_database_tables( $relevanssi_db_version ) {
|
|
435 |
$wpdb->query( $sql ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.DirectDatabaseQuery
|
436 |
}
|
437 |
|
|
|
|
|
|
|
|
|
|
|
438 |
$sql = 'CREATE TABLE ' . $relevanssi_stopword_table . " (stopword varchar(50) $charset_collate_bin_column NOT NULL,
|
439 |
PRIMARY KEY stopword (stopword)) $charset_collate;";
|
440 |
|
@@ -477,7 +495,8 @@ function relevanssi_create_database_tables( $relevanssi_db_version ) {
|
|
477 |
update_option( 'relevanssi_db_version', $relevanssi_db_version );
|
478 |
}
|
479 |
|
480 |
-
|
|
|
481 |
relevanssi_populate_stopwords();
|
482 |
}
|
483 |
}
|
35 |
|
36 |
// Attachment indexing.
|
37 |
add_action( 'delete_attachment', 'relevanssi_remove_doc' );
|
38 |
+
add_action( 'add_attachment', 'relevanssi_insert_edit', 12 );
|
39 |
add_action( 'edit_attachment', 'relevanssi_insert_edit' );
|
40 |
|
41 |
// When a post status changes, check child posts that inherit their status from parent.
|
186 |
require_once 'compatibility/seoframework.php';
|
187 |
}
|
188 |
|
189 |
+
if ( class_exists( 'RankMath', false ) ) {
|
190 |
+
require_once 'compatibility/rankmath.php';
|
191 |
+
}
|
192 |
+
|
193 |
if ( function_exists( 'members_content_permissions_enabled' ) ) {
|
194 |
require_once 'compatibility/members.php';
|
195 |
}
|
239 |
if ( defined( 'CT_VERSION' ) ) {
|
240 |
require_once 'compatibility/oxygen.php';
|
241 |
}
|
242 |
+
|
243 |
+
if ( ! is_array( get_option( 'relevanssi_stopwords' ) ) ) {
|
244 |
+
// Version 2.12 / 4.10 changes stopwords option from a string to an
|
245 |
+
// array to support multilingual stopwords. This function converts old
|
246 |
+
// style to new style. Remove eventually.
|
247 |
+
relevanssi_update_stopwords_setting();
|
248 |
+
}
|
249 |
+
|
250 |
+
if ( ! is_array( get_option( 'relevanssi_synonyms' ) ) ) {
|
251 |
+
// Version 2.12 / 4.10 changes synonyms option from a string to an
|
252 |
+
// array to support multilingual synonyms. This function converts old
|
253 |
+
// style to new style. Remove eventually.
|
254 |
+
relevanssi_update_synonyms_setting();
|
255 |
+
}
|
256 |
}
|
257 |
|
258 |
/**
|
409 |
$relevanssi_term_reverse_idx_exists = false;
|
410 |
$docs_exists = false;
|
411 |
$typeitem_exists = false;
|
412 |
+
$doctermitem_exists = false;
|
413 |
foreach ( $indices as $index ) {
|
414 |
if ( 'terms' === $index->Key_name ) { // phpcs:ignore WordPress.NamingConventions.ValidVariableName
|
415 |
$terms_exists = true;
|
438 |
$wpdb->query( $sql ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.DirectDatabaseQuery
|
439 |
}
|
440 |
|
|
|
|
|
|
|
|
|
|
|
441 |
if ( ! $typeitem_exists ) {
|
442 |
$sql = "CREATE INDEX typeitem ON $relevanssi_table (type(190), item)";
|
443 |
$wpdb->query( $sql ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.DirectDatabaseQuery
|
448 |
$wpdb->query( $sql ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.DirectDatabaseQuery
|
449 |
}
|
450 |
|
451 |
+
if ( $docs_exists ) { // This index was removed in 4.9.2 / 2.11.2.
|
452 |
+
$sql = "DROP INDEX docs ON $relevanssi_table (doc)";
|
453 |
+
$wpdb->query( $sql ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.DirectDatabaseQuery
|
454 |
+
}
|
455 |
+
|
456 |
$sql = 'CREATE TABLE ' . $relevanssi_stopword_table . " (stopword varchar(50) $charset_collate_bin_column NOT NULL,
|
457 |
PRIMARY KEY stopword (stopword)) $charset_collate;";
|
458 |
|
495 |
update_option( 'relevanssi_db_version', $relevanssi_db_version );
|
496 |
}
|
497 |
|
498 |
+
$stopwords = relevanssi_fetch_stopwords();
|
499 |
+
if ( empty( $stopwords ) ) {
|
500 |
relevanssi_populate_stopwords();
|
501 |
}
|
502 |
}
|
lib/install.php
CHANGED
@@ -121,8 +121,8 @@ function _relevanssi_install() {
|
|
121 |
add_option( 'relevanssi_respect_exclude', 'on' );
|
122 |
add_option( 'relevanssi_show_matches', '' );
|
123 |
add_option( 'relevanssi_show_matches_text', '(Search hits: %body% in body, %title% in title, %categories% in categories, %tags% in tags, %taxonomies% in other taxonomies, %comments% in comments. Score: %score%)' );
|
124 |
-
add_option( 'relevanssi_stopwords',
|
125 |
-
add_option( 'relevanssi_synonyms',
|
126 |
add_option( 'relevanssi_throttle', 'on' );
|
127 |
add_option( 'relevanssi_throttle_limit', '500' );
|
128 |
add_option( 'relevanssi_title_boost', $relevanssi_variables['title_boost_default'] );
|
121 |
add_option( 'relevanssi_respect_exclude', 'on' );
|
122 |
add_option( 'relevanssi_show_matches', '' );
|
123 |
add_option( 'relevanssi_show_matches_text', '(Search hits: %body% in body, %title% in title, %categories% in categories, %tags% in tags, %taxonomies% in other taxonomies, %comments% in comments. Score: %score%)' );
|
124 |
+
add_option( 'relevanssi_stopwords', array() );
|
125 |
+
add_option( 'relevanssi_synonyms', array() );
|
126 |
add_option( 'relevanssi_throttle', 'on' );
|
127 |
add_option( 'relevanssi_throttle_limit', '500' );
|
128 |
add_option( 'relevanssi_title_boost', $relevanssi_variables['title_boost_default'] );
|
lib/interface.php
CHANGED
@@ -99,272 +99,6 @@ function relevanssi_options() {
|
|
99 |
echo "<div style='clear:both'></div></div>";
|
100 |
}
|
101 |
|
102 |
-
/**
|
103 |
-
* Updates Relevanssi options.
|
104 |
-
*
|
105 |
-
* Checks the option values and updates the options. It's safe to use $_REQUEST here,
|
106 |
-
* check_admin_referer() is done immediately before this function is called.
|
107 |
-
*/
|
108 |
-
function update_relevanssi_options() {
|
109 |
-
// phpcs:disable WordPress.Security.NonceVerification
|
110 |
-
if ( 'indexing' === $_REQUEST['tab'] ) {
|
111 |
-
relevanssi_turn_off_options(
|
112 |
-
$_REQUEST,
|
113 |
-
array(
|
114 |
-
'relevanssi_expand_shortcodes',
|
115 |
-
'relevanssi_index_author',
|
116 |
-
'relevanssi_index_excerpt',
|
117 |
-
'relevanssi_index_image_files',
|
118 |
-
)
|
119 |
-
);
|
120 |
-
relevanssi_update_intval( $_REQUEST, 'relevanssi_min_word_length', true, 3 );
|
121 |
-
}
|
122 |
-
|
123 |
-
if ( 'searching' === $_REQUEST['tab'] ) {
|
124 |
-
relevanssi_turn_off_options(
|
125 |
-
$_REQUEST,
|
126 |
-
array(
|
127 |
-
'relevanssi_admin_search',
|
128 |
-
'relevanssi_disable_or_fallback',
|
129 |
-
'relevanssi_exact_match_bonus',
|
130 |
-
'relevanssi_polylang_all_languages',
|
131 |
-
'relevanssi_respect_exclude',
|
132 |
-
'relevanssi_throttle',
|
133 |
-
'relevanssi_wpml_only_current',
|
134 |
-
)
|
135 |
-
);
|
136 |
-
relevanssi_update_floatval( $_REQUEST, 'relevanssi_content_boost', true, 1 );
|
137 |
-
relevanssi_update_floatval( $_REQUEST, 'relevanssi_title_boost', true, 1 );
|
138 |
-
relevanssi_update_floatval( $_REQUEST, 'relevanssi_comment_boost', true, 1 );
|
139 |
-
}
|
140 |
-
|
141 |
-
if ( 'logging' === $_REQUEST['tab'] ) {
|
142 |
-
relevanssi_turn_off_options(
|
143 |
-
$_REQUEST,
|
144 |
-
array(
|
145 |
-
'relevanssi_log_queries',
|
146 |
-
'relevanssi_log_queries_with_ip',
|
147 |
-
)
|
148 |
-
);
|
149 |
-
}
|
150 |
-
|
151 |
-
if ( 'excerpts' === $_REQUEST['tab'] ) {
|
152 |
-
relevanssi_turn_off_options(
|
153 |
-
$_REQUEST,
|
154 |
-
array(
|
155 |
-
'relevanssi_excerpt_custom_fields',
|
156 |
-
'relevanssi_excerpts',
|
157 |
-
'relevanssi_expand_highlights',
|
158 |
-
'relevanssi_highlight_comments',
|
159 |
-
'relevanssi_highlight_docs',
|
160 |
-
'relevanssi_hilite_title',
|
161 |
-
'relevanssi_show_matches',
|
162 |
-
)
|
163 |
-
);
|
164 |
-
if ( isset( $_REQUEST['relevanssi_show_matches_text'] ) ) {
|
165 |
-
$value = $_REQUEST['relevanssi_show_matches_text'];
|
166 |
-
$value = str_replace( '"', "'", $value );
|
167 |
-
update_option( 'relevanssi_show_matches_text', $value );
|
168 |
-
}
|
169 |
-
}
|
170 |
-
|
171 |
-
if ( isset( $_REQUEST['relevanssi_synonyms'] ) ) {
|
172 |
-
$linefeeds = array( "\r\n", "\n", "\r" );
|
173 |
-
$value = str_replace( $linefeeds, ';', $_REQUEST['relevanssi_synonyms'] );
|
174 |
-
$value = stripslashes( $value );
|
175 |
-
update_option( 'relevanssi_synonyms', $value );
|
176 |
-
}
|
177 |
-
|
178 |
-
$relevanssi_punct = array();
|
179 |
-
if ( isset( $_REQUEST['relevanssi_punct_quotes'] ) ) {
|
180 |
-
$relevanssi_punct['quotes'] = $_REQUEST['relevanssi_punct_quotes'];
|
181 |
-
}
|
182 |
-
if ( isset( $_REQUEST['relevanssi_punct_hyphens'] ) ) {
|
183 |
-
$relevanssi_punct['hyphens'] = $_REQUEST['relevanssi_punct_hyphens'];
|
184 |
-
}
|
185 |
-
if ( isset( $_REQUEST['relevanssi_punct_ampersands'] ) ) {
|
186 |
-
$relevanssi_punct['ampersands'] = $_REQUEST['relevanssi_punct_ampersands'];
|
187 |
-
}
|
188 |
-
if ( isset( $_REQUEST['relevanssi_punct_decimals'] ) ) {
|
189 |
-
$relevanssi_punct['decimals'] = $_REQUEST['relevanssi_punct_decimals'];
|
190 |
-
}
|
191 |
-
if ( ! empty( $relevanssi_punct ) ) {
|
192 |
-
update_option( 'relevanssi_punctuation', $relevanssi_punct );
|
193 |
-
}
|
194 |
-
|
195 |
-
$post_type_weights = array();
|
196 |
-
$index_post_types = array();
|
197 |
-
$index_taxonomies_list = array();
|
198 |
-
$index_terms_list = array();
|
199 |
-
foreach ( $_REQUEST as $key => $value ) {
|
200 |
-
if ( empty( $value ) ) {
|
201 |
-
$value = 0;
|
202 |
-
}
|
203 |
-
|
204 |
-
if ( 'relevanssi_weight_' === substr( $key, 0, strlen( 'relevanssi_weight_' ) ) ) {
|
205 |
-
$type = substr( $key, strlen( 'relevanssi_weight_' ) );
|
206 |
-
$post_type_weights[ $type ] = $value;
|
207 |
-
}
|
208 |
-
if ( 'relevanssi_taxonomy_weight_' === substr( $key, 0, strlen( 'relevanssi_taxonomy_weight_' ) ) ) {
|
209 |
-
$type = 'post_tagged_with_' . substr( $key, strlen( 'relevanssi_taxonomy_weight_' ) );
|
210 |
-
$post_type_weights[ $type ] = $value;
|
211 |
-
}
|
212 |
-
if ( 'relevanssi_term_weight_' === substr( $key, 0, strlen( 'relevanssi_term_weight_' ) ) ) {
|
213 |
-
$type = 'taxonomy_term_' . substr( $key, strlen( 'relevanssi_term_weight_' ) );
|
214 |
-
$post_type_weights[ $type ] = $value;
|
215 |
-
}
|
216 |
-
if ( 'relevanssi_index_type_' === substr( $key, 0, strlen( 'relevanssi_index_type_' ) ) ) {
|
217 |
-
$type = substr( $key, strlen( 'relevanssi_index_type_' ) );
|
218 |
-
if ( 'on' === $value ) {
|
219 |
-
$index_post_types[ $type ] = true;
|
220 |
-
}
|
221 |
-
}
|
222 |
-
if ( 'relevanssi_index_taxonomy_' === substr( $key, 0, strlen( 'relevanssi_index_taxonomy_' ) ) ) {
|
223 |
-
$type = substr( $key, strlen( 'relevanssi_index_taxonomy_' ) );
|
224 |
-
if ( 'on' === $value ) {
|
225 |
-
$index_taxonomies_list[ $type ] = true;
|
226 |
-
}
|
227 |
-
}
|
228 |
-
if ( 'relevanssi_index_terms_' === substr( $key, 0, strlen( 'relevanssi_index_terms_' ) ) ) {
|
229 |
-
$type = substr( $key, strlen( 'relevanssi_index_terms_' ) );
|
230 |
-
if ( 'on' === $value ) {
|
231 |
-
$index_terms_list[ $type ] = true;
|
232 |
-
}
|
233 |
-
}
|
234 |
-
}
|
235 |
-
|
236 |
-
if ( 'indexing' === $_REQUEST['tab'] ) {
|
237 |
-
update_option( 'relevanssi_index_taxonomies_list', array_keys( $index_taxonomies_list ), false );
|
238 |
-
if ( RELEVANSSI_PREMIUM ) {
|
239 |
-
update_option( 'relevanssi_index_terms', array_keys( $index_terms_list ), false );
|
240 |
-
}
|
241 |
-
}
|
242 |
-
|
243 |
-
if ( count( $post_type_weights ) > 0 ) {
|
244 |
-
update_option( 'relevanssi_post_type_weights', $post_type_weights );
|
245 |
-
}
|
246 |
-
|
247 |
-
if ( count( $index_post_types ) > 0 ) {
|
248 |
-
update_option( 'relevanssi_index_post_types', array_keys( $index_post_types ), false );
|
249 |
-
}
|
250 |
-
|
251 |
-
if ( isset( $_REQUEST['relevanssi_index_fields_select'] ) ) {
|
252 |
-
$fields_option = '';
|
253 |
-
if ( 'all' === $_REQUEST['relevanssi_index_fields_select'] ) {
|
254 |
-
$fields_option = 'all';
|
255 |
-
}
|
256 |
-
if ( 'visible' === $_REQUEST['relevanssi_index_fields_select'] ) {
|
257 |
-
$fields_option = 'visible';
|
258 |
-
}
|
259 |
-
if ( 'some' === $_REQUEST['relevanssi_index_fields_select'] ) {
|
260 |
-
if ( isset( $_REQUEST['relevanssi_index_fields'] ) ) {
|
261 |
-
$fields_option = rtrim( $_REQUEST['relevanssi_index_fields'], " \t\n\r\0\x0B," );
|
262 |
-
}
|
263 |
-
}
|
264 |
-
update_option( 'relevanssi_index_fields', $fields_option, false );
|
265 |
-
}
|
266 |
-
|
267 |
-
if ( isset( $_REQUEST['relevanssi_trim_logs'] ) ) {
|
268 |
-
$trim_logs = $_REQUEST['relevanssi_trim_logs'];
|
269 |
-
if ( ! is_numeric( $trim_logs ) || $trim_logs < 0 ) {
|
270 |
-
$trim_logs = 0;
|
271 |
-
}
|
272 |
-
update_option( 'relevanssi_trim_logs', $trim_logs );
|
273 |
-
}
|
274 |
-
|
275 |
-
if ( isset( $_REQUEST['relevanssi_cat'] ) ) {
|
276 |
-
if ( is_array( $_REQUEST['relevanssi_cat'] ) ) {
|
277 |
-
$csv_cats = implode( ',', $_REQUEST['relevanssi_cat'] );
|
278 |
-
update_option( 'relevanssi_cat', $csv_cats );
|
279 |
-
}
|
280 |
-
} else {
|
281 |
-
if ( isset( $_REQUEST['relevanssi_cat_active'] ) ) {
|
282 |
-
update_option( 'relevanssi_cat', '' );
|
283 |
-
}
|
284 |
-
}
|
285 |
-
|
286 |
-
if ( isset( $_REQUEST['relevanssi_excat'] ) ) {
|
287 |
-
if ( is_array( $_REQUEST['relevanssi_excat'] ) ) {
|
288 |
-
$array_excats = $_REQUEST['relevanssi_excat'];
|
289 |
-
$cat = get_option( 'relevanssi_cat' );
|
290 |
-
if ( $cat ) {
|
291 |
-
$array_cats = explode( ',', $cat );
|
292 |
-
$valid_excats = array();
|
293 |
-
foreach ( $array_excats as $excat ) {
|
294 |
-
if ( ! in_array( $excat, $array_cats, true ) ) {
|
295 |
-
$valid_excats[] = $excat;
|
296 |
-
}
|
297 |
-
}
|
298 |
-
} else {
|
299 |
-
// No category restrictions, so everything's good.
|
300 |
-
$valid_excats = $array_excats;
|
301 |
-
}
|
302 |
-
$csv_excats = implode( ',', $valid_excats );
|
303 |
-
update_option( 'relevanssi_excat', $csv_excats );
|
304 |
-
}
|
305 |
-
} else {
|
306 |
-
if ( isset( $_REQUEST['relevanssi_excat_active'] ) ) {
|
307 |
-
update_option( 'relevanssi_excat', '' );
|
308 |
-
}
|
309 |
-
}
|
310 |
-
|
311 |
-
$options = array(
|
312 |
-
'relevanssi_admin_search' => false,
|
313 |
-
'relevanssi_bg_col' => true,
|
314 |
-
'relevanssi_class' => true,
|
315 |
-
'relevanssi_css' => true,
|
316 |
-
'relevanssi_default_orderby' => true,
|
317 |
-
'relevanssi_disable_or_fallback' => true,
|
318 |
-
'relevanssi_exact_match_bonus' => true,
|
319 |
-
'relevanssi_excerpt_allowable_tags' => true,
|
320 |
-
'relevanssi_excerpt_custom_fields' => true,
|
321 |
-
'relevanssi_excerpt_type' => true,
|
322 |
-
'relevanssi_excerpts' => true,
|
323 |
-
'relevanssi_expand_shortcodes' => false,
|
324 |
-
'relevanssi_expand_highlights' => true,
|
325 |
-
'relevanssi_expst' => true,
|
326 |
-
'relevanssi_fuzzy' => true,
|
327 |
-
'relevanssi_highlight_comments' => true,
|
328 |
-
'relevanssi_highlight_docs' => true,
|
329 |
-
'relevanssi_highlight' => true,
|
330 |
-
'relevanssi_hilite_title' => true,
|
331 |
-
'relevanssi_implicit_operator' => true,
|
332 |
-
'relevanssi_index_author' => false,
|
333 |
-
'relevanssi_index_comments' => false,
|
334 |
-
'relevanssi_index_excerpt' => false,
|
335 |
-
'relevanssi_index_image_files' => false,
|
336 |
-
'relevanssi_log_queries_with_ip' => true,
|
337 |
-
'relevanssi_log_queries' => true,
|
338 |
-
'relevanssi_omit_from_logs' => true,
|
339 |
-
'relevanssi_polylang_all_languages' => true,
|
340 |
-
'relevanssi_respect_exclude' => true,
|
341 |
-
'relevanssi_show_matches' => true,
|
342 |
-
'relevanssi_throttle' => true,
|
343 |
-
'relevanssi_txt_col' => true,
|
344 |
-
'relevanssi_wpml_only_current' => true,
|
345 |
-
);
|
346 |
-
|
347 |
-
if ( isset( $_REQUEST['relevanssi_expst'] ) ) {
|
348 |
-
$_REQUEST['relevanssi_expst'] = trim( $_REQUEST['relevanssi_expst'], ' ,' );
|
349 |
-
}
|
350 |
-
|
351 |
-
array_walk(
|
352 |
-
$options,
|
353 |
-
function( $autoload, $option ) {
|
354 |
-
if ( isset( $_REQUEST[ $option ] ) ) {
|
355 |
-
update_option( $option, $_REQUEST[ $option ], $autoload );
|
356 |
-
}
|
357 |
-
}
|
358 |
-
);
|
359 |
-
|
360 |
-
relevanssi_update_intval( $_REQUEST, 'relevanssi_excerpt_length', true, 10 );
|
361 |
-
|
362 |
-
if ( function_exists( 'relevanssi_update_premium_options' ) ) {
|
363 |
-
relevanssi_update_premium_options();
|
364 |
-
}
|
365 |
-
// phpcs:enable
|
366 |
-
}
|
367 |
-
|
368 |
/**
|
369 |
* Prints out the 'User searches' page.
|
370 |
*/
|
@@ -666,39 +400,6 @@ function relevanssi_date_queries( $days, $title, $version = 'good' ) {
|
|
666 |
}
|
667 |
}
|
668 |
|
669 |
-
/**
|
670 |
-
* Returns 'checked' if the option is enabled.
|
671 |
-
*
|
672 |
-
* @param string $option Value to check.
|
673 |
-
*
|
674 |
-
* @return string If the option is 'on', returns 'checked', otherwise returns an
|
675 |
-
* empty string.
|
676 |
-
*/
|
677 |
-
function relevanssi_check( $option ) {
|
678 |
-
$checked = '';
|
679 |
-
if ( 'on' === $option ) {
|
680 |
-
$checked = 'checked';
|
681 |
-
}
|
682 |
-
return $checked;
|
683 |
-
}
|
684 |
-
|
685 |
-
/**
|
686 |
-
* Returns 'selected' if the option matches a value.
|
687 |
-
*
|
688 |
-
* @param string $option Value to check.
|
689 |
-
* @param string $value The 'selected' value.
|
690 |
-
*
|
691 |
-
* @return string If the option matches the value, returns 'selected', otherwise
|
692 |
-
* returns an empty string.
|
693 |
-
*/
|
694 |
-
function relevanssi_select( $option, $value ) {
|
695 |
-
$selected = '';
|
696 |
-
if ( $option === $value ) {
|
697 |
-
$selected = 'selected';
|
698 |
-
}
|
699 |
-
return $selected;
|
700 |
-
}
|
701 |
-
|
702 |
/**
|
703 |
* Prints out the Relevanssi options form.
|
704 |
*
|
@@ -1005,178 +706,3 @@ function relevanssi_form_tag_weight() {
|
|
1005 |
</tr>
|
1006 |
<?php
|
1007 |
}
|
1008 |
-
|
1009 |
-
/**
|
1010 |
-
* Turns off options, ie. sets them to "off".
|
1011 |
-
*
|
1012 |
-
* If the specified options don't exist in the request array, they are set to
|
1013 |
-
* "off".
|
1014 |
-
*
|
1015 |
-
* @param array $request The _REQUEST array, passed as reference.
|
1016 |
-
* @param array $options An array of option names.
|
1017 |
-
*/
|
1018 |
-
function relevanssi_turn_off_options( &$request, $options ) {
|
1019 |
-
array_walk(
|
1020 |
-
$options,
|
1021 |
-
function( $option ) use ( &$request ) {
|
1022 |
-
if ( ! isset( $request[ $option ] ) ) {
|
1023 |
-
$request[ $option ] = 'off';
|
1024 |
-
}
|
1025 |
-
}
|
1026 |
-
);
|
1027 |
-
}
|
1028 |
-
|
1029 |
-
/**
|
1030 |
-
* Returns 'on' if option exists and value is not 'off', otherwise 'off'.
|
1031 |
-
*
|
1032 |
-
* @param array $request An array of option values.
|
1033 |
-
* @param string $option The key to check.
|
1034 |
-
*
|
1035 |
-
* @return string 'on' or 'off'.
|
1036 |
-
*/
|
1037 |
-
function relevanssi_off_or_on( $request, $option ) {
|
1038 |
-
if ( isset( $request[ $option ] ) && 'off' !== $request[ $option ] ) {
|
1039 |
-
return 'on';
|
1040 |
-
}
|
1041 |
-
return 'off';
|
1042 |
-
}
|
1043 |
-
|
1044 |
-
/**
|
1045 |
-
* Returns an imploded string if the option exists and is an array, an empty
|
1046 |
-
* string otherwise.
|
1047 |
-
*
|
1048 |
-
* @param array $request An array of option values.
|
1049 |
-
* @param string $option The key to check.
|
1050 |
-
* @param string $glue The glue string for implode(), default ','.
|
1051 |
-
*
|
1052 |
-
* @return string Imploded string or an empty string.
|
1053 |
-
*/
|
1054 |
-
function relevanssi_implode( $request, $option, $glue = ',' ) {
|
1055 |
-
if ( isset( $request[ $option ] ) && is_array( $request[ $option ] ) ) {
|
1056 |
-
return implode( $glue, $request[ $option ] );
|
1057 |
-
}
|
1058 |
-
return '';
|
1059 |
-
}
|
1060 |
-
|
1061 |
-
/**
|
1062 |
-
* Returns the intval of the option if it exists, null otherwise.
|
1063 |
-
*
|
1064 |
-
* @param array $request An array of option values.
|
1065 |
-
* @param string $option The key to check.
|
1066 |
-
*
|
1067 |
-
* @return int|null Integer value of the option, or null.
|
1068 |
-
*/
|
1069 |
-
function relevanssi_intval( $request, $option ) {
|
1070 |
-
if ( isset( $request[ $option ] ) ) {
|
1071 |
-
return intval( $request[ $option ] );
|
1072 |
-
}
|
1073 |
-
return null;
|
1074 |
-
}
|
1075 |
-
|
1076 |
-
/**
|
1077 |
-
* Returns a legal value.
|
1078 |
-
*
|
1079 |
-
* @param array $request An array of option values.
|
1080 |
-
* @param string $option The key to check.
|
1081 |
-
* @param array $values The legal values.
|
1082 |
-
* @param string $default The default value.
|
1083 |
-
*
|
1084 |
-
* @return string|null A legal value or the default value, null if the option
|
1085 |
-
* isn't set.
|
1086 |
-
*/
|
1087 |
-
function relevanssi_legal_value( $request, $option, $values, $default ) {
|
1088 |
-
$value = null;
|
1089 |
-
if ( isset( $request[ $option ] ) ) {
|
1090 |
-
$value = $default;
|
1091 |
-
if ( in_array( $request[ $option ], $values, true ) ) {
|
1092 |
-
$value = $request[ $option ];
|
1093 |
-
}
|
1094 |
-
}
|
1095 |
-
return $value;
|
1096 |
-
}
|
1097 |
-
|
1098 |
-
/**
|
1099 |
-
* Sets an on/off option according to the request value.
|
1100 |
-
*
|
1101 |
-
* @param array $request An array of option values.
|
1102 |
-
* @param string $option The key to check.
|
1103 |
-
* @param boolean $autoload Should the option autoload, default true.
|
1104 |
-
*/
|
1105 |
-
function relevanssi_update_off_or_on( $request, $option, $autoload = true ) {
|
1106 |
-
relevanssi_update_legal_value(
|
1107 |
-
$request,
|
1108 |
-
$option,
|
1109 |
-
array( 'off', 'on' ),
|
1110 |
-
'off',
|
1111 |
-
$autoload
|
1112 |
-
);
|
1113 |
-
}
|
1114 |
-
|
1115 |
-
/**
|
1116 |
-
* Sets an option after sanitizing and unslashing the value.
|
1117 |
-
*
|
1118 |
-
* @param array $request An array of option values.
|
1119 |
-
* @param string $option The key to check.
|
1120 |
-
* @param boolean $autoload Should the option autoload, default true.
|
1121 |
-
*/
|
1122 |
-
function relevanssi_update_sanitized( $request, $option, $autoload = true ) {
|
1123 |
-
if ( isset( $request[ $option ] ) ) {
|
1124 |
-
$value = sanitize_text_field( wp_unslash( $request[ $option ] ) );
|
1125 |
-
update_option( $option, $value, $autoload );
|
1126 |
-
}
|
1127 |
-
}
|
1128 |
-
|
1129 |
-
/**
|
1130 |
-
* Sets an option after doing intval.
|
1131 |
-
*
|
1132 |
-
* @param array $request An array of option values.
|
1133 |
-
* @param string $option The key to check.
|
1134 |
-
* @param boolean $autoload Should the option autoload, default true.
|
1135 |
-
* @param int $default The default value if intval() fails, default 0.
|
1136 |
-
*/
|
1137 |
-
function relevanssi_update_intval( $request, $option, $autoload = true, $default = 0 ) {
|
1138 |
-
if ( isset( $request[ $option ] ) ) {
|
1139 |
-
$value = intval( $request[ $option ] );
|
1140 |
-
if ( ! $value ) {
|
1141 |
-
$value = $default;
|
1142 |
-
}
|
1143 |
-
update_option( $option, $value, $autoload );
|
1144 |
-
}
|
1145 |
-
}
|
1146 |
-
|
1147 |
-
/**
|
1148 |
-
* Sets an option after doing floatval.
|
1149 |
-
*
|
1150 |
-
* @param array $request An array of option values.
|
1151 |
-
* @param string $option The key to check.
|
1152 |
-
* @param boolean $autoload Should the option autoload, default true.
|
1153 |
-
* @param int $default The default value if floatval() fails, default 0.
|
1154 |
-
*/
|
1155 |
-
function relevanssi_update_floatval( $request, $option, $autoload = true, $default = 0 ) {
|
1156 |
-
if ( isset( $request[ $option ] ) ) {
|
1157 |
-
$value = floatval( $request[ $option ] );
|
1158 |
-
if ( ! $value ) {
|
1159 |
-
$value = $default;
|
1160 |
-
}
|
1161 |
-
update_option( $option, $value, $autoload );
|
1162 |
-
}
|
1163 |
-
}
|
1164 |
-
|
1165 |
-
/**
|
1166 |
-
* Sets an option with one of the listed legal values.
|
1167 |
-
*
|
1168 |
-
* @param array $request An array of option values.
|
1169 |
-
* @param string $option The key to check.
|
1170 |
-
* @param array $values The legal values.
|
1171 |
-
* @param string $default The default value.
|
1172 |
-
* @param boolean $autoload Should the option autoload, default true.
|
1173 |
-
*/
|
1174 |
-
function relevanssi_update_legal_value( $request, $option, $values, $default, $autoload = true ) {
|
1175 |
-
if ( isset( $request[ $option ] ) ) {
|
1176 |
-
$value = $default;
|
1177 |
-
if ( in_array( $request[ $option ], $values, true ) ) {
|
1178 |
-
$value = $request[ $option ];
|
1179 |
-
}
|
1180 |
-
update_option( $option, $value, $autoload );
|
1181 |
-
}
|
1182 |
-
}
|
99 |
echo "<div style='clear:both'></div></div>";
|
100 |
}
|
101 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
102 |
/**
|
103 |
* Prints out the 'User searches' page.
|
104 |
*/
|
400 |
}
|
401 |
}
|
402 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
403 |
/**
|
404 |
* Prints out the Relevanssi options form.
|
405 |
*
|
706 |
</tr>
|
707 |
<?php
|
708 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
lib/options.php
ADDED
@@ -0,0 +1,322 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* /lib/options.php
|
4 |
+
*
|
5 |
+
* @package Relevanssi
|
6 |
+
* @author Mikko Saari
|
7 |
+
* @license https://wordpress.org/about/gpl/ GNU General Public License
|
8 |
+
* @see https://www.relevanssi.com/
|
9 |
+
*/
|
10 |
+
|
11 |
+
/**
|
12 |
+
* Updates Relevanssi options.
|
13 |
+
*
|
14 |
+
* Checks the option values and updates the options. It's safe to use $_REQUEST
|
15 |
+
* here, check_admin_referer() is done immediately before this function is
|
16 |
+
* called.
|
17 |
+
*/
|
18 |
+
function update_relevanssi_options() {
|
19 |
+
// phpcs:disable WordPress.Security.NonceVerification
|
20 |
+
$data = relevanssi_process_weights_and_indexing( $_REQUEST );
|
21 |
+
$post_type_weights = $data['post_type_weights'];
|
22 |
+
$index_post_types = $data['index_post_types'];
|
23 |
+
$index_taxonomies_list = $data['index_taxonomies_list'];
|
24 |
+
$index_terms_list = $data['index_terms_list'];
|
25 |
+
|
26 |
+
if ( 'indexing' === $_REQUEST['tab'] ) {
|
27 |
+
relevanssi_turn_off_options(
|
28 |
+
$_REQUEST,
|
29 |
+
array(
|
30 |
+
'relevanssi_expand_shortcodes',
|
31 |
+
'relevanssi_index_author',
|
32 |
+
'relevanssi_index_excerpt',
|
33 |
+
'relevanssi_index_image_files',
|
34 |
+
)
|
35 |
+
);
|
36 |
+
relevanssi_update_intval( $_REQUEST, 'relevanssi_min_word_length', true, 3 );
|
37 |
+
update_option( 'relevanssi_index_taxonomies_list', array_keys( $index_taxonomies_list ), false );
|
38 |
+
if ( RELEVANSSI_PREMIUM ) {
|
39 |
+
update_option( 'relevanssi_index_terms', array_keys( $index_terms_list ), false );
|
40 |
+
}
|
41 |
+
}
|
42 |
+
|
43 |
+
if ( 'searching' === $_REQUEST['tab'] ) {
|
44 |
+
relevanssi_turn_off_options(
|
45 |
+
$_REQUEST,
|
46 |
+
array(
|
47 |
+
'relevanssi_admin_search',
|
48 |
+
'relevanssi_disable_or_fallback',
|
49 |
+
'relevanssi_exact_match_bonus',
|
50 |
+
'relevanssi_polylang_all_languages',
|
51 |
+
'relevanssi_respect_exclude',
|
52 |
+
'relevanssi_throttle',
|
53 |
+
'relevanssi_wpml_only_current',
|
54 |
+
)
|
55 |
+
);
|
56 |
+
relevanssi_update_floatval( $_REQUEST, 'relevanssi_content_boost', true, 1, true );
|
57 |
+
relevanssi_update_floatval( $_REQUEST, 'relevanssi_title_boost', true, 1, true );
|
58 |
+
relevanssi_update_floatval( $_REQUEST, 'relevanssi_comment_boost', true, 1, true );
|
59 |
+
}
|
60 |
+
|
61 |
+
if ( 'logging' === $_REQUEST['tab'] ) {
|
62 |
+
relevanssi_turn_off_options(
|
63 |
+
$_REQUEST,
|
64 |
+
array(
|
65 |
+
'relevanssi_log_queries',
|
66 |
+
'relevanssi_log_queries_with_ip',
|
67 |
+
)
|
68 |
+
);
|
69 |
+
}
|
70 |
+
|
71 |
+
if ( 'excerpts' === $_REQUEST['tab'] ) {
|
72 |
+
relevanssi_turn_off_options(
|
73 |
+
$_REQUEST,
|
74 |
+
array(
|
75 |
+
'relevanssi_excerpt_custom_fields',
|
76 |
+
'relevanssi_excerpts',
|
77 |
+
'relevanssi_expand_highlights',
|
78 |
+
'relevanssi_highlight_comments',
|
79 |
+
'relevanssi_highlight_docs',
|
80 |
+
'relevanssi_hilite_title',
|
81 |
+
'relevanssi_show_matches',
|
82 |
+
)
|
83 |
+
);
|
84 |
+
if ( isset( $_REQUEST['relevanssi_show_matches_text'] ) ) {
|
85 |
+
$value = $_REQUEST['relevanssi_show_matches_text'];
|
86 |
+
$value = str_replace( '"', "'", $value );
|
87 |
+
update_option( 'relevanssi_show_matches_text', $value );
|
88 |
+
}
|
89 |
+
}
|
90 |
+
|
91 |
+
if ( isset( $_REQUEST['relevanssi_synonyms'] ) ) {
|
92 |
+
$linefeeds = array( "\r\n", "\n", "\r" );
|
93 |
+
$value = str_replace( $linefeeds, ';', $_REQUEST['relevanssi_synonyms'] );
|
94 |
+
$value = stripslashes( $value );
|
95 |
+
|
96 |
+
$synonym_option = get_option( 'relevanssi_synonyms', array() );
|
97 |
+
$current_language = relevanssi_get_current_language();
|
98 |
+
|
99 |
+
$synonym_option[ $current_language ] = $value;
|
100 |
+
|
101 |
+
update_option( 'relevanssi_synonyms', $synonym_option );
|
102 |
+
}
|
103 |
+
|
104 |
+
$relevanssi_punct = array();
|
105 |
+
if ( isset( $_REQUEST['relevanssi_punct_quotes'] ) ) {
|
106 |
+
$relevanssi_punct['quotes'] = $_REQUEST['relevanssi_punct_quotes'];
|
107 |
+
}
|
108 |
+
if ( isset( $_REQUEST['relevanssi_punct_hyphens'] ) ) {
|
109 |
+
$relevanssi_punct['hyphens'] = $_REQUEST['relevanssi_punct_hyphens'];
|
110 |
+
}
|
111 |
+
if ( isset( $_REQUEST['relevanssi_punct_ampersands'] ) ) {
|
112 |
+
$relevanssi_punct['ampersands'] = $_REQUEST['relevanssi_punct_ampersands'];
|
113 |
+
}
|
114 |
+
if ( isset( $_REQUEST['relevanssi_punct_decimals'] ) ) {
|
115 |
+
$relevanssi_punct['decimals'] = $_REQUEST['relevanssi_punct_decimals'];
|
116 |
+
}
|
117 |
+
if ( ! empty( $relevanssi_punct ) ) {
|
118 |
+
update_option( 'relevanssi_punctuation', $relevanssi_punct );
|
119 |
+
}
|
120 |
+
|
121 |
+
if ( count( $post_type_weights ) > 0 ) {
|
122 |
+
update_option( 'relevanssi_post_type_weights', $post_type_weights );
|
123 |
+
}
|
124 |
+
|
125 |
+
if ( count( $index_post_types ) > 0 ) {
|
126 |
+
update_option( 'relevanssi_index_post_types', array_keys( $index_post_types ), false );
|
127 |
+
}
|
128 |
+
|
129 |
+
if ( isset( $_REQUEST['relevanssi_index_fields_select'] ) ) {
|
130 |
+
$fields_option = '';
|
131 |
+
if ( 'all' === $_REQUEST['relevanssi_index_fields_select'] ) {
|
132 |
+
$fields_option = 'all';
|
133 |
+
}
|
134 |
+
if ( 'visible' === $_REQUEST['relevanssi_index_fields_select'] ) {
|
135 |
+
$fields_option = 'visible';
|
136 |
+
}
|
137 |
+
if ( 'some' === $_REQUEST['relevanssi_index_fields_select'] ) {
|
138 |
+
if ( isset( $_REQUEST['relevanssi_index_fields'] ) ) {
|
139 |
+
$fields_option = rtrim( $_REQUEST['relevanssi_index_fields'], " \t\n\r\0\x0B," );
|
140 |
+
}
|
141 |
+
}
|
142 |
+
update_option( 'relevanssi_index_fields', $fields_option, false );
|
143 |
+
}
|
144 |
+
|
145 |
+
if ( isset( $_REQUEST['relevanssi_trim_logs'] ) ) {
|
146 |
+
$trim_logs = $_REQUEST['relevanssi_trim_logs'];
|
147 |
+
if ( ! is_numeric( $trim_logs ) || $trim_logs < 0 ) {
|
148 |
+
$trim_logs = 0;
|
149 |
+
}
|
150 |
+
update_option( 'relevanssi_trim_logs', $trim_logs );
|
151 |
+
}
|
152 |
+
|
153 |
+
if ( isset( $_REQUEST['relevanssi_cat'] ) ) {
|
154 |
+
if ( is_array( $_REQUEST['relevanssi_cat'] ) ) {
|
155 |
+
$csv_cats = implode( ',', $_REQUEST['relevanssi_cat'] );
|
156 |
+
update_option( 'relevanssi_cat', $csv_cats );
|
157 |
+
}
|
158 |
+
} else {
|
159 |
+
if ( isset( $_REQUEST['relevanssi_cat_active'] ) ) {
|
160 |
+
update_option( 'relevanssi_cat', '' );
|
161 |
+
}
|
162 |
+
}
|
163 |
+
|
164 |
+
if ( isset( $_REQUEST['relevanssi_excat'] ) ) {
|
165 |
+
if ( is_array( $_REQUEST['relevanssi_excat'] ) ) {
|
166 |
+
$array_excats = $_REQUEST['relevanssi_excat'];
|
167 |
+
$cat = get_option( 'relevanssi_cat' );
|
168 |
+
if ( $cat ) {
|
169 |
+
$array_cats = explode( ',', $cat );
|
170 |
+
$valid_excats = array();
|
171 |
+
foreach ( $array_excats as $excat ) {
|
172 |
+
if ( ! in_array( $excat, $array_cats, true ) ) {
|
173 |
+
$valid_excats[] = $excat;
|
174 |
+
}
|
175 |
+
}
|
176 |
+
} else {
|
177 |
+
// No category restrictions, so everything's good.
|
178 |
+
$valid_excats = $array_excats;
|
179 |
+
}
|
180 |
+
$csv_excats = implode( ',', $valid_excats );
|
181 |
+
update_option( 'relevanssi_excat', $csv_excats );
|
182 |
+
}
|
183 |
+
} else {
|
184 |
+
if ( isset( $_REQUEST['relevanssi_excat_active'] ) ) {
|
185 |
+
update_option( 'relevanssi_excat', '' );
|
186 |
+
}
|
187 |
+
}
|
188 |
+
|
189 |
+
$options = array(
|
190 |
+
'relevanssi_admin_search' => false,
|
191 |
+
'relevanssi_bg_col' => true,
|
192 |
+
'relevanssi_class' => true,
|
193 |
+
'relevanssi_css' => true,
|
194 |
+
'relevanssi_default_orderby' => true,
|
195 |
+
'relevanssi_disable_or_fallback' => true,
|
196 |
+
'relevanssi_exact_match_bonus' => true,
|
197 |
+
'relevanssi_excerpt_allowable_tags' => true,
|
198 |
+
'relevanssi_excerpt_custom_fields' => true,
|
199 |
+
'relevanssi_excerpt_type' => true,
|
200 |
+
'relevanssi_excerpts' => true,
|
201 |
+
'relevanssi_exclude_posts' => true,
|
202 |
+
'relevanssi_expand_shortcodes' => false,
|
203 |
+
'relevanssi_expand_highlights' => true,
|
204 |
+
'relevanssi_fuzzy' => true,
|
205 |
+
'relevanssi_highlight_comments' => true,
|
206 |
+
'relevanssi_highlight_docs' => true,
|
207 |
+
'relevanssi_highlight' => true,
|
208 |
+
'relevanssi_hilite_title' => true,
|
209 |
+
'relevanssi_implicit_operator' => true,
|
210 |
+
'relevanssi_index_author' => false,
|
211 |
+
'relevanssi_index_comments' => false,
|
212 |
+
'relevanssi_index_excerpt' => false,
|
213 |
+
'relevanssi_index_image_files' => false,
|
214 |
+
'relevanssi_log_queries_with_ip' => true,
|
215 |
+
'relevanssi_log_queries' => true,
|
216 |
+
'relevanssi_omit_from_logs' => true,
|
217 |
+
'relevanssi_polylang_all_languages' => true,
|
218 |
+
'relevanssi_respect_exclude' => true,
|
219 |
+
'relevanssi_show_matches' => true,
|
220 |
+
'relevanssi_throttle' => true,
|
221 |
+
'relevanssi_txt_col' => true,
|
222 |
+
'relevanssi_wpml_only_current' => true,
|
223 |
+
);
|
224 |
+
|
225 |
+
if ( isset( $_REQUEST['relevanssi_exclude_posts'] ) ) {
|
226 |
+
$_REQUEST['relevanssi_exclude_posts'] = trim( $_REQUEST['relevanssi_exclude_posts'], ' ,' );
|
227 |
+
}
|
228 |
+
|
229 |
+
array_walk(
|
230 |
+
$options,
|
231 |
+
function( $autoload, $option ) {
|
232 |
+
if ( isset( $_REQUEST[ $option ] ) ) {
|
233 |
+
update_option( $option, $_REQUEST[ $option ], $autoload );
|
234 |
+
}
|
235 |
+
}
|
236 |
+
);
|
237 |
+
|
238 |
+
relevanssi_update_intval( $_REQUEST, 'relevanssi_excerpt_length', true, 10 );
|
239 |
+
|
240 |
+
if ( function_exists( 'relevanssi_update_premium_options' ) ) {
|
241 |
+
relevanssi_update_premium_options();
|
242 |
+
}
|
243 |
+
// phpcs:enable
|
244 |
+
}
|
245 |
+
|
246 |
+
/**
|
247 |
+
* Fetches option values for variable name options.
|
248 |
+
*
|
249 |
+
* Goes through all options and picks up all options that have names that
|
250 |
+
* contain post types, taxonomies and so on.
|
251 |
+
*
|
252 |
+
* @param array $request The $_REQUEST array.
|
253 |
+
*
|
254 |
+
* @return array Four arrays containing the required data.
|
255 |
+
*/
|
256 |
+
function relevanssi_process_weights_and_indexing( $request ) {
|
257 |
+
$post_type_weights = array();
|
258 |
+
$index_post_types = array();
|
259 |
+
$index_taxonomies_list = array();
|
260 |
+
$index_terms_list = array();
|
261 |
+
foreach ( $request as $key => $value ) {
|
262 |
+
if ( empty( $value ) ) {
|
263 |
+
$value = 0;
|
264 |
+
}
|
265 |
+
|
266 |
+
if ( 'relevanssi_weight_' === substr( $key, 0, strlen( 'relevanssi_weight_' ) ) ) {
|
267 |
+
$type = substr( $key, strlen( 'relevanssi_weight_' ) );
|
268 |
+
$post_type_weights[ $type ] = $value;
|
269 |
+
}
|
270 |
+
if ( 'relevanssi_taxonomy_weight_' === substr( $key, 0, strlen( 'relevanssi_taxonomy_weight_' ) ) ) {
|
271 |
+
$type = 'post_tagged_with_' . substr( $key, strlen( 'relevanssi_taxonomy_weight_' ) );
|
272 |
+
$post_type_weights[ $type ] = $value;
|
273 |
+
}
|
274 |
+
if ( 'relevanssi_term_weight_' === substr( $key, 0, strlen( 'relevanssi_term_weight_' ) ) ) {
|
275 |
+
$type = 'taxonomy_term_' . substr( $key, strlen( 'relevanssi_term_weight_' ) );
|
276 |
+
$post_type_weights[ $type ] = $value;
|
277 |
+
}
|
278 |
+
if ( 'relevanssi_index_type_' === substr( $key, 0, strlen( 'relevanssi_index_type_' ) ) ) {
|
279 |
+
$type = substr( $key, strlen( 'relevanssi_index_type_' ) );
|
280 |
+
if ( 'on' === $value ) {
|
281 |
+
$index_post_types[ $type ] = true;
|
282 |
+
}
|
283 |
+
}
|
284 |
+
if ( 'relevanssi_index_taxonomy_' === substr( $key, 0, strlen( 'relevanssi_index_taxonomy_' ) ) ) {
|
285 |
+
$type = substr( $key, strlen( 'relevanssi_index_taxonomy_' ) );
|
286 |
+
if ( 'on' === $value ) {
|
287 |
+
$index_taxonomies_list[ $type ] = true;
|
288 |
+
}
|
289 |
+
}
|
290 |
+
if ( 'relevanssi_index_terms_' === substr( $key, 0, strlen( 'relevanssi_index_terms_' ) ) ) {
|
291 |
+
$type = substr( $key, strlen( 'relevanssi_index_terms_' ) );
|
292 |
+
if ( 'on' === $value ) {
|
293 |
+
$index_terms_list[ $type ] = true;
|
294 |
+
}
|
295 |
+
}
|
296 |
+
}
|
297 |
+
|
298 |
+
$post_type_weights = array_map( 'relevanssi_sanitize_weights', $post_type_weights );
|
299 |
+
|
300 |
+
return array(
|
301 |
+
'post_type_weights' => $post_type_weights,
|
302 |
+
'index_post_types' => $index_post_types,
|
303 |
+
'index_taxonomies_list' => $index_taxonomies_list,
|
304 |
+
'index_terms_list' => $index_terms_list,
|
305 |
+
);
|
306 |
+
}
|
307 |
+
|
308 |
+
/**
|
309 |
+
* Takes a value, converts it to float and if it's negative or zero, sets it
|
310 |
+
* to 1.
|
311 |
+
*
|
312 |
+
* @param mixed $weight The weight value, which can be anything user enters.
|
313 |
+
*
|
314 |
+
* @return float The float value of the weight.
|
315 |
+
*/
|
316 |
+
function relevanssi_sanitize_weights( $weight ) {
|
317 |
+
$weight = floatval( $weight );
|
318 |
+
if ( $weight <= 0 ) {
|
319 |
+
$weight = 1;
|
320 |
+
}
|
321 |
+
return $weight;
|
322 |
+
}
|
lib/phrases.php
ADDED
@@ -0,0 +1,277 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* /lib/phrases.php
|
4 |
+
*
|
5 |
+
* @package Relevanssi
|
6 |
+
* @author Mikko Saari
|
7 |
+
* @license https://wordpress.org/about/gpl/ GNU General Public License
|
8 |
+
* @see https://www.relevanssi.com/
|
9 |
+
*/
|
10 |
+
|
11 |
+
/**
|
12 |
+
* Extracts phrases from the search query.
|
13 |
+
*
|
14 |
+
* Finds all phrases wrapped in quotes (curly or straight) from the search
|
15 |
+
* query.
|
16 |
+
*
|
17 |
+
* @param string $query The search query.
|
18 |
+
*
|
19 |
+
* @return array An array of phrases (strings).
|
20 |
+
*/
|
21 |
+
function relevanssi_extract_phrases( $query ) {
|
22 |
+
// iOS uses “” as the default quotes, so Relevanssi needs to understand
|
23 |
+
// that as well.
|
24 |
+
$normalized_query = str_replace( array( '”', '“' ), '"', $query );
|
25 |
+
$pos = relevanssi_stripos( $normalized_query, '"' );
|
26 |
+
|
27 |
+
$phrases = array();
|
28 |
+
while ( false !== $pos ) {
|
29 |
+
if ( $pos + 2 > relevanssi_strlen( $normalized_query ) ) {
|
30 |
+
$pos = false;
|
31 |
+
continue;
|
32 |
+
}
|
33 |
+
$start = relevanssi_stripos( $normalized_query, '"', $pos );
|
34 |
+
$end = false;
|
35 |
+
if ( false !== $start ) {
|
36 |
+
$end = relevanssi_stripos( $normalized_query, '"', $start + 2 );
|
37 |
+
}
|
38 |
+
if ( false === $end ) {
|
39 |
+
// Just one " in the query.
|
40 |
+
$pos = $end;
|
41 |
+
continue;
|
42 |
+
}
|
43 |
+
$phrase = relevanssi_substr(
|
44 |
+
$normalized_query,
|
45 |
+
$start + 1,
|
46 |
+
$end - $start - 1
|
47 |
+
);
|
48 |
+
$phrase = trim( $phrase );
|
49 |
+
|
50 |
+
// Do not count single-word phrases as phrases.
|
51 |
+
if ( ! empty( $phrase ) && count( explode( ' ', $phrase ) ) > 1 ) {
|
52 |
+
$phrases[] = $phrase;
|
53 |
+
}
|
54 |
+
$pos = $end + 1;
|
55 |
+
}
|
56 |
+
|
57 |
+
return $phrases;
|
58 |
+
}
|
59 |
+
|
60 |
+
/**
|
61 |
+
* Generates the MySQL code for restricting the search to phrase hits.
|
62 |
+
*
|
63 |
+
* This function uses relevanssi_extract_phrases() to figure out the phrases in
|
64 |
+
* the search query, then generates MySQL queries to restrict the search to the
|
65 |
+
* posts containing those phrases in the title, content, taxonomy terms or meta
|
66 |
+
* fields.
|
67 |
+
*
|
68 |
+
* @global array $relevanssi_variables The global Relevanssi variables.
|
69 |
+
*
|
70 |
+
* @param string $search_query The search query.
|
71 |
+
* @param string $operator The search operator (AND or OR).
|
72 |
+
*
|
73 |
+
* @return string $queries If not phrase hits are found, an empty string;
|
74 |
+
* otherwise MySQL queries to restrict the search.
|
75 |
+
*/
|
76 |
+
function relevanssi_recognize_phrases( $search_query, $operator = 'AND' ) {
|
77 |
+
global $relevanssi_variables;
|
78 |
+
|
79 |
+
$phrases = relevanssi_extract_phrases( $search_query );
|
80 |
+
|
81 |
+
$all_queries = array();
|
82 |
+
if ( 0 === count( $phrases ) ) {
|
83 |
+
return $all_queries;
|
84 |
+
}
|
85 |
+
|
86 |
+
$custom_fields = relevanssi_get_custom_fields();
|
87 |
+
$taxonomies = get_option( 'relevanssi_index_taxonomies_list', array() );
|
88 |
+
$excerpts = get_option( 'relevanssi_index_excerpt', 'off' );
|
89 |
+
$index_pdf_parent = get_option( 'relevanssi_index_pdf_parent' );
|
90 |
+
|
91 |
+
$phrase_queries = array();
|
92 |
+
$queries = array();
|
93 |
+
|
94 |
+
if (
|
95 |
+
isset( $relevanssi_variables['phrase_targets'] ) &&
|
96 |
+
is_array( $relevanssi_variables['phrase_targets'] )
|
97 |
+
) {
|
98 |
+
$non_targeted_phrases = array();
|
99 |
+
foreach ( $phrases as $phrase ) {
|
100 |
+
if (
|
101 |
+
isset( $relevanssi_variables['phrase_targets'][ $phrase ] ) &&
|
102 |
+
function_exists( 'relevanssi_targeted_phrases' )
|
103 |
+
) {
|
104 |
+
$queries = relevanssi_targeted_phrases( $phrase );
|
105 |
+
} else {
|
106 |
+
$non_targeted_phrases[] = $phrase;
|
107 |
+
}
|
108 |
+
}
|
109 |
+
$phrases = $non_targeted_phrases;
|
110 |
+
}
|
111 |
+
|
112 |
+
$queries = array_merge(
|
113 |
+
$queries,
|
114 |
+
relevanssi_generate_phrase_queries(
|
115 |
+
$phrases,
|
116 |
+
$taxonomies,
|
117 |
+
$custom_fields,
|
118 |
+
$excerpts,
|
119 |
+
$index_pdf_parent
|
120 |
+
)
|
121 |
+
);
|
122 |
+
|
123 |
+
$phrase_queries = array();
|
124 |
+
|
125 |
+
foreach ( $queries as $phrase => $p_queries ) {
|
126 |
+
$p_queries = implode( ' OR relevanssi.doc IN ', $p_queries );
|
127 |
+
$p_queries = "(relevanssi.doc IN $p_queries)";
|
128 |
+
$all_queries[] = $p_queries;
|
129 |
+
|
130 |
+
$phrase_queries[ $phrase ] = $p_queries;
|
131 |
+
}
|
132 |
+
|
133 |
+
$operator = strtoupper( $operator );
|
134 |
+
if ( 'AND' !== $operator && 'OR' !== $operator ) {
|
135 |
+
$operator = 'AND';
|
136 |
+
}
|
137 |
+
|
138 |
+
if ( ! empty( $all_queries ) ) {
|
139 |
+
$all_queries = ' AND ( ' . implode( ' ' . $operator . ' ', $all_queries ) . ' ) ';
|
140 |
+
}
|
141 |
+
|
142 |
+
return array(
|
143 |
+
'and' => $all_queries,
|
144 |
+
'or' => $phrase_queries,
|
145 |
+
);
|
146 |
+
}
|
147 |
+
|
148 |
+
/**
|
149 |
+
* Generates the phrase queries from phrases.
|
150 |
+
*
|
151 |
+
* Takes in phrases and a bunch of parameters and generates the MySQL queries
|
152 |
+
* that restrict the main search query to only posts that have the phrase.
|
153 |
+
*
|
154 |
+
* @param array $phrases A list of phrases to handle.
|
155 |
+
* @param array $taxonomies An array of taxonomy names to use.
|
156 |
+
* @param array $custom_fields A list of custom field names to use.
|
157 |
+
* @param string $excerpts If 'on', include excerpts.
|
158 |
+
* @param string $index_pdf_parent If 'on', include PDF parent.
|
159 |
+
*
|
160 |
+
* @global object $wpdb The WordPress database interface.
|
161 |
+
*
|
162 |
+
* @return array An array of queries sorted by phrase.
|
163 |
+
*/
|
164 |
+
function relevanssi_generate_phrase_queries( $phrases, $taxonomies, $custom_fields, $excerpts, $index_pdf_parent ) {
|
165 |
+
global $wpdb;
|
166 |
+
|
167 |
+
$status = relevanssi_valid_status_array();
|
168 |
+
|
169 |
+
// Add "inherit" to the list of allowed statuses to include attachments.
|
170 |
+
if ( ! strstr( $status, 'inherit' ) ) {
|
171 |
+
$status .= ",'inherit'";
|
172 |
+
}
|
173 |
+
|
174 |
+
$phrase_queries = array();
|
175 |
+
|
176 |
+
foreach ( $phrases as $phrase ) {
|
177 |
+
$queries = array();
|
178 |
+
$phrase = $wpdb->esc_like( $phrase );
|
179 |
+
$phrase = str_replace( array( '‘', '’', "'", '"', '”', '“', '“', '„', '´' ), '_', $phrase );
|
180 |
+
$phrase = htmlspecialchars( $phrase );
|
181 |
+
$phrase = esc_sql( $phrase );
|
182 |
+
|
183 |
+
$excerpt = '';
|
184 |
+
if ( 'on' === $excerpts ) {
|
185 |
+
$excerpt = "OR post_excerpt LIKE '%$phrase%'";
|
186 |
+
}
|
187 |
+
|
188 |
+
$query = "(SELECT ID FROM $wpdb->posts
|
189 |
+
WHERE (post_content LIKE '%$phrase%'
|
190 |
+
OR post_title LIKE '%$phrase%' $excerpt)
|
191 |
+
AND post_status IN ($status))";
|
192 |
+
|
193 |
+
$queries[] = $query;
|
194 |
+
|
195 |
+
if ( $taxonomies ) {
|
196 |
+
$taxonomies_escaped = implode( "','", array_map( 'esc_sql', $taxonomies ) );
|
197 |
+
$taxonomies_sql = "AND s.taxonomy IN ('$taxonomies_escaped')";
|
198 |
+
|
199 |
+
$query = "(SELECT ID FROM
|
200 |
+
$wpdb->posts as p,
|
201 |
+
$wpdb->term_relationships as r,
|
202 |
+
$wpdb->term_taxonomy as s, $wpdb->terms as t
|
203 |
+
WHERE r.term_taxonomy_id = s.term_taxonomy_id
|
204 |
+
AND s.term_id = t.term_id AND p.ID = r.object_id
|
205 |
+
$taxonomies_sql
|
206 |
+
AND t.name LIKE '%$phrase%' AND p.post_status IN ($status))";
|
207 |
+
|
208 |
+
$queries[] = $query;
|
209 |
+
}
|
210 |
+
|
211 |
+
if ( $custom_fields ) {
|
212 |
+
$keys = '';
|
213 |
+
|
214 |
+
if ( is_array( $custom_fields ) ) {
|
215 |
+
if ( ! in_array( '_relevanssi_pdf_content', $custom_fields, true ) ) {
|
216 |
+
array_push( $custom_fields, '_relevanssi_pdf_content' );
|
217 |
+
}
|
218 |
+
|
219 |
+
if ( strpos( implode( ' ', $custom_fields ), '%' ) ) {
|
220 |
+
// ACF repeater fields involved.
|
221 |
+
$custom_fields_regexp = str_replace( '%', '.+', implode( '|', $custom_fields ) );
|
222 |
+
$keys = "AND m.meta_key REGEXP ('$custom_fields_regexp')";
|
223 |
+
} else {
|
224 |
+
$custom_fields_escaped = implode(
|
225 |
+
"','",
|
226 |
+
array_map(
|
227 |
+
'esc_sql',
|
228 |
+
$custom_fields
|
229 |
+
)
|
230 |
+
);
|
231 |
+
$keys = "AND m.meta_key IN ('$custom_fields_escaped')";
|
232 |
+
}
|
233 |
+
}
|
234 |
+
|
235 |
+
if ( 'visible' === $custom_fields ) {
|
236 |
+
$keys = "AND (m.meta_key NOT LIKE '\_%' OR m.meta_key = '_relevanssi_pdf_content')";
|
237 |
+
}
|
238 |
+
|
239 |
+
$query = "(SELECT ID
|
240 |
+
FROM $wpdb->posts AS p, $wpdb->postmeta AS m
|
241 |
+
WHERE p.ID = m.post_id
|
242 |
+
$keys
|
243 |
+
AND m.meta_value LIKE '%$phrase%'
|
244 |
+
AND p.post_status IN ($status))";
|
245 |
+
|
246 |
+
$queries[] = $query;
|
247 |
+
} elseif ( RELEVANSSI_PREMIUM ) {
|
248 |
+
$index_post_types = get_option( 'relevanssi_index_post_types', array() );
|
249 |
+
if ( in_array( 'attachment', $index_post_types, true ) ) {
|
250 |
+
$query = "(SELECT ID
|
251 |
+
FROM $wpdb->posts AS p, $wpdb->postmeta AS m
|
252 |
+
WHERE p.ID = m.post_id
|
253 |
+
AND m.meta_key = '_relevanssi_pdf_content'
|
254 |
+
AND m.meta_value LIKE '%$phrase%'
|
255 |
+
AND p.post_status IN ($status))";
|
256 |
+
|
257 |
+
$queries[] = $query;
|
258 |
+
}
|
259 |
+
}
|
260 |
+
|
261 |
+
if ( 'on' === $index_pdf_parent ) {
|
262 |
+
$query = "(SELECT parent.ID
|
263 |
+
FROM $wpdb->posts AS p, $wpdb->postmeta AS m, $wpdb->posts AS parent
|
264 |
+
WHERE p.ID = m.post_id
|
265 |
+
AND p.post_parent = parent.ID
|
266 |
+
AND m.meta_key = '_relevanssi_pdf_content'
|
267 |
+
AND m.meta_value LIKE '%$phrase%'
|
268 |
+
AND p.post_status = 'inherit')";
|
269 |
+
|
270 |
+
$queries[] = $query;
|
271 |
+
}
|
272 |
+
|
273 |
+
$phrase_queries[ $phrase ] = $queries;
|
274 |
+
}
|
275 |
+
|
276 |
+
return $phrase_queries;
|
277 |
+
}
|
lib/search.php
CHANGED
@@ -885,11 +885,12 @@ function relevanssi_do_query( &$query ) {
|
|
885 |
* One of the key filters for Relevanssi. If you want to modify the results
|
886 |
* Relevanssi finds, use this filter.
|
887 |
*
|
888 |
-
* @param array $filter_data The index 0 has an array of post objects
|
889 |
-
*
|
|
|
890 |
*
|
891 |
-
* @return array The return array composition is the same as the parameter
|
892 |
-
* but Relevanssi only uses the index 0.
|
893 |
*/
|
894 |
$hits_filters_applied = apply_filters( 'relevanssi_hits_filter', $filter_data );
|
895 |
// array_values() to make sure the $hits array is indexed in numerical order
|
@@ -1003,6 +1004,8 @@ function relevanssi_do_query( &$query ) {
|
|
1003 |
$query->posts = $posts;
|
1004 |
$query->post_count = count( $posts );
|
1005 |
|
|
|
|
|
1006 |
return $posts;
|
1007 |
}
|
1008 |
|
885 |
* One of the key filters for Relevanssi. If you want to modify the results
|
886 |
* Relevanssi finds, use this filter.
|
887 |
*
|
888 |
+
* @param array $filter_data The index 0 has an array of post objects (or
|
889 |
+
* post IDs, or parent=>ID pairs, depending on the `fields` parameter) found
|
890 |
+
* in the search, index 1 has the search query string.
|
891 |
*
|
892 |
+
* @return array The return array composition is the same as the parameter
|
893 |
+
* array, but Relevanssi only uses the index 0.
|
894 |
*/
|
895 |
$hits_filters_applied = apply_filters( 'relevanssi_hits_filter', $filter_data );
|
896 |
// array_values() to make sure the $hits array is indexed in numerical order
|
1004 |
$query->posts = $posts;
|
1005 |
$query->post_count = count( $posts );
|
1006 |
|
1007 |
+
$relevanssi_active = false;
|
1008 |
+
|
1009 |
return $posts;
|
1010 |
}
|
1011 |
|
lib/stopwords.php
CHANGED
@@ -40,9 +40,9 @@ function relevanssi_populate_stopwords( $verbose = false ) {
|
|
40 |
return 'database';
|
41 |
}
|
42 |
|
43 |
-
$
|
44 |
$stopword_file = $relevanssi_variables['plugin_dir']
|
45 |
-
. 'stopwords/stopwords.' . $
|
46 |
|
47 |
if ( ! file_exists( $stopword_file ) ) {
|
48 |
$verbose && printf(
|
@@ -53,7 +53,7 @@ function relevanssi_populate_stopwords( $verbose = false ) {
|
|
53 |
"The stopword file for the language '%s' doesn't exist.",
|
54 |
'relevanssi'
|
55 |
),
|
56 |
-
esc_html( $
|
57 |
)
|
58 |
);
|
59 |
return 'no_file';
|
@@ -83,15 +83,19 @@ function relevanssi_populate_stopwords( $verbose = false ) {
|
|
83 |
}
|
84 |
|
85 |
/**
|
86 |
-
* Fetches the list of stopwords.
|
87 |
*
|
88 |
-
* Gets the list of stopwords from the relevanssi_stopwords option
|
|
|
89 |
*
|
90 |
-
* @return array An array of stopwords
|
|
|
91 |
*/
|
92 |
function relevanssi_fetch_stopwords() {
|
93 |
-
$
|
94 |
-
$
|
|
|
|
|
95 |
|
96 |
return $stopword_list;
|
97 |
}
|
@@ -182,27 +186,102 @@ function relevanssi_add_single_stopword( $term ) {
|
|
182 |
return false;
|
183 |
}
|
184 |
|
|
|
185 |
$term = stripslashes( relevanssi_strtolower( $term ) );
|
186 |
-
$stopwords = get_option( 'relevanssi_stopwords', '' );
|
187 |
|
188 |
-
|
189 |
-
if ( in_array( $term, $stopwords_array, true ) ) {
|
190 |
return false;
|
191 |
}
|
192 |
|
193 |
-
$
|
194 |
-
|
195 |
-
|
196 |
-
implode( ',', array_filter( $stopwords_array ) )
|
197 |
-
);
|
198 |
|
199 |
if ( ! $success ) {
|
200 |
return false;
|
201 |
}
|
202 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
203 |
global $wpdb, $relevanssi_variables;
|
204 |
|
205 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
206 |
$wpdb->query(
|
207 |
$wpdb->prepare(
|
208 |
'DELETE FROM ' . $relevanssi_variables['relevanssi_table'] . // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
|
@@ -210,21 +289,27 @@ function relevanssi_add_single_stopword( $term ) {
|
|
210 |
$term
|
211 |
)
|
212 |
);
|
213 |
-
|
214 |
-
return true;
|
215 |
}
|
216 |
|
217 |
/**
|
218 |
-
* Removes all stopwords.
|
219 |
*
|
220 |
-
* Empties the relevanssi_stopwords option.
|
221 |
*
|
222 |
-
* @param boolean $verbose
|
|
|
|
|
223 |
*
|
224 |
* @return boolean True, if able to remove the options.
|
225 |
*/
|
226 |
-
function relevanssi_remove_all_stopwords( $verbose = true ) {
|
227 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
228 |
|
229 |
$verbose && $success && printf(
|
230 |
"<div id='message' class='updated fade'><p>%s</p></div>",
|
@@ -257,23 +342,16 @@ function relevanssi_remove_all_stopwords( $verbose = true ) {
|
|
257 |
* @return boolean True if success, false if not.
|
258 |
*/
|
259 |
function relevanssi_remove_stopword( $term, $verbose = true ) {
|
260 |
-
$stopwords =
|
261 |
-
$
|
262 |
-
|
263 |
-
|
264 |
-
|
265 |
-
|
266 |
-
|
267 |
-
|
268 |
-
$stopwords_array,
|
269 |
-
function( $stopword ) use ( $term ) {
|
270 |
-
return $stopword !== $term;
|
271 |
-
}
|
272 |
-
);
|
273 |
|
274 |
-
|
275 |
-
$success = update_option( 'relevanssi_stopwords', $stopwords );
|
276 |
-
}
|
277 |
|
278 |
$verbose && $success &&
|
279 |
printf(
|
@@ -322,3 +400,16 @@ function relevanssi_remove_stopwords_from_array( $terms ) {
|
|
322 |
$terms_without_stops = array_diff( $terms, $stopword_list );
|
323 |
return $terms_without_stops;
|
324 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
40 |
return 'database';
|
41 |
}
|
42 |
|
43 |
+
$language = relevanssi_get_current_language();
|
44 |
$stopword_file = $relevanssi_variables['plugin_dir']
|
45 |
+
. 'stopwords/stopwords.' . $language;
|
46 |
|
47 |
if ( ! file_exists( $stopword_file ) ) {
|
48 |
$verbose && printf(
|
53 |
"The stopword file for the language '%s' doesn't exist.",
|
54 |
'relevanssi'
|
55 |
),
|
56 |
+
esc_html( $language )
|
57 |
)
|
58 |
);
|
59 |
return 'no_file';
|
83 |
}
|
84 |
|
85 |
/**
|
86 |
+
* Fetches the list of stopwords in the current language.
|
87 |
*
|
88 |
+
* Gets the list of stopwords from the relevanssi_stopwords option using the
|
89 |
+
* current language.
|
90 |
*
|
91 |
+
* @return array An array of stopwords; if nothing is found, returns an empty
|
92 |
+
* array.
|
93 |
*/
|
94 |
function relevanssi_fetch_stopwords() {
|
95 |
+
$current_language = relevanssi_get_current_language();
|
96 |
+
$stopwords_array = get_option( 'relevanssi_stopwords', array() );
|
97 |
+
$stopwords = isset( $stopwords_array[ $current_language ] ) ? $stopwords_array[ $current_language ] : '';
|
98 |
+
$stopword_list = $stopwords ? explode( ',', $stopwords ) : array();
|
99 |
|
100 |
return $stopword_list;
|
101 |
}
|
186 |
return false;
|
187 |
}
|
188 |
|
189 |
+
$stopwords = relevanssi_fetch_stopwords();
|
190 |
$term = stripslashes( relevanssi_strtolower( $term ) );
|
|
|
191 |
|
192 |
+
if ( in_array( $term, $stopwords, true ) ) {
|
|
|
193 |
return false;
|
194 |
}
|
195 |
|
196 |
+
$stopwords[] = $term;
|
197 |
+
|
198 |
+
$success = relevanssi_update_stopwords( $stopwords );
|
|
|
|
|
199 |
|
200 |
if ( ! $success ) {
|
201 |
return false;
|
202 |
}
|
203 |
|
204 |
+
relevanssi_delete_term_from_all_posts( $term );
|
205 |
+
|
206 |
+
return true;
|
207 |
+
}
|
208 |
+
|
209 |
+
/**
|
210 |
+
* Updates the current language stopwords in the stopwords option.
|
211 |
+
*
|
212 |
+
* Fetches the stopwords option, replaces the current language stopwords with
|
213 |
+
* the parameter array and updates the option.
|
214 |
+
*
|
215 |
+
* @param array $stopwords An array of stopwords.
|
216 |
+
*
|
217 |
+
* @return boolean The return value from update_option().
|
218 |
+
*/
|
219 |
+
function relevanssi_update_stopwords( $stopwords ) {
|
220 |
+
$current_language = relevanssi_get_current_language();
|
221 |
+
$stopwords_option = get_option( 'relevanssi_stopwords', array() );
|
222 |
+
|
223 |
+
$stopwords_option[ $current_language ] = implode( ',', array_filter( $stopwords ) );
|
224 |
+
return update_option(
|
225 |
+
'relevanssi_stopwords',
|
226 |
+
$stopwords_option
|
227 |
+
);
|
228 |
+
}
|
229 |
+
|
230 |
+
/**
|
231 |
+
* Deletes a term from all posts in the database, language considered.
|
232 |
+
*
|
233 |
+
* If Polylang or WPML are used, deletes the term only from the posts matching
|
234 |
+
* the current language.
|
235 |
+
*
|
236 |
+
* @param string $term The term to delete.
|
237 |
+
*/
|
238 |
+
function relevanssi_delete_term_from_all_posts( $term ) {
|
239 |
global $wpdb, $relevanssi_variables;
|
240 |
|
241 |
+
if ( function_exists( 'pll_languages_list' ) ) {
|
242 |
+
$term_id = relevanssi_get_language_term_taxonomy_id(
|
243 |
+
relevanssi_get_current_language()
|
244 |
+
);
|
245 |
+
|
246 |
+
$wpdb->query(
|
247 |
+
$wpdb->prepare(
|
248 |
+
// phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
|
249 |
+
"DELETE FROM {$relevanssi_variables['relevanssi_table']}
|
250 |
+
WHERE term=%s
|
251 |
+
AND doc IN (
|
252 |
+
SELECT object_id
|
253 |
+
FROM $wpdb->term_relationships
|
254 |
+
WHERE term_taxonomy_id = %d
|
255 |
+
)",
|
256 |
+
$term,
|
257 |
+
$term_id
|
258 |
+
)
|
259 |
+
);
|
260 |
+
|
261 |
+
return;
|
262 |
+
}
|
263 |
+
|
264 |
+
if ( function_exists( 'icl_object_id' ) && ! function_exists( 'pll_is_translated_post_type' ) ) {
|
265 |
+
$language = relevanssi_get_current_language( false );
|
266 |
+
$wpdb->query(
|
267 |
+
$wpdb->prepare(
|
268 |
+
// phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
|
269 |
+
"DELETE FROM {$relevanssi_variables['relevanssi_table']}
|
270 |
+
WHERE term=%s
|
271 |
+
AND doc IN (
|
272 |
+
SELECT DISTINCT(element_id)
|
273 |
+
FROM {$wpdb->prefix}icl_translations
|
274 |
+
WHERE language_code = %s
|
275 |
+
)",
|
276 |
+
$term,
|
277 |
+
$language
|
278 |
+
)
|
279 |
+
);
|
280 |
+
|
281 |
+
return;
|
282 |
+
}
|
283 |
+
|
284 |
+
// No language defined, just remove from the index.
|
285 |
$wpdb->query(
|
286 |
$wpdb->prepare(
|
287 |
'DELETE FROM ' . $relevanssi_variables['relevanssi_table'] . // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
|
289 |
$term
|
290 |
)
|
291 |
);
|
|
|
|
|
292 |
}
|
293 |
|
294 |
/**
|
295 |
+
* Removes all stopwords in specific language.
|
296 |
*
|
297 |
+
* Empties the relevanssi_stopwords option for particular language.
|
298 |
*
|
299 |
+
* @param boolean $verbose If true, print out notice. Default true.
|
300 |
+
* @param string $language The language code of stopwords. If empty, removes
|
301 |
+
* the stopwords for the current language.
|
302 |
*
|
303 |
* @return boolean True, if able to remove the options.
|
304 |
*/
|
305 |
+
function relevanssi_remove_all_stopwords( $verbose = true, $language = false ) {
|
306 |
+
if ( ! $language ) {
|
307 |
+
$language = relevanssi_get_current_language();
|
308 |
+
}
|
309 |
+
|
310 |
+
$stopwords = get_option( 'relevanssi_stopwords', array() );
|
311 |
+
unset( $stopwords[ $language ] );
|
312 |
+
$success = update_option( 'relevanssi_stopwords', $stopwords );
|
313 |
|
314 |
$verbose && $success && printf(
|
315 |
"<div id='message' class='updated fade'><p>%s</p></div>",
|
342 |
* @return boolean True if success, false if not.
|
343 |
*/
|
344 |
function relevanssi_remove_stopword( $term, $verbose = true ) {
|
345 |
+
$stopwords = relevanssi_fetch_stopwords();
|
346 |
+
$term = stripslashes( $term );
|
347 |
+
$stopwords = array_filter(
|
348 |
+
$stopwords,
|
349 |
+
function( $stopword ) use ( $term ) {
|
350 |
+
return $stopword !== $term;
|
351 |
+
}
|
352 |
+
);
|
|
|
|
|
|
|
|
|
|
|
353 |
|
354 |
+
$success = relevanssi_update_stopwords( $stopwords );
|
|
|
|
|
355 |
|
356 |
$verbose && $success &&
|
357 |
printf(
|
400 |
$terms_without_stops = array_diff( $terms, $stopword_list );
|
401 |
return $terms_without_stops;
|
402 |
}
|
403 |
+
|
404 |
+
/**
|
405 |
+
* Updates the relevanssi_stopwords setting from a simple string to an array
|
406 |
+
* that is required for multilingual stopwords.
|
407 |
+
*/
|
408 |
+
function relevanssi_update_stopwords_setting() {
|
409 |
+
$stopwords = get_option( 'relevanssi_stopwords' );
|
410 |
+
|
411 |
+
$current_language = relevanssi_get_current_language();
|
412 |
+
|
413 |
+
$array_stopwords[ $current_language ] = $stopwords;
|
414 |
+
update_option( 'relevanssi_stopwords', $array_stopwords );
|
415 |
+
}
|
lib/tabs/searching-tab.php
CHANGED
@@ -393,10 +393,10 @@ function relevanssi_searching_tab() {
|
|
393 |
</tr>
|
394 |
<tr>
|
395 |
<th scope="row">
|
396 |
-
<label for='
|
397 |
</th>
|
398 |
<td>
|
399 |
-
<input type='text' name='
|
400 |
<p class="description"><?php esc_html_e( "Enter a comma-separated list of post or page ID's to exclude those pages from the search results.", 'relevanssi' ); ?></p>
|
401 |
<?php if ( RELEVANSSI_PREMIUM ) { ?>
|
402 |
<p class="description"><?php esc_html_e( "With Relevanssi Premium, it's better to use the check box on post edit pages. That will remove the posts completely from the index, and will work with multisite searches unlike this setting.", 'relevanssi' ); ?></p>
|
393 |
</tr>
|
394 |
<tr>
|
395 |
<th scope="row">
|
396 |
+
<label for='relevanssi_exclude_posts'><?php esc_html_e( 'Post exclusion', 'relevanssi' ); ?>
|
397 |
</th>
|
398 |
<td>
|
399 |
+
<input type='text' name='relevanssi_exclude_posts' id='relevanssi_exclude_posts' size='60' value='<?php echo esc_attr( $exclude_posts ); ?>' />
|
400 |
<p class="description"><?php esc_html_e( "Enter a comma-separated list of post or page ID's to exclude those pages from the search results.", 'relevanssi' ); ?></p>
|
401 |
<?php if ( RELEVANSSI_PREMIUM ) { ?>
|
402 |
<p class="description"><?php esc_html_e( "With Relevanssi Premium, it's better to use the check box on post edit pages. That will remove the posts completely from the index, and will work with multisite searches unlike this setting.", 'relevanssi' ); ?></p>
|
lib/tabs/stopwords-tab.php
CHANGED
@@ -28,15 +28,21 @@ function relevanssi_stopwords_tab() {
|
|
28 |
if ( function_exists( 'relevanssi_show_body_stopwords' ) ) {
|
29 |
relevanssi_show_body_stopwords();
|
30 |
} else {
|
31 |
-
printf(
|
|
|
|
|
|
|
|
|
|
|
|
|
32 |
}
|
33 |
|
34 |
/**
|
35 |
* Filters whether the common words list is displayed or not.
|
36 |
*
|
37 |
-
* The list of 25 most common words is displayed by default, but if the
|
38 |
-
* big, displaying the list can take a long time. This filter can
|
39 |
-
* turn the list off.
|
40 |
*
|
41 |
* @param boolean If true, show the list; if false, don't show it.
|
42 |
*/
|
@@ -48,10 +54,17 @@ function relevanssi_stopwords_tab() {
|
|
48 |
/**
|
49 |
* Displays a list of stopwords.
|
50 |
*
|
51 |
-
* Displays the list of stopwords and gives the controls for adding new
|
|
|
52 |
*/
|
53 |
function relevanssi_show_stopwords() {
|
54 |
-
printf(
|
|
|
|
|
|
|
|
|
|
|
|
|
55 |
?>
|
56 |
<table class="form-table" role="presentation">
|
57 |
<tr>
|
@@ -74,17 +87,16 @@ function relevanssi_show_stopwords() {
|
|
74 |
<td>
|
75 |
<ul>
|
76 |
<?php
|
77 |
-
$
|
78 |
-
|
79 |
-
|
80 |
array_walk(
|
81 |
-
$
|
82 |
function ( $term ) {
|
83 |
printf( '<li style="display: inline;"><input type="submit" name="removestopword" value="%s"/></li>', esc_attr( $term ) );
|
84 |
}
|
85 |
);
|
86 |
|
87 |
-
$exportlist = htmlspecialchars( str_replace( ',', ', ', $stopword_list ) );
|
88 |
?>
|
89 |
</ul>
|
90 |
<p>
|
28 |
if ( function_exists( 'relevanssi_show_body_stopwords' ) ) {
|
29 |
relevanssi_show_body_stopwords();
|
30 |
} else {
|
31 |
+
printf(
|
32 |
+
'<p>%s</p>',
|
33 |
+
esc_html__(
|
34 |
+
'Content stopwords are a premium feature where you can set stopwords that only apply to the post content. Those stopwords will still be indexed if they appear in post titles, tags, categories, custom fields or other parts of the post. To use content stopwords, you need Relevanssi Premium.',
|
35 |
+
'relevanssi'
|
36 |
+
)
|
37 |
+
);
|
38 |
}
|
39 |
|
40 |
/**
|
41 |
* Filters whether the common words list is displayed or not.
|
42 |
*
|
43 |
+
* The list of 25 most common words is displayed by default, but if the
|
44 |
+
* index is big, displaying the list can take a long time. This filter can
|
45 |
+
* be used to turn the list off.
|
46 |
*
|
47 |
* @param boolean If true, show the list; if false, don't show it.
|
48 |
*/
|
54 |
/**
|
55 |
* Displays a list of stopwords.
|
56 |
*
|
57 |
+
* Displays the list of stopwords and gives the controls for adding new
|
58 |
+
* stopwords.
|
59 |
*/
|
60 |
function relevanssi_show_stopwords() {
|
61 |
+
printf(
|
62 |
+
'<p>%s</p>',
|
63 |
+
esc_html__(
|
64 |
+
'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.',
|
65 |
+
'relevanssi'
|
66 |
+
)
|
67 |
+
);
|
68 |
?>
|
69 |
<table class="form-table" role="presentation">
|
70 |
<tr>
|
87 |
<td>
|
88 |
<ul>
|
89 |
<?php
|
90 |
+
$stopwords = array_map( 'stripslashes', relevanssi_fetch_stopwords() );
|
91 |
+
sort( $stopwords );
|
92 |
+
$exportlist = htmlspecialchars( implode( ', ', $stopwords ) );
|
93 |
array_walk(
|
94 |
+
$stopwords,
|
95 |
function ( $term ) {
|
96 |
printf( '<li style="display: inline;"><input type="submit" name="removestopword" value="%s"/></li>', esc_attr( $term ) );
|
97 |
}
|
98 |
);
|
99 |
|
|
|
100 |
?>
|
101 |
</ul>
|
102 |
<p>
|
lib/tabs/synonyms-tab.php
CHANGED
@@ -14,7 +14,9 @@
|
|
14 |
* Prints out the synonyms tab in Relevanssi settings.
|
15 |
*/
|
16 |
function relevanssi_synonyms_tab() {
|
17 |
-
$
|
|
|
|
|
18 |
|
19 |
if ( isset( $synonyms ) ) {
|
20 |
$synonyms = str_replace( ';', "\n", $synonyms );
|
14 |
* Prints out the synonyms tab in Relevanssi settings.
|
15 |
*/
|
16 |
function relevanssi_synonyms_tab() {
|
17 |
+
$current_language = relevanssi_get_current_language();
|
18 |
+
$synonyms_array = get_option( 'relevanssi_synonyms', array() );
|
19 |
+
$synonyms = isset( $synonyms_array[ $current_language ] ) ? $synonyms_array[ $current_language ] : '';
|
20 |
|
21 |
if ( isset( $synonyms ) ) {
|
22 |
$synonyms = str_replace( ';', "\n", $synonyms );
|
lib/uninstall.php
CHANGED
@@ -131,6 +131,7 @@ function relevanssi_uninstall_free() {
|
|
131 |
delete_option( 'relevanssi_taxonomies_to_index' );
|
132 |
delete_option( 'relevanssi_highlight_docs_external' );
|
133 |
delete_option( 'relevanssi_word_boundaries' );
|
|
|
134 |
|
135 |
global $wpdb;
|
136 |
$wpdb->query( "DELETE FROM $wpdb->postmeta WHERE meta_key = '_relevanssi_noindex_reason'" );
|
131 |
delete_option( 'relevanssi_taxonomies_to_index' );
|
132 |
delete_option( 'relevanssi_highlight_docs_external' );
|
133 |
delete_option( 'relevanssi_word_boundaries' );
|
134 |
+
delete_option( 'relevanssi_expst' );
|
135 |
|
136 |
global $wpdb;
|
137 |
$wpdb->query( "DELETE FROM $wpdb->postmeta WHERE meta_key = '_relevanssi_noindex_reason'" );
|
lib/utils.php
ADDED
@@ -0,0 +1,867 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* /lib/utils.php
|
4 |
+
*
|
5 |
+
* @package Relevanssi
|
6 |
+
* @author Mikko Saari
|
7 |
+
* @license https://wordpress.org/about/gpl/ GNU General Public License
|
8 |
+
* @see https://www.relevanssi.com/
|
9 |
+
*/
|
10 |
+
|
11 |
+
/**
|
12 |
+
* Returns a Relevanssi_Taxonomy_Walker instance.
|
13 |
+
*
|
14 |
+
* Requires the class file and generates a new Relevanssi_Taxonomy_Walker instance.
|
15 |
+
*
|
16 |
+
* @return object A new Relevanssi_Taxonomy_Walker instance.
|
17 |
+
*/
|
18 |
+
function get_relevanssi_taxonomy_walker() {
|
19 |
+
require_once 'class-relevanssi-taxonomy-walker.php';
|
20 |
+
return new Relevanssi_Taxonomy_Walker();
|
21 |
+
}
|
22 |
+
|
23 |
+
/**
|
24 |
+
* Wraps the relevanssi_mb_trim() function so that it can be used as a callback
|
25 |
+
* for array_walk().
|
26 |
+
*
|
27 |
+
* @since 2.1.4
|
28 |
+
*
|
29 |
+
* @see relevanssi_mb_trim.
|
30 |
+
*
|
31 |
+
* @param string $string String to trim.
|
32 |
+
*/
|
33 |
+
function relevanssi_array_walk_trim( &$string ) {
|
34 |
+
$string = relevanssi_mb_trim( $string );
|
35 |
+
}
|
36 |
+
|
37 |
+
/**
|
38 |
+
* Returns 'checked' if the option is enabled.
|
39 |
+
*
|
40 |
+
* @param string $option Value to check.
|
41 |
+
*
|
42 |
+
* @return string If the option is 'on', returns 'checked', otherwise returns an
|
43 |
+
* empty string.
|
44 |
+
*/
|
45 |
+
function relevanssi_check( $option ) {
|
46 |
+
$checked = '';
|
47 |
+
if ( 'on' === $option ) {
|
48 |
+
$checked = 'checked';
|
49 |
+
}
|
50 |
+
return $checked;
|
51 |
+
}
|
52 |
+
|
53 |
+
/**
|
54 |
+
* Closes tags in a bit of HTML code.
|
55 |
+
*
|
56 |
+
* Used to make sure no tags are left open in excerpts. This method is not
|
57 |
+
* foolproof, but it's good enough for now.
|
58 |
+
*
|
59 |
+
* @param string $html The HTML code to analyze.
|
60 |
+
*
|
61 |
+
* @return string The HTML code, with tags closed.
|
62 |
+
*/
|
63 |
+
function relevanssi_close_tags( $html ) {
|
64 |
+
$result = array();
|
65 |
+
preg_match_all(
|
66 |
+
'#<(?!meta|img|br|hr|input\b)\b([a-z]+)(?: .*)?(?<![/|/ ])>#iU',
|
67 |
+
$html,
|
68 |
+
$result
|
69 |
+
);
|
70 |
+
$opened_tags = $result[1];
|
71 |
+
preg_match_all( '#</([a-z]+)>#iU', $html, $result );
|
72 |
+
$closed_tags = $result[1];
|
73 |
+
$len_opened = count( $opened_tags );
|
74 |
+
if ( count( $closed_tags ) === $len_opened ) {
|
75 |
+
return $html;
|
76 |
+
}
|
77 |
+
$opened_tags = array_reverse( $opened_tags );
|
78 |
+
for ( $i = 0; $i < $len_opened; $i++ ) {
|
79 |
+
if ( ! in_array( $opened_tags[ $i ], $closed_tags, true ) ) {
|
80 |
+
$html .= '</' . $opened_tags[ $i ] . '>';
|
81 |
+
} else {
|
82 |
+
unset(
|
83 |
+
$closed_tags[ array_search( $opened_tags[ $i ], $closed_tags, true ) ]
|
84 |
+
);
|
85 |
+
}
|
86 |
+
}
|
87 |
+
return $html;
|
88 |
+
}
|
89 |
+
|
90 |
+
/**
|
91 |
+
* Prints out debugging notices.
|
92 |
+
*
|
93 |
+
* If WP_CLI is available, prints out the debug notice as a WP_CLI::log(),
|
94 |
+
* otherwise just echo.
|
95 |
+
*
|
96 |
+
* @param string $notice The notice to print out.
|
97 |
+
*/
|
98 |
+
function relevanssi_debug_echo( $notice ) {
|
99 |
+
if ( defined( 'WP_CLI' ) && WP_CLI ) {
|
100 |
+
WP_CLI::log( $notice );
|
101 |
+
} else {
|
102 |
+
echo esc_html( $notice ) . "\n";
|
103 |
+
}
|
104 |
+
}
|
105 |
+
|
106 |
+
/**
|
107 |
+
* Recursively flattens a multidimensional array to produce a string.
|
108 |
+
*
|
109 |
+
* @param array $array The source array.
|
110 |
+
*
|
111 |
+
* @return string The array contents as a string.
|
112 |
+
*/
|
113 |
+
function relevanssi_flatten_array( array $array ) {
|
114 |
+
$return_value = '';
|
115 |
+
foreach ( new RecursiveIteratorIterator( new RecursiveArrayIterator( $array ) ) as $value ) {
|
116 |
+
$return_value .= ' ' . $value;
|
117 |
+
}
|
118 |
+
return trim( $return_value );
|
119 |
+
}
|
120 |
+
|
121 |
+
/**
|
122 |
+
* Generates closing tags for an array of tags.
|
123 |
+
*
|
124 |
+
* @param array $tags Array of tag names.
|
125 |
+
*
|
126 |
+
* @return array $closing_tags Array of closing tags.
|
127 |
+
*/
|
128 |
+
function relevanssi_generate_closing_tags( $tags ) {
|
129 |
+
$closing_tags = array();
|
130 |
+
foreach ( $tags as $tag ) {
|
131 |
+
$a = str_replace( '<', '</', $tag );
|
132 |
+
$b = str_replace( '>', '/>', $tag );
|
133 |
+
|
134 |
+
$closing_tags[] = $a;
|
135 |
+
$closing_tags[] = $b;
|
136 |
+
}
|
137 |
+
return $closing_tags;
|
138 |
+
}
|
139 |
+
|
140 |
+
/**
|
141 |
+
* Returns the locale or language code.
|
142 |
+
*
|
143 |
+
* First checks `pll_current_language()`, then `wpml_current_language`, then
|
144 |
+
* falls back to `get_locale()`.
|
145 |
+
*
|
146 |
+
* @param boolean $locale If true, return locale; if false, return language
|
147 |
+
* code.
|
148 |
+
*
|
149 |
+
* @return string The locale for the current site language.
|
150 |
+
*/
|
151 |
+
function relevanssi_get_current_language( $locale = true ) {
|
152 |
+
$current_language = get_locale();
|
153 |
+
if ( ! $locale ) {
|
154 |
+
$current_language = substr( $locale, 0, 2 );
|
155 |
+
}
|
156 |
+
if ( function_exists( 'pll_current_language' ) ) {
|
157 |
+
$current_language = pll_current_language( $locale ? 'locale' : 'slug' );
|
158 |
+
}
|
159 |
+
if ( function_exists( 'icl_object_id' ) && ! function_exists( 'pll_is_translated_post_type' ) ) {
|
160 |
+
if ( $locale ) {
|
161 |
+
$languages = apply_filters( 'wpml_active_languages', null );
|
162 |
+
foreach ( $languages as $l ) {
|
163 |
+
if ( $l['active'] ) {
|
164 |
+
$current_language = $l['default_locale'];
|
165 |
+
break;
|
166 |
+
}
|
167 |
+
}
|
168 |
+
} else {
|
169 |
+
$current_language = apply_filters( 'wpml_current_language', null );
|
170 |
+
}
|
171 |
+
}
|
172 |
+
|
173 |
+
return $current_language;
|
174 |
+
}
|
175 |
+
|
176 |
+
/**
|
177 |
+
* Gets the permalink to the current post within Loop.
|
178 |
+
*
|
179 |
+
* Uses get_permalink() to get the permalink, then adds the 'highlight'
|
180 |
+
* parameter if necessary using relevanssi_add_highlight().
|
181 |
+
*
|
182 |
+
* @return string The permalink.
|
183 |
+
*/
|
184 |
+
function relevanssi_get_permalink() {
|
185 |
+
/**
|
186 |
+
* Filters the permalink.
|
187 |
+
*
|
188 |
+
* @param string The permalink, generated by get_permalink().
|
189 |
+
*/
|
190 |
+
$permalink = apply_filters( 'relevanssi_permalink', get_permalink() );
|
191 |
+
return $permalink;
|
192 |
+
}
|
193 |
+
|
194 |
+
/**
|
195 |
+
* Replacement for get_post() that uses the Relevanssi post cache.
|
196 |
+
*
|
197 |
+
* Tries to fetch the post from the Relevanssi post cache. If that doesn't work,
|
198 |
+
* gets the post using get_post().
|
199 |
+
*
|
200 |
+
* @param int $post_id The post ID.
|
201 |
+
* @param int $blog_id The blog ID, default -1.
|
202 |
+
*
|
203 |
+
* @return object The post object.
|
204 |
+
*/
|
205 |
+
function relevanssi_get_post( $post_id, $blog_id = -1 ) {
|
206 |
+
if ( function_exists( 'relevanssi_premium_get_post' ) ) {
|
207 |
+
return relevanssi_premium_get_post( $post_id, $blog_id );
|
208 |
+
}
|
209 |
+
|
210 |
+
global $relevanssi_post_array;
|
211 |
+
|
212 |
+
$post = null;
|
213 |
+
if ( isset( $relevanssi_post_array[ $post_id ] ) ) {
|
214 |
+
$post = $relevanssi_post_array[ $post_id ];
|
215 |
+
}
|
216 |
+
if ( ! $post ) {
|
217 |
+
$post = get_post( $post_id );
|
218 |
+
|
219 |
+
$relevanssi_post_array[ $post_id ] = $post;
|
220 |
+
}
|
221 |
+
return $post;
|
222 |
+
}
|
223 |
+
|
224 |
+
/**
|
225 |
+
* Returns the term taxonomy ID for a term based on term ID.
|
226 |
+
*
|
227 |
+
* @global object $wpdb The WordPress database interface.
|
228 |
+
*
|
229 |
+
* @param int $term_id The term ID.
|
230 |
+
* @param string $taxonomy The taxonomy.
|
231 |
+
*
|
232 |
+
* @return int Term taxonomy ID.
|
233 |
+
*/
|
234 |
+
function relevanssi_get_term_tax_id( $term_id, $taxonomy ) {
|
235 |
+
global $wpdb;
|
236 |
+
return $wpdb->get_var(
|
237 |
+
$wpdb->prepare(
|
238 |
+
"SELECT term_taxonomy_id FROM $wpdb->term_taxonomy WHERE term_id = %d AND taxonomy = %s",
|
239 |
+
$term_id,
|
240 |
+
$taxonomy
|
241 |
+
)
|
242 |
+
);
|
243 |
+
}
|
244 |
+
|
245 |
+
/**
|
246 |
+
* Fetches the taxonomy based on term ID.
|
247 |
+
*
|
248 |
+
* Fetches the taxonomy from wp_term_taxonomy based on term_id.
|
249 |
+
*
|
250 |
+
* @global object $wpdb The WordPress database interface.
|
251 |
+
* @param int $term_id The term ID.
|
252 |
+
* @deprecated Will be removed in future versions.
|
253 |
+
* @return string $taxonomy The term taxonomy.
|
254 |
+
*/
|
255 |
+
function relevanssi_get_term_taxonomy( $term_id ) {
|
256 |
+
global $wpdb;
|
257 |
+
|
258 |
+
$taxonomy = $wpdb->get_var( $wpdb->prepare( "SELECT taxonomy FROM $wpdb->term_taxonomy WHERE term_id = %d", $term_id ) ); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
|
259 |
+
return $taxonomy;
|
260 |
+
}
|
261 |
+
|
262 |
+
/**
|
263 |
+
* Gets a list of tags for post.
|
264 |
+
*
|
265 |
+
* Replacement for get_the_tags() that does the same, but applies Relevanssi
|
266 |
+
* search term highlighting on the results.
|
267 |
+
*
|
268 |
+
* @param string $before What is printed before the tags, default null.
|
269 |
+
* @param string $separator The separator between items, default ', '.
|
270 |
+
* @param string $after What is printed after the tags, default ''.
|
271 |
+
* @param int $post_id The post ID. Default current post ID (in the Loop).
|
272 |
+
*/
|
273 |
+
function relevanssi_get_the_tags( $before = null, $separator = ', ', $after = '', $post_id = null ) {
|
274 |
+
return relevanssi_the_tags( $before, $separator, $after, false, $post_id );
|
275 |
+
}
|
276 |
+
|
277 |
+
/**
|
278 |
+
* Returns an imploded string if the option exists and is an array, an empty
|
279 |
+
* string otherwise.
|
280 |
+
*
|
281 |
+
* @param array $request An array of option values.
|
282 |
+
* @param string $option The key to check.
|
283 |
+
* @param string $glue The glue string for implode(), default ','.
|
284 |
+
*
|
285 |
+
* @return string Imploded string or an empty string.
|
286 |
+
*/
|
287 |
+
function relevanssi_implode( $request, $option, $glue = ',' ) {
|
288 |
+
if ( isset( $request[ $option ] ) && is_array( $request[ $option ] ) ) {
|
289 |
+
return implode( $glue, $request[ $option ] );
|
290 |
+
}
|
291 |
+
return '';
|
292 |
+
}
|
293 |
+
|
294 |
+
/**
|
295 |
+
* Returns the intval of the option if it exists, null otherwise.
|
296 |
+
*
|
297 |
+
* @param array $request An array of option values.
|
298 |
+
* @param string $option The key to check.
|
299 |
+
*
|
300 |
+
* @return int|null Integer value of the option, or null.
|
301 |
+
*/
|
302 |
+
function relevanssi_intval( $request, $option ) {
|
303 |
+
if ( isset( $request[ $option ] ) ) {
|
304 |
+
return intval( $request[ $option ] );
|
305 |
+
}
|
306 |
+
return null;
|
307 |
+
}
|
308 |
+
|
309 |
+
/**
|
310 |
+
* Launches an asynchronous Ajax action.
|
311 |
+
*
|
312 |
+
* Makes a wp_remote_post() call with the specific action. Handles nonce
|
313 |
+
* verification.
|
314 |
+
*
|
315 |
+
* @see wp_remove_post()
|
316 |
+
* @see wp_create_nonce()
|
317 |
+
*
|
318 |
+
* @param string $action The action to trigger (also the name of the
|
319 |
+
* nonce).
|
320 |
+
* @param array $payload_args The parameters sent to the action. Defaults to
|
321 |
+
* an empty array.
|
322 |
+
*
|
323 |
+
* @return WP_Error|array The wp_remote_post() response or WP_Error on failure.
|
324 |
+
*/
|
325 |
+
function relevanssi_launch_ajax_action( $action, $payload_args = array() ) {
|
326 |
+
$cookies = array();
|
327 |
+
foreach ( $_COOKIE as $name => $value ) {
|
328 |
+
$cookies[] = "$name=" . rawurlencode(
|
329 |
+
is_array( $value ) ? wp_json_encode( $value ) : $value
|
330 |
+
);
|
331 |
+
}
|
332 |
+
$default_payload = array(
|
333 |
+
'action' => $action,
|
334 |
+
'_nonce' => wp_create_nonce( $action ),
|
335 |
+
);
|
336 |
+
$payload = array_merge( $default_payload, $payload_args );
|
337 |
+
$args = array(
|
338 |
+
'timeout' => 0.01,
|
339 |
+
'blocking' => false,
|
340 |
+
'body' => $payload,
|
341 |
+
'headers' => array(
|
342 |
+
'cookie' => implode( '; ', $cookies ),
|
343 |
+
),
|
344 |
+
);
|
345 |
+
$url = admin_url( 'admin-ajax.php' );
|
346 |
+
return wp_remote_post( $url, $args );
|
347 |
+
}
|
348 |
+
|
349 |
+
/**
|
350 |
+
* Returns a legal value.
|
351 |
+
*
|
352 |
+
* @param array $request An array of option values.
|
353 |
+
* @param string $option The key to check.
|
354 |
+
* @param array $values The legal values.
|
355 |
+
* @param string $default The default value.
|
356 |
+
*
|
357 |
+
* @return string|null A legal value or the default value, null if the option
|
358 |
+
* isn't set.
|
359 |
+
*/
|
360 |
+
function relevanssi_legal_value( $request, $option, $values, $default ) {
|
361 |
+
$value = null;
|
362 |
+
if ( isset( $request[ $option ] ) ) {
|
363 |
+
$value = $default;
|
364 |
+
if ( in_array( $request[ $option ], $values, true ) ) {
|
365 |
+
$value = $request[ $option ];
|
366 |
+
}
|
367 |
+
}
|
368 |
+
return $value;
|
369 |
+
}
|
370 |
+
|
371 |
+
/**
|
372 |
+
* Multibyte friendly case-insensitive string comparison.
|
373 |
+
*
|
374 |
+
* If multibyte string functions are available, do strcmp() after using
|
375 |
+
* mb_strtoupper() to both strings. Otherwise use strcasecmp().
|
376 |
+
*
|
377 |
+
* @param string $str1 First string to compare.
|
378 |
+
* @param string $str2 Second string to compare.
|
379 |
+
* @param string $encoding The encoding to use, default mb_internal_encoding().
|
380 |
+
*
|
381 |
+
* @return int $val Returns < 0 if str1 is less than str2; > 0 if str1 is
|
382 |
+
* greater than str2, and 0 if they are equal.
|
383 |
+
*/
|
384 |
+
function relevanssi_mb_strcasecmp( $str1, $str2, $encoding = null ) {
|
385 |
+
if ( ! function_exists( 'mb_internal_encoding' ) ) {
|
386 |
+
return strnatcasecmp( $str1, $str2 );
|
387 |
+
} else {
|
388 |
+
if ( null === $encoding ) {
|
389 |
+
$encoding = mb_internal_encoding();
|
390 |
+
}
|
391 |
+
return strnatcmp( mb_strtoupper( $str1, $encoding ), mb_strtoupper( $str2, $encoding ) );
|
392 |
+
}
|
393 |
+
}
|
394 |
+
|
395 |
+
/**
|
396 |
+
* Trims multibyte strings.
|
397 |
+
*
|
398 |
+
* Removes the 194+160 non-breakable spaces, removes null bytes and removes
|
399 |
+
* whitespace.
|
400 |
+
*
|
401 |
+
* @param string $string The source string.
|
402 |
+
*
|
403 |
+
* @return string Trimmed string.
|
404 |
+
*/
|
405 |
+
function relevanssi_mb_trim( $string ) {
|
406 |
+
$string = str_replace( chr( 194 ) . chr( 160 ), '', $string );
|
407 |
+
$string = str_replace( "\0", '', $string );
|
408 |
+
$string = preg_replace( '/(^\s+)|(\s+$)/us', '', $string );
|
409 |
+
return $string;
|
410 |
+
}
|
411 |
+
|
412 |
+
/**
|
413 |
+
* Returns 'on' if option exists and value is not 'off', otherwise 'off'.
|
414 |
+
*
|
415 |
+
* @param array $request An array of option values.
|
416 |
+
* @param string $option The key to check.
|
417 |
+
*
|
418 |
+
* @return string 'on' or 'off'.
|
419 |
+
*/
|
420 |
+
function relevanssi_off_or_on( $request, $option ) {
|
421 |
+
if ( isset( $request[ $option ] ) && 'off' !== $request[ $option ] ) {
|
422 |
+
return 'on';
|
423 |
+
}
|
424 |
+
return 'off';
|
425 |
+
}
|
426 |
+
|
427 |
+
/**
|
428 |
+
* Removes quotes (", ”, “) from a string.
|
429 |
+
*
|
430 |
+
* @param string $string The string to clean.
|
431 |
+
*
|
432 |
+
* @return string The cleaned string.
|
433 |
+
*/
|
434 |
+
function relevanssi_remove_quotes( $string ) {
|
435 |
+
return str_replace( array( '”', '“', '"' ), '', $string );
|
436 |
+
}
|
437 |
+
|
438 |
+
/**
|
439 |
+
* Removes quotes from array keys. Does not keep array values.
|
440 |
+
*
|
441 |
+
* Used to remove phrase quotes from search term array, which have the format
|
442 |
+
* of (term => hits). The number of hits is not needed, so this function
|
443 |
+
* discards it as a side effect.
|
444 |
+
*
|
445 |
+
* @param array $array An array to process.
|
446 |
+
*
|
447 |
+
* @return array The same array with quotes removed from the keys.
|
448 |
+
*/
|
449 |
+
function relevanssi_remove_quotes_from_array_keys( $array ) {
|
450 |
+
$array = array_keys( $array );
|
451 |
+
array_walk(
|
452 |
+
$array,
|
453 |
+
function( &$key ) {
|
454 |
+
$key = relevanssi_remove_quotes( $key );
|
455 |
+
}
|
456 |
+
);
|
457 |
+
return array_flip( $array );
|
458 |
+
}
|
459 |
+
|
460 |
+
/**
|
461 |
+
* Returns "off".
|
462 |
+
*
|
463 |
+
* Useful for returning "off" to filters easily.
|
464 |
+
*
|
465 |
+
* @return string A string with value "off".
|
466 |
+
*/
|
467 |
+
function relevanssi_return_off() {
|
468 |
+
return 'off';
|
469 |
+
}
|
470 |
+
|
471 |
+
/**
|
472 |
+
* Sanitizes hex color strings.
|
473 |
+
*
|
474 |
+
* A copy of sanitize_hex_color(), because that isn't always available.
|
475 |
+
*
|
476 |
+
* @param string $color A hex color string to sanitize.
|
477 |
+
*
|
478 |
+
* @return string Sanitized hex string, or an empty string.
|
479 |
+
*/
|
480 |
+
function relevanssi_sanitize_hex_color( $color ) {
|
481 |
+
if ( '' === $color ) {
|
482 |
+
return '';
|
483 |
+
}
|
484 |
+
|
485 |
+
if ( '#' !== substr( $color, 0, 1 ) ) {
|
486 |
+
$color = '#' . $color;
|
487 |
+
}
|
488 |
+
|
489 |
+
// 3 or 6 hex digits, or the empty string.
|
490 |
+
if ( preg_match( '|^#([A-Fa-f0-9]{3}){1,2}$|', $color ) ) {
|
491 |
+
return $color;
|
492 |
+
}
|
493 |
+
|
494 |
+
return '';
|
495 |
+
}
|
496 |
+
|
497 |
+
/**
|
498 |
+
* Returns 'selected' if the option matches a value.
|
499 |
+
*
|
500 |
+
* @param string $option Value to check.
|
501 |
+
* @param string $value The 'selected' value.
|
502 |
+
*
|
503 |
+
* @return string If the option matches the value, returns 'selected', otherwise
|
504 |
+
* returns an empty string.
|
505 |
+
*/
|
506 |
+
function relevanssi_select( $option, $value ) {
|
507 |
+
$selected = '';
|
508 |
+
if ( $option === $value ) {
|
509 |
+
$selected = 'selected';
|
510 |
+
}
|
511 |
+
return $selected;
|
512 |
+
}
|
513 |
+
|
514 |
+
/**
|
515 |
+
* Strips invisible elements from text.
|
516 |
+
*
|
517 |
+
* Strips <style>, <script>, <object>, <embed>, <applet>, <noscript>, <noembed>,
|
518 |
+
* <iframe>, and <del> tags and their contents from the text.
|
519 |
+
*
|
520 |
+
* @param string $text The source text.
|
521 |
+
*
|
522 |
+
* @return string The processed text.
|
523 |
+
*/
|
524 |
+
function relevanssi_strip_invisibles( $text ) {
|
525 |
+
$text = preg_replace(
|
526 |
+
array(
|
527 |
+
'@<style[^>]*?>.*?</style>@siu',
|
528 |
+
'@<script[^>]*?.*?</script>@siu',
|
529 |
+
'@<object[^>]*?.*?</object>@siu',
|
530 |
+
'@<embed[^>]*?.*?</embed>@siu',
|
531 |
+
'@<applet[^>]*?.*?</applet>@siu',
|
532 |
+
'@<noscript[^>]*?.*?</noscript>@siu',
|
533 |
+
'@<noembed[^>]*?.*?</noembed>@siu',
|
534 |
+
'@<iframe[^>]*?.*?</iframe>@siu',
|
535 |
+
'@<del[^>]*?.*?</del>@siu',
|
536 |
+
),
|
537 |
+
' ',
|
538 |
+
$text
|
539 |
+
);
|
540 |
+
return $text;
|
541 |
+
}
|
542 |
+
|
543 |
+
/**
|
544 |
+
* Strips tags from contents, keeping the allowed tags.
|
545 |
+
*
|
546 |
+
* The allowable tags are read from the relevanssi_excerpt_allowable_tags
|
547 |
+
* option. Spaces are added between tags before removing the tags, so that
|
548 |
+
* words don't get stuck together. The function also remove invisible content.
|
549 |
+
*
|
550 |
+
* @see relevanssi_strip_invisibles
|
551 |
+
*
|
552 |
+
* @param string $content The content.
|
553 |
+
*
|
554 |
+
* @return string The content without tags.
|
555 |
+
*/
|
556 |
+
function relevanssi_strip_tags( $content ) {
|
557 |
+
$content = relevanssi_strip_invisibles( $content );
|
558 |
+
$content = preg_replace( '/(<\/[^>]+?>)(<[^>\/][^>]*?>)/', '$1 $2', $content );
|
559 |
+
return strip_tags(
|
560 |
+
$content,
|
561 |
+
get_option( 'relevanssi_excerpt_allowable_tags', '' )
|
562 |
+
);
|
563 |
+
}
|
564 |
+
|
565 |
+
/**
|
566 |
+
* Returns the position of substring in the string.
|
567 |
+
*
|
568 |
+
* Uses mb_stripos() if possible, falls back to mb_strpos() and mb_strtoupper()
|
569 |
+
* if that cannot be found, and falls back to just strpos() if even that is not
|
570 |
+
* possible.
|
571 |
+
*
|
572 |
+
* @param string $haystack String where to look.
|
573 |
+
* @param string $needle The string to look for.
|
574 |
+
* @param int $offset Where to start, default 0.
|
575 |
+
*
|
576 |
+
* @return mixed False, if no result or $offset outside the length of $haystack,
|
577 |
+
* otherwise the position (which can be non-false 0!).
|
578 |
+
*/
|
579 |
+
function relevanssi_stripos( $haystack, $needle, $offset = 0 ) {
|
580 |
+
if ( $offset > relevanssi_strlen( $haystack ) ) {
|
581 |
+
return false;
|
582 |
+
}
|
583 |
+
|
584 |
+
if ( preg_match( '/[\?\*]/', $needle ) ) {
|
585 |
+
// There's a ? or an * in the string, which means it's a wildcard search
|
586 |
+
// query (a Premium feature) and requires some extra steps.
|
587 |
+
$needle_regex = str_replace(
|
588 |
+
array( '?', '*' ),
|
589 |
+
array( '.', '.*' ),
|
590 |
+
$needle
|
591 |
+
);
|
592 |
+
$pos_found = false;
|
593 |
+
while ( ! $pos_found ) {
|
594 |
+
preg_match(
|
595 |
+
"/$needle_regex/i",
|
596 |
+
$haystack,
|
597 |
+
$matches,
|
598 |
+
PREG_OFFSET_CAPTURE,
|
599 |
+
$offset
|
600 |
+
);
|
601 |
+
/**
|
602 |
+
* This trickery is necessary, because PREG_OFFSET_CAPTURE gives
|
603 |
+
* wrong offsets for multibyte strings. The mb_strlen() gives the
|
604 |
+
* correct offset, the rest of this is because the $offset received
|
605 |
+
* as a parameter can be before the first $position, leading to an
|
606 |
+
* infinite loop.
|
607 |
+
*/
|
608 |
+
$pos = isset( $matches[0][1] )
|
609 |
+
? mb_strlen( substr( $haystack, 0, $matches[0][1] ) )
|
610 |
+
: false;
|
611 |
+
if ( $pos && $pos > $offset ) {
|
612 |
+
$pos_found = true;
|
613 |
+
} elseif ( $pos ) {
|
614 |
+
$offset++;
|
615 |
+
} else {
|
616 |
+
$pos_found = true;
|
617 |
+
}
|
618 |
+
}
|
619 |
+
} elseif ( function_exists( 'mb_stripos' ) ) {
|
620 |
+
if ( '' === $haystack ) {
|
621 |
+
$pos = false;
|
622 |
+
} else {
|
623 |
+
$pos = mb_stripos( $haystack, $needle, $offset );
|
624 |
+
}
|
625 |
+
} elseif ( function_exists( 'mb_strpos' ) && function_exists( 'mb_strtoupper' ) && function_exists( 'mb_substr' ) ) {
|
626 |
+
$pos = mb_strpos(
|
627 |
+
mb_strtoupper( $haystack ),
|
628 |
+
mb_strtoupper( $needle ),
|
629 |
+
$offset
|
630 |
+
);
|
631 |
+
} else {
|
632 |
+
$pos = strpos( strtoupper( $haystack ), strtoupper( $needle ), $offset );
|
633 |
+
}
|
634 |
+
return $pos;
|
635 |
+
}
|
636 |
+
|
637 |
+
/**
|
638 |
+
* Returns the length of the string.
|
639 |
+
*
|
640 |
+
* Uses mb_strlen() if available, otherwise falls back to strlen().
|
641 |
+
*
|
642 |
+
* @param string $s The string to measure.
|
643 |
+
*
|
644 |
+
* @return int The length of the string.
|
645 |
+
*/
|
646 |
+
function relevanssi_strlen( $s ) {
|
647 |
+
if ( function_exists( 'mb_strlen' ) ) {
|
648 |
+
return mb_strlen( $s );
|
649 |
+
}
|
650 |
+
return strlen( $s );
|
651 |
+
}
|
652 |
+
|
653 |
+
/**
|
654 |
+
* Multibyte friendly strtolower.
|
655 |
+
*
|
656 |
+
* If multibyte string functions are available, returns mb_strtolower() and
|
657 |
+
* falls back to strtolower() if multibyte functions are not available.
|
658 |
+
*
|
659 |
+
* @param string $string The string to lowercase.
|
660 |
+
*
|
661 |
+
* @return string $string The string in lowercase.
|
662 |
+
*/
|
663 |
+
function relevanssi_strtolower( $string ) {
|
664 |
+
if ( ! function_exists( 'mb_strtolower' ) ) {
|
665 |
+
return strtolower( $string );
|
666 |
+
} else {
|
667 |
+
return mb_strtolower( $string );
|
668 |
+
}
|
669 |
+
}
|
670 |
+
|
671 |
+
/**
|
672 |
+
* Multibyte friendly substr.
|
673 |
+
*
|
674 |
+
* If multibyte string functions are available, returns mb_substr() and falls
|
675 |
+
* back to substr() if multibyte functions are not available.
|
676 |
+
*
|
677 |
+
* @param string $string The source string.
|
678 |
+
* @param int $start If start is non-negative, the returned string will
|
679 |
+
* start at the start'th position in str, counting from zero. If start is
|
680 |
+
* negative, the returned string will start at the start'th character from the
|
681 |
+
* end of string.
|
682 |
+
* @param int $length Maximum number of characters to use from string. If
|
683 |
+
* omitted or null is passed, extract all characters to the end of the string.
|
684 |
+
*
|
685 |
+
* @return string $string The string in lowercase.
|
686 |
+
*/
|
687 |
+
function relevanssi_substr( $string, $start, $length = null ) {
|
688 |
+
if ( ! function_exists( 'mb_substr' ) ) {
|
689 |
+
return substr( $string, $start, $length );
|
690 |
+
} else {
|
691 |
+
return mb_substr( $string, $start, $length );
|
692 |
+
}
|
693 |
+
}
|
694 |
+
|
695 |
+
/**
|
696 |
+
* Prints out the post excerpt.
|
697 |
+
*
|
698 |
+
* Prints out the post excerpt from $post->post_excerpt, unless the post is
|
699 |
+
* protected. Only works in the Loop.
|
700 |
+
*
|
701 |
+
* @global $post The global post object.
|
702 |
+
*/
|
703 |
+
function relevanssi_the_excerpt() {
|
704 |
+
global $post;
|
705 |
+
if ( ! post_password_required( $post ) ) {
|
706 |
+
echo '<p>' . $post->post_excerpt . '</p>'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
707 |
+
} else {
|
708 |
+
esc_html_e( 'There is no excerpt because this is a protected post.', 'relevanssi' );
|
709 |
+
}
|
710 |
+
}
|
711 |
+
|
712 |
+
/**
|
713 |
+
* Echoes out the permalink to the current post within Loop.
|
714 |
+
*
|
715 |
+
* Uses get_permalink() to get the permalink, then adds the 'highlight'
|
716 |
+
* parameter if necessary using relevanssi_add_highlight(), then echoes it out.
|
717 |
+
*/
|
718 |
+
function relevanssi_the_permalink() {
|
719 |
+
echo esc_url( relevanssi_get_permalink() );
|
720 |
+
}
|
721 |
+
|
722 |
+
/**
|
723 |
+
* Prints out a list of tags for post.
|
724 |
+
*
|
725 |
+
* Replacement for the_tags() that does the same, but applies Relevanssi search term
|
726 |
+
* highlighting on the results.
|
727 |
+
*
|
728 |
+
* @param string $before What is printed before the tags, default null.
|
729 |
+
* @param string $separator The separator between items, default ', '.
|
730 |
+
* @param string $after What is printed after the tags, default ''.
|
731 |
+
* @param boolean $echo If true, echo, otherwise return the result. Default true.
|
732 |
+
* @param int $post_id The post ID. Default current post ID (in the Loop).
|
733 |
+
*/
|
734 |
+
function relevanssi_the_tags( $before = null, $separator = ', ', $after = '', $echo = true, $post_id = null ) {
|
735 |
+
$tag_list = get_the_tag_list( $before, $separator, $after, $post_id );
|
736 |
+
$found = preg_match_all( '~<a href=".*?" rel="tag">(.*?)</a>~', $tag_list, $matches );
|
737 |
+
if ( $found ) {
|
738 |
+
$originals = $matches[0];
|
739 |
+
$tag_names = $matches[1];
|
740 |
+
$highlighted = array();
|
741 |
+
|
742 |
+
$count = count( $matches[0] );
|
743 |
+
for ( $i = 0; $i < $count; $i++ ) {
|
744 |
+
$highlighted_tag_name = relevanssi_highlight_terms( $tag_names[ $i ], get_search_query(), true );
|
745 |
+
$highlighted[ $i ] = str_replace( '>' . $tag_names[ $i ] . '<', '>' . $highlighted_tag_name . '<', $originals[ $i ] );
|
746 |
+
}
|
747 |
+
|
748 |
+
$tag_list = str_replace( $originals, $highlighted, $tag_list );
|
749 |
+
}
|
750 |
+
|
751 |
+
if ( $echo ) {
|
752 |
+
echo $tag_list; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
753 |
+
} else {
|
754 |
+
return $tag_list;
|
755 |
+
}
|
756 |
+
}
|
757 |
+
|
758 |
+
/**
|
759 |
+
* Turns off options, ie. sets them to "off".
|
760 |
+
*
|
761 |
+
* If the specified options don't exist in the request array, they are set to
|
762 |
+
* "off".
|
763 |
+
*
|
764 |
+
* @param array $request The _REQUEST array, passed as reference.
|
765 |
+
* @param array $options An array of option names.
|
766 |
+
*/
|
767 |
+
function relevanssi_turn_off_options( &$request, $options ) {
|
768 |
+
array_walk(
|
769 |
+
$options,
|
770 |
+
function( $option ) use ( &$request ) {
|
771 |
+
if ( ! isset( $request[ $option ] ) ) {
|
772 |
+
$request[ $option ] = 'off';
|
773 |
+
}
|
774 |
+
}
|
775 |
+
);
|
776 |
+
}
|
777 |
+
|
778 |
+
/**
|
779 |
+
* Sets an option after doing floatval.
|
780 |
+
*
|
781 |
+
* @param array $request An array of option values.
|
782 |
+
* @param string $option The key to check.
|
783 |
+
* @param boolean $autoload Should the option autoload, default true.
|
784 |
+
* @param int $default The default value if floatval() fails, default 0.
|
785 |
+
* @param boolean $positive If true, replace negative values and zeroes with
|
786 |
+
* $default.
|
787 |
+
*/
|
788 |
+
function relevanssi_update_floatval( $request, $option, $autoload = true, $default = 0, $positive = false ) {
|
789 |
+
if ( isset( $request[ $option ] ) ) {
|
790 |
+
$value = floatval( $request[ $option ] );
|
791 |
+
if ( ! $value ) {
|
792 |
+
$value = $default;
|
793 |
+
}
|
794 |
+
if ( $positive && $value <= 0 ) {
|
795 |
+
$value = $default;
|
796 |
+
}
|
797 |
+
update_option( $option, $value, $autoload );
|
798 |
+
}
|
799 |
+
}
|
800 |
+
|
801 |
+
/**
|
802 |
+
* Sets an option after doing intval.
|
803 |
+
*
|
804 |
+
* @param array $request An array of option values.
|
805 |
+
* @param string $option The key to check.
|
806 |
+
* @param boolean $autoload Should the option autoload, default true.
|
807 |
+
* @param int $default The default value if intval() fails, default 0.
|
808 |
+
*/
|
809 |
+
function relevanssi_update_intval( $request, $option, $autoload = true, $default = 0 ) {
|
810 |
+
if ( isset( $request[ $option ] ) ) {
|
811 |
+
$value = intval( $request[ $option ] );
|
812 |
+
if ( ! $value ) {
|
813 |
+
$value = $default;
|
814 |
+
}
|
815 |
+
update_option( $option, $value, $autoload );
|
816 |
+
}
|
817 |
+
}
|
818 |
+
|
819 |
+
/**
|
820 |
+
* Sets an option with one of the listed legal values.
|
821 |
+
*
|
822 |
+
* @param array $request An array of option values.
|
823 |
+
* @param string $option The key to check.
|
824 |
+
* @param array $values The legal values.
|
825 |
+
* @param string $default The default value.
|
826 |
+
* @param boolean $autoload Should the option autoload, default true.
|
827 |
+
*/
|
828 |
+
function relevanssi_update_legal_value( $request, $option, $values, $default, $autoload = true ) {
|
829 |
+
if ( isset( $request[ $option ] ) ) {
|
830 |
+
$value = $default;
|
831 |
+
if ( in_array( $request[ $option ], $values, true ) ) {
|
832 |
+
$value = $request[ $option ];
|
833 |
+
}
|
834 |
+
update_option( $option, $value, $autoload );
|
835 |
+
}
|
836 |
+
}
|
837 |
+
|
838 |
+
/**
|
839 |
+
* Sets an on/off option according to the request value.
|
840 |
+
*
|
841 |
+
* @param array $request An array of option values.
|
842 |
+
* @param string $option The key to check.
|
843 |
+
* @param boolean $autoload Should the option autoload, default true.
|
844 |
+
*/
|
845 |
+
function relevanssi_update_off_or_on( $request, $option, $autoload = true ) {
|
846 |
+
relevanssi_update_legal_value(
|
847 |
+
$request,
|
848 |
+
$option,
|
849 |
+
array( 'off', 'on' ),
|
850 |
+
'off',
|
851 |
+
$autoload
|
852 |
+
);
|
853 |
+
}
|
854 |
+
|
855 |
+
/**
|
856 |
+
* Sets an option after sanitizing and unslashing the value.
|
857 |
+
*
|
858 |
+
* @param array $request An array of option values.
|
859 |
+
* @param string $option The key to check.
|
860 |
+
* @param boolean $autoload Should the option autoload, default true.
|
861 |
+
*/
|
862 |
+
function relevanssi_update_sanitized( $request, $option, $autoload = true ) {
|
863 |
+
if ( isset( $request[ $option ] ) ) {
|
864 |
+
$value = sanitize_text_field( wp_unslash( $request[ $option ] ) );
|
865 |
+
update_option( $option, $value, $autoload );
|
866 |
+
}
|
867 |
+
}
|
readme.txt
CHANGED
@@ -3,9 +3,9 @@ Contributors: msaari
|
|
3 |
Donate link: https://www.relevanssi.com/buy-premium/
|
4 |
Tags: search, relevance, better search, product search, woocommerce search
|
5 |
Requires at least: 4.9
|
6 |
-
Tested up to: 5.
|
7 |
Requires PHP: 7.0
|
8 |
-
Stable tag: 4.
|
9 |
License: GPLv2 or later
|
10 |
License URI: http://www.gnu.org/licenses/gpl-2.0.html
|
11 |
|
@@ -133,6 +133,15 @@ Each document database is full of useless words. All the little words that appea
|
|
133 |
* John Calahan for extensive 4.0 beta testing.
|
134 |
|
135 |
== Changelog ==
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
136 |
= 4.9.1 =
|
137 |
* Changed behaviour: The `relevanssi_excerpt_part` filter hook now gets the post ID as a second parameter. The documentation for the filter has been fixed to match actual use: this filter is applied to the excerpt part after the highlighting and the ellipsis have been added.
|
138 |
* Changed behaviour: The `relevanssi_index_custom_fields` filter hook is no longer used when determining which custom fields are used for phrase searching. If you have a use case where this change matters, please contact us.
|
@@ -189,27 +198,10 @@ Each document database is full of useless words. All the little words that appea
|
|
189 |
* Minor fix: The doc count update, which is a heavy task, is now moved to an asynchronous action to avoid slowing down the site for users.
|
190 |
* Minor fix: Relevanssi only updates doc count on `relevanssi_insert_edit()` when the post is indexed.
|
191 |
|
192 |
-
= 4.7.2 =
|
193 |
-
* Minor fix: Media Library searches failed if Relevanssi was enabled in the WP admin, but the `attachment` post type wasn't indexed. Relevanssi will no longer block the default Media Library search in these cases.
|
194 |
-
* Minor fix: Adds more backwards compatibility for the `relevanssi_indexing_restriction` change, there's now an alert on indexing tab if there's a problem.
|
195 |
-
|
196 |
-
= 4.7.1 =
|
197 |
-
* New feature: New filter hook `relevanssi_post_content_after_shortcodes` filters the post content after shortcodes have been processed but before the HTML tags are stripped.
|
198 |
-
* Minor fix: Adds more backwards compatibility for the `relevanssi_indexing_restriction` change.
|
199 |
-
|
200 |
-
= 4.7.0 =
|
201 |
-
* New feature: New filter hook `relevanssi_admin_search_blocked_post_types` makes it easy to block Relevanssi from searching a specific post type in the admin dashboard. There's built-in support for Reusable Content Blocks `rc_blocks` post type, for example.
|
202 |
-
* New feature: The reason why a post is not indexed is now stored in the `_relevanssi_noindex_reason` custom field.
|
203 |
-
* Changed behaviour: The `relevanssi_indexing_restriction` filter hook has a changed format. Instead of a string value, the filter now expects an array with the MySQL query in the index 'mysql' and a reason in string format in 'reason'. There's some temporary backwards compatibility for this.
|
204 |
-
* Changed behaviour: Relevanssi now applies minimum word length when tokenizing search query terms.
|
205 |
-
* Changed behaviour: Content stopwords are removed from the search queries when doing excerpts and highlights. When Relevanssi uses the untokenized search terms for excerpt-building, stopwords are removed from those words. This should lead to better excerpts.
|
206 |
-
* Minor fix: Improves handling of emoji in indexing. If the database supports emoji, they are allowed, otherwise they are encoded.
|
207 |
-
|
208 |
-
= 4.6.0 =
|
209 |
-
* Changed behaviour: Phrases in OR search are now less restrictive. A search for 'foo "bar baz"' used to only return posts with the "bar baz" phrase, but now also posts with just the word 'foo' will be returned.
|
210 |
-
* Minor fix: User Access Manager showed drafts in search results for all users. This is now fixed.
|
211 |
-
|
212 |
== Upgrade notice ==
|
|
|
|
|
|
|
213 |
= 4.9.1 =
|
214 |
* Bug fixing, better Oxygen Builder compatibility.
|
215 |
|
@@ -226,16 +218,4 @@ Each document database is full of useless words. All the little words that appea
|
|
226 |
* WooCommerce 4.4 compatibility, other minor fixes.
|
227 |
|
228 |
= 4.8.0 =
|
229 |
-
* Fixes a major bug in comment indexing, if you include comments in the index rebuild the index after updating.
|
230 |
-
|
231 |
-
= 4.7.2 =
|
232 |
-
* Improved backwards compatibility for the `relevanssi_indexing_restriction` filter hook change, better Media Library support.
|
233 |
-
|
234 |
-
= 4.7.1 =
|
235 |
-
* Improved backwards compatibility for the `relevanssi_indexing_restriction` filter hook change.
|
236 |
-
|
237 |
-
= 4.7.0 =
|
238 |
-
* The `relevanssi_indexing_restriction` filter hook has been changed, stopwords are handled in a different way in excerpts.
|
239 |
-
|
240 |
-
= 4.6.0 =
|
241 |
-
* Changes how phrases work in OR search and fixes a User Access Manager issue.
|
3 |
Donate link: https://www.relevanssi.com/buy-premium/
|
4 |
Tags: search, relevance, better search, product search, woocommerce search
|
5 |
Requires at least: 4.9
|
6 |
+
Tested up to: 5.6.1
|
7 |
Requires PHP: 7.0
|
8 |
+
Stable tag: 4.10.0
|
9 |
License: GPLv2 or later
|
10 |
License URI: http://www.gnu.org/licenses/gpl-2.0.html
|
11 |
|
133 |
* John Calahan for extensive 4.0 beta testing.
|
134 |
|
135 |
== Changelog ==
|
136 |
+
= 4.10.0 =
|
137 |
+
* New feature: Relevanssi now supports multilingual synonyms and stopwords. Relevanssi now has a different set of synonyms and stopwords for each language. This feature is compatible with WPML and Polylang.
|
138 |
+
* New feature: SEO by Rank Math compatibility is added: posts marked as 'noindex' with Rank Math are not indexed by Relevanssi.
|
139 |
+
* Minor fix: With keyword matching set to 'whole words' and the 'expand highlights' disabled, words that ended with an 's' weren't highlighted correctly.
|
140 |
+
* Minor fix: The 'Post exclusion' setting didn't work correctly. It has been fixed.
|
141 |
+
* Minor fix: It's now impossible to set negative weights in searching settings. They did not work as expected anyway.
|
142 |
+
* Minor fix: Relevanssi had an unnecessary index on the `doc` column in the `wp_relevanssi` database table. It is now removed to save space. Thanks to Matthew Wang.
|
143 |
+
* Minor fix: Improved Oxygen Builder support makes sure `ct_builder_shortcodes` custom field is always indexed.
|
144 |
+
|
145 |
= 4.9.1 =
|
146 |
* Changed behaviour: The `relevanssi_excerpt_part` filter hook now gets the post ID as a second parameter. The documentation for the filter has been fixed to match actual use: this filter is applied to the excerpt part after the highlighting and the ellipsis have been added.
|
147 |
* Changed behaviour: The `relevanssi_index_custom_fields` filter hook is no longer used when determining which custom fields are used for phrase searching. If you have a use case where this change matters, please contact us.
|
198 |
* Minor fix: The doc count update, which is a heavy task, is now moved to an asynchronous action to avoid slowing down the site for users.
|
199 |
* Minor fix: Relevanssi only updates doc count on `relevanssi_insert_edit()` when the post is indexed.
|
200 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
201 |
== Upgrade notice ==
|
202 |
+
= 4.10.0 =
|
203 |
+
* Adds support for multilingual stopwords and synonyms.
|
204 |
+
|
205 |
= 4.9.1 =
|
206 |
* Bug fixing, better Oxygen Builder compatibility.
|
207 |
|
218 |
* WooCommerce 4.4 compatibility, other minor fixes.
|
219 |
|
220 |
= 4.8.0 =
|
221 |
+
* Fixes a major bug in comment indexing, if you include comments in the index rebuild the index after updating.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
relevanssi.php
CHANGED
@@ -13,7 +13,7 @@
|
|
13 |
* Plugin Name: Relevanssi
|
14 |
* Plugin URI: https://www.relevanssi.com/
|
15 |
* Description: This plugin replaces WordPress search with a relevance-sorting search.
|
16 |
-
* Version: 4.
|
17 |
* Author: Mikko Saari
|
18 |
* Author URI: http://www.mikkosaari.fi/
|
19 |
* Text Domain: relevanssi
|
@@ -63,23 +63,28 @@ $relevanssi_variables['comment_boost_default'] = 0.75;
|
|
63 |
$relevanssi_variables['post_type_weight_defaults']['post_tag'] = 0.75;
|
64 |
$relevanssi_variables['post_type_weight_defaults']['category'] = 0.75;
|
65 |
$relevanssi_variables['post_type_index_defaults'] = array( 'post', 'page' );
|
66 |
-
$relevanssi_variables['database_version'] =
|
67 |
$relevanssi_variables['file'] = __FILE__;
|
68 |
$relevanssi_variables['plugin_dir'] = plugin_dir_path( __FILE__ );
|
69 |
$relevanssi_variables['plugin_basename'] = plugin_basename( __FILE__ );
|
70 |
-
$relevanssi_variables['plugin_version'] = '4.
|
71 |
|
72 |
require_once 'lib/admin-ajax.php';
|
73 |
require_once 'lib/common.php';
|
|
|
74 |
require_once 'lib/excerpts-highlights.php';
|
75 |
require_once 'lib/indexing.php';
|
76 |
require_once 'lib/init.php';
|
77 |
require_once 'lib/install.php';
|
78 |
require_once 'lib/interface.php';
|
79 |
require_once 'lib/log.php';
|
|
|
|
|
|
|
80 |
require_once 'lib/search.php';
|
81 |
require_once 'lib/search-tax-query.php';
|
82 |
require_once 'lib/search-query-restrictions.php';
|
83 |
require_once 'lib/shortcodes.php';
|
84 |
require_once 'lib/sorting.php';
|
85 |
require_once 'lib/stopwords.php';
|
|
13 |
* Plugin Name: Relevanssi
|
14 |
* Plugin URI: https://www.relevanssi.com/
|
15 |
* Description: This plugin replaces WordPress search with a relevance-sorting search.
|
16 |
+
* Version: 4.10.0
|
17 |
* Author: Mikko Saari
|
18 |
* Author URI: http://www.mikkosaari.fi/
|
19 |
* Text Domain: relevanssi
|
63 |
$relevanssi_variables['post_type_weight_defaults']['post_tag'] = 0.75;
|
64 |
$relevanssi_variables['post_type_weight_defaults']['category'] = 0.75;
|
65 |
$relevanssi_variables['post_type_index_defaults'] = array( 'post', 'page' );
|
66 |
+
$relevanssi_variables['database_version'] = 6;
|
67 |
$relevanssi_variables['file'] = __FILE__;
|
68 |
$relevanssi_variables['plugin_dir'] = plugin_dir_path( __FILE__ );
|
69 |
$relevanssi_variables['plugin_basename'] = plugin_basename( __FILE__ );
|
70 |
+
$relevanssi_variables['plugin_version'] = '4.10.0';
|
71 |
|
72 |
require_once 'lib/admin-ajax.php';
|
73 |
require_once 'lib/common.php';
|
74 |
+
require_once 'lib/didyoumean.php';
|
75 |
require_once 'lib/excerpts-highlights.php';
|
76 |
require_once 'lib/indexing.php';
|
77 |
require_once 'lib/init.php';
|
78 |
require_once 'lib/install.php';
|
79 |
require_once 'lib/interface.php';
|
80 |
require_once 'lib/log.php';
|
81 |
+
require_once 'lib/options.php';
|
82 |
+
require_once 'lib/phrases.php';
|
83 |
+
require_once 'lib/privacy.php';
|
84 |
require_once 'lib/search.php';
|
85 |
require_once 'lib/search-tax-query.php';
|
86 |
require_once 'lib/search-query-restrictions.php';
|
87 |
require_once 'lib/shortcodes.php';
|
88 |
require_once 'lib/sorting.php';
|
89 |
require_once 'lib/stopwords.php';
|
90 |
+
require_once 'lib/utils.php';
|
stopwords/stopword.zh_TW
ADDED
@@ -0,0 +1,769 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
$stopwords = array(
|
3 |
+
"$",
|
4 |
+
"0",
|
5 |
+
"1",
|
6 |
+
"2",
|
7 |
+
"3",
|
8 |
+
"4",
|
9 |
+
"5",
|
10 |
+
"6",
|
11 |
+
"7",
|
12 |
+
"8",
|
13 |
+
"9",
|
14 |
+
"?",
|
15 |
+
"_",
|
16 |
+
"“",
|
17 |
+
"”",
|
18 |
+
"、",
|
19 |
+
"。",
|
20 |
+
"《",
|
21 |
+
"》",
|
22 |
+
"一",
|
23 |
+
"一些",
|
24 |
+
"一何",
|
25 |
+
"一切",
|
26 |
+
"一則",
|
27 |
+
"一方面",
|
28 |
+
"一旦",
|
29 |
+
"一來",
|
30 |
+
"一樣",
|
31 |
+
"一般",
|
32 |
+
"一轉眼",
|
33 |
+
"萬一",
|
34 |
+
"上",
|
35 |
+
"上下",
|
36 |
+
"下",
|
37 |
+
"不",
|
38 |
+
"不僅",
|
39 |
+
"不但",
|
40 |
+
"不光",
|
41 |
+
"不單",
|
42 |
+
"不只",
|
43 |
+
"不外乎",
|
44 |
+
"不如",
|
45 |
+
"不妨",
|
46 |
+
"不盡",
|
47 |
+
"不盡然",
|
48 |
+
"不得",
|
49 |
+
"不怕",
|
50 |
+
"不惟",
|
51 |
+
"不成",
|
52 |
+
"不拘",
|
53 |
+
"不料",
|
54 |
+
"不是",
|
55 |
+
"不比",
|
56 |
+
"不然",
|
57 |
+
"不特",
|
58 |
+
"不獨",
|
59 |
+
"不管",
|
60 |
+
"不至於",
|
61 |
+
"不若",
|
62 |
+
"不論",
|
63 |
+
"不過",
|
64 |
+
"不問",
|
65 |
+
"與",
|
66 |
+
"與其",
|
67 |
+
"與其說",
|
68 |
+
"與否",
|
69 |
+
"與此同時",
|
70 |
+
"且",
|
71 |
+
"且不說",
|
72 |
+
"且說",
|
73 |
+
"兩者",
|
74 |
+
"個",
|
75 |
+
"個別",
|
76 |
+
"臨",
|
77 |
+
"為",
|
78 |
+
"為了",
|
79 |
+
"為什麼",
|
80 |
+
"為何",
|
81 |
+
"為止",
|
82 |
+
"為此",
|
83 |
+
"為著",
|
84 |
+
"乃",
|
85 |
+
"乃至",
|
86 |
+
"乃至於",
|
87 |
+
"麼",
|
88 |
+
"之",
|
89 |
+
"之一",
|
90 |
+
"之所以",
|
91 |
+
"之類",
|
92 |
+
"烏乎",
|
93 |
+
"乎",
|
94 |
+
"乘",
|
95 |
+
"也",
|
96 |
+
"也好",
|
97 |
+
"也罷",
|
98 |
+
"了",
|
99 |
+
"二來",
|
100 |
+
"於",
|
101 |
+
"於是",
|
102 |
+
"於是乎",
|
103 |
+
"云云",
|
104 |
+
"云爾",
|
105 |
+
"些",
|
106 |
+
"亦",
|
107 |
+
"人",
|
108 |
+
"人們",
|
109 |
+
"人家",
|
110 |
+
"什麼",
|
111 |
+
"什麼樣",
|
112 |
+
"今",
|
113 |
+
"介於",
|
114 |
+
"仍",
|
115 |
+
"仍舊",
|
116 |
+
"從",
|
117 |
+
"從此",
|
118 |
+
"從而",
|
119 |
+
"他",
|
120 |
+
"他人",
|
121 |
+
"他們",
|
122 |
+
"以",
|
123 |
+
"以上",
|
124 |
+
"以為",
|
125 |
+
"以便",
|
126 |
+
"以免",
|
127 |
+
"以及",
|
128 |
+
"以故",
|
129 |
+
"以期",
|
130 |
+
"以來",
|
131 |
+
"以至",
|
132 |
+
"以至於",
|
133 |
+
"以致",
|
134 |
+
"們",
|
135 |
+
"任",
|
136 |
+
"任何",
|
137 |
+
"任憑",
|
138 |
+
"似的",
|
139 |
+
"但",
|
140 |
+
"但凡",
|
141 |
+
"但是",
|
142 |
+
"何",
|
143 |
+
"何以",
|
144 |
+
"何況",
|
145 |
+
"何處",
|
146 |
+
"何時",
|
147 |
+
"余外",
|
148 |
+
"作為",
|
149 |
+
"你",
|
150 |
+
"你們",
|
151 |
+
"使",
|
152 |
+
"使得",
|
153 |
+
"例如",
|
154 |
+
"依",
|
155 |
+
"依據",
|
156 |
+
"依照",
|
157 |
+
"便於",
|
158 |
+
"俺",
|
159 |
+
"俺們",
|
160 |
+
"倘",
|
161 |
+
"倘使",
|
162 |
+
"倘或",
|
163 |
+
"倘然",
|
164 |
+
"倘若",
|
165 |
+
"借",
|
166 |
+
"假使",
|
167 |
+
"假如",
|
168 |
+
"假若",
|
169 |
+
"儻然",
|
170 |
+
"像",
|
171 |
+
"兒",
|
172 |
+
"先不先",
|
173 |
+
"光是",
|
174 |
+
"全體",
|
175 |
+
"全部",
|
176 |
+
"兮",
|
177 |
+
"關於",
|
178 |
+
"其",
|
179 |
+
"其一",
|
180 |
+
"其中",
|
181 |
+
"其二",
|
182 |
+
"其他",
|
183 |
+
"其餘",
|
184 |
+
"其它",
|
185 |
+
"其次",
|
186 |
+
"具體地說",
|
187 |
+
"具體說來",
|
188 |
+
"兼之",
|
189 |
+
"內",
|
190 |
+
"再",
|
191 |
+
"再其次",
|
192 |
+
"再則",
|
193 |
+
"再有",
|
194 |
+
"再者",
|
195 |
+
"再者說",
|
196 |
+
"再說",
|
197 |
+
"冒",
|
198 |
+
"沖",
|
199 |
+
"況且",
|
200 |
+
"幾",
|
201 |
+
"幾時",
|
202 |
+
"凡",
|
203 |
+
"凡是",
|
204 |
+
"憑",
|
205 |
+
"憑藉",
|
206 |
+
"出於",
|
207 |
+
"出來",
|
208 |
+
"分別",
|
209 |
+
"則",
|
210 |
+
"則甚",
|
211 |
+
"別",
|
212 |
+
"別人",
|
213 |
+
"別處",
|
214 |
+
"別是",
|
215 |
+
"別的",
|
216 |
+
"別管",
|
217 |
+
"別說",
|
218 |
+
"到",
|
219 |
+
"前後",
|
220 |
+
"前此",
|
221 |
+
"前者",
|
222 |
+
"加之",
|
223 |
+
"加以",
|
224 |
+
"即",
|
225 |
+
"即令",
|
226 |
+
"即使",
|
227 |
+
"即便",
|
228 |
+
"即如",
|
229 |
+
"即或",
|
230 |
+
"即若",
|
231 |
+
"卻",
|
232 |
+
"去",
|
233 |
+
"又",
|
234 |
+
"又及",
|
235 |
+
"及",
|
236 |
+
"及其",
|
237 |
+
"及至",
|
238 |
+
"反之",
|
239 |
+
"反而",
|
240 |
+
"反過來",
|
241 |
+
"反過來說",
|
242 |
+
"受到",
|
243 |
+
"另",
|
244 |
+
"另一方面",
|
245 |
+
"另外",
|
246 |
+
"另悉",
|
247 |
+
"只",
|
248 |
+
"只當",
|
249 |
+
"只怕",
|
250 |
+
"只是",
|
251 |
+
"只有",
|
252 |
+
"只消",
|
253 |
+
"只要",
|
254 |
+
"只限",
|
255 |
+
"叫",
|
256 |
+
"叮咚",
|
257 |
+
"可",
|
258 |
+
"可以",
|
259 |
+
"可是",
|
260 |
+
"可見",
|
261 |
+
"各",
|
262 |
+
"各個",
|
263 |
+
"各位",
|
264 |
+
"各種",
|
265 |
+
"各自",
|
266 |
+
"同",
|
267 |
+
"同時",
|
268 |
+
"後",
|
269 |
+
"後者",
|
270 |
+
"向",
|
271 |
+
"向使",
|
272 |
+
"向著",
|
273 |
+
"嚇",
|
274 |
+
"嗎",
|
275 |
+
"否則",
|
276 |
+
"吧",
|
277 |
+
"吧噠",
|
278 |
+
"吱",
|
279 |
+
"呀",
|
280 |
+
"呃",
|
281 |
+
"嘔",
|
282 |
+
"唄",
|
283 |
+
"嗚",
|
284 |
+
"嗚呼",
|
285 |
+
"呢",
|
286 |
+
"呵",
|
287 |
+
"呵呵",
|
288 |
+
"呸",
|
289 |
+
"呼哧",
|
290 |
+
"咋",
|
291 |
+
"和",
|
292 |
+
"咚",
|
293 |
+
"咦",
|
294 |
+
"咧",
|
295 |
+
"咱",
|
296 |
+
"咱們",
|
297 |
+
"咳",
|
298 |
+
"哇",
|
299 |
+
"哈",
|
300 |
+
"哈哈",
|
301 |
+
"哉",
|
302 |
+
"哎",
|
303 |
+
"哎呀",
|
304 |
+
"哎喲",
|
305 |
+
"嘩",
|
306 |
+
"喲",
|
307 |
+
"哦",
|
308 |
+
"哩",
|
309 |
+
"哪",
|
310 |
+
"哪個",
|
311 |
+
"哪些",
|
312 |
+
"哪兒",
|
313 |
+
"哪天",
|
314 |
+
"哪年",
|
315 |
+
"哪怕",
|
316 |
+
"哪樣",
|
317 |
+
"哪邊",
|
318 |
+
"哪裡",
|
319 |
+
"哼",
|
320 |
+
"哼唷",
|
321 |
+
"唉",
|
322 |
+
"唯有",
|
323 |
+
"啊",
|
324 |
+
"啐",
|
325 |
+
"啥",
|
326 |
+
"啦",
|
327 |
+
"啪達",
|
328 |
+
"啷噹",
|
329 |
+
"喂",
|
330 |
+
"喏",
|
331 |
+
"喔唷",
|
332 |
+
"嘍",
|
333 |
+
"嗡",
|
334 |
+
"嗡嗡",
|
335 |
+
"嗬",
|
336 |
+
"嗯",
|
337 |
+
"噯",
|
338 |
+
"嘎",
|
339 |
+
"嘎登",
|
340 |
+
"噓",
|
341 |
+
"嘛",
|
342 |
+
"嘻",
|
343 |
+
"嘿",
|
344 |
+
"嘿嘿",
|
345 |
+
"因",
|
346 |
+
"因為",
|
347 |
+
"因了",
|
348 |
+
"因此",
|
349 |
+
"因著",
|
350 |
+
"因而",
|
351 |
+
"固然",
|
352 |
+
"在",
|
353 |
+
"在下",
|
354 |
+
"在於",
|
355 |
+
"地",
|
356 |
+
"基於",
|
357 |
+
"處在",
|
358 |
+
"多",
|
359 |
+
"多麼",
|
360 |
+
"多少",
|
361 |
+
"大",
|
362 |
+
"大家",
|
363 |
+
"她",
|
364 |
+
"她們",
|
365 |
+
"好",
|
366 |
+
"如",
|
367 |
+
"如上",
|
368 |
+
"如上所述",
|
369 |
+
"如下",
|
370 |
+
"如何",
|
371 |
+
"如其",
|
372 |
+
"如同",
|
373 |
+
"如是",
|
374 |
+
"如果",
|
375 |
+
"如此",
|
376 |
+
"如若",
|
377 |
+
"始而",
|
378 |
+
"孰料",
|
379 |
+
"孰知",
|
380 |
+
"寧",
|
381 |
+
"寧可",
|
382 |
+
"寧願",
|
383 |
+
"寧肯",
|
384 |
+
"它",
|
385 |
+
"它們",
|
386 |
+
"對",
|
387 |
+
"對於",
|
388 |
+
"對待",
|
389 |
+
"對方",
|
390 |
+
"對比",
|
391 |
+
"將",
|
392 |
+
"小",
|
393 |
+
"爾",
|
394 |
+
"爾後",
|
395 |
+
"爾爾",
|
396 |
+
"尚且",
|
397 |
+
"就",
|
398 |
+
"就是",
|
399 |
+
"就是了",
|
400 |
+
"就是說",
|
401 |
+
"就算",
|
402 |
+
"就要",
|
403 |
+
"盡",
|
404 |
+
"儘管",
|
405 |
+
"儘管如此",
|
406 |
+
"豈但",
|
407 |
+
"己",
|
408 |
+
"已",
|
409 |
+
"已矣",
|
410 |
+
"巴",
|
411 |
+
"巴巴",
|
412 |
+
"並",
|
413 |
+
"並且",
|
414 |
+
"並非",
|
415 |
+
"庶乎",
|
416 |
+
"庶幾",
|
417 |
+
"開外",
|
418 |
+
"開始",
|
419 |
+
"歸",
|
420 |
+
"歸齊",
|
421 |
+
"當",
|
422 |
+
"當地",
|
423 |
+
"當然",
|
424 |
+
"當著",
|
425 |
+
"彼",
|
426 |
+
"彼時",
|
427 |
+
"彼此",
|
428 |
+
"往",
|
429 |
+
"待",
|
430 |
+
"很",
|
431 |
+
"得",
|
432 |
+
"得了",
|
433 |
+
"怎",
|
434 |
+
"怎麼",
|
435 |
+
"怎麼辦",
|
436 |
+
"怎麼樣",
|
437 |
+
"怎奈",
|
438 |
+
"怎樣",
|
439 |
+
"總之",
|
440 |
+
"總的來看",
|
441 |
+
"總的來說",
|
442 |
+
"總的說來",
|
443 |
+
"總而言之",
|
444 |
+
"恰恰相反",
|
445 |
+
"您",
|
446 |
+
"惟其",
|
447 |
+
"慢說",
|
448 |
+
"我",
|
449 |
+
"我們",
|
450 |
+
"或",
|
451 |
+
"或則",
|
452 |
+
"或是",
|
453 |
+
"或曰",
|
454 |
+
"或者",
|
455 |
+
"截至",
|
456 |
+
"所",
|
457 |
+
"所以",
|
458 |
+
"所在",
|
459 |
+
"所幸",
|
460 |
+
"所有",
|
461 |
+
"才",
|
462 |
+
"才能",
|
463 |
+
"打",
|
464 |
+
"打從",
|
465 |
+
"把",
|
466 |
+
"抑或",
|
467 |
+
"拿",
|
468 |
+
"按",
|
469 |
+
"按照",
|
470 |
+
"換句話說",
|
471 |
+
"換言之",
|
472 |
+
"據",
|
473 |
+
"據此",
|
474 |
+
"接著",
|
475 |
+
"故",
|
476 |
+
"故此",
|
477 |
+
"故而",
|
478 |
+
"旁人",
|
479 |
+
"無",
|
480 |
+
"無寧",
|
481 |
+
"無論",
|
482 |
+
"既",
|
483 |
+
"既往",
|
484 |
+
"既是",
|
485 |
+
"既然",
|
486 |
+
"時候",
|
487 |
+
"是",
|
488 |
+
"是以",
|
489 |
+
"是的",
|
490 |
+
"曾",
|
491 |
+
"替",
|
492 |
+
"替代",
|
493 |
+
"最",
|
494 |
+
"有",
|
495 |
+
"有些",
|
496 |
+
"有關",
|
497 |
+
"有及",
|
498 |
+
"有時",
|
499 |
+
"有的",
|
500 |
+
"望",
|
501 |
+
"朝",
|
502 |
+
"朝著",
|
503 |
+
"本",
|
504 |
+
"本人",
|
505 |
+
"本地",
|
506 |
+
"本著",
|
507 |
+
"本身",
|
508 |
+
"來",
|
509 |
+
"來著",
|
510 |
+
"來自",
|
511 |
+
"來說",
|
512 |
+
"極了",
|
513 |
+
"果然",
|
514 |
+
"果真",
|
515 |
+
"某",
|
516 |
+
"某個",
|
517 |
+
"某些",
|
518 |
+
"某某",
|
519 |
+
"根據",
|
520 |
+
"歟",
|
521 |
+
"正值",
|
522 |
+
"正如",
|
523 |
+
"正巧",
|
524 |
+
"正是",
|
525 |
+
"此",
|
526 |
+
"此地",
|
527 |
+
"此處",
|
528 |
+
"此外",
|
529 |
+
"此時",
|
530 |
+
"此次",
|
531 |
+
"此間",
|
532 |
+
"毋寧",
|
533 |
+
"每",
|
534 |
+
"每當",
|
535 |
+
"比",
|
536 |
+
"比及",
|
537 |
+
"比如",
|
538 |
+
"比方",
|
539 |
+
"沒奈何",
|
540 |
+
"沿",
|
541 |
+
"沿著",
|
542 |
+
"漫說",
|
543 |
+
"焉",
|
544 |
+
"然則",
|
545 |
+
"然後",
|
546 |
+
"然而",
|
547 |
+
"照",
|
548 |
+
"照著",
|
549 |
+
"猶且",
|
550 |
+
"猶自",
|
551 |
+
"甚且",
|
552 |
+
"甚麼",
|
553 |
+
"甚或",
|
554 |
+
"甚而",
|
555 |
+
"甚至",
|
556 |
+
"甚至於",
|
557 |
+
"用",
|
558 |
+
"用來",
|
559 |
+
"由",
|
560 |
+
"由於",
|
561 |
+
"由是",
|
562 |
+
"由此",
|
563 |
+
"由此可見",
|
564 |
+
"的",
|
565 |
+
"的確",
|
566 |
+
"的話",
|
567 |
+
"直到",
|
568 |
+
"相對而言",
|
569 |
+
"省得",
|
570 |
+
"看",
|
571 |
+
"眨眼",
|
572 |
+
"著",
|
573 |
+
"著呢",
|
574 |
+
"矣",
|
575 |
+
"矣乎",
|
576 |
+
"矣哉",
|
577 |
+
"離",
|
578 |
+
"竟而",
|
579 |
+
"第",
|
580 |
+
"等",
|
581 |
+
"等到",
|
582 |
+
"等等",
|
583 |
+
"簡言之",
|
584 |
+
"管",
|
585 |
+
"類如",
|
586 |
+
"緊接著",
|
587 |
+
"縱",
|
588 |
+
"縱令",
|
589 |
+
"縱使",
|
590 |
+
"縱然",
|
591 |
+
"經",
|
592 |
+
"經過",
|
593 |
+
"結果",
|
594 |
+
"給",
|
595 |
+
"繼之",
|
596 |
+
"繼後",
|
597 |
+
"繼而",
|
598 |
+
"綜上所述",
|
599 |
+
"罷了",
|
600 |
+
"者",
|
601 |
+
"而",
|
602 |
+
"而且",
|
603 |
+
"而況",
|
604 |
+
"而後",
|
605 |
+
"而外",
|
606 |
+
"而已",
|
607 |
+
"而是",
|
608 |
+
"而言",
|
609 |
+
"能",
|
610 |
+
"能否",
|
611 |
+
"騰",
|
612 |
+
"自",
|
613 |
+
"自個兒",
|
614 |
+
"自從",
|
615 |
+
"自各兒",
|
616 |
+
"自後",
|
617 |
+
"自家",
|
618 |
+
"自己",
|
619 |
+
"自打",
|
620 |
+
"自身",
|
621 |
+
"至",
|
622 |
+
"至於",
|
623 |
+
"至今",
|
624 |
+
"至若",
|
625 |
+
"致",
|
626 |
+
"般的",
|
627 |
+
"若",
|
628 |
+
"若夫",
|
629 |
+
"若是",
|
630 |
+
"若果",
|
631 |
+
"若非",
|
632 |
+
"莫不然",
|
633 |
+
"莫如",
|
634 |
+
"莫若",
|
635 |
+
"雖",
|
636 |
+
"雖則",
|
637 |
+
"雖然",
|
638 |
+
"雖說",
|
639 |
+
"被",
|
640 |
+
"要",
|
641 |
+
"要不",
|
642 |
+
"要不是",
|
643 |
+
"要不然",
|
644 |
+
"要麼",
|
645 |
+
"要是",
|
646 |
+
"譬喻",
|
647 |
+
"譬如",
|
648 |
+
"讓",
|
649 |
+
"許多",
|
650 |
+
"論",
|
651 |
+
"設使",
|
652 |
+
"設或",
|
653 |
+
"設若",
|
654 |
+
"誠如",
|
655 |
+
"誠然",
|
656 |
+
"該",
|
657 |
+
"說來",
|
658 |
+
"諸",
|
659 |
+
"諸位",
|
660 |
+
"諸如",
|
661 |
+
"誰",
|
662 |
+
"誰人",
|
663 |
+
"誰料",
|
664 |
+
"誰知",
|
665 |
+
"賊死",
|
666 |
+
"賴以",
|
667 |
+
"趕",
|
668 |
+
"起",
|
669 |
+
"起見",
|
670 |
+
"趁",
|
671 |
+
"趁著",
|
672 |
+
"越是",
|
673 |
+
"距",
|
674 |
+
"跟",
|
675 |
+
"較",
|
676 |
+
"較之",
|
677 |
+
"邊",
|
678 |
+
"過",
|
679 |
+
"還",
|
680 |
+
"還是",
|
681 |
+
"還有",
|
682 |
+
"還要",
|
683 |
+
"這",
|
684 |
+
"這一來",
|
685 |
+
"這個",
|
686 |
+
"這麼",
|
687 |
+
"這麼些",
|
688 |
+
"這麼樣",
|
689 |
+
"這麼點兒",
|
690 |
+
"這些",
|
691 |
+
"這會兒",
|
692 |
+
"這兒",
|
693 |
+
"這就是說",
|
694 |
+
"這時",
|
695 |
+
"這樣",
|
696 |
+
"這次",
|
697 |
+
"這般",
|
698 |
+
"這邊",
|
699 |
+
"這裡",
|
700 |
+
"進而",
|
701 |
+
"連",
|
702 |
+
"連同",
|
703 |
+
"逐步",
|
704 |
+
"通過",
|
705 |
+
"遵循",
|
706 |
+
"遵照",
|
707 |
+
"那",
|
708 |
+
"那個",
|
709 |
+
"那麼",
|
710 |
+
"那麼些",
|
711 |
+
"那麼樣",
|
712 |
+
"那些",
|
713 |
+
"那會兒",
|
714 |
+
"那兒",
|
715 |
+
"那時",
|
716 |
+
"那樣",
|
717 |
+
"那般",
|
718 |
+
"那邊",
|
719 |
+
"那裡",
|
720 |
+
"都",
|
721 |
+
"鄙人",
|
722 |
+
"鑒於",
|
723 |
+
"針對",
|
724 |
+
"阿",
|
725 |
+
"除",
|
726 |
+
"除了",
|
727 |
+
"除外",
|
728 |
+
"除開",
|
729 |
+
"除此之外",
|
730 |
+
"除非",
|
731 |
+
"隨",
|
732 |
+
"隨後",
|
733 |
+
"隨時",
|
734 |
+
"隨著",
|
735 |
+
"難道說",
|
736 |
+
"非但",
|
737 |
+
"非徒",
|
738 |
+
"非特",
|
739 |
+
"非獨",
|
740 |
+
"靠",
|
741 |
+
"順",
|
742 |
+
"順著",
|
743 |
+
"首先",
|
744 |
+
"!",
|
745 |
+
",",
|
746 |
+
":",
|
747 |
+
";",
|
748 |
+
"?",
|
749 |
+
"「",
|
750 |
+
"」",
|
751 |
+
"(",
|
752 |
+
")",
|
753 |
+
"可能",
|
754 |
+
"閱讀",
|
755 |
+
"延伸",
|
756 |
+
"表示",
|
757 |
+
"已經",
|
758 |
+
"沒有",
|
759 |
+
"其實",
|
760 |
+
"容易",
|
761 |
+
"時間",
|
762 |
+
"需要",
|
763 |
+
"芊淩",
|
764 |
+
"很多",
|
765 |
+
"不會",
|
766 |
+
"像是",
|
767 |
+
"近幾年",
|
768 |
+
"注意");
|
769 |
+
?>
|