Changed source root directory

This commit is contained in:
2026-03-05 16:30:11 +01:00
parent dc85447ee1
commit 538f85d7a2
5868 changed files with 749734 additions and 99 deletions

View File

@@ -0,0 +1,48 @@
<?php
/**
* Asset cache busting utility.
*
* @package contact-form-7-mailchimp-extension
* @author renzo.johnson@gmail.com
* @copyright 2014-2026 https://renzojohnson.com
* @license GPL-3.0+
*/
defined( 'ABSPATH' ) || exit;
class Cmatic_Buster {
private string $plugin_version;
private bool $is_debug;
private static ?self $instance = null;
public function __construct( string $plugin_version = SPARTAN_MCE_VERSION, ?bool $is_debug = null ) {
$this->plugin_version = $plugin_version;
$this->is_debug = $is_debug ?? ( defined( 'WP_DEBUG' ) && WP_DEBUG );
}
public function get_version( string $file_path ): string {
$version_parts = array( $this->plugin_version );
if ( file_exists( $file_path ) ) {
$version_parts[] = (string) filemtime( $file_path );
$version_parts[] = substr( md5_file( $file_path ), 0, 8 );
}
if ( $this->is_debug ) {
$version_parts[] = (string) time();
}
return implode( '-', $version_parts );
}
public static function instance(): self {
if ( null === self::$instance ) {
self::$instance = new self();
}
return self::$instance;
}
}

View File

