feat: add React FieldRenderer system for settings and metaboxes
Complete React-based field rendering system that replaces WPCFTO Vue.js layer while maintaining PHP field configuration compatibility. Components: - FieldRenderer: Main renderer with tabs support (metabox) and direct mode (settings) - FieldTypes: 15+ field types (Text, Number, Select, Radio, Date, etc.) - RepeaterField: Collapsible repeater with currency label parsing - DependencyEngine: Show/hide fields based on conditions - ValidationEngine: Client-side validation with error messages - SettingsRenderer: Settings page with AJAX save to wp_options Features: - Repeater rows collapsed by default with readable currency titles - Searchable dropdowns using Popover + Command pattern - Proper label resolution for pre-selected values - Hidden input sync for WordPress form submission Also includes: - FieldConfigBridge: Transform PHP configs to React format - Updated Settings.php for React-based settings page - Radio-group UI component - wp-admin-restore.css for admin panel isolation
This commit is contained in:
@@ -24,6 +24,11 @@ class Access {
|
||||
add_filter( 'formipay/access-config', [$this, 'source_config'] );
|
||||
add_filter( 'formipay/access-config', [$this, 'details_config'] );
|
||||
|
||||
add_action( 'add_meta_boxes', [$this, 'add_react_metabox'] );
|
||||
add_action( 'admin_footer-post.php', [$this, 'render_react_metabox_template'] );
|
||||
add_action( 'admin_footer-post-new.php', [$this, 'render_react_metabox_template'] );
|
||||
add_action( 'save_post', [$this, 'save_access_metabox_fields'], 10, 2 );
|
||||
|
||||
// Admin Page
|
||||
add_action( 'wp_ajax_formipay_access_items_get_products', [$this, 'formipay_access_items_get_products'] );
|
||||
add_action( 'wp_ajax_formipay-tabledata-access-items', [$this, 'formipay_tabledata_access_items'] );
|
||||
@@ -90,82 +95,77 @@ class Access {
|
||||
|
||||
public function enqueue_admin() {
|
||||
// Assets now handled by ReactAdmin class
|
||||
return;
|
||||
}
|
||||
|
||||
$screen = get_current_screen();
|
||||
public function add_react_metabox() {
|
||||
add_meta_box(
|
||||
'formipay_access_settings',
|
||||
__('Settings', 'formipay'),
|
||||
[$this, 'render_react_metabox'],
|
||||
'formipay-access',
|
||||
'normal',
|
||||
'high'
|
||||
);
|
||||
}
|
||||
|
||||
// Check that we are on the 'Checker' post editor screen
|
||||
if ( $screen->post_type === 'formipay-access' && $screen->base === 'post' ) {
|
||||
|
||||
wp_enqueue_style( 'formipay-admin-pages', FORMIPAY_URL . 'admin/assets/css/admin-pages.css', [], FORMIPAY_VERSION );
|
||||
wp_enqueue_style( 'sweetalert2', FORMIPAY_URL . 'vendor/SweetAlert2/sweetalert2.min.css', [], '11.14.4', 'all');
|
||||
wp_enqueue_script( 'sweetalert2', FORMIPAY_URL . 'vendor/SweetAlert2/sweetalert2.min.js', ['jquery'], '11.14.4', true);
|
||||
public function render_react_metabox($post) {
|
||||
echo '<div data-formipay-field-renderer="access" data-post-id="' . esc_attr($post->ID) . '"></div>';
|
||||
}
|
||||
|
||||
wp_localize_script( 'sweetalert2', 'formipay_admin', [
|
||||
'ajax_url' => admin_url('admin-ajax.php'),
|
||||
'site_url' => site_url(),
|
||||
] );
|
||||
public function render_react_metabox_template() {
|
||||
global $post;
|
||||
|
||||
if (!$post || $post->post_type !== 'formipay-access') {
|
||||
return;
|
||||
}
|
||||
|
||||
if($current_screen->id == 'formipay_page_formipay-access-items') {
|
||||
|
||||
wp_enqueue_style( 'sweetalert2', FORMIPAY_URL . 'vendor/SweetAlert2/sweetalert2.min.css', [], 'all');
|
||||
wp_enqueue_style( 'formipay-admin-pages', FORMIPAY_URL . 'admin/assets/css/admin-pages.css', [], FORMIPAY_VERSION, 'all' );
|
||||
wp_enqueue_style( 'gridjs', FORMIPAY_URL . 'vendor/GridJS/gridjs.mermaid.min.css', [], '6.2.0', 'all' );
|
||||
wp_enqueue_style( 'choices', FORMIPAY_URL . 'vendor/ChoicesJS/choices.min.css', [], FORMIPAY_VERSION, 'all' );
|
||||
wp_enqueue_style( 'formipay-admin-pages', FORMIPAY_URL . 'admin/assets/css/admin-pages.css', [], FORMIPAY_VERSION, 'all' );
|
||||
wp_enqueue_style( 'page-access-items', FORMIPAY_URL . 'admin/assets/css/page-access-items.css', [], FORMIPAY_VERSION, 'all' );
|
||||
$config = \Formipay\Admin\FieldConfigBridge::get_config_for_post($post->ID, $post->post_type);
|
||||
?>
|
||||
<script type="text/javascript">
|
||||
window.formipayFieldConfig = <?php echo wp_json_encode($config); ?>;
|
||||
</script>
|
||||
<?php
|
||||
}
|
||||
|
||||
wp_enqueue_script( 'sweetalert2', FORMIPAY_URL . 'vendor/SweetAlert2/sweetalert2.min.js', ['jquery'], '11.14.5', true);
|
||||
wp_enqueue_script( 'formipay-admin-pages', FORMIPAY_URL . 'admin/assets/js/admin-pages.js', ['jquery'], FORMIPAY_VERSION, true );
|
||||
wp_enqueue_script( 'gridjs', FORMIPAY_URL . 'vendor/GridJS/gridjs.production.min.js', ['jquery'], '6.2.0', true );
|
||||
wp_enqueue_script( 'choices', FORMIPAY_URL . 'vendor/ChoicesJS/choices.min.js', [], FORMIPAY_VERSION, true );
|
||||
wp_enqueue_script( 'page-access-items', FORMIPAY_URL . 'admin/assets/js/page-access-items.js', ['jquery', 'gridjs'], FORMIPAY_VERSION, true );
|
||||
|
||||
wp_localize_script( 'page-access-items', 'formipay_access_page', [
|
||||
'ajax_url' => admin_url('admin-ajax.php'),
|
||||
'site_url' => site_url(),
|
||||
'columns' => [
|
||||
'id' => esc_html__( 'ID', 'formipay' ),
|
||||
'title' => esc_html__( 'Title', 'formipay' ),
|
||||
'type' => esc_html__( 'Type', 'formipay' ),
|
||||
'products' => esc_html__( 'Product Relation', 'formipay' ),
|
||||
'status' => esc_html__( 'Status', 'formipay' ),
|
||||
],
|
||||
'filter_form' => [
|
||||
'products' => [
|
||||
'placeholder' => esc_html__( 'Filter by Product', 'formipay' ),
|
||||
'noresult_text' => esc_html__( 'No results found', 'formipay' )
|
||||
]
|
||||
],
|
||||
'modal' => [
|
||||
'add' => [
|
||||
'title' => esc_html__( 'Your New Item Title', 'formipay' ),
|
||||
'validation' => esc_html__( 'Item\'s title is still empty. Please input the title before continue', 'formipay' ),
|
||||
'cancelButton' => esc_html__( 'Cancel', 'formipay' ),
|
||||
'confirmButton' => esc_html__( 'Create New Item', 'formipay' )
|
||||
],
|
||||
'delete' => [
|
||||
'question' => esc_html__( 'Do you want to delete the item?', 'formipay' ),
|
||||
'cancelButton' => esc_html__( 'Cancel', 'formipay' ),
|
||||
'confirmButton' => esc_html__( 'Delete Permanently', 'formipay' )
|
||||
],
|
||||
'bulk_delete' => [
|
||||
'question' => esc_html__( 'Do you want to delete the selected the form(s)?', 'formipay' ),
|
||||
'cancelButton' => esc_html__( 'Cancel', 'formipay' ),
|
||||
'confirmButton' => esc_html__( 'Confirm', 'formipay' )
|
||||
],
|
||||
'duplicate' => [
|
||||
'question' => esc_html__( 'Do you want to duplicate the item?', 'formipay' ),
|
||||
'cancelButton' => esc_html__( 'Cancel', 'formipay' ),
|
||||
'confirmButton' => esc_html__( 'Confirm', 'formipay' )
|
||||
],
|
||||
],
|
||||
'nonce' => wp_create_nonce('formipay-admin-access-nonce')
|
||||
] );
|
||||
public function save_access_metabox_fields($post_id, $post) {
|
||||
if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) {
|
||||
return $post_id;
|
||||
}
|
||||
|
||||
if (!current_user_can('manage_options')) {
|
||||
return $post_id;
|
||||
}
|
||||
|
||||
if (!isset($_POST['_wpnonce']) || !wp_verify_nonce($_POST['_wpnonce'], 'update-post_' . $post_id)) {
|
||||
return $post_id;
|
||||
}
|
||||
|
||||
if ($post->post_type !== 'formipay-access') {
|
||||
return $post_id;
|
||||
}
|
||||
|
||||
$meta_fields = [
|
||||
'access_type',
|
||||
'access_document',
|
||||
'access_redirect_url',
|
||||
'access_download_source',
|
||||
'access_attachment',
|
||||
'access_url',
|
||||
'button_text',
|
||||
'details_icon',
|
||||
'details_filetype',
|
||||
'details_filesize',
|
||||
'details_short_description',
|
||||
];
|
||||
|
||||
foreach ($meta_fields as $field) {
|
||||
if (isset($_POST[$field])) {
|
||||
$value = wp_unslash($_POST[$field]);
|
||||
update_post_meta($post_id, $field, $value);
|
||||
}
|
||||
}
|
||||
|
||||
return $post_id;
|
||||
}
|
||||
|
||||
public function cpt_post_fields_box($boxes) {
|
||||
|
||||
460
includes/Admin/FieldConfigBridge.php
Normal file
460
includes/Admin/FieldConfigBridge.php
Normal file
@@ -0,0 +1,460 @@
|
||||
<?php
|
||||
namespace Formipay\Admin;
|
||||
|
||||
use Formipay\Traits\SingletonTrait;
|
||||
|
||||
if (!defined('ABSPATH')) exit;
|
||||
|
||||
/**
|
||||
* FieldConfigBridge - Transforms WPCFTO field config arrays to React-ready JSON
|
||||
*
|
||||
* Bridges the gap between PHP field configurations and the React FieldRenderer:
|
||||
* - Collects field configs from filter system
|
||||
* - Loads saved post meta / option values
|
||||
* - Transforms PHP config format to React-ready structure
|
||||
* - Handles dynamic currency field expansion
|
||||
* - Handles dependency transformation
|
||||
*/
|
||||
class FieldConfigBridge {
|
||||
|
||||
use SingletonTrait;
|
||||
|
||||
/**
|
||||
* Map CPT types to their filter names
|
||||
*/
|
||||
private static $cpt_filter_map = [
|
||||
'formipay-coupon' => 'formipay/coupon-config',
|
||||
'formipay-product' => 'formipay/product-config',
|
||||
'formipay-form' => 'formipay/form-config',
|
||||
'formipay-access' => 'formipay/access-config',
|
||||
];
|
||||
|
||||
/**
|
||||
* Get complete field configuration for a post with saved values
|
||||
*
|
||||
* @param int $post_id Post ID
|
||||
* @param string $post_type Post type
|
||||
* @return array React-ready field configuration
|
||||
*/
|
||||
public static function get_config_for_post($post_id, $post_type) {
|
||||
$filter_name = self::$cpt_filter_map[$post_type] ?? null;
|
||||
|
||||
if (!$filter_name) {
|
||||
return [];
|
||||
}
|
||||
|
||||
// Get field configuration from filters
|
||||
$fields = apply_filters($filter_name, []);
|
||||
|
||||
// Load saved values from post meta
|
||||
$values = self::load_post_meta_values($post_id, $fields);
|
||||
|
||||
// Transform to React-ready format
|
||||
return self::transform_fields_config($fields, $values, $post_id, $post_type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get configuration for settings page (wp_options based)
|
||||
*
|
||||
* @param string $option_name Option name
|
||||
* @return array React-ready field configuration
|
||||
*/
|
||||
public static function get_config_for_settings($option_name = 'formipay_settings') {
|
||||
// Get settings values
|
||||
$settings = get_option($option_name, []);
|
||||
|
||||
// Get field definitions directly from Settings class
|
||||
$settings_instance = \Formipay\Settings::get_instance();
|
||||
$tabs_config = $settings_instance->get_settings_fields();
|
||||
|
||||
// Transform to React-ready format
|
||||
return self::transform_settings_config($settings, $tabs_config, $option_name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Transform settings config to React-ready format
|
||||
*
|
||||
* @param array $settings Settings values
|
||||
* @param array $tabs_config Tab configuration from Settings class
|
||||
* @param string $option_name Option name
|
||||
* @return array React-ready configuration
|
||||
*/
|
||||
private static function transform_settings_config($settings, $tabs_config, $option_name) {
|
||||
$tabs = [];
|
||||
|
||||
foreach ($tabs_config as $tab_key => $tab_data) {
|
||||
if (!isset($tab_data['name']) || !isset($tab_data['fields'])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$tab = [
|
||||
'id' => self::slugify($tab_data['name']),
|
||||
'label' => $tab_data['name'],
|
||||
'fields' => [],
|
||||
];
|
||||
|
||||
$current_group = null;
|
||||
|
||||
foreach ($tab_data['fields'] as $field_name => $field) {
|
||||
// Handle group_title (section headers)
|
||||
if (isset($field['type']) && $field['type'] === 'group_title') {
|
||||
$group_value = $field['group'] ?? '';
|
||||
|
||||
if ($group_value === 'started') {
|
||||
$current_group = [
|
||||
'type' => 'section',
|
||||
'label' => $field['label'] ?? '',
|
||||
'description' => $field['description'] ?? '',
|
||||
];
|
||||
} elseif ($group_value === 'ended') {
|
||||
$current_group = null;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// Add section if we have one
|
||||
if ($current_group !== null) {
|
||||
$tab['fields'][] = $current_group;
|
||||
$current_group = null;
|
||||
}
|
||||
|
||||
// Transform field configuration
|
||||
$transformed_field = self::transform_settings_field($field_name, $field, $settings);
|
||||
|
||||
// Add to fields
|
||||
$tab['fields'][] = $transformed_field;
|
||||
}
|
||||
|
||||
$tabs[] = $tab;
|
||||
}
|
||||
|
||||
return [
|
||||
'tabs' => $tabs,
|
||||
'optionName' => $option_name,
|
||||
'globalCurrencies' => self::get_global_currencies_data(),
|
||||
'nonce' => wp_create_nonce('formipay-field-config'),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Load saved post meta values for all fields
|
||||
*
|
||||
* @param int $post_id Post ID
|
||||
* @param array $fields Field configuration array
|
||||
* @return array Field values keyed by field name
|
||||
*/
|
||||
private static function load_post_meta_values($post_id, $fields) {
|
||||
$values = [];
|
||||
|
||||
// The config structure is: $fields['formipay_{cpt}_settings'][tab_key] = ['name' => ..., 'fields' => [...]]
|
||||
foreach ($fields as $container_key => $container_data) {
|
||||
if (!is_array($container_data)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach ($container_data as $tab_key => $tab_data) {
|
||||
if (!isset($tab_data['fields']) || !is_array($tab_data['fields'])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach ($tab_data['fields'] as $field_name => $field) {
|
||||
// Skip group_title fields
|
||||
if (isset($field['type']) && $field['type'] === 'group_title') {
|
||||
continue;
|
||||
}
|
||||
|
||||
$meta_value = get_post_meta($post_id, $field_name, true);
|
||||
$values[$field_name] = $meta_value !== '' ? $meta_value : ($field['value'] ?? null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $values;
|
||||
}
|
||||
|
||||
/**
|
||||
* Transform fields config to React-ready format
|
||||
*
|
||||
* @param array $fields Raw field configuration from PHP
|
||||
* @param array $values Saved field values
|
||||
* @param int $post_id Post ID
|
||||
* @param string $post_type Post type
|
||||
* @return array React-ready configuration
|
||||
*/
|
||||
private static function transform_fields_config($fields, $values, $post_id, $post_type) {
|
||||
$tabs = [];
|
||||
|
||||
// The config structure is: $fields['formipay_{cpt}_settings'][tab_key] = ['name' => ..., 'fields' => [...]]
|
||||
foreach ($fields as $container_key => $container_data) {
|
||||
if (!is_array($container_data)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach ($container_data as $tab_key => $tab_data) {
|
||||
if (!isset($tab_data['name']) || !isset($tab_data['fields'])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$tab = [
|
||||
'id' => $tab_key,
|
||||
'label' => $tab_data['name'],
|
||||
'fields' => [],
|
||||
];
|
||||
|
||||
$current_group = null;
|
||||
|
||||
foreach ($tab_data['fields'] as $field_name => $field) {
|
||||
// Handle group_title (section headers)
|
||||
if (isset($field['type']) && $field['type'] === 'group_title') {
|
||||
$group_value = $field['group'] ?? '';
|
||||
|
||||
if ($group_value === 'started') {
|
||||
$current_group = [
|
||||
'type' => 'section',
|
||||
'label' => $field['label'] ?? '',
|
||||
'description' => $field['description'] ?? '',
|
||||
];
|
||||
} elseif ($group_value === 'ended') {
|
||||
$current_group = null;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// Add section if we have one
|
||||
if ($current_group !== null) {
|
||||
$tab['fields'][] = $current_group;
|
||||
$current_group = null;
|
||||
}
|
||||
|
||||
// Transform field configuration
|
||||
$transformed_field = self::transform_single_field($field_name, $field, $values);
|
||||
|
||||
// Add to fields
|
||||
$tab['fields'][] = $transformed_field;
|
||||
}
|
||||
|
||||
$tabs[] = $tab;
|
||||
}
|
||||
}
|
||||
|
||||
return [
|
||||
'tabs' => $tabs,
|
||||
'postId' => $post_id,
|
||||
'postType' => $post_type,
|
||||
'globalCurrencies' => self::get_global_currencies_data(),
|
||||
'nonce' => wp_create_nonce('formipay-field-config'),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Transform a single field configuration
|
||||
*
|
||||
* @param string $field_name Field name
|
||||
* @param array $field Field configuration
|
||||
* @param array $values Saved values
|
||||
* @return array Transformed field config
|
||||
*/
|
||||
private static function transform_single_field($field_name, $field, $values) {
|
||||
$transformed = [
|
||||
'name' => $field_name,
|
||||
'type' => self::transform_field_type($field['type'] ?? 'text'),
|
||||
'label' => $field['label'] ?? '',
|
||||
'description' => $field['description'] ?? '',
|
||||
'value' => $values[$field_name] ?? $field['value'] ?? '',
|
||||
'required' => !empty($field['required']),
|
||||
'dependency' => isset($field['dependency']) ? self::transform_dependency($field['dependency']) : null,
|
||||
];
|
||||
|
||||
// Add options for select/radio fields
|
||||
if (isset($field['options'])) {
|
||||
$transformed['options'] = $field['options'];
|
||||
}
|
||||
|
||||
// Add subtype info for currency fields
|
||||
if (isset($field['step'])) {
|
||||
$transformed['step'] = $field['step'];
|
||||
}
|
||||
if (isset($field['min'])) {
|
||||
$transformed['min'] = $field['min'];
|
||||
}
|
||||
if (isset($field['placeholder'])) {
|
||||
$transformed['placeholder'] = $field['placeholder'];
|
||||
}
|
||||
|
||||
// Handle special field types
|
||||
if (isset($field['repeater_fields'])) {
|
||||
$transformed['fields'] = $field['repeater_fields'];
|
||||
}
|
||||
|
||||
// Pass post_type for autocomplete fields (CPTs)
|
||||
if (isset($field['post_type'])) {
|
||||
$transformed['post_type'] = $field['post_type'];
|
||||
}
|
||||
|
||||
// Pass object_type for autocomplete fields (e.g., 'user' for WP Users)
|
||||
if (isset($field['object_type'])) {
|
||||
$transformed['object_type'] = $field['object_type'];
|
||||
}
|
||||
|
||||
return $transformed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Transform field type from PHP to React format
|
||||
*
|
||||
* @param string $type PHP field type
|
||||
* @return string React field type
|
||||
*/
|
||||
private static function transform_field_type($type) {
|
||||
$type_map = [
|
||||
'checkbox' => 'switch', // Use toggle switch UI
|
||||
'tinymce' => 'editor', // Rich text editor
|
||||
'editor' => 'editor', // Map 'editor' type to tinymce field
|
||||
'image' => 'image', // WordPress Media Library picker
|
||||
'notification_message' => 'notification', // Notification banner component
|
||||
];
|
||||
|
||||
return $type_map[$type] ?? $type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Transform dependency from PHP to React format
|
||||
*
|
||||
* @param array $dependency PHP dependency config
|
||||
* @return array React dependency config
|
||||
*/
|
||||
private static function transform_dependency($dependency) {
|
||||
// Handle multiple dependencies (AND logic)
|
||||
if (isset($dependency[0]) && is_array($dependency[0])) {
|
||||
return [
|
||||
'mode' => 'and',
|
||||
'rules' => array_map([self::class, 'transform_single_dependency'], $dependency),
|
||||
];
|
||||
}
|
||||
|
||||
return self::transform_single_dependency($dependency);
|
||||
}
|
||||
|
||||
/**
|
||||
* Transform a single dependency rule
|
||||
*
|
||||
* @param array $dependency PHP dependency
|
||||
* @return array React dependency rule
|
||||
*/
|
||||
private static function transform_single_dependency($dependency) {
|
||||
$rule = [
|
||||
'field' => $dependency['key'] ?? '',
|
||||
];
|
||||
|
||||
$value = $dependency['value'] ?? '';
|
||||
|
||||
// Handle special values
|
||||
if ($value === 'not_empty') {
|
||||
$rule['operator'] = 'not_empty';
|
||||
} elseif ($value === 'empty') {
|
||||
$rule['operator'] = 'empty';
|
||||
} else {
|
||||
$rule['value'] = $value;
|
||||
$rule['operator'] = 'eq';
|
||||
}
|
||||
|
||||
return $rule;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get global currencies data for React
|
||||
*
|
||||
* @return array Currencies data
|
||||
*/
|
||||
private static function get_global_currencies_data() {
|
||||
$currencies = get_global_currency_array();
|
||||
|
||||
return array_map(function($currency) {
|
||||
$parts = explode(':::', $currency['currency']);
|
||||
|
||||
return [
|
||||
'currency' => $currency['currency'],
|
||||
'code' => $parts[0] ?? '',
|
||||
'title' => $parts[1] ?? '',
|
||||
'symbol' => $parts[2] ?? $parts[1] ?? '',
|
||||
'decimalDigits' => intval($currency['decimal_digits'] ?? 2),
|
||||
'decimalSymbol' => $currency['decimal_symbol'] ?? '.',
|
||||
'thousandSeparator' => $currency['thousand_separator'] ?? ',',
|
||||
'flag' => formipay_get_flag_by_currency($currency['currency']),
|
||||
];
|
||||
}, $currencies);
|
||||
}
|
||||
|
||||
/**
|
||||
* Transform a single settings field configuration
|
||||
*
|
||||
* @param string $field_name Field name
|
||||
* @param array $field Field configuration
|
||||
* @param array $settings Saved settings values
|
||||
* @return array Transformed field config
|
||||
*/
|
||||
private static function transform_settings_field($field_name, $field, $settings) {
|
||||
$transformed = [
|
||||
'name' => $field_name,
|
||||
'type' => self::transform_field_type($field['type'] ?? 'text'),
|
||||
'label' => $field['label'] ?? '',
|
||||
'description' => $field['description'] ?? '',
|
||||
'value' => $settings[$field_name] ?? $field['value'] ?? '',
|
||||
'required' => !empty($field['required']),
|
||||
'dependency' => isset($field['dependency']) ? self::transform_dependency($field['dependency']) : null,
|
||||
];
|
||||
|
||||
// Add options for select/radio/multi_checkbox fields
|
||||
if (isset($field['options'])) {
|
||||
$transformed['options'] = $field['options'];
|
||||
}
|
||||
|
||||
// Add options for image_select
|
||||
if (isset($field['options']) && $field['type'] === 'image_select') {
|
||||
$transformed['width'] = $field['width'] ?? 100;
|
||||
$transformed['height'] = $field['height'] ?? 100;
|
||||
}
|
||||
|
||||
// Add hints for hint_textarea
|
||||
if (isset($field['hints'])) {
|
||||
$transformed['hints'] = $field['hints'];
|
||||
}
|
||||
|
||||
// Add step/min for number fields
|
||||
if (isset($field['step'])) {
|
||||
$transformed['step'] = $field['step'];
|
||||
}
|
||||
if (isset($field['min'])) {
|
||||
$transformed['min'] = $field['min'];
|
||||
}
|
||||
if (isset($field['placeholder'])) {
|
||||
$transformed['placeholder'] = $field['placeholder'];
|
||||
}
|
||||
if (isset($field['rows'])) {
|
||||
$transformed['rows'] = $field['rows'];
|
||||
}
|
||||
|
||||
// Handle repeater fields
|
||||
if (isset($field['fields'])) {
|
||||
// Transform sub-fields to include name property
|
||||
$sub_fields = [];
|
||||
foreach ($field['fields'] as $sub_field_name => $sub_field) {
|
||||
$sub_field['name'] = $sub_field_name;
|
||||
$sub_fields[] = $sub_field;
|
||||
}
|
||||
$transformed['fields'] = $sub_fields;
|
||||
}
|
||||
|
||||
return $transformed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert string to URL-safe slug
|
||||
*
|
||||
* @param string $text Text to slugify
|
||||
* @return string URL-safe slug
|
||||
*/
|
||||
private static function slugify($text) {
|
||||
return strtolower(sanitize_title($text));
|
||||
}
|
||||
}
|
||||
@@ -13,6 +13,9 @@ class ReactAdmin {
|
||||
add_action( 'admin_enqueue_scripts', [$this, 'enqueue_assets'] );
|
||||
add_filter( 'formipay/admin/data', [$this, 'localize_data'] );
|
||||
|
||||
// AJAX endpoint for field configuration
|
||||
add_action( 'wp_ajax_formipay-get-field-config', [$this, 'ajax_get_field_config'] );
|
||||
|
||||
}
|
||||
|
||||
public function enqueue_assets() {
|
||||
@@ -40,18 +43,16 @@ class ReactAdmin {
|
||||
$dependencies = $assets_file['dependencies'] ?? [];
|
||||
|
||||
// Filter out icon build dependencies - they're bundled, not separate scripts
|
||||
$original_count = count($dependencies);
|
||||
$dependencies = array_values(array_filter($dependencies, function($dep) {
|
||||
return strpos($dep, 'wp-icons/build/') === false;
|
||||
}));
|
||||
error_log('[Formipay] Filtered dependencies: ' . $original_count . ' -> ' . count($dependencies));
|
||||
|
||||
$version = $assets_file['version'] ?? FORMIPAY_VERSION;
|
||||
|
||||
wp_enqueue_style(
|
||||
'formipay-admin-style',
|
||||
$build_url . '/admin.css',
|
||||
[],
|
||||
['wp-admin', 'colors', 'dashicons', 'common', 'forms', 'admin-menu', 'dashboard', 'list-tables', 'edit', 'revisions', 'media', 'themes', 'about', 'nav-menus'],
|
||||
$version
|
||||
);
|
||||
|
||||
@@ -72,10 +73,6 @@ class ReactAdmin {
|
||||
'siteUrl' => site_url(),
|
||||
]);
|
||||
|
||||
// Debug logging
|
||||
error_log('[Formipay] Enqueuing React assets on screen: ' . $screen->id);
|
||||
error_log('[Formipay] Page data: ' . wp_json_encode($data));
|
||||
|
||||
wp_localize_script('formipay-admin', 'formipayAdmin', $data);
|
||||
|
||||
}
|
||||
@@ -161,4 +158,27 @@ class ReactAdmin {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* AJAX handler for getting field configuration
|
||||
*/
|
||||
public function ajax_get_field_config() {
|
||||
|
||||
check_ajax_referer( 'formipay-admin', '_wpnonce', '', true );
|
||||
|
||||
if (!current_user_can('manage_options')) {
|
||||
wp_send_json_error(['message' => __('Unauthorized', 'formipay')]);
|
||||
}
|
||||
|
||||
$post_id = isset($_REQUEST['post_id']) ? intval($_REQUEST['post_id']) : 0;
|
||||
$post_type = isset($_REQUEST['post_type']) ? sanitize_text_field($_REQUEST['post_type']) : '';
|
||||
|
||||
if (!$post_id || !$post_type) {
|
||||
wp_send_json_error(['message' => __('Invalid request', 'formipay')]);
|
||||
}
|
||||
|
||||
$config = FieldConfigBridge::get_config_for_post($post_id, $post_type);
|
||||
|
||||
wp_send_json_success($config);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -42,6 +42,10 @@ class Coupon {
|
||||
// React Metabox
|
||||
add_action( 'add_meta_boxes', [$this, 'add_react_metabox'] );
|
||||
add_action( 'admin_footer-post.php', [$this, 'render_react_metabox_template'] );
|
||||
add_action( 'admin_footer-post-new.php', [$this, 'render_react_metabox_template'] );
|
||||
|
||||
// Save coupon data via WordPress save_post hook (regular hook with post type check inside)
|
||||
add_action( 'save_post', [$this, 'save_coupon_on_post_update'], 10, 2 );
|
||||
|
||||
// Order
|
||||
add_filter( 'formipay/order/order-details', [$this, 'order_details'], 99, 3 );
|
||||
@@ -112,13 +116,6 @@ class Coupon {
|
||||
if ($screen && $screen->post_type === 'formipay-coupon' && $screen->base === 'post') {
|
||||
wp_enqueue_style('sweetalert2', FORMIPAY_URL . 'vendor/SweetAlert2/sweetalert2.min.css', [], '11.14.4', 'all');
|
||||
wp_enqueue_script('sweetalert2', FORMIPAY_URL . 'vendor/SweetAlert2/sweetalert2.min.js', ['jquery'], '11.14.4', true);
|
||||
|
||||
// Localize admin data
|
||||
wp_localize_script('formipay-admin', 'formipayAdmin', [
|
||||
'ajaxUrl' => admin_url('admin-ajax.php'),
|
||||
'siteUrl' => site_url(),
|
||||
'nonce' => wp_create_nonce('formipay-admin'),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -154,6 +151,7 @@ class Coupon {
|
||||
$rules_group_1 = array(
|
||||
'rules_general_group' => array(
|
||||
'type' => 'group_title',
|
||||
'label' => __( 'General', 'formipay' ),
|
||||
'group' => 'started'
|
||||
),
|
||||
'active' => array(
|
||||
@@ -169,6 +167,7 @@ class Coupon {
|
||||
'fixed' => __( 'Fixed', 'formipay' ),
|
||||
'percentage' => __( 'Percentage', 'formipay' )
|
||||
),
|
||||
'value' => 'fixed'
|
||||
),
|
||||
'amount_percentage' => array(
|
||||
'type' => 'number',
|
||||
@@ -209,8 +208,7 @@ class Coupon {
|
||||
$currency_symbol = formipay_get_currency_data_by_value($currency['currency'], 'symbol');
|
||||
$currency_title = ucwords(formipay_get_currency_data_by_value($currency['currency'], 'title'));
|
||||
$decimal_digits = intval($currency['decimal_digits']);
|
||||
$step = $decimal_digits * 10;
|
||||
$step = $step > 0 ? 1 / $step : 1;
|
||||
$step = $decimal_digits > 0 ? pow(10, -$decimal_digits) : 1;
|
||||
|
||||
$rules_group_2['amount_fixed_'.$currency_symbol] = array(
|
||||
'type' => 'number',
|
||||
@@ -238,7 +236,11 @@ class Coupon {
|
||||
),
|
||||
'step' => $step,
|
||||
'min' => 0,
|
||||
'placeholder' => __( 'Enter Max Amount...', 'formipay' )
|
||||
'placeholder' => __( 'Enter Max Amount...', 'formipay' ),
|
||||
'dependency' => array(
|
||||
'key' => 'type',
|
||||
'value' => 'percentage'
|
||||
)
|
||||
);
|
||||
|
||||
}
|
||||
@@ -324,7 +326,8 @@ class Coupon {
|
||||
'use_limit' => array(
|
||||
'type' => 'number',
|
||||
'label' => __( 'Usage Limit', 'formipay' ),
|
||||
'description' => __( 'Leave it empty or 0 (zero) to set it as unlimited usage.', 'formipay' )
|
||||
'description' => __( 'Leave it empty or 0 (zero) to set it as unlimited usage.', 'formipay' ),
|
||||
'placeholder' => __( 'Set limit...', 'formipay' )
|
||||
),
|
||||
'date_limit' => array(
|
||||
'type' => 'date',
|
||||
@@ -343,11 +346,11 @@ class Coupon {
|
||||
'label' => __( 'Products', 'formipay' ),
|
||||
'description' => __( 'Only selected product(s) can use the coupon. Leave empty to apply to all products.', 'formipay' )
|
||||
),
|
||||
'customers' => array(
|
||||
'users' => array(
|
||||
'type' => 'autocomplete',
|
||||
'post_type' => array('formipay-product'),
|
||||
'object_type' => 'user',
|
||||
'label' => __( 'Customers', 'formipay' ),
|
||||
'description' => __( 'Only selected customer(s) can use the coupon. Leave empty to apply to all customers.', 'formipay' )
|
||||
'description' => __( 'Only selected registered customer(s) can use this coupon. Leave empty to apply to all customers.', 'formipay' )
|
||||
)
|
||||
);
|
||||
|
||||
@@ -588,6 +591,14 @@ class Coupon {
|
||||
}
|
||||
|
||||
$date_limit = formipay_get_post_meta($coupon->ID, 'date_limit');
|
||||
$date_limit_display = 'none';
|
||||
if ($date_limit && $date_limit !== '') {
|
||||
if (preg_match('/^\d{4}-\d{2}-\d{2}/', $date_limit)) {
|
||||
$date_limit_display = $date_limit;
|
||||
} elseif (is_numeric($date_limit)) {
|
||||
$date_limit_display = formipay_date('Y-m-d', intval($date_limit) / 1000);
|
||||
}
|
||||
}
|
||||
|
||||
$type = formipay_get_post_meta($coupon->ID, 'type');
|
||||
$amount_meta_key = "amount_$type";
|
||||
@@ -622,7 +633,7 @@ class Coupon {
|
||||
'case_sensitive' => formipay_get_post_meta($coupon->ID, 'case_sensitive'),
|
||||
'usage_count' => $this->count_order_by_coupon_code(get_the_title($coupon->ID)),
|
||||
'usages' => $this->count_order_by_coupon_code(get_the_title($coupon->ID)),
|
||||
'date_limit' => false !== $date_limit ? formipay_date('Y-m-d', intval(formipay_get_post_meta($coupon->ID, 'date_limit')) / 1000) : 'none',
|
||||
'date_limit' => $date_limit_display,
|
||||
'active' => $is_active ? 'on' : 'off',
|
||||
'post_status' => $is_active ? 'active' : 'inactive',
|
||||
'status' => $is_active ? 'active' : 'inactive'
|
||||
@@ -885,7 +896,7 @@ class Coupon {
|
||||
'max_amounts' => [],
|
||||
'forms' => formipay_get_post_meta($post_id, 'forms') ?: [],
|
||||
'products' => formipay_get_post_meta($post_id, 'products') ?: [],
|
||||
'customers' => formipay_get_post_meta($post_id, 'customers') ?: [],
|
||||
'users' => formipay_get_post_meta($post_id, 'users') ?: [],
|
||||
];
|
||||
|
||||
// Get fixed amounts for each currency
|
||||
@@ -987,7 +998,7 @@ class Coupon {
|
||||
}
|
||||
|
||||
// Save relation fields
|
||||
$relation_fields = ['forms', 'products', 'customers'];
|
||||
$relation_fields = ['forms', 'products', 'users'];
|
||||
foreach ($relation_fields as $field) {
|
||||
if ( isset($_REQUEST[$field]) ) {
|
||||
$values = is_array($_REQUEST[$field]) ? array_map('intval', $_REQUEST[$field]) : [];
|
||||
@@ -1006,7 +1017,7 @@ class Coupon {
|
||||
*/
|
||||
public function add_react_metabox() {
|
||||
add_meta_box(
|
||||
'formipay_coupon_reactor_metabox',
|
||||
'formipay_coupon_settings',
|
||||
__( 'Coupon Settings', 'formipay' ),
|
||||
[$this, 'render_react_metabox'],
|
||||
'formipay-coupon',
|
||||
@@ -1021,9 +1032,9 @@ class Coupon {
|
||||
public function render_react_metabox($post) {
|
||||
?>
|
||||
<div
|
||||
data-formipay-metabox="coupon"
|
||||
data-formipay-field-renderer="coupon"
|
||||
data-post-id="<?php echo esc_attr($post->ID); ?>"
|
||||
class="formipay-react-metabox-container"
|
||||
class="formipay-field-renderer-container"
|
||||
>
|
||||
<div class="formipay-loading">
|
||||
<div class="formipay-spinner"></div>
|
||||
@@ -1042,9 +1053,39 @@ class Coupon {
|
||||
return;
|
||||
}
|
||||
|
||||
// Get global currencies
|
||||
// Add Toaster container for toast notifications
|
||||
echo '<div id="formipay-toaster-container" class="formipay-design-system" style="position:fixed;bottom:20px;right:20px;z-index:99999;"></div>';
|
||||
|
||||
// Get field configuration for this post
|
||||
$config = \Formipay\Admin\FieldConfigBridge::get_config_for_post($post->ID, $post->post_type);
|
||||
|
||||
// Pass config to JavaScript
|
||||
?>
|
||||
<script type="text/javascript">
|
||||
window.formipayFieldConfig = <?php echo wp_json_encode($config); ?>;
|
||||
</script>
|
||||
<?php
|
||||
|
||||
// Keep the legacy global currencies for backward compatibility during transition
|
||||
$global_currencies = get_global_currency_array();
|
||||
|
||||
// Fallback: if no multicurrencies configured, use default currency
|
||||
if (empty($global_currencies)) {
|
||||
$formipay_settings = get_option('formipay_settings');
|
||||
$default_currency_raw = formipay_default_currency('raw');
|
||||
|
||||
if (!empty($default_currency_raw)) {
|
||||
$global_currencies = [
|
||||
[
|
||||
'currency' => $default_currency_raw,
|
||||
'decimal_digits' => isset($formipay_settings['default_currency_decimal_digits']) ? intval($formipay_settings['default_currency_decimal_digits']) : 2,
|
||||
'decimal_symbol' => isset($formipay_settings['default_currency_decimal_symbol']) ? $formipay_settings['default_currency_decimal_symbol'] : '.',
|
||||
'thousand_separator' => isset($formipay_settings['default_currency_thousand_separator']) ? $formipay_settings['default_currency_thousand_separator'] : ',',
|
||||
]
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
<script type="text/javascript">
|
||||
window.formipayGlobalCurrencies = <?php echo wp_json_encode($global_currencies); ?>;
|
||||
@@ -1062,6 +1103,7 @@ class Coupon {
|
||||
|
||||
/**
|
||||
* Autocomplete search for relation fields
|
||||
* Supports CPTs (post_type) and WP Users (object_type=user)
|
||||
*/
|
||||
public function formipay_autocomplete_search() {
|
||||
|
||||
@@ -1072,9 +1114,56 @@ class Coupon {
|
||||
}
|
||||
|
||||
$post_type = isset($_REQUEST['post_type']) ? sanitize_text_field(wp_unslash($_REQUEST['post_type'])) : '';
|
||||
$object_type = isset($_REQUEST['object_type']) ? sanitize_text_field(wp_unslash($_REQUEST['object_type'])) : '';
|
||||
$search = isset($_REQUEST['search']) ? sanitize_text_field(wp_unslash($_REQUEST['search'])) : '';
|
||||
$include = isset($_REQUEST['include']) ? array_map('intval', (array) $_REQUEST['include']) : [];
|
||||
|
||||
// Handle WP Users
|
||||
if ($object_type === 'user') {
|
||||
// Resolve labels for specific IDs (pre-selected items)
|
||||
if (!empty($include)) {
|
||||
$users = get_users(['include' => $include, 'fields' => ['ID', 'display_name', 'user_email']]);
|
||||
|
||||
$results = [];
|
||||
if (!empty($users)) {
|
||||
foreach ($users as $user) {
|
||||
$results[] = [
|
||||
'value' => $user->ID,
|
||||
'label' => $user->display_name . ' (' . $user->user_email . ')',
|
||||
];
|
||||
}
|
||||
}
|
||||
wp_send_json_success($results);
|
||||
return;
|
||||
}
|
||||
|
||||
// Search by keyword
|
||||
if (strlen($search) < 2) {
|
||||
wp_send_json_error( [ 'message' => 'Invalid request' ] );
|
||||
}
|
||||
|
||||
$users = get_users([
|
||||
'search' => '*' . $search . '*',
|
||||
'search_columns' => ['display_name', 'user_email', 'user_login'],
|
||||
'number' => 20,
|
||||
'fields' => ['ID', 'display_name', 'user_email'],
|
||||
]);
|
||||
|
||||
$results = [];
|
||||
if (!empty($users)) {
|
||||
foreach ($users as $user) {
|
||||
$results[] = [
|
||||
'value' => $user->ID,
|
||||
'label' => $user->display_name . ' (' . $user->user_email . ')',
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
wp_send_json_success($results);
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle CPTs (posts)
|
||||
if (empty($post_type)) {
|
||||
wp_send_json_error( [ 'message' => 'Invalid request' ] );
|
||||
}
|
||||
@@ -1125,4 +1214,78 @@ class Coupon {
|
||||
wp_send_json_success($results);
|
||||
}
|
||||
|
||||
/**
|
||||
* Save coupon data via WordPress save_post hook
|
||||
* Called when user clicks WordPress Update button
|
||||
*/
|
||||
public function save_coupon_on_post_update($post_id, $post) {
|
||||
// Check if this is an autosave
|
||||
if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) {
|
||||
return $post_id;
|
||||
}
|
||||
|
||||
// Check permissions
|
||||
if (!current_user_can('manage_options')) {
|
||||
return $post_id;
|
||||
}
|
||||
|
||||
// Verify nonce
|
||||
if (!isset($_POST['_wpnonce']) || !wp_verify_nonce($_POST['_wpnonce'], 'update-post_' . $post_id)) {
|
||||
return $post_id;
|
||||
}
|
||||
|
||||
// Only save for our coupon post type
|
||||
if ($post->post_type !== 'formipay-coupon') {
|
||||
return $post_id;
|
||||
}
|
||||
|
||||
// Get global currencies for currency field processing
|
||||
$global_currencies = get_global_currency_array();
|
||||
|
||||
// Save basic meta fields
|
||||
$meta_fields = [
|
||||
'active',
|
||||
'type',
|
||||
'amount_percentage',
|
||||
'case_sensitive',
|
||||
'free_shipping',
|
||||
'quantity_active',
|
||||
'use_limit',
|
||||
'date_limit',
|
||||
];
|
||||
|
||||
foreach ($meta_fields as $field) {
|
||||
if (isset($_POST[$field])) {
|
||||
$value = wp_unslash($_POST[$field]);
|
||||
update_post_meta($post_id, $field, $value);
|
||||
}
|
||||
}
|
||||
|
||||
// Save fixed amounts for each currency
|
||||
foreach ($global_currencies as $currency) {
|
||||
$symbol = formipay_get_currency_data_by_value($currency['currency'], 'symbol');
|
||||
|
||||
if (isset($_POST['amount_fixed_' . $symbol])) {
|
||||
update_post_meta($post_id, 'amount_fixed_' . $symbol, wp_unslash($_POST['amount_fixed_' . $symbol]));
|
||||
}
|
||||
|
||||
if (isset($_POST['max_amount_' . $symbol])) {
|
||||
update_post_meta($post_id, 'max_amount_' . $symbol, wp_unslash($_POST['max_amount_' . $symbol]));
|
||||
}
|
||||
}
|
||||
|
||||
// Save relation fields
|
||||
$relation_fields = ['forms', 'products', 'users'];
|
||||
foreach ($relation_fields as $field) {
|
||||
if (isset($_POST[$field])) {
|
||||
$values = is_array($_POST[$field]) ? array_map('intval', $_POST[$field]) : [];
|
||||
update_post_meta($post_id, $field, $values);
|
||||
} else {
|
||||
delete_post_meta($post_id, $field);
|
||||
}
|
||||
}
|
||||
|
||||
return $post_id;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
<?php
|
||||
namespace Formipay;
|
||||
use Formipay\Traits\SingletonTrait;
|
||||
use Formipay\Admin\FieldConfigBridge;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) exit;
|
||||
|
||||
@@ -11,16 +12,39 @@ class Settings {
|
||||
/**
|
||||
* Initializes the plugin by setting filters and administration functions.
|
||||
*/
|
||||
|
||||
|
||||
protected function __construct() {
|
||||
|
||||
add_filter( 'wpcfto_options_page_setup', [$this, 'theme_option'] );
|
||||
// Register our submenu page
|
||||
add_action( 'admin_menu', [$this, 'add_settings_page'] );
|
||||
|
||||
add_action( 'admin_enqueue_scripts', [$this, 'enqueue'] );
|
||||
add_action( 'admin_footer', [$this, 'render_react_settings_template'] );
|
||||
|
||||
// AJAX handler for saving settings from React
|
||||
add_action( 'wp_ajax_formipay_save_settings', [$this, 'ajax_save_settings'] );
|
||||
|
||||
}
|
||||
|
||||
public function theme_option($setups){
|
||||
/**
|
||||
* Add settings submenu page
|
||||
*/
|
||||
public function add_settings_page() {
|
||||
add_submenu_page(
|
||||
'formipay', // Parent slug
|
||||
__('Formipay Settings', 'formipay'), // Page title
|
||||
__('Settings', 'formipay'), // Menu title
|
||||
'manage_options', // Capability
|
||||
'formipay-settings', // Menu slug
|
||||
[$this, 'render_settings_page'] // Callback function
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get settings field configuration for React
|
||||
* Direct method without WPCFTO dependency
|
||||
*/
|
||||
public function get_settings_fields() {
|
||||
$gateways = apply_filters( 'formipay/form-config/tab:payments/gateways', [] );
|
||||
|
||||
$payment_checkboxes = [];
|
||||
@@ -253,7 +277,7 @@ class Settings {
|
||||
'value' => __( 'Input your {{media}} to get new access link.', 'formipay' ),
|
||||
'submenu' => __( 'Thank-You Page', 'formipay' ),
|
||||
'hints' => array(
|
||||
'media' => __( 'Contact Media', 'formipay' )
|
||||
'media' => __( 'Contact Media', 'formipay' )
|
||||
),
|
||||
'description' => __( 'Use {{media}} shortcode to define what media of contact the buyer can receive the access link.', 'formipay' )
|
||||
),
|
||||
@@ -268,59 +292,115 @@ class Settings {
|
||||
|
||||
$pages_fields = apply_filters( 'formipay/global-settings/tab:pages', $pages_fields );
|
||||
|
||||
$global = array(
|
||||
'General' => array(
|
||||
$tabs_config = [
|
||||
'General' => [
|
||||
'name' => __( 'General', 'formipay' ),
|
||||
'fields' => $general_fields
|
||||
),
|
||||
'Pages' => array(
|
||||
],
|
||||
'Pages' => [
|
||||
'name' => __( 'Pages', 'formipay' ),
|
||||
'fields' => $pages_fields
|
||||
)
|
||||
);
|
||||
]
|
||||
];
|
||||
|
||||
$global = apply_filters( 'formipay/global-settings', $global );
|
||||
// Allow other modules to add/modify tabs
|
||||
$tabs_config = apply_filters( 'formipay/global-settings', $tabs_config );
|
||||
|
||||
foreach($global as $key => $value){
|
||||
$fields[$key] = $value;
|
||||
}
|
||||
return $tabs_config;
|
||||
}
|
||||
|
||||
$setups[] = array(
|
||||
'option_name' => 'formipay_settings',
|
||||
'title' => __('Formipay', 'formipay'),
|
||||
'sub_title' => __('Settings', 'formipay'),
|
||||
'logo' => FORMIPAY_URL . 'admin/assets/img/formipay-logo-circle-white.png',
|
||||
'page' => array(
|
||||
'parent_slug' => 'formipay',
|
||||
'page_title' => __('Formipay Settings', 'formipay'),
|
||||
'menu_title' => __('Settings', 'formipay'),
|
||||
'menu_slug' => 'formipay-settings',
|
||||
'position' => 40,
|
||||
),
|
||||
|
||||
'fields' => $fields
|
||||
);
|
||||
|
||||
return $setups;
|
||||
/**
|
||||
* Render settings page (empty container for React)
|
||||
*/
|
||||
public function render_settings_page() {
|
||||
?>
|
||||
<div class="wrap">
|
||||
<div id="formipay-settings-page-container"></div>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
|
||||
public function enqueue() {
|
||||
|
||||
|
||||
global $current_screen;
|
||||
|
||||
if ( $current_screen->id === 'formipay_page_formipay-settings' ) {
|
||||
wp_enqueue_style('admin-setting-style', FORMIPAY_URL . 'admin/assets/css/global-setting.css', [], FORMIPAY_VERSION, 'all' );
|
||||
wp_enqueue_script('admin-setting-script', FORMIPAY_URL . 'admin/assets/js/admin-setting.js', ['jquery'], FORMIPAY_VERSION, true);
|
||||
// Enqueue React admin assets for FieldRenderer
|
||||
wp_enqueue_script( 'formipay-admin', FORMIPAY_URL . 'build/admin.js', ['react', 'react-dom'], FORMIPAY_VERSION, true );
|
||||
wp_enqueue_style( 'formipay-admin', FORMIPAY_URL . 'build/admin.css', [], FORMIPAY_VERSION, 'all' );
|
||||
}
|
||||
}
|
||||
|
||||
wp_localize_script( 'admin-setting-script', 'formipay_admin_setting', [
|
||||
'ajax_url' => admin_url('admin-ajax.php'),
|
||||
'site_url' => site_url(),
|
||||
'nonce' => wp_create_nonce('formipay-admin-nonce'),
|
||||
'multicurrency' => formipay_is_multi_currency_active(),
|
||||
'all_currencies' => formipay_currency_as_options(),
|
||||
'global_selected_currencies' => formipay_global_currency_options(),
|
||||
'default_currency' => formipay_default_currency()
|
||||
] );
|
||||
/**
|
||||
* Render React settings template in admin footer
|
||||
* This outputs the config JSON and mount point for the React FieldRenderer
|
||||
*/
|
||||
public function render_react_settings_template() {
|
||||
global $current_screen;
|
||||
|
||||
// Only render on settings page
|
||||
if ( $current_screen->id !== 'formipay_page_formipay-settings' ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the configuration for settings
|
||||
$config = FieldConfigBridge::get_config_for_settings('formipay_settings');
|
||||
$config_json = wp_json_encode($config);
|
||||
|
||||
?>
|
||||
<div id="formipay-settings-react" data-formipay-settings-config="<?php echo esc_attr($config_json); ?>"></div>
|
||||
<script>
|
||||
// Move the React mount point to our page container
|
||||
jQuery(document).ready(function($) {
|
||||
$('#formipay-settings-react').appendTo('#formipay-settings-page-container');
|
||||
});
|
||||
</script>
|
||||
<?php
|
||||
}
|
||||
|
||||
/**
|
||||
* AJAX handler for saving settings from React
|
||||
*/
|
||||
public function ajax_save_settings() {
|
||||
// Verify nonce
|
||||
check_ajax_referer( 'formipay-field-config', 'nonce', true );
|
||||
|
||||
// Check permissions
|
||||
if (!current_user_can('manage_options')) {
|
||||
wp_send_json_error([
|
||||
'message' => __( 'You do not have permission to save settings.', 'formipay' )
|
||||
]);
|
||||
}
|
||||
|
||||
// Get settings data
|
||||
$settings = isset($_POST['settings']) ? json_decode(sanitize_text_field(wp_unslash($_POST['settings'])), true) : [];
|
||||
|
||||
if (empty($settings)) {
|
||||
wp_send_json_error([
|
||||
'message' => __( 'No settings data received.', 'formipay' )
|
||||
]);
|
||||
}
|
||||
|
||||
// Remove nonce from settings before saving
|
||||
unset($settings['nonce']);
|
||||
|
||||
// Get existing settings
|
||||
$existing_settings = get_option('formipay_settings', []);
|
||||
|
||||
// Merge with existing settings to preserve values not in current form
|
||||
$updated_settings = array_merge($existing_settings, $settings);
|
||||
|
||||
// Update option
|
||||
$result = update_option('formipay_settings', $updated_settings);
|
||||
|
||||
if ($result) {
|
||||
wp_send_json_success([
|
||||
'message' => __( 'Settings saved successfully.', 'formipay' )
|
||||
]);
|
||||
} else {
|
||||
wp_send_json_error([
|
||||
'message' => __( 'Failed to save settings.', 'formipay' )
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user