HEX
Server: Apache
System: Linux WWW 6.1.0-40-amd64 #1 SMP PREEMPT_DYNAMIC Debian 6.1.153-1 (2025-09-20) x86_64
User: web11 (1011)
PHP: 8.2.29
Disabled: NONE
Upload Files
File: /var/www/biblioteka/wp-content/plugins/better-search/includes/heatmap.php
<?php
/**
 * Better Search Heatmap Functions
 *
 * @package Better_Search
 */

use WebberZone\Better_Search\Util\Helpers;

// If this file is called directly, then abort execution.
if ( ! defined( 'WPINC' ) ) {
	die;
}


/**
 * Display the header table on the search results page.
 *
 * @since 3.0.0
 *
 * @see get_bsearch_heatmap() for all available arguments.
 *
 * @param string|array $args {
 *     Optional. Array or string of parameters.
 *
 *     @type bool        $daily  True for Daily, False for Overall searches.
 *     @type string      $before Markup to prepend to the relevance score.
 *     @type string      $after  Markup to append to the relevance score.
 *     @type bool        $echo   Echo or return?
 *     @type string|null $title  Title of the heatmap. If null, then use 'title' or 'title_daily' setting.
 * }
 * @return void|string Void if 'echo' argument is true, the title attribute if 'echo' is false.
 */
function the_bsearch_heatmap( $args = array() ) {

	$defaults = array(
		'daily'  => false,
		'before' => '',
		'after'  => '',
		'echo'   => true,
		'title'  => null,
	);
	$args     = wp_parse_args( $args, $defaults );

	$heatmap = get_bsearch_heatmap( $args );

	if ( is_null( $args['title'] ) ) {
		$title = $args['daily'] ? bsearch_get_option( 'title_daily' ) : bsearch_get_option( 'title' );
	} else {
		$title = $args['title'];
	}

	$output  = '<div class="bsearch_heatmap">';
	$output .= '<h2>' . $title . '</h2>';
	$output .= $args['before'] . $heatmap . $args['after'];

	if ( bsearch_get_option( 'show_credit' ) ) {
		$output .= Helpers::get_credit_link();
	}

	$output .= '</div>';

	/**
	 * Filters the displayed heatmap.
	 *
	 * @since 3.0.0
	 *
	 * @param string $output The heatmap.
	 * @param array  $args   Arguments array.
	 */
	$output = apply_filters( 'the_bsearch_heatmap', $output, $args );

	if ( $args['echo'] ) {
		echo $output; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
	} else {
		return $output;
	}
}


/**
 * Get the Search Heatmap.
 *
 * @since 1.2
 *
 * @param string|array $args {
 *     Optional. Array or string of parameters.
 *
 *     @type bool     $daily                      True for Daily, False for Overall searches.
 *     @type int      $daily_range                Number of days if `$daily` is true.
 *     @type int      $smallest                   Smallest font size used to display search terms. Paired
 *                                                with the value of `$unit`, to determine CSS text
 *                                                size unit.
 *     @type int      $largest                    Largest font size used to display search terms. Paired
 *                                                with the value of `$unit`, to determine CSS text
 *                                                size unit.
 *     @type string   $unit                       CSS text size unit to use with the `$smallest`
 *                                                and `$largest` values. Accepts any valid CSS text
 *                                                size unit. Default 'pt'.
 *     @type string   $hot                        Font color of largest search term.
 *     @type string   $cold                       Font color of smallest search term.
 *     @type int      $number                     The number of search terms to return. Accepts any
 *                                                positive integer or zero to return all.
 *     @type string   $before_term                Text to display before the search term.
 *     @type string   $after_term                 Text to display after the search term.
 *     @type bool     link_nofollow               Whether to add the "nofollow" attribute to the link.
 *     @type bool     link_new_window             Whether to add the "_blank" attribute to the link.
 *     @type string   $format                     Format to display the search terms cloud in. Accepts 'flat'
 *                                                (search terms separated with spaces), 'list' (search terms displayed
 *                                                in an unordered list), or 'array' (returns an array).
 *                                                Default 'flat'.
 *     @type string   $separator                  HTML or text to separate the search terms. Default "\n" (newline).
 *     @type string   $orderby                    Value to order search terms by. Accepts 'name' or 'count'.
 *                                                Default 'name'. The {@see 'bsearch_heatmap_sort'} filter
 *                                                can also affect how search terms are sorted.
 *     @type string   $order                      How to order the search terms. Accepts 'ASC' (ascending),
 *                                                'DESC' (descending), or 'RAND' (random). Default 'RAND'.
 *     @type string   $topic_count_text           Nooped plural text from _n_noop() to supply to
 *                                                search result counts. Default null.
 *     @type bool|int $show_count                 Whether to display the search result counts. Default 0. Accepts
 *                                                0, 1, or their bool equivalents.
 *     @type string   $no_results_text            Text to display if there are no search results.
 * }
 * @return string|string[] Search terms cloud as a string or an array, depending on 'format' argument.
 */