@@ -0,0 +1,97 @@
<?php
/**
* Debug file logger.
*
* @package contact-form-7-mailchimp-extension
* @author renzo.johnson@gmail.com
* @copyright 2014-2026 https://renzojohnson.com
* @license GPL-3.0+
*/
defined( 'ABSPATH' ) || exit;
class Cmatic_File_Logger implements Cmatic_Logger_Interface {
private $is_write_enabled = false;
private $log_prefix;
public function __construct( $context = 'ChimpMatic', $enabled = false ) {
$this->is_write_enabled = (bool) $enabled && ( defined( 'WP_DEBUG_LOG' ) && WP_DEBUG_LOG );
$this->log_prefix = '[' . sanitize_key( $context ) . ']';
}
public function log( string $level, string $message, $context = null ): void {
if ( ! $this->is_write_enabled ) {
return;
}
$level_str = strtoupper( $level );
$log_message = "[ChimpMatic Lite] {$this->log_prefix} [{$level_str}] " . trim( $message );
if ( ! is_null( $context ) ) {
$context_string = $this->format_data( $context );
$log_message .= ' | Data: ' . $context_string;
}
error_log( $log_message ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log
}
private function format_data( $data ) {
if ( is_string( $data ) ) {
return $data;
}
if ( ! is_array( $data ) ) {
return wp_json_encode( $data, JSON_UNESCAPED_SLASHES );
}
if ( isset( $data['email_address'] ) && isset( $data['status'] ) ) {
$summary = array(
'email' => $data['email_address'] ?? '',
'status' => $data['status'] ?? '',
'id' => $data['id'] ?? '',
);
return wp_json_encode( $summary, JSON_UNESCAPED_SLASHES );
}
if ( isset( $data['url'] ) && isset( $data['payload'] ) ) {
$merge_fields = $data['payload']['merge_fields'] ?? array();
if ( is_object( $merge_fields ) ) {
$merge_fields = (array) $merge_fields;
}
$summary = array(
'url' => $data['url'],
'email' => $data['payload']['email_address'] ?? '',
'status' => $data['payload']['status'] ?? '',
'fields' => is_array( $merge_fields ) ? array_keys( $merge_fields ) : array(),
);
return wp_json_encode( $summary, JSON_UNESCAPED_SLASHES );
}
$json = wp_json_encode( $data, JSON_UNESCAPED_SLASHES );
if ( strlen( $json ) <= 1000 ) {
return $json;
}
return substr( $json, 0, 1000 ) . '... [truncated]';
}
private function map_numeric_level_to_string( $numeric_level ) {
switch ( (int) $numeric_level ) {
case 1:
return 'INFO';
case 2:
return 'DEBUG';
case 3:
return 'WARNING';
case 4:
return 'ERROR';
case 5:
return 'CRITICAL';
default:
return 'UNKNOWN';
}
}
}

View File

@@ -0,0 +1,42 @@
<?php
/**
* Lite field restrictions and limits.
*
* @package contact-form-7-mailchimp-extension
* @author renzo.johnson@gmail.com
* @copyright 2014-2026 https://renzojohnson.com
* @license GPL-3.0+
*/
defined( 'ABSPATH' ) || exit;
class Cmatic_Lite_Get_Fields {
public static function cmatic_lite_fields() {
return array(
'source',
'ip_signup',
'subscribed',
'timestamp_signup',
'member_rating',
'location',
'email_client',
'vip',
'language',
'email_type',
'consents_to_one_to_one_messaging',
);
}
public static function cmatic_lite_sections() {
return array(
'tags',
'interests',
'marketing_permissions',
);
}
public static function cmatic_lite_merge_fields() {
return 6;
}
}

View File

@@ -0,0 +1,86 @@
<?php
/**
* URL tracking and link builder.
*
* @package contact-form-7-mailchimp-extension
* @author renzo.johnson@gmail.com
* @copyright 2014-2026 https://renzojohnson.com
* @license GPL-3.0+
*/
defined( 'ABSPATH' ) || exit;
final class Cmatic_Pursuit {
const PLUGIN_ID = 'chimpmatic_lite';
const BASE_URLS = array(
'docs' => 'https://chimpmatic.com',
'pricing' => 'https://chimpmatic.com/pricing',
'support' => 'https://chimpmatic.com/contact',
'promo' => 'https://chimpmatic.com/almost-there',
'home' => 'https://chimpmatic.com',
'author' => 'https://renzojohnson.com',
);
private function __construct() {}
public static function url( string $base_url, string $medium, string $content = '', string $campaign = '' ): string {
if ( empty( $base_url ) ) {
return '';
}
$params = array(
'utm_source' => self::PLUGIN_ID,
'utm_medium' => self::sanitize( $medium ),
'utm_campaign' => $campaign ? self::sanitize( $campaign ) : 'plugin_' . gmdate( 'Y' ),
);
if ( $content ) {
$params['utm_content'] = self::sanitize( $content );
}
return add_query_arg( $params, $base_url );
}
public static function docs( string $slug = '', string $content = '' ): string {
$base = self::BASE_URLS['docs'];
if ( $slug ) {
$base = trailingslashit( $base ) . ltrim( $slug, '/' );
}
return self::url( $base, 'plugin', $content, 'docs' );
}
public static function upgrade( string $content = '' ): string {
return self::url( self::BASE_URLS['pricing'], 'plugin', $content, 'upgrade' );
}
public static function support( string $content = '' ): string {
return self::url( self::BASE_URLS['support'], 'plugin', $content, 'support' );
}
public static function promo( string $content = '', int $discount = 40 ): string {
$params = array(
'u_source' => self::PLUGIN_ID,
'u_medium' => 'banner',
'u_campaign' => 'promo_' . $discount . 'off',
);
if ( $content ) {
$params['u_content'] = self::sanitize( $content );
}
return add_query_arg( $params, self::BASE_URLS['promo'] );
}
public static function home( string $content = '' ): string {
return self::url( self::BASE_URLS['home'], 'plugin', $content, 'brand' );
}
public static function author( string $content = '' ): string {
return self::url( self::BASE_URLS['author'], 'plugin', $content, 'author' );
}
public static function adminbar( string $destination, string $content = '' ): string {
$base = self::BASE_URLS[ $destination ] ?? self::BASE_URLS['home'];
return self::url( $base, 'adminbar', $content, $destination );
}
private static function sanitize( string $value ): string {
return preg_replace( '/[^a-z0-9_]/', '', str_replace( array( ' ', '-' ), '_', strtolower( trim( $value ) ) ) );
}
}

View File

@@ -0,0 +1,251 @@
<?php
/**
* Remote data fetcher with caching.
*
* @package contact-form-7-mailchimp-extension
* @author renzo.johnson@gmail.com
* @copyright 2014-2026 https://renzojohnson.com
* @license GPL-3.0+
*/
defined( 'ABSPATH' ) || exit;
class CMatic_Remote_Fetcher {
private $config;
private $defaults = array(
'url' => '',
'cache_key' => 'cmatic_remote_data',
'cache_duration' => DAY_IN_SECONDS,
'retry_interval' => 600, // 10 minutes in seconds
'max_retries' => 3,
'retry_count_key' => 'cmatic_retry_count',
'cron_hook' => 'cmatic_fetch_retry',
'timeout' => 15,
'fallback_data' => array(),
'parser_callback' => null,
);
public function __construct( $config = array() ) {
$this->config = wp_parse_args( $config, $this->defaults );
add_action( $this->config['cron_hook'], array( $this, 'cron_retry_fetch' ) );
}
public function get_data() {
$cached_data = $this->get_cache();
if ( false !== $cached_data ) {
return $cached_data;
}
$fresh_data = $this->fetch_fresh_data();
if ( false !== $fresh_data ) {
$this->set_cache( $fresh_data );
$this->clear_retry_schedule();
return $fresh_data;
}
$this->schedule_retry();
return $this->get_fallback_data();
}
public function get_cache() {
return get_transient( $this->config['cache_key'] );
}
public function set_cache( $data ) {
return set_transient(
$this->config['cache_key'],
$data,
$this->config['cache_duration']
);
}
public function clear_cache() {
return delete_transient( $this->config['cache_key'] );
}
private function fetch_fresh_data() {
if ( empty( $this->config['url'] ) ) {
return false;
}
$response = wp_remote_get(
$this->config['url'],
array(
'timeout' => $this->config['timeout'],
'user-agent' => 'WordPress/' . get_bloginfo( 'version' ) . '; ' . home_url(),
)
);
if ( is_wp_error( $response ) ) {
return false;
}
$response_code = wp_remote_retrieve_response_code( $response );
if ( 200 !== $response_code ) {
return false;
}
$body = wp_remote_retrieve_body( $response );
if ( empty( $body ) ) {
return false;
}
return $this->parse_content( $body );
}
/** Parse fetched content. */
private function parse_content( $content ) {
if ( is_callable( $this->config['parser_callback'] ) ) {
return call_user_func( $this->config['parser_callback'], $content );
}
$json_data = $this->parse_pricing_json( $content );
if ( false !== $json_data ) {
return $json_data;
}
return $this->parse_pricing_html( $content );
}
private function parse_pricing_json( $content ) {
$json = json_decode( $content, true );
if ( null === $json || ! is_array( $json ) ) {
return false;
}
if ( ! isset( $json['regular_price'] ) || ! isset( $json['discount_percent'] ) ) {
return false;
}
$pricing_data = array(
'regular_price' => (int) $json['regular_price'],
'sale_price' => isset( $json['sale_price'] ) ? (float) $json['sale_price'] : null,
'discount_percent' => (int) $json['discount_percent'],
'coupon_code' => isset( $json['coupon_code'] ) ? sanitize_text_field( $json['coupon_code'] ) : null,
'last_updated' => current_time( 'mysql' ),
);
if ( null === $pricing_data['sale_price'] ) {
$discount_amount = $pricing_data['regular_price'] * ( $pricing_data['discount_percent'] / 100 );
$pricing_data['sale_price'] = $pricing_data['regular_price'] - $discount_amount;
}
if ( null === $pricing_data['coupon_code'] ) {
$pricing_data['coupon_code'] = 'NOW' . $pricing_data['discount_percent'];
}
$pricing_data['formatted'] = sprintf(
'$%d → $%s • Save %d%%',
$pricing_data['regular_price'],
number_format( $pricing_data['sale_price'], 2 ),
$pricing_data['discount_percent']
);
return $pricing_data;
}
private function parse_pricing_html( $html ) {
$pricing_data = array(
'regular_price' => null,
'sale_price' => null,
'discount_percent' => null,
'coupon_code' => null,
'formatted' => null,
'last_updated' => current_time( 'mysql' ),
);
if ( preg_match( '/Single\s+Site[^$]*\$(\d+)\/year/i', $html, $matches ) ) {
$pricing_data['regular_price'] = (int) $matches[1];
}
if ( preg_match( '/(\d+)%\s+Off/i', $html, $matches ) ) {
$pricing_data['discount_percent'] = (int) $matches[1];
}
if ( preg_match( '/coupon\s+code\s+["\']([A-Z0-9]+)["\']/i', $html, $matches ) ) {
$pricing_data['coupon_code'] = sanitize_text_field( $matches[1] );
}
if ( $pricing_data['regular_price'] && $pricing_data['discount_percent'] ) {
$discount_amount = $pricing_data['regular_price'] * ( $pricing_data['discount_percent'] / 100 );
$pricing_data['sale_price'] = $pricing_data['regular_price'] - $discount_amount;
}
if ( $pricing_data['regular_price'] && $pricing_data['sale_price'] && $pricing_data['discount_percent'] ) {
$pricing_data['formatted'] = sprintf(
'$%d → $%s • Save %d%%',
$pricing_data['regular_price'],
number_format( $pricing_data['sale_price'], 2 ),
$pricing_data['discount_percent']
);
}
if ( null === $pricing_data['regular_price'] ) {
return false;
}
return $pricing_data;
}
private function get_fallback_data() {
if ( ! empty( $this->config['fallback_data'] ) ) {
return $this->config['fallback_data'];
}
return array(
'regular_price' => 39,
'sale_price' => 29.25,
'discount_percent' => 25,
'coupon_code' => 'NOW25',
'formatted' => '$39 → $29.25 • Save 25%',
'last_updated' => null,
);
}
private function schedule_retry() {
$retry_count = (int) get_option( $this->config['retry_count_key'], 0 );
if ( $retry_count >= $this->config['max_retries'] ) {
return;
}
update_option( $this->config['retry_count_key'], $retry_count + 1 );
if ( ! wp_next_scheduled( $this->config['cron_hook'] ) ) {
wp_schedule_single_event(
time() + $this->config['retry_interval'],
$this->config['cron_hook']
);
}
}
public function cron_retry_fetch() {
$fresh_data = $this->fetch_fresh_data();
if ( false !== $fresh_data ) {
$this->set_cache( $fresh_data );
$this->clear_retry_schedule();
} else {
$this->schedule_retry();
}
}
private function clear_retry_schedule() {
$timestamp = wp_next_scheduled( $this->config['cron_hook'] );
if ( $timestamp ) {
wp_unschedule_event( $timestamp, $this->config['cron_hook'] );
}
delete_option( $this->config['retry_count_key'] );
}
public function clear_all() {
$this->clear_cache();
$this->clear_retry_schedule();
}
}

View File

@@ -0,0 +1,89 @@
<?php
/**
* Utility functions for ChimpMatic.
*
* @package contact-form-7-mailchimp-extension
* @author renzo.johnson@gmail.com
* @copyright 2014-2026 https://renzojohnson.com
* @license GPL-3.0+
*/
defined( 'ABSPATH' ) || exit;
final class Cmatic_Utils {
const CMATIC_FB_A = 'chimpmatic';
public static function validate_bool( $value ): bool {
if ( is_bool( $value ) ) {
return $value;
}
if ( is_int( $value ) || is_float( $value ) ) {
return (bool) $value;
}
if ( is_string( $value ) ) {
$v = strtolower( trim( $value ) );
if ( '' === $v ) {
return false;
}
if ( is_numeric( $v ) ) {
return 0.0 !== (float) $v;
}
static $true = array( 'true', 'on', 'yes', 'y', '1' );
static $false = array( 'false', 'off', 'no', 'n', '0' );
if ( in_array( $v, $true, true ) ) {
return true;
}
if ( in_array( $v, $false, true ) ) {
return false;
}
}
return false;
}
public static function get_first( $array, $default = '' ) {
if ( is_array( $array ) && ! empty( $array ) ) {
return reset( $array );
}
return $default;
}
public static function get_newest_form_id(): ?int {
$forms = get_posts(
array(
'post_type' => 'wpcf7_contact_form',
'posts_per_page' => 1,
'orderby' => 'ID',
'order' => 'DESC',
'post_status' => 'publish',
'fields' => 'ids',
)
);
return ! empty( $forms ) ? (int) $forms[0] : null;
}
public static function get_days_since( int $timestamp ): int {
$datetime_now = new \DateTime( 'now' );
$datetime_from = new \DateTime( '@' . $timestamp );
$diff = date_diff( $datetime_now, $datetime_from );
return (int) $diff->format( '%a' );
}
private function __construct() {}
private function __clone() {}
public function __wakeup() {
throw new \Exception( 'Cannot unserialize singleton' );
}
}