File "UserInterface.php"

Full Path: /home/vantageo/public_html/cache/.wp-cli/wp-content/plugins/ajax-search-for-woocommerce/includes/Analytics/UserInterface.php
File size: 22.17 KB
MIME-type: text/x-php
Charset: utf-8

<?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 don’t 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;
    }

}