840 lines
29 KiB
PHP
840 lines
29 KiB
PHP
<?php
|
|
|
|
class CHECKER_SHORTCODE extends SHEET_DATA_CHECKER_PRO
|
|
{
|
|
/**
|
|
* A reference to an instance of this class.
|
|
*/
|
|
private static $instance;
|
|
|
|
/**
|
|
* Returns an instance of this class.
|
|
*/
|
|
public static function get_instance()
|
|
{
|
|
return self::$instance;
|
|
}
|
|
|
|
/**
|
|
* Initializes the plugin by setting filters and administration functions.
|
|
*/
|
|
public function __construct()
|
|
{
|
|
// Load security class
|
|
if (!class_exists("CHECKER_SECURITY")) {
|
|
require_once SHEET_CHECKER_PRO_PATH . "includes/class-Security.php";
|
|
}
|
|
// Ensure CAPTCHA helper is available
|
|
if (!class_exists("CHECKER_CAPTCHA_HELPER")) {
|
|
require_once SHEET_CHECKER_PRO_PATH . "includes/helpers/class-Captcha-Helper.php";
|
|
}
|
|
|
|
add_shortcode("checker", [$this, "content"]);
|
|
add_action("wp_enqueue_scripts", [$this, "enqueue"]);
|
|
|
|
add_action("wp_ajax_checker_public_validation", [
|
|
$this,
|
|
"checker_public_validation",
|
|
]);
|
|
add_action("wp_ajax_nopriv_checker_public_validation", [
|
|
$this,
|
|
"checker_public_validation",
|
|
]);
|
|
|
|
add_action("wp_ajax_checker_load_all_data", [
|
|
$this,
|
|
"checker_load_all_data",
|
|
]);
|
|
add_action("wp_ajax_nopriv_checker_load_all_data", [
|
|
$this,
|
|
"checker_load_all_data",
|
|
]);
|
|
|
|
add_action("wp_ajax_checker_clear_cache", [
|
|
$this,
|
|
"checker_clear_cache",
|
|
]);
|
|
add_action("wp_ajax_nopriv_checker_clear_cache", [
|
|
$this,
|
|
"checker_clear_cache_public",
|
|
]);
|
|
}
|
|
|
|
public function enqueue()
|
|
{
|
|
wp_enqueue_style(
|
|
"datatable",
|
|
"https://cdn.datatables.net/1.13.7/css/jquery.dataTables.min.css",
|
|
[],
|
|
"all",
|
|
);
|
|
wp_enqueue_style(
|
|
"checker-pro",
|
|
SHEET_CHECKER_PRO_URL .
|
|
"assets/public.css?ver=" .
|
|
SHEET_CHECKER_PRO_VERSION,
|
|
[],
|
|
"all",
|
|
);
|
|
|
|
wp_enqueue_script(
|
|
"datatable",
|
|
"https://cdn.datatables.net/1.13.7/js/jquery.dataTables.min.js",
|
|
["jquery"],
|
|
null,
|
|
true
|
|
);
|
|
wp_enqueue_script(
|
|
"checker-pro",
|
|
SHEET_CHECKER_PRO_URL . "assets/public.js",
|
|
["jquery", "datatable"],
|
|
SHEET_CHECKER_PRO_VERSION,
|
|
true
|
|
);
|
|
|
|
// Pass nonce and i18n strings to JavaScript
|
|
wp_localize_script("checker-pro", "checkerSecurity", [
|
|
"nonce" => wp_create_nonce("checker_ajax_nonce"),
|
|
"ajaxurl" => admin_url("admin-ajax.php"),
|
|
"i18n" => [
|
|
"refresh_page" => __("Refresh Page", "sheet-data-checker-pro"),
|
|
"session_expired" => __("Session expired. Please refresh the page and try again.", "sheet-data-checker-pro"),
|
|
"recaptcha_failed" => __("reCAPTCHA verification failed. Please try again.", "sheet-data-checker-pro"),
|
|
"turnstile_failed" => __("Turnstile verification failed. Please try again.", "sheet-data-checker-pro"),
|
|
"rate_limited" => __("Too many attempts. Please try again later.", "sheet-data-checker-pro"),
|
|
"security_error" => __("Security validation failed.", "sheet-data-checker-pro"),
|
|
"loading" => __("Loading...", "sheet-data-checker-pro"),
|
|
"searching" => __("Searching...", "sheet-data-checker-pro"),
|
|
"error_occurred" => __("An error occurred. Please try again.", "sheet-data-checker-pro"),
|
|
],
|
|
]);
|
|
}
|
|
|
|
public function content($atts, $content = null)
|
|
{
|
|
if (!isset($atts["id"])) {
|
|
return;
|
|
}
|
|
|
|
$post_id = $atts["id"];
|
|
$checker = get_post_meta($post_id, "checker", true);
|
|
|
|
// Load CAPTCHA scripts if enabled
|
|
if (class_exists('CHECKER_CAPTCHA_HELPER')) {
|
|
CHECKER_CAPTCHA_HELPER::load_captcha_scripts($post_id);
|
|
}
|
|
|
|
$checker = wp_parse_args($checker, [
|
|
"link" => "",
|
|
"description" => "",
|
|
"card" => [
|
|
"width" => 500,
|
|
"background" => "#cccccc",
|
|
"bg_opacity" => 50,
|
|
"border_radius" => 1,
|
|
"box_shadow" => "10px 5px 15px -5px",
|
|
"box_shadow_color" => "#333333",
|
|
"title" => "#333333",
|
|
"title_align" => "left",
|
|
"description" => "#333333",
|
|
"description_align" => "left",
|
|
"divider" => "#333333",
|
|
"divider_width" => 1,
|
|
],
|
|
"field" => [
|
|
"label" => "block",
|
|
"label-color" => "#333333",
|
|
],
|
|
"fields" => [],
|
|
"search_button" => [
|
|
"text" => "Search",
|
|
"bg_color" => "#cccccc",
|
|
"text_color" => "#333333",
|
|
"position" => "flex-end",
|
|
],
|
|
"back_button" => [
|
|
"text" => "Back",
|
|
"bg_color" => "#cccccc",
|
|
"text_color" => "#333333",
|
|
"position" => "flex-start",
|
|
],
|
|
"result" => [
|
|
"display" => "vertical-tabel",
|
|
"header" => "#333333",
|
|
"value" => "#333333",
|
|
"columns" => [],
|
|
"border_width" => 1,
|
|
],
|
|
]);
|
|
|
|
$url = isset($checker["link"]) ? (string)$checker["link"] : '';
|
|
|
|
$link_format = $url ? substr($url, -3) : 'csv';
|
|
|
|
// Set the delimiter based on the format
|
|
$delimiter = $link_format == "tsv" ? "\t" : ","; // Use tab for TSV, comma for CSV
|
|
|
|
// Validate allowed host
|
|
if (!$this->is_allowed_sheet_url($url)) {
|
|
wp_send_json_error([
|
|
"message" => __("Sheet URL is not allowed. Please use an approved domain.", "sheet-data-checker-pro"),
|
|
"type" => "error",
|
|
]);
|
|
return;
|
|
}
|
|
|
|
// Use WordPress HTTP API instead of fopen for better server compatibility
|
|
$data = $this->fetch_remote_csv_data($url, $delimiter);
|
|
$background_color = isset($checker["card"]["background"]) ? $checker["card"]["background"] : '#ffffff';
|
|
if ($checker["card"]["bg_opacity"] < 100) {
|
|
$background_color =
|
|
$checker["card"]["background"] .
|
|
"" .
|
|
$checker["card"]["bg_opacity"];
|
|
}
|
|
|
|
$render = "";
|
|
$render .=
|
|
'<div class="dw-checker-container" id="checker-' . $post_id . '" data-hp-name="">';
|
|
$render .=
|
|
'<form class="dw-checker-wrapper dw-checker-form"
|
|
style="max-width: 100%;
|
|
background-color: ' .
|
|
$background_color .
|
|
';
|
|
width: ' .
|
|
$checker["card"]["width"] .
|
|
'px;
|
|
padding: ' .
|
|
$checker["card"]["padding"] .
|
|
'em;
|
|
border-radius: ' .
|
|
$checker["card"]["border_radius"] .
|
|
'em;
|
|
box-shadow: ' .
|
|
$checker["card"]["box_shadow"] .
|
|
" " .
|
|
$checker["card"]["box_shadow_color"] .
|
|
';
|
|
">';
|
|
$render .=
|
|
'<div class="dw-checker-title"
|
|
style="color: ' .
|
|
$checker["card"]["title"] .
|
|
';
|
|
text-align: ' .
|
|
$checker["card"]["title_align"] .
|
|
';"
|
|
>' .
|
|
get_the_title($post_id) .
|
|
"</div>";
|
|
$render .=
|
|
'<div class="dw-checker-description"
|
|
style="color: ' .
|
|
$checker["card"]["description"] .
|
|
';
|
|
text-align: ' .
|
|
$checker["card"]["description_align"] .
|
|
';"
|
|
>' .
|
|
$checker["description"] .
|
|
"</div>";
|
|
|
|
$render .=
|
|
'<hr class="dw-checker-divider"
|
|
style="border-color: ' .
|
|
$checker["card"]["divider"] .
|
|
';
|
|
border-width: ' .
|
|
$checker["card"]["divider_width"] .
|
|
'px;">';
|
|
|
|
$render .= '<div class="dw-checker-form-fields">';
|
|
if (isset($checker["fields"]) && !empty($checker["fields"])) {
|
|
foreach ($checker["fields"] as $key => $field) {
|
|
if ($field["type"] == "text") {
|
|
$render .=
|
|
'<div class="dw-checker-field">
|
|
<label for="' .
|
|
$key .
|
|
'" style="color: ' .
|
|
$checker["field"]["label-color"] .
|
|
";display: " .
|
|
$checker["field"]["label"] .
|
|
';">
|
|
' .
|
|
$field["label"] .
|
|
'
|
|
</label>
|
|
<input name="' .
|
|
$key .
|
|
'" placeholder="' .
|
|
$field["placeholder"] .
|
|
'" class="dw-checker-inputs" data-kolom="' .
|
|
$field["kolom"] .
|
|
'" required/>
|
|
</div>';
|
|
} else {
|
|
$options = "";
|
|
$option_array = [];
|
|
foreach ($data as $all_data) {
|
|
foreach ($all_data as $_key => $_value) {
|
|
if (
|
|
$_key == $field["kolom"] &&
|
|
!in_array($_value, $option_array)
|
|
) {
|
|
$option_array[] = $_value;
|
|
}
|
|
}
|
|
}
|
|
asort($option_array);
|
|
if (!empty($option_array)) {
|
|
foreach ($option_array as $val) {
|
|
$options .=
|
|
'<option value="' .
|
|
$val .
|
|
'">' .
|
|
$val .
|
|
"</option>";
|
|
}
|
|
}
|
|
$render .=
|
|
'<div class="dw-checker-field">
|
|
<label for="' .
|
|
$key .
|
|
'" style="color: ' .
|
|
$checker["field"]["label-color"] .
|
|
";display: " .
|
|
$checker["field"]["label"] .
|
|
';">
|
|
' .
|
|
$field["kolom"] .
|
|
'
|
|
</label>
|
|
<select name="' .
|
|
$key .
|
|
'" placeholder="' .
|
|
$field["placeholder"] .
|
|
'" class="dw-checker-inputs" data-kolom="' .
|
|
$field["kolom"] .
|
|
'" required>
|
|
<option value="" disabled selected>-- ' .
|
|
$field["placeholder"] .
|
|
' --</option>
|
|
' .
|
|
$options .
|
|
'
|
|
</select>
|
|
</div>';
|
|
}
|
|
}
|
|
}
|
|
$render .= "</div>";
|
|
|
|
// CAPTCHA fields
|
|
if (class_exists('CHECKER_CAPTCHA_HELPER')) {
|
|
$render .= CHECKER_CAPTCHA_HELPER::get_captcha_fields($post_id);
|
|
}
|
|
|
|
$render .=
|
|
'<hr class="dw-checker-divider"
|
|
style="border-color: ' .
|
|
$checker["card"]["divider"] .
|
|
';
|
|
border-width: ' .
|
|
$checker["card"]["divider_width"] .
|
|
'px;">';
|
|
|
|
// Add honeypot field if enabled (invisible to users, catches bots)
|
|
if (CHECKER_SECURITY::is_enabled($checker, 'honeypot')) {
|
|
$hp_name = 'hp_' . wp_generate_password(8, false, false);
|
|
$render = str_replace('data-hp-name=""', 'data-hp-name="' . esc_attr($hp_name) . '"', $render);
|
|
$render .= '<div class="dw-checker-hp-field" style="position:absolute;left:-9999px;top:-9999px;opacity:0;height:0;width:0;overflow:hidden;" aria-hidden="true">';
|
|
$render .= '<label for="' . esc_attr($hp_name . '_' . $post_id) . '">' . esc_html__('Leave this field empty', 'sheet-data-checker-pro') . '</label>';
|
|
$render .= '<input type="text" name="' . esc_attr($hp_name) . '" id="' . esc_attr($hp_name . '_' . $post_id) . '" value="" tabindex="-1" autocomplete="off" data-hp-field="1">';
|
|
$render .= '</div>';
|
|
}
|
|
|
|
$render .=
|
|
'<div class="dw-checker-buttons dw-checker-form-button" style="justify-content: ' .
|
|
$checker["search_button"]["position"] .
|
|
'">';
|
|
$render .=
|
|
'<button type="button" data-checker="' .
|
|
$post_id .
|
|
'" class="search-button"
|
|
data-btn-text="' .
|
|
$checker["search_button"]["text"] .
|
|
'"
|
|
style="background-color: ' .
|
|
$checker["search_button"]["bg_color"] .
|
|
';
|
|
color: ' .
|
|
$checker["search_button"]["text_color"] .
|
|
';">
|
|
' .
|
|
$checker["search_button"]["text"] .
|
|
'
|
|
</button>
|
|
</div>';
|
|
|
|
$render .= "</form>";
|
|
|
|
$render .=
|
|
'<div class="dw-checker-wrapper dw-checker-result"
|
|
style="display:none; max-width: 100%;
|
|
background-color: ' .
|
|
$checker["card"]["background"] .
|
|
';
|
|
width: ' .
|
|
$checker["card"]["width"] .
|
|
'px;
|
|
padding: ' .
|
|
$checker["card"]["padding"] .
|
|
'em;
|
|
border-radius: ' .
|
|
$checker["card"]["border_radius"] .
|
|
'em;
|
|
box-shadow: ' .
|
|
$checker["card"]["box_shadow"] .
|
|
" " .
|
|
$checker["card"]["box_shadow_color"] .
|
|
';
|
|
">';
|
|
$render .=
|
|
'<div class="dw-checker-title"
|
|
style="color: ' .
|
|
$checker["card"]["title"] .
|
|
';
|
|
text-align: ' .
|
|
$checker["card"]["title_align"] .
|
|
';"
|
|
></div>';
|
|
$render .=
|
|
'<div class="dw-checker-description"
|
|
style="color: ' .
|
|
$checker["card"]["description"] .
|
|
';
|
|
text-align: ' .
|
|
$checker["card"]["description_align"] .
|
|
';"
|
|
></div>';
|
|
|
|
$render .=
|
|
'<hr class="dw-checker-divider"
|
|
style="border-color: ' .
|
|
$checker["card"]["divider"] .
|
|
';
|
|
border-width: ' .
|
|
$checker["card"]["divider_width"] .
|
|
'px;">';
|
|
|
|
$render .= '<div class="dw-checker-results"></div>';
|
|
|
|
$render .=
|
|
'<hr class="dw-checker-divider"
|
|
style="border-color: ' .
|
|
$checker["card"]["divider"] .
|
|
';
|
|
border-width: ' .
|
|
$checker["card"]["divider_width"] .
|
|
'px;">';
|
|
|
|
$render .=
|
|
'<div class="dw-checker-buttons dw-checker-result-button" style="justify-content: ' .
|
|
$checker["back_button"]["position"] .
|
|
'">';
|
|
$render .=
|
|
'<button type="button" class="back-button" data-checker=' .
|
|
$post_id .
|
|
'
|
|
style="background-color: ' .
|
|
$checker["back_button"]["bg_color"] .
|
|
';
|
|
color: ' .
|
|
$checker["back_button"]["text_color"] .
|
|
';">
|
|
' .
|
|
$checker["back_button"]["text"] .
|
|
'
|
|
</button>';
|
|
$render .= "</div>";
|
|
|
|
$render .= "</div>";
|
|
$render .= "</div>";
|
|
$render .= '<div class="dw-checker-bottom-results"></div>';
|
|
|
|
// Pass settings to frontend as data attributes
|
|
$render .=
|
|
'<script type="application/json" id="checker-settings-' .
|
|
$post_id .
|
|
'" class="checker-settings-data">';
|
|
$render .= json_encode([
|
|
"checker_id" => $post_id,
|
|
"initial_display" =>
|
|
$checker["result"]["initial_display"] ?? "hidden",
|
|
"filter_mode" => $checker["result"]["filter_mode"] ?? "search",
|
|
"max_records" => $checker["result"]["max_records"] ?? 100,
|
|
"url_params_enabled" => $checker["url_params"]["enabled"] ?? "no",
|
|
"url_params_auto_search" =>
|
|
$checker["url_params"]["auto_search"] ?? "no",
|
|
]);
|
|
$render .= "</script>";
|
|
|
|
return $render;
|
|
}
|
|
|
|
/**
|
|
* Fetch remote CSV/TSV data using WordPress HTTP API
|
|
* Replaces fopen() for better server compatibility
|
|
*/
|
|
private function fetch_remote_csv_data($url, $delimiter, $limit = null, $force_refresh = false)
|
|
{
|
|
$data = [];
|
|
|
|
// Build cache key
|
|
$cache_key = 'checker_csv_' . md5($url . $delimiter . $limit);
|
|
|
|
// Check cache first (unless force refresh)
|
|
if (!$force_refresh && !is_admin()) {
|
|
$cached_data = get_transient($cache_key);
|
|
if ($cached_data !== false) {
|
|
return $cached_data;
|
|
}
|
|
}
|
|
|
|
// Use WordPress HTTP API to fetch remote file
|
|
$response = wp_remote_get($url, [
|
|
'timeout' => 30,
|
|
'sslverify' => false // For local development
|
|
]);
|
|
|
|
if (is_wp_error($response)) {
|
|
error_log(
|
|
"Failed to fetch remote file: " .
|
|
$response->get_error_message(),
|
|
);
|
|
return $data;
|
|
}
|
|
|
|
$body = wp_remote_retrieve_body($response);
|
|
if (empty($body)) {
|
|
error_log("Empty response from remote file: " . $url);
|
|
return $data;
|
|
}
|
|
|
|
// Parse CSV/TSV data
|
|
$lines = explode("\n", $body);
|
|
if (empty($lines)) {
|
|
return $data;
|
|
}
|
|
|
|
// Get headers from first line
|
|
$keys = str_getcsv($lines[0], $delimiter);
|
|
|
|
// Process data rows
|
|
$count = 0;
|
|
for ($i = 1; $i < count($lines); $i++) {
|
|
if (empty(trim($lines[$i]))) {
|
|
continue; // Skip empty lines
|
|
}
|
|
|
|
$row = str_getcsv($lines[$i], $delimiter);
|
|
if (count($keys) === count($row)) {
|
|
$data[] = array_combine($keys, $row);
|
|
$count++;
|
|
|
|
// Apply limit if specified
|
|
if ($limit && $count >= $limit) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Cache the data for 5 minutes (unless in admin)
|
|
if (!is_admin()) {
|
|
set_transient($cache_key, $data, 5 * MINUTE_IN_SECONDS);
|
|
}
|
|
|
|
return $data;
|
|
}
|
|
|
|
/**
|
|
* AJAX handler to clear cache for a specific checker (admin only)
|
|
*/
|
|
public function checker_clear_cache()
|
|
{
|
|
check_ajax_referer('checker_ajax_nonce', 'security');
|
|
|
|
if (!current_user_can('edit_posts')) {
|
|
wp_send_json_error(['message' => 'Unauthorized']);
|
|
return;
|
|
}
|
|
|
|
$post_id = isset($_REQUEST['checker_id']) ? intval($_REQUEST['checker_id']) : 0;
|
|
if (!$post_id) {
|
|
wp_send_json_error(['message' => 'Invalid checker ID']);
|
|
return;
|
|
}
|
|
|
|
$checker = get_post_meta($post_id, 'checker', true);
|
|
if (!$checker || !isset($checker['link'])) {
|
|
wp_send_json_error(['message' => 'Checker not found']);
|
|
return;
|
|
}
|
|
|
|
$url = (string)$checker['link'];
|
|
$link_format = $url ? substr($url, -3) : 'csv';
|
|
$delimiter = $link_format == 'tsv' ? "\t" : ",";
|
|
|
|
// Clear all possible cache variations for this checker
|
|
$limits = [null, 10, 100, 500, 1000];
|
|
$cleared = 0;
|
|
foreach ($limits as $limit) {
|
|
$cache_key = 'checker_csv_' . md5($url . $delimiter . $limit);
|
|
if (delete_transient($cache_key)) {
|
|
$cleared++;
|
|
}
|
|
}
|
|
|
|
wp_send_json_success([
|
|
'message' => sprintf(__('Cache cleared successfully (%d entries)', 'sheet-data-checker-pro'), $cleared),
|
|
'cleared' => $cleared
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* AJAX handler to clear cache for frontend users (public)
|
|
*/
|
|
public function checker_clear_cache_public()
|
|
{
|
|
check_ajax_referer('checker_ajax_nonce', 'security');
|
|
|
|
$post_id = isset($_REQUEST['checker_id']) ? intval($_REQUEST['checker_id']) : 0;
|
|
if (!$post_id) {
|
|
wp_send_json_error(['message' => __('Invalid checker ID', 'sheet-data-checker-pro')]);
|
|
return;
|
|
}
|
|
|
|
$checker = get_post_meta($post_id, 'checker', true);
|
|
if (!$checker || !isset($checker['link'])) {
|
|
wp_send_json_error(['message' => __('Checker not found', 'sheet-data-checker-pro')]);
|
|
return;
|
|
}
|
|
|
|
$url = (string)$checker['link'];
|
|
$link_format = $url ? substr($url, -3) : 'csv';
|
|
$delimiter = $link_format == 'tsv' ? "\t" : ",";
|
|
|
|
// Clear all possible cache variations for this checker
|
|
$limits = [null, 10, 100, 500, 1000];
|
|
$cleared = 0;
|
|
foreach ($limits as $limit) {
|
|
$cache_key = 'checker_csv_' . md5($url . $delimiter . $limit);
|
|
if (delete_transient($cache_key)) {
|
|
$cleared++;
|
|
}
|
|
}
|
|
|
|
wp_send_json_success([
|
|
'message' => __('Data refreshed successfully!', 'sheet-data-checker-pro'),
|
|
'cleared' => $cleared
|
|
]);
|
|
}
|
|
|
|
public function checker_public_validation()
|
|
{
|
|
$post_id = isset($_REQUEST["checker_id"]) ? intval($_REQUEST["checker_id"]) : 0;
|
|
|
|
if (!$post_id) {
|
|
wp_send_json_error(["message" => "Invalid checker ID", "type" => "error"]);
|
|
return;
|
|
}
|
|
|
|
$checker = get_post_meta($post_id, "checker", true);
|
|
|
|
// Enforce nonce
|
|
if (!isset($_REQUEST['security']) || !check_ajax_referer('checker_ajax_nonce', 'security', false)) {
|
|
wp_send_json_error([
|
|
"message" => __("Security check failed. Please refresh the page.", "sheet-data-checker-pro"),
|
|
"type" => "nonce_expired",
|
|
]);
|
|
return;
|
|
}
|
|
|
|
// Unified security verification (rate limit, honeypot, reCAPTCHA, Turnstile)
|
|
$security_error = CHECKER_SECURITY::verify_all_security($post_id, $checker, $_REQUEST);
|
|
if ($security_error !== null) {
|
|
wp_send_json_error([
|
|
"message" => $security_error["message"],
|
|
"type" => $security_error["type"],
|
|
]);
|
|
return;
|
|
}
|
|
|
|
$url = isset($checker["link"]) ? (string)$checker["link"] : '';
|
|
|
|
$link_format = $url ? substr($url, -3) : 'csv';
|
|
|
|
// Set the delimiter based on the format
|
|
$delimiter = $link_format == "tsv" ? "\t" : ","; // Use tab for TSV, comma for CSV
|
|
|
|
// Use WordPress HTTP API instead of fopen for better server compatibility
|
|
$data = $this->fetch_remote_csv_data($url, $delimiter);
|
|
|
|
$validator = isset($_REQUEST["validate"]) && is_array($_REQUEST["validate"]) ? $_REQUEST["validate"] : [];
|
|
$validation = [];
|
|
foreach ($validator as $validate) {
|
|
$kolom = isset($validate["kolom"]) ? (string)$validate["kolom"] : '';
|
|
$val = isset($validate["value"]) ? (string)$validate["value"] : '';
|
|
if ($kolom !== '') {
|
|
$validation[$kolom] = $val;
|
|
}
|
|
}
|
|
$validator_count = count($validator);
|
|
$result = [];
|
|
if (!empty($data)) {
|
|
foreach ($data as $row) {
|
|
$valid = [];
|
|
foreach ($row as $header => $value) {
|
|
// Ensure string types to avoid null deprecation warnings
|
|
$header = ($header !== null) ? (string)$header : '';
|
|
$value = ($value !== null) ? (string)$value : '';
|
|
$id = "_" . strtolower(str_replace(" ", "_", $header));
|
|
$include = false;
|
|
if (isset($validation[$header])) {
|
|
$validation_value = isset($validation[$header]) ? (string)$validation[$header] : '';
|
|
if (
|
|
isset($checker["fields"][$id]["match"]) &&
|
|
$checker["fields"][$id]["match"] == "match" &&
|
|
strtolower($value) == strtolower($validation_value)
|
|
) {
|
|
$include = true;
|
|
}
|
|
if (
|
|
isset($checker["fields"][$id]["match"]) &&
|
|
$checker["fields"][$id]["match"] == "contain" &&
|
|
$validation_value !== '' &&
|
|
false !== strpos(strtolower($value), strtolower($validation_value))
|
|
) {
|
|
$include = true;
|
|
}
|
|
if ($include) {
|
|
$valid[$header] = $value;
|
|
}
|
|
}
|
|
}
|
|
if ($validator_count !== count($valid)) {
|
|
continue;
|
|
}
|
|
$result[] = $row;
|
|
}
|
|
}
|
|
|
|
$send = [
|
|
"count" => count($result),
|
|
"rows" => $result,
|
|
"settings" => $checker["result"],
|
|
"output" => $checker["output"],
|
|
];
|
|
|
|
wp_send_json($send);
|
|
}
|
|
|
|
/**
|
|
* Load all data from sheet (for show all mode)
|
|
*/
|
|
public function checker_load_all_data()
|
|
{
|
|
$post_id = isset($_REQUEST["checker_id"])
|
|
? intval($_REQUEST["checker_id"])
|
|
: 0;
|
|
$limit = isset($_REQUEST["limit"]) ? intval($_REQUEST["limit"]) : 100;
|
|
$is_initial_load = isset($_REQUEST["initial_load"]) && $_REQUEST["initial_load"] === "yes";
|
|
|
|
if (!$post_id) {
|
|
wp_send_json_error(["message" => "Invalid checker ID", "type" => "error"]);
|
|
return;
|
|
}
|
|
|
|
$checker = get_post_meta($post_id, "checker", true);
|
|
|
|
if (!$checker || !isset($checker["link"])) {
|
|
wp_send_json_error(["message" => "Checker not found", "type" => "error"]);
|
|
return;
|
|
}
|
|
|
|
// Enforce nonce
|
|
if (!isset($_REQUEST['security']) || !check_ajax_referer('checker_ajax_nonce', 'security', false)) {
|
|
wp_send_json_error([
|
|
"message" => __("Security check failed. Please refresh the page.", "sheet-data-checker-pro"),
|
|
"type" => "nonce_expired",
|
|
]);
|
|
return;
|
|
}
|
|
|
|
// For initial load in show-all mode, skip CAPTCHA but still check rate limit and honeypot
|
|
// This allows the page to load while CAPTCHA widget renders
|
|
$skip_captcha = $is_initial_load && CHECKER_SECURITY::get_setting($checker, 'result', 'skip_captcha_initial', 'yes') === 'yes';
|
|
|
|
// Unified security verification
|
|
$security_error = CHECKER_SECURITY::verify_all_security($post_id, $checker, $_REQUEST, $skip_captcha);
|
|
if ($security_error !== null) {
|
|
wp_send_json_error([
|
|
"message" => $security_error["message"],
|
|
"type" => $security_error["type"],
|
|
]);
|
|
return;
|
|
}
|
|
|
|
$url = isset($checker["link"]) ? (string)$checker["link"] : '';
|
|
$link_format = $url ? substr($url, -3) : 'csv';
|
|
$delimiter = $link_format == "tsv" ? "\t" : ",";
|
|
|
|
if (!$this->is_allowed_sheet_url($url)) {
|
|
wp_send_json_error([
|
|
"message" => __("Sheet URL is not allowed. Please use an approved domain.", "sheet-data-checker-pro"),
|
|
"type" => "error",
|
|
]);
|
|
return;
|
|
}
|
|
|
|
// Use WordPress HTTP API instead of fopen for better server compatibility
|
|
$data = $this->fetch_remote_csv_data($url, $delimiter, $limit);
|
|
|
|
wp_send_json([
|
|
"count" => count($data),
|
|
"rows" => $data,
|
|
"settings" => $checker["result"],
|
|
"output" => $checker["output"],
|
|
"url_params" => $checker["url_params"] ?? [],
|
|
"filter_mode" => $checker["result"]["filter_mode"] ?? "search",
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* Allowlist check for sheet URL host
|
|
*/
|
|
private function is_allowed_sheet_url($url)
|
|
{
|
|
$host = parse_url($url, PHP_URL_HOST);
|
|
if (!$host) {
|
|
return false;
|
|
}
|
|
|
|
$allowed = apply_filters(
|
|
'sheet_checker_allowed_hosts',
|
|
[
|
|
'docs.google.com',
|
|
'drive.google.com',
|
|
'docs.googleusercontent.com',
|
|
wp_parse_url(home_url(), PHP_URL_HOST),
|
|
]
|
|
);
|
|
|
|
$allowed = array_filter(array_unique(array_map('strtolower', $allowed)));
|
|
|
|
return in_array(strtolower($host), $allowed, true);
|
|
}
|
|
}
|