- F-15: uninstall now removes ALL espinstanda_* options and transients (incl. the encrypted password and its self-provisioned key). - F-16: register the "Gravity Forms required" admin notice unconditionally so it is reachable when GF is inactive (gform_loaded never fires then). - F-22: self-heal the daily retention cron on admin_init (matches the schema self-heal). - F-19: re-validate the stored payload via Field_Mapper::validate_payload() before a resubmit (DB-tamper trust boundary). - F-20: increment the retry count on resubmit and dispatch the previously-dead espinstanda_resubmit_success notification event. - F-23: emit the theme's Form-5 date-range JS only on pages that embed a Gravity Form and disconnect the MutationObserver (was observing the DOM subtree forever). - F-24: bump plugin to 1.1.0; add readme.txt changelog documenting the audit fixes and the required ops actions. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
135 lines
5.0 KiB
PHP
135 lines
5.0 KiB
PHP
<?php
|
|
/**
|
|
* Plugin Name: ESP INSTANDA Start Quote Integration
|
|
* Plugin URI: https://getfused.com
|
|
* Description: Pushes Gravity Forms "Start a Quote" submissions into INSTANDA's Package API with write-ahead transaction logging, native entry recording, admin resubmit, and a test-mode lock. Form-agnostic - attach the feed to any form.
|
|
* Version: 1.1.0
|
|
* Requires at least: 7.0
|
|
* Requires PHP: 8.5
|
|
* Author: Getfused
|
|
* Author URI: https://getfused.com
|
|
* Text Domain: esp-instanda-integration
|
|
* License: GPL-2.0-or-later
|
|
* Gravity Forms tested up to: 2.10.4
|
|
*/
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace ESP\Instanda;
|
|
|
|
if (!defined('ABSPATH')) {
|
|
exit;
|
|
}
|
|
|
|
const MIN_PHP = '8.5';
|
|
const MIN_WP = '7.0';
|
|
const MIN_GF = '2.10.3';
|
|
const PLUGIN_FILE = __FILE__;
|
|
const CLEANUP_HOOK = 'esp_instanda_cleanup';
|
|
|
|
require_once __DIR__ . '/includes/class-guard.php';
|
|
require_once __DIR__ . '/includes/class-logger.php';
|
|
require_once __DIR__ . '/includes/class-config.php';
|
|
require_once __DIR__ . '/includes/class-transaction-store.php';
|
|
require_once __DIR__ . '/includes/class-cron-cleanup.php';
|
|
|
|
/* ------------------------------------------------------------------ activation */
|
|
|
|
\register_activation_hook(__FILE__, static function (): void {
|
|
// Create the write-ahead table; schedule daily retention cleanup.
|
|
Transaction_Store::install_table();
|
|
if (!\wp_next_scheduled(CLEANUP_HOOK)) {
|
|
\wp_schedule_event(time(), 'daily', CLEANUP_HOOK);
|
|
}
|
|
});
|
|
|
|
\register_deactivation_hook(__FILE__, static function (): void {
|
|
\wp_clear_scheduled_hook(CLEANUP_HOOK);
|
|
});
|
|
|
|
/* ------------------------------------------------------------------ guards (#5) */
|
|
|
|
/**
|
|
* Refuse to initialize on an unsupported platform - but never wp_die(). Show an
|
|
* admin notice and bail so the site stays up.
|
|
*/
|
|
function platform_blockers(): array {
|
|
$errors = [];
|
|
|
|
if (PHP_VERSION_ID < 80500) {
|
|
$errors[] = sprintf('PHP %s or newer is required (running %s).', MIN_PHP, PHP_VERSION);
|
|
}
|
|
|
|
$wp_version = $GLOBALS['wp_version'] ?? '0';
|
|
if (version_compare((string) $wp_version, MIN_WP, '<')) {
|
|
$errors[] = sprintf('WordPress %s or newer is required.', MIN_WP);
|
|
}
|
|
|
|
if (!class_exists('GFForms')) {
|
|
$errors[] = 'Gravity Forms is required and is not active.';
|
|
} elseif (version_compare((string) \GFForms::$version, MIN_GF, '<')) {
|
|
$errors[] = sprintf('Gravity Forms %s or newer is required.', MIN_GF);
|
|
}
|
|
|
|
return $errors;
|
|
}
|
|
|
|
\add_action('admin_init', static function (): void {
|
|
if (!empty(platform_blockers())) {
|
|
return;
|
|
}
|
|
// Keep the schema current if the plugin was updated without re-activation.
|
|
if (Transaction_Store::needs_migration()) {
|
|
Transaction_Store::install_table();
|
|
}
|
|
// Self-heal the retention cron if its scheduled event was ever lost (the schema
|
|
// self-heals above; the cron must too, or retention silently stops forever).
|
|
if (!\wp_next_scheduled(CLEANUP_HOOK)) {
|
|
\wp_schedule_event(time(), 'daily', CLEANUP_HOOK);
|
|
}
|
|
});
|
|
|
|
// Requirements notice registered UNCONDITIONALLY: gform_loaded never fires when Gravity
|
|
// Forms is absent, so a notice registered only inside it would be unreachable in exactly
|
|
// the case (GF missing) an admin most needs to see it.
|
|
\add_action('admin_notices', static function (): void {
|
|
$errors = platform_blockers();
|
|
if ($errors) {
|
|
echo '<div class="notice notice-error"><p><strong>ESP INSTANDA Integration is inactive:</strong> '
|
|
. esc_html(implode(' ', $errors)) . '</p></div>';
|
|
}
|
|
});
|
|
|
|
/* ------------------------------------------------ Gravity Forms Add-On registration */
|
|
|
|
\add_action('gform_loaded', Guard::wrap(static function (): void {
|
|
if (!empty(platform_blockers()) || !method_exists('GFForms', 'include_feed_addon_framework')) {
|
|
return; // the requirements notice is registered unconditionally above
|
|
}
|
|
|
|
\GFForms::include_feed_addon_framework();
|
|
|
|
require_once __DIR__ . '/includes/class-field-mapper.php';
|
|
require_once __DIR__ . '/includes/class-api-client.php';
|
|
require_once __DIR__ . '/includes/class-transaction-store.php';
|
|
require_once __DIR__ . '/includes/class-submission.php';
|
|
require_once __DIR__ . '/includes/class-entry-integration.php';
|
|
require_once __DIR__ . '/includes/class-failure-surface.php';
|
|
require_once __DIR__ . '/includes/class-notifier.php';
|
|
require_once __DIR__ . '/includes/class-go-live.php';
|
|
require_once __DIR__ . '/includes/class-transactions-table.php';
|
|
require_once __DIR__ . '/includes/class-boost-compat.php';
|
|
require_once __DIR__ . '/includes/class-instanda-addon.php';
|
|
|
|
\GFAddOn::register(Instanda_AddOn::class);
|
|
}, 5));
|
|
|
|
/* ----------------------------------------------------------------- cron cleanup */
|
|
|
|
\add_action(CLEANUP_HOOK, Guard::wrap(static function (): void {
|
|
// Retention purge of delivered-only records is implemented in M10.
|
|
if (class_exists(__NAMESPACE__ . '\\Cron_Cleanup')) {
|
|
Cron_Cleanup::run();
|
|
}
|
|
}));
|