connectionRepository = $connectionRepository; $this->queueRepository = $queueRepository; $this->logRepository = $logRepository; $this->settingsController = $settingsController; $this->connectionController = $connectionController; $this->queueController = $queueController; } /** * Route and handle admin requests. */ public function handleAdminActions(): void { // Process activation redirect to Setup Wizard (only once upon plugin activation). if (get_option('oneclickformsync_show_wizard_on_activation') === '1') { delete_option('oneclickformsync_show_wizard_on_activation'); if (!wp_doing_ajax() && !wp_doing_cron() && current_user_can('manage_options')) { wp_safe_redirect(admin_url('admin.php?page=1-click-form-sync-wizard')); exit; } } $isOneClickFormSyncAction = isset($_POST['oneclickformsync_save_settings']) || isset($_POST['oneclickformsync_add_connection']) || isset($_GET['oneclickformsync_delete_connection']) || isset($_GET['oneclickformsync_retry_item']) || isset($_GET['oneclickformsync_delete_item']) || isset($_GET['oneclickformsync_clear_logs']); if ($isOneClickFormSyncAction) { $this->checkPermissions(); } // Process developer mode logs clear. if (isset($_GET['oneclickformsync_clear_logs'])) { $nonce = isset($_GET['_wpnonce']) ? sanitize_text_field($_GET['_wpnonce']) : ''; if (wp_verify_nonce($nonce, 'oneclickformsync_clear_logs_action')) { $this->logRepository->clearAll(); wp_safe_redirect( add_query_arg( ['oneclickformsync_logs_cleared' => '1'], admin_url('admin.php?page=1-click-form-sync-logs') ) ); exit; } } $this->settingsController->handleSaveSettings(); $this->connectionController->handleAddConnection(); $this->connectionController->handleDeleteConnection(); $this->queueController->handleRetryItem(); $this->queueController->handleDeleteItem(); // Process Forminator sub-menu settings save action. if (isset($_POST['oneclickformsync_save_forminator_settings'])) { $nonce = isset($_POST['_wpnonce']) ? sanitize_text_field($_POST['_wpnonce']) : ''; if (wp_verify_nonce($nonce, 'oneclickformsync_save_forminator_settings_action')) { $this->checkPermissions(); // Save per-form configurations if (isset($_POST['forminator_form_settings']) && is_array($_POST['forminator_form_settings'])) { foreach ($_POST['forminator_form_settings'] as $formId => $config) { $formId = (int)$formId; if ($formId > 0 && is_array($config)) { $customMapping = []; if (isset($config['custom_mapping']) && is_array($config['custom_mapping'])) { foreach ($config['custom_mapping'] as $row) { $header = sanitize_text_field($row['header'] ?? ''); $fieldId = sanitize_text_field($row['field_id'] ?? ''); if (!empty($header) && !empty($fieldId)) { $customMapping[] = [ 'header' => $header, 'field_id' => $fieldId, ]; } } } $savedConfig = [ 'enabled' => isset($config['enabled']) && ($config['enabled'] === '1' || $config['enabled'] === 'true'), 'connection_id' => sanitize_text_field($config['connection_id'] ?? ''), 'spreadsheet_id' => sanitize_text_field($config['spreadsheet_id'] ?? ''), 'worksheet_name' => sanitize_text_field($config['worksheet_name'] ?? 'Sheet1'), 'mapping_mode' => sanitize_text_field($config['mapping_mode'] ?? 'auto'), 'create_headers' => isset($config['create_headers']) ? ($config['create_headers'] === '1' || $config['create_headers'] === 'true') : true, 'include_submission_date' => isset($config['include_submission_date']) && ($config['include_submission_date'] === '1' || $config['include_submission_date'] === 'true'), 'include_page_url' => isset($config['include_page_url']) && ($config['include_page_url'] === '1' || $config['include_page_url'] === 'true'), 'include_submission_time' => isset($config['include_submission_time']) && ($config['include_submission_time'] === '1' || $config['include_submission_time'] === 'true'), 'include_submission_id' => isset($config['include_submission_id']) && ($config['include_submission_id'] === '1' || $config['include_submission_id'] === 'true'), 'custom_mapping' => $customMapping, 'custom_utm_params' => sanitize_text_field($config['custom_utm_params'] ?? ''), ]; update_option('oneclickformsync_forminator_settings_' . $formId, $savedConfig); } } } wp_safe_redirect(admin_url('admin.php?page=1-click-form-sync-forminator&oneclickformsync_updated=1')); exit; } } } /** * Enforce strict administrative capabilities validation. * * @return void */ private function checkPermissions(): void { if (!current_user_can('manage_options')) { wp_die(esc_html__('You do not have permission to perform this action.', '1-click-form-sync'), 403); } } /** * Render Forminator Submenu Settings View. */ public function renderForminatorSettings(): void { $this->checkPermissions(); $connections = $this->connectionRepository->findAll(); // Fetch all published Forminator form posts $formPosts = get_posts([ 'post_type' => 'forminator_forms', 'post_status' => 'any', 'posts_per_page' => -1, 'orderby' => 'title', 'order' => 'ASC', ]); $forminatorForms = []; foreach ($formPosts as $post) { $formId = $post->ID; $formTitle = !empty($post->post_title) ? $post->post_title : sprintf(__('Form #%d', '1-click-form-sync'), $formId); $formSettings = get_option('oneclickformsync_forminator_settings_' . $formId, []); // Extract Forminator fields for mapping helper $formFields = []; $formMeta = get_post_meta($formId, 'forminator_form_meta', true); if (is_array($formMeta) && isset($formMeta['fields']) && is_array($formMeta['fields'])) { foreach ($formMeta['fields'] as $field) { $fId = $field['id'] ?? ''; $fLabel = $field['field_label'] ?? $field['title'] ?? $fId; if (!empty($fId)) { $formFields[] = [ 'id' => $fId, 'label' => $fLabel, ]; } } } $forminatorForms[] = [ 'id' => $formId, 'title' => $formTitle, 'settings' => $formSettings, 'fields' => $formFields, ]; } include dirname(__DIR__, 2) . '/templates/forminator-settings.php'; } /** * Enqueue styles in admin area. */ public function enqueueAssets(): void { wp_enqueue_script( 'oneclickformsync-tailwind-js', plugins_url('assets/js/tailwind.js', dirname(__DIR__, 1)), [], '3.4.1', false ); wp_enqueue_style( 'oneclickformsync-admin-style', plugins_url('assets/css/admin.css', dirname(__DIR__, 1)), [], '1.0.0' ); // Register a client-side script that triggers queue processing and instant provider toggling. wp_register_script('oneclickformsync-admin-js', false, ['jquery']); wp_enqueue_script('oneclickformsync-admin-js'); $queueNonce = wp_create_nonce('oneclickformsync_process_queue_ajax_nonce'); $toggleNonce = wp_create_nonce('oneclickformsync_toggle_provider_ajax_nonce'); $inlineJs = " jQuery(document).ready(function($) { function triggerQueue() { $.post(ajaxurl, { action: 'oneclickformsync_process_queue_ajax', nonce: '{$queueNonce}' }, function(response) { if (response.success && response.data && response.data.processed > 0) { window.location.reload(); } }); } triggerQueue(); setTimeout(triggerQueue, 22000); window.oneclickformsyncToggleProvider = function(input, provider) { var \$card = $(input).closest('.oneclickformsync-provider-card'); var isChecked = input.checked; \$card.toggleClass('is-checked', isChecked); $.post(ajaxurl, { action: 'oneclickformsync_toggle_provider_ajax', nonce: '{$toggleNonce}', provider: provider, enabled: isChecked ? 'true' : 'false' }); }; }); "; wp_add_inline_script('oneclickformsync-admin-js', $inlineJs); // Conditionally load scripts for specific pages $page = isset($_GET['page']) ? sanitize_text_field($_GET['page']) : ''; if ($page === '1-click-form-sync-queue') { wp_enqueue_script( 'oneclickformsync-queue-js', plugins_url('assets/js/queue.js', dirname(__DIR__, 1)), ['jquery'], '1.0.0', true ); } elseif ($page === '1-click-form-sync-submissions') { wp_enqueue_script( 'oneclickformsync-submissions-js', plugins_url('assets/js/submissions.js', dirname(__DIR__, 1)), ['jquery'], '1.0.0', true ); } elseif ($page === '1-click-form-sync-wizard') { wp_enqueue_script( 'oneclickformsync-wizard-js', plugins_url('assets/js/wizard.js', dirname(__DIR__, 1)), ['jquery'], '1.0.0', true ); wp_localize_script('oneclickformsync-wizard-js', 'oneclickformsyncWizardParams', [ 'dashboardUrl' => admin_url('admin.php?page=1-click-form-sync'), 'finishText' => esc_html__('Finish Setup', '1-click-form-sync'), 'nextText' => esc_html__('Next Step', '1-click-form-sync'), ]); } } /** * Render Dashboard View. */ public function renderDashboard(): void { $this->checkPermissions(); $connections = $this->connectionRepository->findAll(); $queueCount = count($this->queueRepository->findPending(100)); // Get logs statistics. $logs = $this->logRepository->findAll(5, 0); // Count stats from database tables directly. global $wpdb; $logsTable = $wpdb->prefix . 'oneclickformsync_logs'; $queueTable = $wpdb->prefix . 'oneclickformsync_queue'; $totalSuccess = (int)$wpdb->get_var($wpdb->prepare("SELECT COUNT(*) FROM {$logsTable} WHERE status = %s", 'success')); $totalFailed = (int)$wpdb->get_var($wpdb->prepare("SELECT COUNT(*) FROM {$logsTable} WHERE status = %s", 'failed')); $totalQueued = (int)$wpdb->get_var("SELECT COUNT(*) FROM {$queueTable} WHERE status = 'pending'"); $elementorActive = class_exists('Elementor\Plugin'); $elementorProActive = class_exists('ElementorPro\Plugin'); $wpformsActive = class_exists('WPForms\WPForms') || class_exists('WPForms_Lite') || function_exists('wpforms'); include dirname(__DIR__, 2) . '/templates/dashboard.php'; } /** * Render Connections View. */ public function renderConnections(): void { $this->checkPermissions(); $connections = $this->connectionRepository->findAll(); $clientId = $this->settingsController->getClientId(); $clientSecret = $this->settingsController->getClientSecret(); $redirectUrl = $this->oauthManagerRedirectUriHelper(); include dirname(__DIR__, 2) . '/templates/connections.php'; } /** * Render Activity Logs View. */ public function renderLogs(): void { $this->checkPermissions(); $page = isset($_GET['paged']) ? max(1, (int)$_GET['paged']) : 1; $limit = 20; $offset = ($page - 1) * $limit; $logs = $this->logRepository->findAll($limit, $offset); $totalLogs = $this->logRepository->countAll(); $totalPages = (int)ceil($totalLogs / $limit); include dirname(__DIR__, 2) . '/templates/logs.php'; } /** * Render Retry Queue View. */ public function renderQueue(): void { $this->checkPermissions(); global $wpdb; $queueTable = $wpdb->prefix . 'oneclickformsync_queue'; $rows = $wpdb->get_results("SELECT * FROM {$queueTable} ORDER BY next_attempt_at ASC", ARRAY_A); $queueItems = []; $submissionIds = []; foreach ($rows as $row) { $item = new \OneClickFormSync\Queue\QueueItem($row); $queueItems[] = $item; $payload = $item->getPayload(); if (isset($payload['submission_id'])) { $submissionIds[] = $payload['submission_id']; } } $relatedLogs = []; if (!empty($submissionIds)) { $logsTable = $wpdb->prefix . 'oneclickformsync_logs'; $placeholders = implode(',', array_fill(0, count($submissionIds), '%s')); $logRows = $wpdb->get_results( $wpdb->prepare("SELECT * FROM {$logsTable} WHERE submission_id IN ($placeholders) ORDER BY created_at DESC", ...$submissionIds), ARRAY_A ); foreach ($logRows as $logRow) { $relatedLogs[$logRow['submission_id']][] = $logRow; } } include dirname(__DIR__, 2) . '/templates/queue.php'; } /** * Render Settings View. */ public function renderSettings(): void { $this->checkPermissions(); $clientId = $this->settingsController->getClientId(); $clientSecret = $this->settingsController->getClientSecret(); $enabledFormProviders = $this->settingsController->getEnabledFormProviders(); $redirectUrl = $this->oauthManagerRedirectUriHelper(); $elementorProActive = class_exists('ElementorPro\Plugin') || defined('ELEMENTOR_PRO_VERSION'); $wpformsActive = class_exists('WPForms\WPForms') || class_exists('WPForms_Lite') || function_exists('wpforms'); $forminatorActive = class_exists('Forminator') || class_exists('Forminator_API') || function_exists('forminator'); include dirname(__DIR__, 2) . '/templates/settings.php'; } /** * Render Setup Guide View. */ public function renderGuide(): void { $this->checkPermissions(); $activeProvider = isset($_GET['provider']) ? sanitize_text_field($_GET['provider']) : 'forminator'; $redirectUrl = $this->oauthManagerRedirectUriHelper(); include dirname(__DIR__, 2) . '/templates/guide.php'; } /** * Render System Status View. */ public function renderStatus(): void { $this->checkPermissions(); $checker = new RequirementsChecker(); $checker->check(); $errors = $checker->getErrors(); $phpVersion = PHP_VERSION; global $wp_version; $wpVersion = $wp_version; $opensslActive = extension_loaded('openssl'); $sodiumActive = extension_loaded('sodium'); $elementorActive = class_exists('Elementor\Plugin'); $elementorProActive = class_exists('ElementorPro\Plugin'); include dirname(__DIR__, 2) . '/templates/status.php'; } /** * Render Setup Wizard View. */ public function renderWizard(): void { $this->checkPermissions(); $connections = $this->connectionRepository->findAll(); $clientId = $this->settingsController->getClientId(); $clientSecret = $this->settingsController->getClientSecret(); $enabledFormProviders = $this->settingsController->getEnabledFormProviders(); $redirectUrl = $this->oauthManagerRedirectUriHelper(); $elementorProActive = class_exists('ElementorPro\Plugin') || defined('ELEMENTOR_PRO_VERSION'); $wpformsActive = class_exists('WPForms\WPForms') || class_exists('WPForms_Lite') || function_exists('wpforms'); include dirname(__DIR__, 2) . '/templates/wizard.php'; } /** * Render Submissions View. */ public function renderSubmissions(): void { $this->checkPermissions(); global $wpdb; $logsTable = $wpdb->prefix . 'oneclickformsync_logs'; $rows = $wpdb->get_results( "SELECT l1.* FROM {$logsTable} l1 INNER JOIN ( SELECT MAX(id) as max_id FROM {$logsTable} GROUP BY submission_id ) l2 ON l1.id = l2.max_id ORDER BY l1.id DESC LIMIT 50", ARRAY_A ); $submissions = []; foreach ($rows as $row) { $submissions[] = new \OneClickFormSync\Logging\LogEntry($row); } include dirname(__DIR__, 2) . '/templates/submissions.php'; } /** * Helper to get redirect URL. */ private function oauthManagerRedirectUriHelper(): string { return admin_url('admin-post.php?action=oneclickformsync_oauth_callback'); } /** * AJAX handler to toggle form provider on/off in real-time. * * @return void */ public function handleToggleProviderAjax(): void { $nonce = sanitize_text_field($_POST['nonce'] ?? ''); if (!wp_verify_nonce($nonce, 'oneclickformsync_toggle_provider_ajax_nonce')) { wp_send_json_error(['message' => 'Invalid nonce']); } if (!current_user_can('manage_options')) { wp_send_json_error(['message' => 'Permission denied']); } $provider = sanitize_text_field($_POST['provider'] ?? ''); $enabled = isset($_POST['enabled']) && ($_POST['enabled'] === 'true' || $_POST['enabled'] === '1'); if (empty($provider)) { wp_send_json_error(['message' => 'Invalid provider']); } $currentEnabled = $this->settingsController->getEnabledFormProviders(); if ($enabled) { if (!in_array($provider, $currentEnabled, true)) { $currentEnabled[] = $provider; } } else { $currentEnabled = array_values(array_filter($currentEnabled, function ($p) use ($provider) { return $p !== $provider; })); } update_option(SettingsController::ENABLED_PROVIDERS_OPTION, $currentEnabled); wp_send_json_success(['enabled_providers' => $currentEnabled]); } }