function get_bsearch_heatmap( $args = array() ) {

	$defaults = array(
		'daily'            => false,
		'daily_range'      => absint( bsearch_get_option( 'daily_range' ) ),
		'smallest'         => absint( bsearch_get_option( 'heatmap_smallest' ) ),
		'largest'          => absint( bsearch_get_option( 'heatmap_largest' ) ),
		'unit'             => bsearch_get_option( 'heatmap_unit', 'pt' ),
		'hot'              => bsearch_get_option( 'heatmap_hot' ),
		'cold'             => bsearch_get_option( 'heatmap_cold' ),
		'number'           => absint( bsearch_get_option( 'heatmap_limit' ) ),
		'before_term'      => bsearch_get_option( 'heatmap_before' ),
		'after_term'       => bsearch_get_option( 'heatmap_after' ),
		'link_nofollow'    => bsearch_get_option( 'link_nofollow' ),
		'link_new_window'  => bsearch_get_option( 'link_new_window' ),
		'format'           => 'flat',
		'separator'        => "\n",
		'orderby'          => 'count',
		'order'            => 'RAND',
		'topic_count_text' => null,
		'show_count'       => 0,
		'no_results_text'  => __( 'No searches made yet', 'better-search' ),
	);

	// Parse incomming $args into an array and merge it with $defaults.
	$args = wp_parse_args( $args, $defaults );

	// Sanitize args.
	$args = Helpers::sanitize_args( $args );

	$output = ( 'array' === $args['format'] ) ? array() : '';

	$results = get_bsearch_heatmap_counts( $args );

	if ( empty( $results ) ) {
		return $args['no_results_text'];
	}

	// First look for nooped plural support via topic_count_text.
	if ( isset( $args['topic_count_text'] ) ) {
		$translate_nooped_plural = $args['topic_count_text'];
	} else {
		/* translators: %s: Number of items (search results). */
		$translate_nooped_plural = _n_noop( '%s search', '%s searches', 'better-search' );
	}

	/**
	 * Filters how the items in a search results heatmap are sorted.
	 *
	 * @since 3.0.0
	 *
	 * @param object[] $results Ordered array of search results.
	 * @param array    $args    Arguments array.
	 */
	$results_sorted = apply_filters( 'tag_cloud_sort', $results, $args );
	if ( empty( $results_sorted ) ) {
		return $args['no_results_text'];
	}

	if ( $results_sorted !== $results ) {
		$results = $results_sorted;
		unset( $results_sorted );
	} elseif ( 'RAND' === $args['order'] ) {
			shuffle( $results );
	} else {

		// SQL cannot save you; this is a second (potentially different) sort on a subset of data.
		if ( 'name' === $args['orderby'] ) {
			uasort( $results, '_wp_object_name_sort_cb' );
		} else {
			uasort(
				$results,
				function ( $a, $b ): int {
					return $a->count <=> $b->count;
				}
			);
		}

		if ( 'DESC' === $args['order'] ) {
			$results = array_reverse( $results, true );
		}
	}

	if ( $args['number'] > 0 ) {
		$results = array_slice( $results, 0, $args['number'] );
	}

		$counts = wp_list_pluck( $results, 'count' );

		$min    = min( $counts );
		$max    = max( $counts );
		$spread = absint( $max - $min );

		// Calculate various font sizes.
		$fontspread = $args['largest'] - $args['smallest'];
	if ( 0 !== $spread ) {
		$fontstep = $fontspread / $spread;
	} else {
		$fontstep = 0;
	}

		// Calculate colors.
		$hotdec  = Helpers::html2rgb( $args['hot'] );
		$colddec = Helpers::html2rgb( $args['cold'] );
	for ( $i = 0; $i < 3; $i++ ) {
		$coldval[]     = $colddec[ $i ];
		$hotval[]      = $hotdec[ $i ];
		$colorspread[] = $hotdec[ $i ] - $colddec[ $i ];
		if ( 0 !== $spread ) {
			$colorstep[] = ( $hotdec[ $i ] - $colddec[ $i ] ) / $spread;
		} else {
			$colorstep[] = 0;
		}
	}

	// Determine if aria-label is to be displayed.
	$aria_label = false;
	if ( $args['show_count'] || 0 !== $fontspread ) {
		$aria_label = true;
	}

	foreach ( $results as $key => $result ) {
		$count     = $result->count;
		$searchvar = $result->name;
		$url       = add_query_arg( array( 's' => rawurlencode( $searchvar ) ), home_url( '/' ) );
		$fraction  = $count - $min;
		$fontsize  = $args['smallest'] + $fontstep * $fraction;

		$color = '';

		for ( $i = 0; $i < 3; $i++ ) {
			$color .= dechex( absint( $coldval[ $i ] + ( $colorstep[ $i ] * $fraction ) ) );
		}
		$style = sprintf( 'font-size:%1$s%2$s;color:#%3$s;', round( $fontsize ), $args['unit'], $color );

		/**
		 * Filter the value of the style tag of heatmap links.
		 *
		 * @since 2.5.0
		 *
		 * @param string $style     Value of the style tag of the link.
		 * @param string $searchvar Search term.
		 * @param object $result    Search results object.
		 */
		$style = apply_filters( 'bsearch_heatmap_style', $style, $searchvar, $result );

		$class = 'bsearch_heatmap_link';

		/**
		 * Filter the value of the class tag of heatmap links.
		 *
		 * @since 2.5.0
		 *
		 * @param string $style     Value of the class tag of the link.
		 * @param string $searchvar Search term.
		 * @param object $result    Search results object.
		 */
		$class = apply_filters( 'bsearch_heatmap_class', $class, $searchvar, $result );

		$formatted_count = sprintf( translate_nooped_plural( $translate_nooped_plural, $count ), Helpers::number_format_i18n( $count ) );

		$title = '';

		/**
		 * Filter the value of the title tag of heatmap links.
		 *
		 * @since 2.5.0
		 *
		 * @param string $title     Value of the title tag of the link.
		 * @param string $searchvar Search term.
		 * @param int    $count     Count.
		 * @param object $result    Search results object.
		 */
		$title = apply_filters( 'bsearch_heatmap_title', $title, $searchvar, $count, $result );

		$rel    = $args['link_nofollow'] ? 'rel="nofollow"' : '';
		$target = $args['link_new_window'] ? 'target="_blank"' : '';
		$title  = ! empty( $title ) ? sprintf( 'title="%s"', esc_attr( $title ) ) : '';

		$a[] = sprintf(
			'%1$s<a href="%2$s" %3$s style="%4$s" class="%5$s" %6$s %7$s %8$s>%9$s%10$s</a>%11$s',
			$args['before_term'],
			esc_url( $url ),
			$title,
			esc_attr( $style ),
			esc_attr( $class . ' bsearch_heatmap_link_position_' . ( $key + 1 ) ),
			$rel,
			$target,
			$aria_label ? sprintf( ' aria-label="%1$s (%2$s)"', esc_attr( $result->name ), esc_attr( $formatted_count ) ) : '',
			esc_html( $searchvar ),
			$args['show_count'] ? '<span class="bsearch_heatmap_link_count"> (' . Helpers::number_format_i18n( $count ) . ')</span>' : '',
			$args['after_term']
		);
	}

	switch ( $args['format'] ) {
		case 'array':
			$output =& $a;
			break;
		case 'list':
			$output  = "<ul class='bsearch_heatmap_list' role='list'>\n\t<li>";
			$output .= implode( "</li>\n\t<li>", $a );
			$output .= "</li>\n</ul>\n";
			break;
		default:
			$output = implode( $args['separator'], $a );
			break;
	}

	/**
	 * Filter formatted string with the search heatmap
	 *
	 * @since   1.2
	 *
	 * @param   string          $output     Formatted excerpt
	 * @param   string|array    $args       Arguments
	 */
	return apply_filters( 'get_bsearch_heatmap', $output, $args );
}


