566 lines
19 KiB
PHP
566 lines
19 KiB
PHP
<?php
|
|
|
|
class 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()
|
|
{
|
|
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";
|
|
}
|
|
|
|
// 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()) {
|
|
// Schedule cleanup of old security logs
|
|
add_action("wp", [$this, "schedule_log_cleanup"]);
|
|
|
|
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("add_meta_boxes", [$this, "add_checker_metabox"]);
|
|
add_action("save_post_checker", [$this, "save_checker_metabox"]);
|
|
|
|
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()
|
|
{
|
|
$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",
|
|
];
|
|
|
|
$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()
|
|
{
|
|
$screen = get_current_screen();
|
|
|
|
// Check that we are on the 'Checker' post editor screen
|
|
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( '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?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",
|
|
["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.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?ver=" .
|
|
SHEET_CHECKER_PRO_VERSION,
|
|
);
|
|
}
|
|
|
|
public function filter_cpt_columns($columns)
|
|
{
|
|
// this will add the column to the end of the array
|
|
$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)
|
|
{
|
|
//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;
|
|
|
|
//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(
|
|
"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 save_checker_metabox($post_id)
|
|
{
|
|
// Save metabox data
|
|
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);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 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);
|
|
}
|
|
return $data;
|
|
}
|
|
|
|
// Convert null to empty string
|
|
if ($data === null) {
|
|
return '';
|
|
}
|
|
|
|
return $data;
|
|
}
|
|
|
|
/**
|
|
* 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_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 = [];
|
|
|
|
if (isset($checker['fields']) && count($checker['fields']) > 0) {
|
|
foreach ($checker['fields'] as $key => $field) {
|
|
$response[$key] = $field;
|
|
|
|
$rowHeader = [];
|
|
if (is_array($headers)) {
|
|
foreach($headers as $index => $header){
|
|
$id = '_'.strtolower($header);
|
|
$rowHeader[$index] = $id;
|
|
}
|
|
}
|
|
$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] : ''
|
|
];
|
|
}
|
|
|
|
error_log('[REPEATER] Response keys: ' . print_r(array_keys($response), true));
|
|
wp_send_json_success(['fields' => $response]);
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
$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);
|
|
|
|
// $header = $this->parse_header_kolom($json);
|
|
|
|
if (!empty($headers)) {
|
|
$output_data = [];
|
|
|
|
foreach ($headers as $key) {
|
|
$id = strtolower(str_replace([' ', '.'], '_', $key));
|
|
|
|
$output_data[] = [
|
|
'key' => $key,
|
|
'id' => $id,
|
|
'hide' => isset($checker['output'][$id]['hide']) ? $checker['output'][$id]['hide'] : 'no',
|
|
'type' => isset($checker['output'][$id]['type']) ? $checker['output'][$id]['type'] : 'text',
|
|
'button_text' => isset($checker['output'][$id]['button_text']) ? $checker['output'][$id]['button_text'] : '',
|
|
'prefix' => isset($checker['output'][$id]['prefix']) ? $checker['output'][$id]['prefix'] : '',
|
|
'bg_color' => isset($checker['output'][$id]['bg_color']) ? $checker['output'][$id]['bg_color'] : '#cccccc',
|
|
'text_color' => isset($checker['output'][$id]['text_color']) ? $checker['output'][$id]['text_color'] : '#000000',
|
|
'display' => isset($checker['result']['display']) && $checker['result']['display'] == 'card'
|
|
];
|
|
}
|
|
|
|
wp_send_json_success(['data' => $output_data]);
|
|
} else {
|
|
wp_send_json_error("No headers found");
|
|
}
|
|
}
|
|
|
|
public function parse_header_kolom($json)
|
|
{
|
|
if (!is_array($json)) {
|
|
$json = json_decode($json, true);
|
|
}
|
|
$header = array_keys($json[0]);
|
|
return $header;
|
|
}
|
|
|
|
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)) {
|
|
$options[] = $val;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return $options;
|
|
}
|
|
|
|
/**
|
|
* 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");
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 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
|
|
}
|
|
}
|
|
}
|