Files
torebki-fabiola.pl/wp-content/plugins/ajax-search-for-woocommerce/includes/Analytics/UserInterface.php
2026-03-05 13:07:40 +01:00

563 lines
22 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
namespace DgoraWcas\Analytics;
use DgoraWcas\Helpers ;
use DgoraWcas\Multilingual ;
// Exit if accessed directly
if ( !defined( 'ABSPATH' ) ) {
exit;
}
class UserInterface
{
const SECTION_ID = 'dgwt_wcas_analytics' ;
const LOAD_INTERFACE_NONCE = 'analytics-load-interface' ;
const LOAD_MORE_CRITICAL_SEARCHES_NONCE = 'analytics-load-more-critical-searches' ;
const LOAD_MORE_AUTOCOMPLETE_NONCE = 'analytics-load-more-autocomplete' ;
const LOAD_MORE_SEARCH_PAGE_NONCE = 'analytics-load-more-search-page' ;
const CRITICAL_CHECK_NONCE = 'analytics-critical-check' ;
const EXCLUDE_CRITICAL_PHRASE_NONCE = 'analytics-exclude-critical-phrase' ;
const RESET_STATS_NONCE = 'analytics-reset-stats' ;
const EXPORT_STATS_CSV_NONCE = 'analytics-export-stats-csv' ;
const CSS_CLASS_PLACEHOLDER = 'js-dgwt-wcas-stats-placeholder' ;
const CRITICAL_SEARCHES_LOAD_LIMIT = 10 ;
const TABLE_ROW_LIMIT_LIMIT = 10 ;
/**
* @var Analytics
*/
private $analytics ;
/**
* Constructor
*
* @param Analytics $analytics
*/
public function __construct( Analytics $analytics )
{
$this->analytics = $analytics;
}
/**
* Init the class
*
* @return void
*/
public function init()
{
// Draw settings page
add_filter( 'dgwt/wcas/settings/sections', array( $this, 'addSettingsSection' ) );
add_filter( 'dgwt/wcas/settings', array( $this, 'addSettingsTab' ) );
add_filter( 'dgwt/wcas/scripts/admin/localize', array( $this, 'localizeSettings' ) );
// AJAX callbacks
add_action( 'wp_ajax_dgwt_wcas_load_stats_interface', array( $this, 'loadInterface' ) );
add_action( 'wp_ajax_dgwt_wcas_laod_more_critical_searches', array( $this, 'loadMoreCriticalSearches' ) );
add_action( 'wp_ajax_dgwt_wcas_laod_more_autocomplete', array( $this, 'loadMoreAutocomplete' ) );
add_action( 'wp_ajax_dgwt_wcas_laod_more_search_page', array( $this, 'loadMoreSearchPage' ) );
add_action( 'wp_ajax_dgwt_wcas_check_critical_phrase', array( $this, 'checkCriticalPhrase' ) );
add_action( 'wp_ajax_dgwt_wcas_exclude_critical_phrase', array( $this, 'excludeCriticalPhrase' ) );
add_action( 'wp_ajax_dgwt_wcas_reset_stats', array( $this, 'resetStats' ) );
add_action( 'wp_ajax_dgwt_wcas_export_stats_csv', array( $this, 'exportStats' ) );
if ( $this->analytics->isModuleEnabled() ) {
add_action( DGWT_WCAS_SETTINGS_KEY . '-form_end_' . self::SECTION_ID, array( $this, 'tabContent' ) );
}
}
/**
* Content of "Analytics" tab on Settings page
*
* @param array $sections
*
* @return array
*/
public function addSettingsSection( $sections )
{
$sections[28] = array(
'id' => self::SECTION_ID,
'title' => __( 'Analytics', 'ajax-search-for-woocommerce' ),
);
return $sections;
}
/**
* Add "Analytics" tab on Settings page
*
* @param array $settings
*
* @return array
*/
public function addSettingsTab( $settings )
{
$searchAnalyticsLink = 'https://fibosearch.com/documentation/features/fibosearch-analytics/';
$settings[self::SECTION_ID] = apply_filters( 'dgwt/wcas/settings/section=analytics', array(
100 => array(
'name' => 'analytics_head',
'label' => __( 'Search Analytics', 'ajax-search-for-woocommerce' ),
'type' => 'head',
'class' => 'dgwt-wcas-sgs-header',
),
110 => array(
'name' => 'analytics_enabled',
'label' => __( 'Enable search analytics', 'ajax-search-for-woocommerce' ) . ' ' . Helpers::createQuestionMark( 'enable_search_analytics', sprintf( __( 'Search analytics system helps to eliminate search phrases that dont return any results. Also, allows to explore trending keywords. <a target="_blank" href="%s">Find our more</a> how to use and customize FiboSearch Analytics.', 'ajax-search-for-woocommerce' ), $searchAnalyticsLink ) ),
'type' => 'checkbox',
'class' => 'dgwt-wcas-options-cb-toggle js-dgwt-wcas-cbtgroup-analytics-critial-searches-widget',
'size' => 'small',
'default' => 'off',
),
120 => array(
'name' => 'analytics_critical_searches_widget_enabled',
'label' => __( 'Show widget with critical searches in Dashboard', 'ajax-search-for-woocommerce' ),
'type' => 'checkbox',
'class' => 'js-dgwt-wcas-cbtgroup-analytics-critial-searches-widget',
'size' => 'small',
'default' => 'off',
),
) );
return $settings;
}
/**
* Pass data to JavaScript on the settings page
*
* @param array $localize
*
* @return array
*/
public function localizeSettings( $localize )
{
$localize['analytics'] = array(
'nonce' => array(
'analytics_load_interface' => wp_create_nonce( self::LOAD_INTERFACE_NONCE ),
'load_more_critical_searches' => wp_create_nonce( self::LOAD_MORE_CRITICAL_SEARCHES_NONCE ),
'load_more_autocomplete' => wp_create_nonce( self::LOAD_MORE_AUTOCOMPLETE_NONCE ),
'load_more_search_page' => wp_create_nonce( self::LOAD_MORE_SEARCH_PAGE_NONCE ),
'check_critical_phrase' => wp_create_nonce( self::CRITICAL_CHECK_NONCE ),
'exclude_critical_phrase' => wp_create_nonce( self::EXCLUDE_CRITICAL_PHRASE_NONCE ),
'reset_stats' => wp_create_nonce( self::RESET_STATS_NONCE ),
'export_stats_csv' => wp_create_nonce( self::EXPORT_STATS_CSV_NONCE ),
),
'enabled' => $this->analytics->isModuleEnabled(),
'images' => array(
'placeholder' => DGWT_WCAS_URL . 'assets/img/admin-stats-placeholder.png',
),
'labels' => array(
'reset_stats_confirm' => __( 'Are you sure you want to reset stats?', 'ajax-search-for-woocommerce' ),
),
);
return $localize;
}
/**
* Load content for "Analytics" tab on Settings page
*
* @return void
*/
public function tabContent()
{
if ( Multilingual::isMultilingual() ) {
echo $this->getLanguageSwitcher() ;
}
echo '<div class="dgwt-wcas-analytics-body ' . self::CSS_CLASS_PLACEHOLDER . '"></div>' ;
}
/**
* Get HTML of language switcher
*
* @return string
*/
private function getLanguageSwitcher()
{
$vars = array(
'multilingual' => array(
'is-multilingual' => true,
'current-lang' => Multilingual::getCurrentLanguage(),
'langs' => array(),
),
);
foreach ( Multilingual::getLanguages() as $lang ) {
$vars['multilingual']['langs'][$lang] = Multilingual::getLanguageField( $lang, 'name' );
}
ob_start();
require DGWT_WCAS_DIR . 'partials/admin/stats/langs.php';
return ob_get_clean();
}
/**
* Load an interface (AJAX callback)
*
* @return void
*/
public function loadInterface()
{
if ( !current_user_can( ( Helpers::shopManagerHasAccess() ? 'manage_woocommerce' : 'manage_options' ) ) ) {
wp_die( -1, 403 );
}
check_ajax_referer( self::LOAD_INTERFACE_NONCE );
$lang = ( !empty($_REQUEST['lang']) && Multilingual::isLangCode( sanitize_key( $_REQUEST['lang'] ) ) ? sanitize_key( $_REQUEST['lang'] ) : '' );
$data = array(
'html' => '',
);
ob_start();
$vars = $this->getVars( $lang );
require DGWT_WCAS_DIR . 'partials/admin/stats/stats.php';
$data['html'] = ob_get_clean();
wp_send_json_success( $data );
}
/**
* Load more critical searches
*
* @return void
*/
public function loadMoreCriticalSearches()
{
if ( !current_user_can( ( Helpers::shopManagerHasAccess() ? 'manage_woocommerce' : 'manage_options' ) ) ) {
wp_die( -1, 403 );
}
check_ajax_referer( self::LOAD_MORE_CRITICAL_SEARCHES_NONCE );
$lang = ( !empty($_REQUEST['lang']) && Multilingual::isLangCode( sanitize_key( $_REQUEST['lang'] ) ) ? sanitize_key( $_REQUEST['lang'] ) : '' );
$offset = ( !empty($_REQUEST['loaded']) ? absint( $_REQUEST['loaded'] ) : 0 );
$html = '';
$data = new Data();
if ( !empty($lang) ) {
$data->setLang( $lang );
}
$total = $data->getTotalCriticalSearches();
$critical = $data->getCriticalSearches( self::CRITICAL_SEARCHES_LOAD_LIMIT, $offset );
if ( !empty($critical) ) {
ob_start();
$i = $offset + 1;
foreach ( $critical as $row ) {
require DGWT_WCAS_DIR . 'partials/admin/stats/critical-searches-row.php';
$i++;
}
$html = ob_get_clean();
}
$toLoad = $total - $offset - count( $critical );
$more = min( self::CRITICAL_SEARCHES_LOAD_LIMIT, $toLoad );
$data = array(
'html' => $html,
'more' => $more,
'more_label' => '',
);
if ( $more > 0 ) {
$data['more_label'] = sprintf( _n(
'load another %d phrase',
'load another %d phrases',
$more,
'ajax-search-for-woocommerce'
), $more );
}
wp_send_json_success( $data );
}
/**
* Load more autocomplete searches with results
*
* @return void
*/
public function loadMoreAutocomplete()
{
if ( !current_user_can( ( Helpers::shopManagerHasAccess() ? 'manage_woocommerce' : 'manage_options' ) ) ) {
wp_die( -1, 403 );
}
check_ajax_referer( self::LOAD_MORE_AUTOCOMPLETE_NONCE );
$lang = ( !empty($_REQUEST['lang']) && Multilingual::isLangCode( sanitize_key( $_REQUEST['lang'] ) ) ? sanitize_key( $_REQUEST['lang'] ) : '' );
// Autocomplete
$data = new Data();
if ( !empty($lang) ) {
$data->setLang( $lang );
}
$data->setContext( 'autocomplete' );
$phrases = $data->getPhrasesWithResults( 100 );
ob_start();
$i = 1;
foreach ( $phrases as $row ) {
require DGWT_WCAS_DIR . 'partials/admin/stats/ac-searches-row.php';
$i++;
}
$html = ob_get_clean();
$data = array(
'html' => $html,
);
wp_send_json_success( $data );
}
/**
* Load more search page searches with results
*
* @return void
*/
public function loadMoreSearchPage()
{
if ( !current_user_can( ( Helpers::shopManagerHasAccess() ? 'manage_woocommerce' : 'manage_options' ) ) ) {
wp_die( -1, 403 );
}
check_ajax_referer( self::LOAD_MORE_SEARCH_PAGE_NONCE );
$lang = ( !empty($_REQUEST['lang']) && Multilingual::isLangCode( sanitize_key( $_REQUEST['lang'] ) ) ? sanitize_key( $_REQUEST['lang'] ) : '' );
// Search page
$data = new Data();
if ( !empty($lang) ) {
$data->setLang( $lang );
}
$data->setContext( 'search-results-page' );
$phrases = $data->getPhrasesWithResults( 100 );
ob_start();
$i = 1;
foreach ( $phrases as $row ) {
require DGWT_WCAS_DIR . 'partials/admin/stats/sp-searches-row.php';
$i++;
}
$html = ob_get_clean();
$data = array(
'html' => $html,
);
wp_send_json_success( $data );
}
/**
* Check if the phrase returns results
*
* @return void
*/
public function checkCriticalPhrase()
{
if ( !current_user_can( ( Helpers::shopManagerHasAccess() ? 'manage_woocommerce' : 'manage_options' ) ) ) {
wp_die( -1, 403 );
}
check_ajax_referer( self::CRITICAL_CHECK_NONCE );
$data = array(
'html' => '',
'status' => '',
);
$phrase = ( !empty($_REQUEST['phrase']) ? $_REQUEST['phrase'] : '' );
if ( empty($phrase) ) {
wp_send_json_error( 'empty phrase' );
}
if ( !dgoraAsfwFs()->is_premium() ) {
$res = DGWT_WCAS()->nativeSearch->getSearchResults( $phrase, true, 'autocomplete' );
if ( is_array( $res ) && isset( $res['total'] ) ) {
$total = absint( $res['total'] );
if ( $total > 0 ) {
$data['status'] = 'with-results';
$data['html'] = $this->getCriticalPhraseMessage( $data['status'], $total );
} else {
$data['status'] = 'without-results';
$data['html'] = $this->getCriticalPhraseMessage( $data['status'] );
}
} else {
$data['status'] = 'error';
$data['html'] = $this->getCriticalPhraseMessage( $data['status'] );
}
} else {
}
wp_send_json_success( $data );
}
/**
* Check critical phrase - messages on response
*
* @param string $context
* @param int $total
*
* @return string
*/
public function getCriticalPhraseMessage( $context = '' )
{
$html = '';
//This phrase returns X products.
switch ( $context ) {
case 'with-results':
$html = '<p>';
$html .= '<b class="dgwt-wcas-analytics-text-good">' . __( "Perfect!", 'ajax-search-for-woocommerce' ) . '</b>';
$html .= ' ' . __( "It's sorted.", 'ajax-search-for-woocommerce' );
$html .= ' ' . __( 'This phrase returns some results.', 'ajax-search-for-woocommerce' );
$html .= ' ' . __( 'Click the button below to remove this phrase from the list.', 'ajax-search-for-woocommerce' );
$html .= '<button class="button button-small dgwt-wcas-analytics-btn-mark js-dgwt-wcas-analytics-exclude-phrase"><span class="dashicons dashicons-yes"></span> ' . __( 'Mark this phrase as resolved', 'ajax-search-for-woocommerce' ) . '</button>';
$html .= '</p>';
break;
case 'without-results':
$html = '<p>';
$html .= '<b class="dgwt-wcas-analytics-text-poorly">' . __( "Poor!", 'ajax-search-for-woocommerce' ) . '</b>';
$html .= ' ' . __( "Still this phrase doesn't return any results. Learn how to fix it.", 'ajax-search-for-woocommerce' );
$html .= '</p>';
break;
case 'wrong-index':
$html = '<p>';
$html .= __( "Can't check the status. The search index hasn't been completed. Go to the Indexer tab and wait until the search index is completed.", 'ajax-search-for-woocommerce' );
$html .= '<button class="button button-small dgwt-wcas-analytics-btn-mark js-dgwt-wcas-analytics-check-indexer">' . __( 'Check the indexer status', 'ajax-search-for-woocommerce' ) . '</button>';
$html .= '</p>';
break;
case 'error':
$html = '<p>';
$html .= __( 'Something went wrong', 'ajax-search-for-woocommerce' );
$html .= '</p>';
break;
}
return $html;
}
/**
* Unmark a phrase as critical. AJAX callback
*
* @return void
*/
public function excludeCriticalPhrase()
{
if ( !current_user_can( ( Helpers::shopManagerHasAccess() ? 'manage_woocommerce' : 'manage_options' ) ) ) {
wp_die( -1, 403 );
}
check_ajax_referer( self::EXCLUDE_CRITICAL_PHRASE_NONCE );
$phrase = ( !empty($_REQUEST['phrase']) ? $_REQUEST['phrase'] : '' );
$lang = ( !empty($_REQUEST['lang']) && Multilingual::isLangCode( $_REQUEST['lang'] ) ? sanitize_key( $_REQUEST['lang'] ) : '' );
if ( !empty($phrase) ) {
$data = new Data();
if ( Multilingual::isMultilingual() && !empty($lang) ) {
$data->setLang( $lang );
}
if ( $data->markAsSolved( $phrase ) ) {
wp_send_json_success( '<p>' . __( 'This phrase has been resolved! This row will disappear after refreshing the page.', 'ajax-search-for-woocommerce' ) . '</p>' );
}
}
wp_send_json_error( 'empty phrase' );
}
/**
* Reset stats. AJAX callback
*
* @return void
*/
public function resetStats()
{
if ( !current_user_can( ( Helpers::shopManagerHasAccess() ? 'manage_woocommerce' : 'manage_options' ) ) ) {
wp_die( -1, 403 );
}
check_ajax_referer( self::RESET_STATS_NONCE );
Database::wipeAllRecords();
wp_send_json_success();
}
/**
* Export stats. AJAX callback
*
* @return void
*/
public function exportStats()
{
if ( !current_user_can( ( Helpers::shopManagerHasAccess() ? 'manage_woocommerce' : 'manage_options' ) ) ) {
wp_die( -1, 403 );
}
check_ajax_referer( self::EXPORT_STATS_CSV_NONCE );
if ( !class_exists( 'WC_CSV_Exporter', false ) ) {
require_once WC_ABSPATH . 'includes/export/abstract-wc-csv-exporter.php';
}
$exporter = new CSVExporter();
$context = ( isset( $_GET['context'] ) ? sanitize_key( $_GET['context'] ) : '' );
$exporter->set_context( $context );
$lang = ( !empty($_REQUEST['lang']) && Multilingual::isLangCode( sanitize_key( $_REQUEST['lang'] ) ) ? sanitize_key( $_REQUEST['lang'] ) : '' );
if ( !empty($lang) ) {
$exporter->set_lang( $lang );
}
$exporter->export();
}
/**
* Prepare vars for the view
*
* @param string $lang
*
* @return array
*/
private function getVars( $lang = '' )
{
$data = new Data();
if ( Multilingual::isMultilingual() ) {
$data->setLang( $lang );
}
$mainUrl = 'https://fibosearch.com/lack-of-queries-insight-really-hurts-your-sales/';
$vars = array(
'days' => $this->getExpirationInDays(),
'autocomplete' => array(),
'search-page' => array(),
'critical-searches' => array(),
'critical-searches-total' => 0,
'critical-searches-more' => 0,
'returning-results-percent' => 0,
'returning-results-percent-poorly' => false,
'links' => array(
'synonyms' => $mainUrl . '#synonyms',
'support' => 'https://fibosearch.com/contact/',
),
'table-info' => Helpers::getTableInfo( Database::getTableName() ),
);
// Autocomplete
$data->setContext( 'autocomplete' );
$vars['autocomplete'] = array(
'with-results' => $data->getPhrasesWithResults( self::TABLE_ROW_LIMIT_LIMIT ),
'total-with-results-uniq' => $data->getTotalSearches( true, true ),
'total-without-results-uniq' => $data->getTotalSearches( false, true ),
'total-with-results' => $data->getTotalSearches( true ),
'total-without-results' => $data->getTotalSearches( false ),
'total-results' => 0,
);
$vars['autocomplete']['total-results-uniq'] = $vars['autocomplete']['total-with-results-uniq'] + $vars['autocomplete']['total-without-results-uniq'];
$vars['autocomplete']['total-results'] = $vars['autocomplete']['total-with-results'] + $vars['autocomplete']['total-without-results'];
// WooCommerce Search Results Page
$data->setContext( 'search-results-page' );
$vars['search-page'] = array(
'with-results' => $data->getPhrasesWithResults( self::TABLE_ROW_LIMIT_LIMIT ),
'total-with-results-uniq' => $data->getTotalSearches( true, true ),
'total-without-results-uniq' => $data->getTotalSearches( false, true ),
'total-with-results' => $data->getTotalSearches( true ),
'total-without-results' => $data->getTotalSearches( false ),
'total-results' => 0,
);
$vars['search-page']['total-results-uniq'] = $vars['search-page']['total-with-results-uniq'] + $vars['search-page']['total-without-results-uniq'];
$vars['search-page']['total-results'] = $vars['search-page']['total-with-results'] + $vars['search-page']['total-without-results'];
// Common
$vars['total'] = $vars['autocomplete']['total-results'];
if ( $vars['total'] > 0 ) {
$vars['returning-results-percent'] = round( $vars['autocomplete']['total-with-results'] * 100 / $vars['total'] );
$vars['returning-results-percent-satisfying'] = $data->isSearchesReturningResutlsSatisfying( $vars['returning-results-percent'] );
}
// Critical searches
$critical = $data->getCriticalSearches( self::CRITICAL_SEARCHES_LOAD_LIMIT );
if ( !empty($critical) ) {
$vars['critical-searches'] = $critical;
$vars['critical-searches-total'] = $data->getTotalCriticalSearches();
$toLoad = $vars['critical-searches-total'] - count( $critical );
$vars['critical-searches-more'] = min( self::CRITICAL_SEARCHES_LOAD_LIMIT, $toLoad );
if ( $vars['critical-searches-total'] < self::CRITICAL_SEARCHES_LOAD_LIMIT ) {
$vars['critical-searches-more'] = 0;
}
}
return $vars;
}
/**
* The records will be removed from the database after passing X days
*
* @return int
*/
public function getExpirationInDays()
{
$days = Maintenance::ANALYTICS_EXPIRATION_IN_DAYS;
if ( defined( 'DGWT_WCAS_ANALYTICS_EXPIRATION_IN_DAYS' ) && intval( DGWT_WCAS_ANALYTICS_EXPIRATION_IN_DAYS ) > 0 ) {
$days = intval( DGWT_WCAS_ANALYTICS_EXPIRATION_IN_DAYS );
}
return $days;
}
}