/**
 * Get the Search Heatmap terms with their counts.
 *
 * @since 2.5.0
 *
 * @param  array|string $args Heatmap Parameters.
 * @return array              Array of heatmap terms.
 */
function get_bsearch_heatmap_counts( $args = array() ) {
	global $wpdb;

	$defaults = array(
		'daily'       => false,
		'number'      => absint( bsearch_get_option( 'heatmap_limit' ) ),
		'daily_range' => absint( bsearch_get_option( 'daily_range' ) ),
	);

	// Parse incomming $args into an array and merge it with $defaults.
	$args = wp_parse_args( $args, $defaults );

	$table_name = Helpers::get_bsearch_table( $args['daily'] );

	if ( ! $args['daily'] ) {
		$sargs = array(
			$args['number'],
		);

		$sql = "
			SELECT searchvar as name, cntaccess as count
			FROM {$table_name}
			WHERE searchvar <> ''
			ORDER BY count DESC, searchvar ASC
			LIMIT %d
		";
	} else {
		$current_date = Helpers::get_from_date( null, $args['daily_range'] );

		$sargs = array(
			$current_date,
			$args['number'],
		);

		$sql = "
			SELECT DISTINCT searchvar as name, SUM(cntaccess) as count
			FROM {$table_name}
			WHERE dp_date >= '%s'
			GROUP BY searchvar
			ORDER BY count DESC, searchvar ASC
			LIMIT %d
		";
	}

	$results = $wpdb->get_results( $wpdb->prepare( $sql, $sargs ) ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared,WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching

	/**
	 * Filter formatted string with the search heatmap
	 *
	 * @since 2.5.0
	 *
	 * @param array $results Array of search terms.
	 * @param array $args    Array of arguments.
	 */
	return apply_filters( 'get_bsearch_heatmap_counts', $results, $args );
}