clean build for version 1.4.5 with fixes of security funtionalities, logic branches, etc. Already tested and working fine

This commit is contained in:
dwindown
2026-01-07 15:10:47 +07:00
parent 31b3398c2f
commit 0ba62b435a
26 changed files with 8962 additions and 2804 deletions

View File

@@ -1,437 +1,439 @@
<?php
class SHEET_DATA_CHECKER_PRO {
class SHEET_DATA_CHECKER_PRO
{
/**
* A reference to an instance of this class.
*/
private static $instance;
/**
* 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;
}
/**
* Returns an instance of this class.
*/
public static function get_instance() {
/**
* Initializes the plugin by setting filters and administration functions.
*/
public function __construct()
{
add_action("init", [$this, "create_custom_post_type"]);
add_action("admin_enqueue_scripts", [$this, "enqueue_bootstrap_admin"]);
return self::$instance;
}
/**
* Initializes the plugin by setting filters and administration functions.
*/
public function __construct() {
add_action( 'init', [$this, 'create_custom_post_type'] );
add_action( 'admin_enqueue_scripts', [$this, 'enqueue_bootstrap_admin'] );
if (!class_exists('CHECKER_LICENSE')) {
include SHEET_CHECKER_PRO_PATH . 'includes/class-License.php';
if (!class_exists("CHECKER_LICENSE")) {
include SHEET_CHECKER_PRO_PATH . "includes/class-License.php";
}
// Load CAPTCHA helper class
if (!class_exists("CHECKER_CAPTCHA_HELPER")) {
include SHEET_CHECKER_PRO_PATH .
"includes/helpers/class-Captcha-Helper.php";
}
// Load security dashboard
if (!class_exists("CHECKER_SECURITY_DASHBOARD")) {
include SHEET_CHECKER_PRO_PATH .
"admin/class-Security-Dashboard.php";
}
// Load security logger
if (!class_exists("CHECKER_SECURITY_LOGGER")) {
include SHEET_CHECKER_PRO_PATH .
"includes/logs/class-Security-Logger.php";
}
$lis = new CHECKER_LICENSE();
if(true == $lis->the_lis()){
add_filter( 'manage_checker_posts_columns', [$this, 'filter_cpt_columns']);
add_action( 'manage_checker_posts_custom_column', [$this, 'action_custom_columns_content'], 10, 2 );
if (true == $lis->the_lis()) {
// Schedule cleanup of old security logs
add_action("wp", [$this, "schedule_log_cleanup"]);
add_action( 'add_meta_boxes', [$this, 'add_checker_metabox'] );
add_action( 'save_post_checker', [$this, 'save_checker_metabox'] );
add_filter("manage_checker_posts_columns", [
$this,
"filter_cpt_columns",
]);
add_action(
"manage_checker_posts_custom_column",
[$this, "action_custom_columns_content"],
10,
2,
);
add_action( 'wp_ajax_load_repeater_field_card', [$this, 'load_repeater_field_card'] );
add_action( 'wp_ajax_load_output_setting', [$this, 'load_output_setting'] );
add_action("add_meta_boxes", [$this, "add_checker_metabox"]);
add_action("save_post_checker", [$this, "save_checker_metabox"]);
if (!class_exists('CHECKER_SHORTCODE')) {
require 'class-Shortcode.php';
add_action("wp_ajax_load_repeater_field_card", [
$this,
"load_repeater_field_card",
]);
add_action("wp_ajax_load_output_setting", [
$this,
"load_output_setting",
]);
if (!class_exists("CHECKER_SHORTCODE")) {
require "class-Shortcode.php";
}
new CHECKER_SHORTCODE();
}
add_action("checker_security_log_cleanup", [
$this,
"cleanup_security_logs",
]);
}
public function create_custom_post_type() {
public function create_custom_post_type()
{
$labels = [
"name" => "Checker",
"singular_name" => "Checker",
"menu_name" => "Checkers",
"add_new" => "Add New",
"add_new_item" => "Add New Checker",
"edit" => "Edit",
"edit_item" => "Edit Checker",
"new_item" => "New Checker",
"view" => "View",
"view_item" => "View Checker",
"search_items" => "Search Checkers",
"not_found" => "No checkers found",
"not_found_in_trash" => "No checkers found in trash",
"parent" => "Parent Checker",
];
$labels = array(
'name' => 'Checker',
'singular_name' => 'Checker',
'menu_name' => 'Checkers',
'add_new' => 'Add New',
'add_new_item' => 'Add New Checker',
'edit' => 'Edit',
'edit_item' => 'Edit Checker',
'new_item' => 'New Checker',
'view' => 'View',
'view_item' => 'View Checker',
'search_items' => 'Search Checkers',
'not_found' => 'No checkers found',
'not_found_in_trash' => 'No checkers found in trash',
'parent' => 'Parent Checker'
);
$args = array(
'label' => 'Checkers',
'description' => 'Checkers for your sheet data',
'labels' => $labels,
'public' => false,
'menu_position' => 4,
'menu_icon' => SHEET_CHECKER_PRO_URL .'assets/icons8-validation-menu-icon.png',
'supports' => array( 'title' ),
'hierarchical' => true,
'taxonomies' => array( 'category' ),
'has_archive' => false,
'rewrite' => array( 'slug' => 'checkers' ),
'show_ui' => true,
'show_in_menu' => true,
'show_in_rest' => false,
'query_var' => true,
);
register_post_type( 'checker', $args );
$args = [
"label" => "Checkers",
"description" => "Checkers for your sheet data",
"labels" => $labels,
"public" => false,
"menu_position" => 4,
"menu_icon" =>
SHEET_CHECKER_PRO_URL .
"assets/icons8-validation-menu-icon.png",
"supports" => ["title"],
"hierarchical" => true,
"taxonomies" => ["category"],
"has_archive" => false,
"rewrite" => ["slug" => "checkers"],
"show_ui" => true,
"show_in_menu" => true,
"show_in_rest" => false,
"query_var" => true,
];
register_post_type("checker", $args);
}
public function enqueue_bootstrap_admin() {
public function enqueue_bootstrap_admin()
{
$screen = get_current_screen();
// Check that we are on the 'Checker' post editor screen
if ( $screen && $screen->id === 'checker' ) {
if ($screen && $screen->id === "checker") {
// Enqueue Bootstrap CSS
wp_enqueue_style( 'bootstrap', 'https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css' );
wp_enqueue_style(
"bootstrap",
"https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css",
);
// wp_enqueue_style( 'bs-table', 'https://unpkg.com/bootstrap-table@1.22.1/dist/bootstrap-table.min.css' );
wp_enqueue_style( 'bs-icon', 'https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.1/font/bootstrap-icons.css' );
wp_enqueue_style( 'checker-editor', SHEET_CHECKER_PRO_URL . 'assets/admin-editor.css' );
wp_enqueue_style( 'datatables', 'https://cdn.datatables.net/2.2.2/css/dataTables.dataTables.css' );
wp_enqueue_style(
"bs-icon",
"https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.1/font/bootstrap-icons.css",
);
wp_enqueue_style(
"checker-editor",
SHEET_CHECKER_PRO_URL .
"assets/admin-editor.css?ver=" .
SHEET_CHECKER_PRO_VERSION,
);
wp_enqueue_style(
"datatables",
"https://cdn.datatables.net/2.2.2/css/dataTables.dataTables.css",
);
// Enqueue Bootstrap JS
wp_enqueue_script( 'bootstrap', 'https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js', array( 'jquery' ), '4.5.2', true );
wp_enqueue_script( 'handlebarjs', 'https://cdnjs.cloudflare.com/ajax/libs/handlebars.js/4.7.8/handlebars.min.js', ['jquery'], '4.7.8', true );
wp_enqueue_script(
"bootstrap",
"https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js",
["jquery"],
"4.5.2",
true,
);
wp_enqueue_script(
"handlebarjs",
"https://cdnjs.cloudflare.com/ajax/libs/handlebars.js/4.7.8/handlebars.min.js",
["jquery"],
"4.7.8",
true,
);
// wp_enqueue_script( 'bs-table', 'https://unpkg.com/bootstrap-table@1.22.1/dist/bootstrap-table.min.js', ['jquery'], '1.22.1', true );
wp_enqueue_script( 'checker-editor', SHEET_CHECKER_PRO_URL . 'assets/admin-editor-interactions.js', ['jquery', 'handlebarjs'], true );
wp_enqueue_script( 'datatables', 'https://cdn.datatables.net/2.2.2/js/dataTables.js', ['jquery'], true );
wp_enqueue_script( 'datatables', 'https://cdn.datatables.net/responsive/3.0.4/js/dataTables.responsive.js', ['jquery'], true );
wp_enqueue_script( 'datatables', 'https://cdn.datatables.net/responsive/3.0.4/js/responsive.dataTables.js', ['jquery'], true );
wp_enqueue_script(
"checker-editor",
SHEET_CHECKER_PRO_URL . "assets/admin-editor.js",
["jquery", "handlebarjs"],
SHEET_CHECKER_PRO_VERSION,
true
);
// Pass nonce to admin JavaScript - MUST be after enqueue but before interactions script
wp_localize_script("checker-editor", "checkerAdminSecurity", [
"nonce" => wp_create_nonce("checker_admin_ajax_nonce"),
"ajaxurl" => admin_url("admin-ajax.php"),
]);
wp_enqueue_script(
"checker-editor-interactions",
SHEET_CHECKER_PRO_URL . "assets/admin-editor-interactions.js",
["jquery", "handlebarjs", "checker-editor"],
SHEET_CHECKER_PRO_VERSION,
true
);
wp_enqueue_script(
"datatables",
"https://cdn.datatables.net/2.2.2/js/dataTables.js",
["jquery"],
true,
);
wp_enqueue_script(
"datatables",
"https://cdn.datatables.net/responsive/3.0.4/js/dataTables.responsive.js",
["jquery"],
true,
);
wp_enqueue_script(
"datatables",
"https://cdn.datatables.net/responsive/3.0.4/js/responsive.dataTables.js",
["jquery"],
true,
);
}
wp_enqueue_style( 'checker-editor', SHEET_CHECKER_PRO_URL . 'assets/admin.css' );
wp_enqueue_style(
"checker-editor",
SHEET_CHECKER_PRO_URL .
"assets/admin.css?ver=" .
SHEET_CHECKER_PRO_VERSION,
);
}
public function filter_cpt_columns( $columns ) {
public function filter_cpt_columns($columns)
{
// this will add the column to the end of the array
$columns['shortcode'] = 'Shortcode';
$columns["shortcode"] = "Shortcode";
//add more columns as needed
// as with all filters, we need to return the passed content/variable
return $columns;
}
public function action_custom_columns_content ( $column_id, $post_id ) {
public function action_custom_columns_content($column_id, $post_id)
{
//run a switch statement for all of the custom columns created
switch( $column_id ) {
case 'shortcode':
echo '<input class="dw-checker-post-table-input" value=\'[checker id="'.$post_id.'"]\' />';
break;
switch ($column_id) {
case "shortcode":
echo '<input class="dw-checker-post-table-input" value=\'[checker id="' .
$post_id .
'"]\' />';
break;
//add more items here as needed, just make sure to use the column_id in the filter for each new item.
}
}
public function add_checker_metabox() {
add_meta_box(
'checker_preview',
'Preview',
[$this, 'preview_checker_metabox'],
'checker',
'normal',
'default'
);
add_meta_box(
'checker_options',
'Options',
[$this, 'render_checker_metabox'],
'checker',
'normal',
'default'
);
if(isset($_GET['post']) && isset($_GET['action'])){
add_meta_box(
'checker_shortcode',
'Shortcode',
[$this, 'shortcode_checker_metabox'],
'checker',
'side',
'high'
);
}
}
public function shortcode_checker_metabox() {
?>
<div class="mb-2">Use shortcode below:</div>
<input value='[checker id="<?=$_GET['post']?>"]' class="form-control border-dark" readonly>
<?php
public function add_checker_metabox()
{
add_meta_box(
"dw_checker_preview",
"Preview",
[$this, "preview_checker_metabox"],
"checker",
"normal",
"high",
);
add_meta_box(
"dw_checker_setting",
"Settings",
[$this, "render_checker_metabox"],
"checker",
"normal",
"default",
);
}
public function preview_checker_metabox($post) {
$checker = get_post_meta( $post->ID, 'checker', true );
$checker = wp_parse_args( $checker, [
'link' => '',
'description' => '',
'card' => [
'width' => 500,
'background' => '#cccccc',
'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' => 'tabel',
'header' => '#333333',
'value' => '#333333',
'columns' => [],
'border_width' => 1
]
] );
require_once SHEET_CHECKER_PRO_PATH . 'templates/editor/preview.php';
}
public function render_checker_metabox( $post ) {
// Retrieve existing values from the database
$checker = get_post_meta( $post->ID, 'checker', true );
$checker = wp_parse_args( $checker, [
'link' => '',
'description' => '',
'card' => [
'width' => 500,
'background' => '#cccccc',
'bg_opacity' => 100,
'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' => 'table',
'header' => '#333333',
'value' => '#333333',
'divider' => '#333333',
'divider_width' => 1
]
] );
require_once SHEET_CHECKER_PRO_PATH . 'templates/editor/settings.php';
}
public function save_checker_metabox( $post_id ) {
public function save_checker_metabox($post_id)
{
// Save metabox data
if ( isset( $_POST['checker'] ) ) {
error_log(print_r($_POST['checker'], true));
update_post_meta( $post_id, 'checker', $_POST['checker'] );
if (isset($_POST["checker"])) {
$checker = $_POST["checker"];
// Sanitize all values to prevent null deprecation warnings
$checker = $this->sanitize_array_recursive($checker);
update_post_meta($post_id, "checker", $checker);
}
}
public function load_repeater_field_card_depracated() {
$post_id = $_REQUEST['pid'];
$checker = get_post_meta( $post_id, 'checker', true );
$json = json_decode(stripslashes($_REQUEST['json']), true);
if(isset($checker['fields']) && count($checker['fields']) > 0){
foreach($checker['fields'] as $key => $field){
?>
<div class="card shadow repeater-card gap-2 position-relative">
<div class="card-body">
<div class="row mb-2">
<div class="col-3"><label class="form-label fw-bold mb-0">Field ID</label></div>
<div class="col-9">
<input class="form-control field-id" value="<?= $key ?>" />
</div>
</div>
<div class="row mb-2">
<div class="col-3"><label class="form-label fw-bold mb-0">Column</label></div>
<div class="col-9">
<select name="checker[fields][<?= $key ?>][kolom]" class="form-select border select-kolom">
<?php
if($json){
$header = $this->parse_header_kolom($json);
if(!empty($header)){
foreach($header as $name){
if( $field['kolom'] == $name ){
echo '<option value="'.$name.'" selected>'.$name.'</option>';
}else{
echo '<option value="'.$name.'">'.$name.'</option>';
}
}
}
}
?>
</select>
</div>
</div>
<div class="row mb-2">
<div class="col-3"><label class="form-label fw-bold mb-0">Type</label></div>
<div class="col-9">
<select name="checker[fields][<?= $key ?>][type]" class="form-select border select-field-type">
<option value="text" <?= ($field['type'] == 'text') ? 'selected' : '' ?>>Text</option>
<option value="select" <?= ($field['type'] == 'select') ? 'selected' : '' ?>>Select</option>
</select>
</div>
</div>
<div class="row mb-2">
<div class="col-3"><label class="form-label fw-bold mb-0">Label</label></div>
<div class="col-9">
<input name="checker[fields][<?= $key ?>][label]" class="form-control field-label" value="<?= $field['label'] ?? '' ?>" />
</div>
</div>
<div class="row mb-2">
<div class="col-3"><label class="form-label fw-bold mb-0">Placeholder</label></div>
<div class="col-9">
<input name="checker[fields][<?= $key ?>][placeholder]" class="form-control field-placeholder" value="<?= $field['placeholder'] ?? '' ?>" />
</div>
</div>
<div class="row mb-2">
<div class="col-3"><label class="form-label fw-bold mb-0">Value Matcher</label></div>
<div class="col-9">
<select name="checker[fields][<?= $key ?>][match]" class="form-select border select-match-type">
<option value="match" <?= ($field['match'] == 'match') ? 'selected' : '' ?>>Match</option>
<option value="contain" <?= ($field['match'] == 'contain') ? 'selected' : '' ?>>Contain</option>
</select>
</div>
</div>
<div class="card-buttons d-flex gap-2 flex-column position-absolute">
<button type="button" class="btn btn-primary py-1 px-2 add-form-card"><i class="bi bi-plus"></i></button>
<button type="button" class="btn btn-danger py-1 px-2 delete-form-card"><i class="bi bi-dash"></i></button>
</div>
</div>
</div>
<?php
/**
* Recursively sanitize array values to prevent null deprecation warnings
* Converts null values to empty strings
*
* @param mixed $data Data to sanitize
* @return mixed Sanitized data
*/
private function sanitize_array_recursive($data)
{
if (is_array($data)) {
foreach ($data as $key => $value) {
$data[$key] = $this->sanitize_array_recursive($value);
}
}else{
?>
<div class="card shadow repeater-card gap-2 position-relative">
<div class="card-body">
<div class="row mb-2">
<div class="col-3"><label class="form-label fw-bold mb-0">Field ID</label></div>
<div class="col-9">
<input class="form-control field-id" value="" />
</div>
</div>
<div class="row mb-2">
<div class="col-3"><label class="form-label fw-bold mb-0">Column</label></div>
<div class="col-9">
<select name="" class="form-select border select-kolom">
<?php
if($json){
$header = $this->parse_header_kolom($json);
if(!empty($header)){
foreach($header as $key => $name){
if( $key == 0 ){
echo '<option value="'.$name.'" selected>'.$name.'</option>';
}else{
echo '<option value="'.$name.'">'.$name.'</option>';
}
}
}
}
?>
</select>
</div>
</div>
<div class="row mb-2">
<div class="col-3"><label class="form-label fw-bold mb-0">Type</label></div>
<div class="col-9">
<select name="" class="form-select border select-field-type">
<option value="text" selected>Text</option>
<option value="select">Select</option>
</select>
</div>
</div>
<div class="row mb-2">
<div class="col-3"><label class="form-label fw-bold mb-0">Label</label></div>
<div class="col-9">
<input name="" class="form-control field-label" value="" />
</div>
</div>
<div class="row mb-2">
<div class="col-3"><label class="form-label fw-bold mb-0">Placeholder</label></div>
<div class="col-9">
<input name="" class="form-control field-placeholder" value="" />
</div>
</div>
<div class="row mb-2">
<div class="col-3"><label class="form-label fw-bold mb-0">Value Matcher</label></div>
<div class="col-9">
<select name="" class="form-select border select-match-type">
<option value="match" selected>Match</option>
<option value="contain">Contain</option>
</select>
</div>
</div>
<div class="card-buttons d-flex gap-2 flex-column position-absolute">
<button type="button" class="btn btn-primary py-1 px-2 add-form-card"><i class="bi bi-plus"></i></button>
<button type="button" class="btn btn-danger py-1 px-2 delete-form-card"><i class="bi bi-dash"></i></button>
</div>
</div>
</div>
<?php
return $data;
}
exit();
// Convert null to empty string
if ($data === null) {
return '';
}
return $data;
}
public function load_repeater_field_card() {
$post_id = $_REQUEST['pid'];
/**
* Recursively merge two arrays, with the second array's values taking precedence
* Unlike array_merge_recursive, this doesn't create arrays for scalar values
*
* @param array $defaults Default values
* @param array $args Values to merge
* @return array Merged array
*/
private function array_merge_recursive_distinct(array $defaults, array $args)
{
$merged = $defaults;
foreach ($args as $key => $value) {
if (is_array($value) && isset($merged[$key]) && is_array($merged[$key])) {
$merged[$key] = $this->array_merge_recursive_distinct($merged[$key], $value);
} else {
$merged[$key] = $value;
}
}
return $merged;
}
public function render_checker_metabox($post)
{
// Retrieve existing values from the database
$checker = get_post_meta($post->ID, "checker", true);
$post_id = $post->ID;
// Define default values - include ALL keys that templates access
$defaults = [
"link" => "",
"description" => "",
"card" => [
"width" => 500,
"background" => "#ffffff",
"title" => "#333333",
"description" => "#666666",
"divider" => "#cccccc",
"divider_width" => 1,
"title_align" => "center",
"description_align" => "center",
],
"field" => [
"label" => "block",
"label-color" => "#333333",
],
"fields" => [],
"search_button" => [
"position" => "flex-start",
"text" => "Search",
"bg_color" => "#333333",
"text_color" => "#ffffff",
],
"back_button" => [
"position" => "flex-start",
"text" => "Back",
"bg_color" => "#333333",
"text_color" => "#ffffff",
],
"result" => [
"initial_display" => "hidden",
"filter_mode" => "search",
"max_records" => 100,
"display" => "vertical-table",
"header" => "#333333",
"value" => "#666666",
"divider" => "#cccccc",
"divider_width" => 1,
"card_grid" => [
"desktop" => 4,
"tablet" => 2,
"mobile" => 1,
],
],
"url_params" => [
"enabled" => "no",
],
"output" => [],
];
// Parse and merge with defaults (deep merge for nested arrays)
$checker = is_array($checker) ? $this->array_merge_recursive_distinct($defaults, $checker) : $defaults;
require_once SHEET_CHECKER_PRO_PATH . "templates/editor/settings.php";
}
public function preview_checker_metabox($post)
{
// Retrieve existing values from the database
$checker = get_post_meta($post->ID, "checker", true);
// Define default values
$defaults = [
"link" => "",
"description" => "",
"card" => [
"width" => 500,
"background" => "#ffffff",
"title" => "#333333",
"description" => "#666666",
"divider" => "#cccccc",
"divider_width" => 1,
],
];
// Parse and merge with defaults
$checker = wp_parse_args($checker, $defaults);
require_once SHEET_CHECKER_PRO_PATH . "templates/editor/preview.php";
}
public function load_repeater_field_card()
{
$nonce_ok = check_ajax_referer('checker_admin_ajax_nonce', 'security', false);
if (false === $nonce_ok && !current_user_can('edit_posts')) {
wp_send_json_error('invalid_nonce', 403);
}
$post_id = isset($_REQUEST['pid']) ? absint($_REQUEST['pid']) : 0;
// Require capability for existing posts; for new posts rely on logged-in nonce
if ($post_id && !current_user_can('edit_posts')) {
wp_send_json_error('Unauthorized request', 403);
}
if (!$post_id && !is_user_logged_in()) {
wp_send_json_error('Unauthorized request', 403);
}
$checker = get_post_meta($post_id, 'checker', true);
$headers = $_REQUEST['headers'];
$headers_raw = isset($_REQUEST['headers']) ? (array) $_REQUEST['headers'] : [];
$headers = array_map('sanitize_text_field', $headers_raw);
error_log('[REPEATER] Post ID: ' . $post_id);
error_log('[REPEATER] Has fields: ' . (isset($checker['fields']) && count($checker['fields']) > 0 ? 'YES' : 'NO'));
error_log('[REPEATER] Headers count: ' . (is_array($headers) ? count($headers) : '0'));
$response = [];
@@ -440,52 +442,53 @@ class SHEET_DATA_CHECKER_PRO {
$response[$key] = $field;
$rowHeader = [];
foreach($headers as $index => $header){
$id = '_'.strtolower($header);
$rowHeader[$index] = $id;
if (is_array($headers)) {
foreach($headers as $index => $header){
$id = '_'.strtolower($header);
$rowHeader[$index] = $id;
}
}
$response[$key]['selected_kolom'] = $response[$key]['kolom'];
$response[$key]['selected_kolom'] = isset($response[$key]['kolom']) ? $response[$key]['kolom'] : '';
$response[$key]['kolom'] = $headers;
}
} else {
// No saved fields - create one default field
error_log('[REPEATER] Creating default field');
$response['field_1'] = [
'type' => 'text',
'label' => '',
'placeholder' => '',
'match' => 'match',
'kolom' => $headers,
'selected_kolom' => is_array($headers) && count($headers) > 0 ? $headers[0] : ''
];
}
wp_send_json($response);
exit();
error_log('[REPEATER] Response keys: ' . print_r(array_keys($response), true));
wp_send_json_success(['fields' => $response]);
}
public function load_column_checkbox() {
$post_id = $_REQUEST['pid'];
$checker = get_post_meta( $post_id, 'checker', true );
$json = json_decode(stripslashes($_REQUEST['json']), true);
$header = $this->parse_header_kolom($json);
if(count($header) > 0){
foreach($header as $key){
$checked = '';
if(isset($checker['result']['columns']) && in_array($key, $checker['result']['columns'])){
$checked = ' checked';
}
?>
<div class="form-check">
<input class="form-check-input" type="checkbox" value="<?= $key ?>" id="checker-item-<?= strtolower(str_replace(' ', '_', $key)) ?>" name="checker[result][columns][]"<?=$checked?>>
<label class="form-check-label" for="checker-item-<?= strtolower(str_replace(' ', '_', $key)) ?>">
<?= $key ?>
</label>
</div>
<?php
}
public function load_output_setting()
{
$nonce_ok = check_ajax_referer('checker_admin_ajax_nonce', 'security', false);
if (false === $nonce_ok && !current_user_can('edit_posts')) {
wp_send_json_error('invalid_nonce', 403);
}
exit();
}
$post_id = isset($_REQUEST['pid']) ? absint($_REQUEST['pid']) : 0;
// Require capability for existing posts; for new posts rely on logged-in nonce
if ($post_id && !current_user_can('edit_posts')) {
wp_send_json_error('Unauthorized request', 403);
}
if (!$post_id && !is_user_logged_in()) {
wp_send_json_error('Unauthorized request', 403);
}
$checker = $post_id ? get_post_meta($post_id, 'checker', true) : [];
$headers_raw = isset($_REQUEST['headers']) ? (array) $_REQUEST['headers'] : [];
$headers = array_map('sanitize_text_field', $headers_raw);
public function load_output_setting() {
$post_id = $_REQUEST['pid'];
$checker = get_post_meta($post_id, 'checker', true);
$headers = $_REQUEST['headers'];
// $header = $this->parse_header_kolom($json);
if (!empty($headers)) {
@@ -509,41 +512,54 @@ class SHEET_DATA_CHECKER_PRO {
wp_send_json_success(['data' => $output_data]);
} else {
wp_send_json_error('No headers found');
wp_send_json_error("No headers found");
}
exit();
}
public function parse_options($json, $kolom) {
public function parse_header_kolom($json)
{
if (!is_array($json)) {
$json = json_decode($json, true);
}
$header = array_keys($json[0]);
return $header;
}
$json = json_decode($json, true);
public function parse_options($json, $kolom)
{
$options = [];
if($json){
foreach($json as $key => $value){
foreach($value as $name => $val){
if($name == $kolom){
if(!in_array($val, $options)){
if ($json) {
foreach ($json as $key => $value) {
foreach ($value as $name => $val) {
if ($name == $kolom) {
if (!in_array($val, $options)) {
$options[] = $val;
}
}
}
}
}
return $options;
}
public function parse_header_kolom($json) {
$header = [];
if(!is_array($json)){
$json = json_decode($json, true);
/**
* Schedule cleanup of old security logs
*/
public function schedule_log_cleanup()
{
// Schedule cleanup if not already scheduled
if (!wp_next_scheduled("checker_security_log_cleanup")) {
wp_schedule_event(time(), "daily", "checker_security_log_cleanup");
}
$header = array_keys($json[0]);
return $header;
}
}
/**
* Cleanup old security logs
*/
public static function cleanup_security_logs()
{
if (class_exists("CHECKER_SECURITY_LOGGER")) {
CHECKER_SECURITY_LOGGER::cleanup_old_logs(90); // Keep logs for 90 days
}
}
}