verifyAjaxRequest(); $this->checkAdminReferrer('wp_rest', 'wps_nonce'); $this->checkCapability('manage'); $purgeDays = Request::get('purge-days', 0, 'number'); if ($purgeDays < 30) { throw new Exception(esc_html__('The number of days to purge must be at least 30.', 'wp-statistics'), 400); } $result = Purge::purge_data($purgeDays); Ajax::success($result); } catch (Exception $e) { Ajax::error($e->getMessage(), null, $e->getCode()); } } /** * Setup an AJAX action to purge visitors. */ public function purgeVisitors() { try { $this->verifyAjaxRequest(); $this->checkAdminReferrer('wp_rest', 'wps_nonce'); $this->checkCapability('manage'); $hits = Request::get('purge-hits'); $ip = Request::get('ip-address'); $browser = Request::get('agent-name'); $platform = Request::get('platform-name'); // Throw error if all fields are empty if (!$hits && !$ip && !$browser && !$platform) { throw new Exception(esc_html__('Please select at least one field to purge.', 'wp-statistics'), 400); } // Purge visitors by hits if ($hits !== false) { if (!is_numeric($hits) || $hits < 10) { throw new Exception(esc_html__('View count must be 10 or more! Please enter a valid number and try again.', 'wp-statistics'), 400); } $result = Query::delete('visitor')->where('hits', '>', $hits)->execute(); } // Purge visitors by IP if ($ip !== false) { if (!filter_var($ip, FILTER_VALIDATE_IP)) { throw new Exception(esc_html__('Invalid IP address! Please enter a valid IP address and try again.', 'wp-statistics'), 400); } $result = Query::delete('visitor')->where('ip', '=', $ip)->execute(); } // Purge visitors by browser if ($browser !== false) { $result = Query::delete('visitor')->where('agent', '=', $browser)->execute(); } // Purge visitors by platform if ($platform !== false) { $result = Query::delete('visitor')->where('platform', '=', $platform)->execute(); } // Throw error on failure if ($result === false) { throw new Exception('Could not purge visitors. Please try again.', 500); } Ajax::success(sprintf(esc_html__('Removed %s visitors.', 'wp-statistics'), "$result")); } catch (Exception $e) { Ajax::error($e->getMessage(), null, $e->getCode()); } } /** * Setup an AJAX action to clear user IDs from the database. */ public function clearUserIds() { try { $this->verifyAjaxRequest(); $this->checkAdminReferrer('wp_rest', 'wps_nonce'); $this->checkCapability('manage'); $result = Query::update('visitor') ->set(['user_id' => 0]) ->execute(); if ($result === false) { throw new Exception('Could not remove user IDs. Please try again.', 500); } Ajax::success(esc_html__('User IDs removed.', 'wp-statistics')); } catch (Exception $e) { Ajax::error($e->getMessage(), null, $e->getCode()); } } /** * Setup an AJAX action to clear UA strings from the database. */ public function clearUAStrings() { try { $this->verifyAjaxRequest(); $this->checkAdminReferrer('wp_rest', 'wps_nonce'); $this->checkCapability('manage'); $result = Query::update('visitor') ->set(['UAString' => null]) ->execute(); if ($result === false) { throw new Exception('Could not remove user agent strings. Please try again.', 500); } Ajax::success(esc_html__('User agent strings removed.', 'wp-statistics')); } catch (Exception $e) { Ajax::error($e->getMessage(), null, $e->getCode()); } } /** * Setup an AJAX action to delete word count data from the database. */ public function deleteWordCountData() { try { $this->verifyAjaxRequest(); $this->checkAdminReferrer('wp_rest', 'wps_nonce'); $this->checkCapability('manage'); Option::deleteOptionGroup('word_count_process_initiated', 'jobs'); $result = Query::delete('postmeta') ->where('meta_key', '=', 'wp_statistics_words_count') ->execute(); if ($result === false) { throw new Exception('Could not clear word count data. Please try again.', 500); } Ajax::success(esc_html__('Word count data cleared.', 'wp-statistics')); } catch (Exception $e) { Ajax::error($e->getMessage(), null, $e->getCode()); } } /** * Setup an AJAX action to clean up query params from the database. */ public function cleanUpQueryParams() { try { $this->verifyAjaxRequest(); $this->checkAdminReferrer('wp_rest', 'wps_nonce'); $this->checkCapability('manage'); // Get allowed query params $allowedQueryParams = Helper::get_query_params_allow_list(); // Get all rows from pages table $pages = Query::select('*') ->from('pages') ->where('uri', 'LIKE', '%?%') ->getAll(); if ($pages) { // Update query strings based on allow list foreach ($pages as $page) { Query::update('pages') ->set(['uri' => Helper::FilterQueryStringUrl($page->uri, $allowedQueryParams)]) ->where('page_id', '=', $page->page_id) ->execute(); } } Ajax::success(esc_html__('Cleaned up query parameters in saved URLs.', 'wp-statistics')); } catch (Exception $e) { Ajax::error($e->getMessage(), null, $e->getCode()); } } /** * Setup an AJAX action to clean up event data from the database. */ public function cleanUpEventData() { try { $this->verifyAjaxRequest(); $this->checkAdminReferrer('wp_rest', 'wps_nonce'); $this->checkCapability('manage'); $eventName = Request::get('event_name'); if (!$eventName) { throw new Exception(esc_html__('Enter a valid event name.', 'wp-statistics'), 400); } $eventsModel = new EventsModel(); $result = $eventsModel->deleteEvents(['event_name' => $eventName]); if ($result === false) { throw new Exception('Could not remove event data. Please try again.', 500); } Ajax::success(sprintf(esc_html__('Event data removed for %s', 'wp-statistics'), "$eventName")); } catch (Exception $e) { Ajax::error($e->getMessage(), null, $e->getCode()); } } /** * Update GeoIP data for visitors with incomplete information. */ public function updateCountryData() { try { $this->verifyAjaxRequest(); $this->checkAdminReferrer('wp_rest', 'wps_nonce'); $this->checkCapability('manage'); $providerMap = [ 'maxmind' => MaxmindGeoIPProvider::class, 'dbip' => DbIpProvider::class ]; $method = Option::get('geoip_location_detection_method', 'maxmind'); $method = false; $provider = $providerMap[$method] ?? $providerMap['maxmind']; // First download/update the GeoIP database GeolocationFactory::downloadDatabase($provider); // Update GeoIP data for visitors with incomplete information BackgroundProcessFactory::getBackgroundProcess('update_unknown_visitor_geoip')->process(); Ajax::success(esc_html__('GeoIP update started.', 'wp-statistics')); } catch (Exception $e) { Ajax::error(sprintf(esc_html__('GeoIP update failed: %s', 'wp-statistics'), $e->getMessage()), null, $e->getCode()); } } /** * Handles AJAX requests to update the source channel data for visitors. */ public function updateSourceChannelData() { try { $this->verifyAjaxRequest(); $this->checkAdminReferrer('wp_rest', 'wps_nonce'); $this->checkCapability('manage'); BackgroundProcessFactory::getBackgroundProcess('update_visitors_source_channel')->process(); Ajax::success(esc_html__('Source channel update started.', 'wp-statistics')); } catch (Exception $e) { Ajax::error(sprintf(esc_html__('Source channel update failed: %s', 'wp-statistics'), $e->getMessage()), null, $e->getCode()); } } /** * Handles AJAX requests to anonymize IP addresses of visitors. */ public function hashIps() { try { $this->verifyAjaxRequest(); $this->checkAdminReferrer('wp_rest', 'wps_nonce'); $this->checkCapability('manage'); $result = IP::Update_HashIP_Visitor(); Ajax::success(sprintf(esc_html__('Anonymized `%d` IP addresses.', 'wp-statistics'), $result)); } catch (Exception $e) { Ajax::error(sprintf(esc_html__('IP anonymization failed: %s', 'wp-statistics'), $e->getMessage()), null, $e->getCode()); } } /** * Callback function to check the database schema via AJAX. */ public function recheckSchema() { try { $this->verifyAjaxRequest(); $this->checkAdminReferrer('wp_rest', 'wps_nonce'); $this->checkCapability('manage'); $schemaCheckResult = SchemaMaintainer::check(true); $databaseStatus = $schemaCheckResult['status'] ?? null; if ($databaseStatus !== 'success') { throw new Exception(esc_html__('Database issues were found. Refresh this page to show the `Repair` button.', 'wp-statistics')); } Ajax::success(esc_html__('Database looks good. No issues found.', 'wp-statistics')); } catch (Exception $e) { Ajax::error($e->getMessage(), null, $e->getCode()); } } /** * Handles AJAX requests to repair database schema issues */ public function repairSchema() { try { $this->verifyAjaxRequest(); $this->checkAdminReferrer('wp_rest', 'wps_nonce'); $this->checkCapability('manage'); $schemaRepairResult = SchemaMaintainer::repair(); $databaseStatus = $schemaRepairResult['status'] ?? null; if ($databaseStatus !== 'success') { throw new Exception(esc_html__('Could not repair the database. Please try again.', 'wp-statistics')); } Ajax::success(esc_html__('Database schema issues were repaired.', 'wp-statistics')); } catch (Exception $e) { Ajax::error(sprintf(esc_html__('Failed to repair database schema: %s', 'wp-statistics'), $e->getMessage()), null, $e->getCode()); } } /** * Handles AJAX requests to save historical data settings. */ public function handleHistoricalSettingForm() { try { $this->verifyAjaxRequest(); $this->checkAdminReferrer('wps_optimization'); $this->checkCapability('manage'); $visitors = Request::get('visitors', 0, 'number'); $visits = Request::get('visits', 0, 'number'); $storedVisitors = Historical::get('visitors'); $storedVisits = Historical::get('visits'); // Update historical visitors if ($visitors != $storedVisitors) { $result = Query::update('historical') ->set(['value' => $visitors]) ->where('category', '=','visitors') ->execute(); // If no record to update, insert it if (!$result) { Query::insert('historical') ->set(['value' => $visitors, 'category' => 'visitors', 'page_id' => -1, 'uri' => '-1']) ->execute(); } } // Update historical visits if ($visits != $storedVisits) { $result = Query::update('historical') ->set(['value' => $visits]) ->where('category', '=','visits') ->execute(); // If no record to update, insert it if (!$result) { Query::insert('historical') ->set(['value' => $visits, 'category' => 'visits', 'page_id' => -2, 'uri' => '-2']) ->execute(); } } Notice::addFlashNotice(esc_html__('Historical Data Successfully Updated.', "wp-statistics"), "success"); Ajax::success(); } catch (Exception $e) { Notice::addFlashNotice($e->getMessage(), "error"); Ajax::error($e->getMessage(), null, $e->getCode()); } } }