WP maple cart and page subtitle plugin upload

This commit is contained in:
Rodolfo Martinez 2025-12-12 18:30:26 -05:00
parent b3e87772ec
commit c85895d306
18 changed files with 5741 additions and 0 deletions

View file

@ -0,0 +1,795 @@
<?php
/**
* Database handler for Maple Carts.
*
* @package MapleCarts
*/
defined( 'ABSPATH' ) || exit;
/**
* Database class.
*/
class Maple_Carts_DB {
/**
* Create database tables on activation.
*/
public static function activate() {
self::create_tables();
self::seed_default_emails();
self::set_default_options();
// Schedule cron events.
if ( ! wp_next_scheduled( 'maple_carts_send_emails' ) ) {
wp_schedule_event( time(), 'every_five_minutes', 'maple_carts_send_emails' );
}
if ( ! wp_next_scheduled( 'maple_carts_cleanup' ) ) {
wp_schedule_event( time(), 'daily', 'maple_carts_cleanup' );
}
// Add custom cron interval.
add_filter( 'cron_schedules', [ __CLASS__, 'add_cron_interval' ] );
update_option( 'maple_carts_version', MAPLE_CARTS_VERSION );
}
/**
* Add custom cron interval.
*
* @param array $schedules Existing schedules.
* @return array
*/
public static function add_cron_interval( $schedules ) {
$schedules['every_five_minutes'] = [
'interval' => 300,
'display' => __( 'Every Five Minutes', 'maple-carts' ),
];
return $schedules;
}
/**
* Create database tables.
*/
public static function create_tables() {
global $wpdb;
$charset_collate = $wpdb->get_charset_collate();
$carts_table = $wpdb->prefix . MAPLE_CARTS_TABLE;
$emails_table = $wpdb->prefix . MAPLE_CARTS_EMAILS_TABLE;
$log_table = $wpdb->prefix . MAPLE_CARTS_EMAIL_LOG_TABLE;
require_once ABSPATH . 'wp-admin/includes/upgrade.php';
// Abandoned carts table.
$sql = "CREATE TABLE {$carts_table} (
id BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
session_id VARCHAR(60) NOT NULL,
email VARCHAR(200) DEFAULT NULL,
cart_contents LONGTEXT,
cart_total DECIMAL(10,2) DEFAULT 0,
items_count INT DEFAULT 0,
currency VARCHAR(10) DEFAULT 'USD',
customer_name VARCHAR(200) DEFAULT NULL,
customer_phone VARCHAR(50) DEFAULT NULL,
billing_data LONGTEXT,
status ENUM('active','abandoned','recovered','lost','converted') DEFAULT 'active',
abandonment_type ENUM('browsing','checkout','payment') DEFAULT NULL,
recovery_url VARCHAR(255) DEFAULT NULL,
coupon_code VARCHAR(50) DEFAULT NULL,
recovery_source VARCHAR(50) DEFAULT NULL,
unsubscribed TINYINT(1) DEFAULT 0,
ip_address VARCHAR(45) DEFAULT NULL,
user_agent TEXT,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
first_seen_at DATETIME DEFAULT NULL,
last_tracked_at DATETIME DEFAULT NULL,
abandoned_at DATETIME DEFAULT NULL,
recovered_at DATETIME DEFAULT NULL,
PRIMARY KEY (id),
UNIQUE KEY session_id (session_id),
KEY email (email),
KEY status (status),
KEY created_at (created_at),
KEY updated_at (updated_at),
KEY last_tracked_at (last_tracked_at),
KEY status_tracked (status, last_tracked_at)
) {$charset_collate};";
dbDelta( $sql );
// Email templates table.
$sql = "CREATE TABLE {$emails_table} (
id BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
name VARCHAR(200) NOT NULL,
subject VARCHAR(500) NOT NULL,
body LONGTEXT NOT NULL,
delay_value INT(11) DEFAULT 1,
delay_unit ENUM('minutes','hours','days') DEFAULT 'hours',
is_active TINYINT(1) DEFAULT 0,
include_coupon TINYINT(1) DEFAULT 0,
coupon_type ENUM('percent','fixed_cart') DEFAULT 'percent',
coupon_amount DECIMAL(10,2) DEFAULT 10,
coupon_expires_days INT(11) DEFAULT 7,
sort_order INT(11) DEFAULT 0,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (id),
KEY is_active (is_active),
KEY sort_order (sort_order)
) {$charset_collate};";
dbDelta( $sql );
// Email log table.
$sql = "CREATE TABLE {$log_table} (
id BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
cart_id BIGINT(20) UNSIGNED NOT NULL,
email_template_id BIGINT(20) UNSIGNED NOT NULL,
scheduled_at DATETIME NOT NULL,
sent_at DATETIME DEFAULT NULL,
status ENUM('pending','sent','failed','cancelled') DEFAULT 'pending',
coupon_code VARCHAR(50) DEFAULT NULL,
error_message TEXT,
PRIMARY KEY (id),
KEY cart_id (cart_id),
KEY email_template_id (email_template_id),
KEY status (status),
KEY scheduled_at (scheduled_at)
) {$charset_collate};";
dbDelta( $sql );
}
/**
* Set default options.
*/
public static function set_default_options() {
$defaults = [
'enabled' => 'yes',
'cart_cutoff_time' => 15, // Minutes before cart is considered abandoned.
'delete_after_days' => 90, // Days to keep abandoned cart data.
'email_mode' => 'builtin', // 'builtin' or 'mailjet'.
'mailjet_api_key' => '',
'mailjet_secret_key'=> '',
'from_name' => get_bloginfo( 'name' ),
'from_email' => get_option( 'admin_email' ),
'reply_to' => get_option( 'admin_email' ),
'notify_admin' => 'yes',
'admin_email' => get_option( 'admin_email' ),
'require_consent' => 'no',
'consent_text' => __( 'I agree to receive cart reminder emails if I don\'t complete my purchase.', 'maple-carts' ),
'show_delete_link' => 'no',
'exclude_roles' => [],
'gdpr_notice' => '',
];
$existing = get_option( 'maple_carts_settings', [] );
$merged = array_merge( $defaults, $existing );
update_option( 'maple_carts_settings', $merged );
}
/**
* Seed default email templates.
*/
public static function seed_default_emails() {
global $wpdb;
$table = $wpdb->prefix . MAPLE_CARTS_EMAILS_TABLE;
$count = $wpdb->get_var( "SELECT COUNT(*) FROM {$table}" );
if ( $count > 0 ) {
return;
}
$templates = [
[
'name' => __( 'First Reminder', 'maple-carts' ),
'subject' => __( '{{customer_name}}, you left something behind!', 'maple-carts' ),
'body' => self::get_default_email_body( 1 ),
'delay_value' => 1,
'delay_unit' => 'hours',
'is_active' => 0,
'include_coupon' => 0,
'sort_order' => 1,
],
[
'name' => __( 'Second Reminder', 'maple-carts' ),
'subject' => __( 'Your cart is waiting, {{customer_name}}', 'maple-carts' ),
'body' => self::get_default_email_body( 2 ),
'delay_value' => 24,
'delay_unit' => 'hours',
'is_active' => 0,
'include_coupon' => 0,
'sort_order' => 2,
],
[
'name' => __( 'Final Reminder with Discount', 'maple-carts' ),
'subject' => __( 'Last chance! {{coupon_amount}} off your cart', 'maple-carts' ),
'body' => self::get_default_email_body( 3 ),
'delay_value' => 3,
'delay_unit' => 'days',
'is_active' => 0,
'include_coupon' => 1,
'coupon_type' => 'percent',
'coupon_amount' => 10,
'sort_order' => 3,
],
];
foreach ( $templates as $template ) {
$wpdb->insert( $table, $template );
}
}
/**
* Get default email body.
*
* @param int $template_num Template number.
* @return string
*/
private static function get_default_email_body( $template_num ) {
$bodies = [
1 => '<p>Hi {{customer_name}},</p>
<p>We noticed you left some items in your cart at {{site_name}}. No worries your cart is saved and ready when you are!</p>
<p>{{cart_contents}}</p>
<p><a href="{{recovery_url}}" style="background-color:#0073aa;color:#ffffff;padding:12px 24px;text-decoration:none;border-radius:4px;display:inline-block;">Complete Your Order</a></p>
<p>If you have any questions, just reply to this email.</p>
<p>Thanks,<br>{{site_name}}</p>
<p style="font-size:12px;color:#666;">{{unsubscribe_link}}{{delete_data_link}}</p>',
2 => '<p>Hi {{customer_name}},</p>
<p>Just a friendly reminder that your cart at {{site_name}} is still waiting for you.</p>
<p>{{cart_contents}}</p>
<p><strong>Cart Total: {{cart_total}}</strong></p>
<p><a href="{{recovery_url}}" style="background-color:#0073aa;color:#ffffff;padding:12px 24px;text-decoration:none;border-radius:4px;display:inline-block;">Return to Your Cart</a></p>
<p>Best,<br>{{site_name}}</p>
<p style="font-size:12px;color:#666;">{{unsubscribe_link}}{{delete_data_link}}</p>',
3 => '<p>Hi {{customer_name}},</p>
<p>This is your last reminder about the items in your cart. To help you complete your purchase, we\'re offering you an exclusive discount!</p>
<p>{{cart_contents}}</p>
<p><strong>Use code <span style="background:#f0f0f0;padding:4px 8px;font-family:monospace;">{{coupon_code}}</span> for {{coupon_amount}} off!</strong></p>
<p><a href="{{recovery_url}}" style="background-color:#0073aa;color:#ffffff;padding:12px 24px;text-decoration:none;border-radius:4px;display:inline-block;">Claim Your Discount</a></p>
<p>This offer expires in {{coupon_expires}} days.</p>
<p>Thanks,<br>{{site_name}}</p>
<p style="font-size:12px;color:#666;">{{unsubscribe_link}}{{delete_data_link}}</p>',
];
return $bodies[ $template_num ] ?? $bodies[1];
}
/**
* Get a cart by session ID.
*
* @param string $session_id Session ID.
* @return object|null
*/
public static function get_cart_by_session( $session_id ) {
global $wpdb;
$table = $wpdb->prefix . MAPLE_CARTS_TABLE;
return $wpdb->get_row( $wpdb->prepare( "SELECT * FROM {$table} WHERE session_id = %s", $session_id ) );
}
/**
* Get a cart by email.
*
* @param string $email Email address.
* @param array $statuses Statuses to filter by.
* @return object|null
*/
public static function get_cart_by_email( $email, $statuses = [ 'active', 'abandoned' ] ) {
global $wpdb;
$table = $wpdb->prefix . MAPLE_CARTS_TABLE;
$placeholders = implode( ',', array_fill( 0, count( $statuses ), '%s' ) );
$params = array_merge( [ $email ], $statuses );
return $wpdb->get_row(
$wpdb->prepare(
"SELECT * FROM {$table} WHERE email = %s AND status IN ({$placeholders}) ORDER BY created_at DESC LIMIT 1",
$params
)
);
}
/**
* Insert or update a cart.
*
* @param array $data Cart data.
* @return int|false Cart ID or false on failure.
*/
public static function save_cart( $data ) {
global $wpdb;
$table = $wpdb->prefix . MAPLE_CARTS_TABLE;
// Always update last_tracked_at when saving.
$data['last_tracked_at'] = current_time( 'mysql' );
$existing = null;
if ( ! empty( $data['session_id'] ) ) {
$existing = self::get_cart_by_session( $data['session_id'] );
}
if ( $existing ) {
$wpdb->update( $table, $data, [ 'id' => $existing->id ] );
$cart_id = $existing->id;
} else {
// Set first_seen_at for new carts.
$data['first_seen_at'] = current_time( 'mysql' );
$wpdb->insert( $table, $data );
$cart_id = $wpdb->insert_id;
}
// Fire webhook for integrations (Facebook CAPI, etc.).
if ( $cart_id ) {
$cart = self::get_cart_by_id( $cart_id );
if ( $cart ) {
/**
* Fires when a cart is created or updated.
* Useful for Facebook CAPI, retargeting pixels, etc.
*
* @param object $cart Cart object.
*/
do_action( 'maple_carts_cart_updated', $cart );
}
}
return $cart_id;
}
/**
* Update cart status.
*
* @param int $cart_id Cart ID.
* @param string $status New status.
* @param array $extra Extra data to update.
*/
public static function update_cart_status( $cart_id, $status, $extra = [] ) {
global $wpdb;
$table = $wpdb->prefix . MAPLE_CARTS_TABLE;
$data = array_merge( [ 'status' => $status ], $extra );
if ( 'abandoned' === $status ) {
$data['abandoned_at'] = current_time( 'mysql' );
} elseif ( 'recovered' === $status ) {
$data['recovered_at'] = current_time( 'mysql' );
}
$wpdb->update( $table, $data, [ 'id' => $cart_id ] );
// Fire hooks for integrations (Mailjet, etc.).
$cart = self::get_cart_by_id( $cart_id );
if ( $cart ) {
if ( 'abandoned' === $status ) {
/**
* Fires when a cart is marked as abandoned.
*
* @param int $cart_id Cart ID.
* @param object $cart Cart object.
*/
do_action( 'maple_carts_cart_abandoned', $cart_id, $cart );
} elseif ( 'recovered' === $status ) {
/**
* Fires when a cart is recovered.
*
* @param int $cart_id Cart ID.
* @param object $cart Cart object.
*/
do_action( 'maple_carts_cart_recovered', $cart_id, $cart );
} elseif ( 'converted' === $status ) {
/**
* Fires when a cart is converted (order placed).
*
* @param int $cart_id Cart ID.
* @param object $cart Cart object.
*/
do_action( 'maple_carts_cart_converted', $cart_id, $cart );
}
}
}
/**
* Get cart by ID.
*
* @param int $cart_id Cart ID.
* @return object|null
*/
public static function get_cart_by_id( $cart_id ) {
global $wpdb;
$table = $wpdb->prefix . MAPLE_CARTS_TABLE;
return $wpdb->get_row( $wpdb->prepare( "SELECT * FROM {$table} WHERE id = %d", $cart_id ) );
}
/**
* Get carts by status.
*
* @param string $status Status.
* @param int $limit Limit.
* @param int $offset Offset.
* @return array
*/
public static function get_carts_by_status( $status, $limit = 50, $offset = 0 ) {
global $wpdb;
$table = $wpdb->prefix . MAPLE_CARTS_TABLE;
return $wpdb->get_results(
$wpdb->prepare(
"SELECT * FROM {$table} WHERE status = %s ORDER BY created_at DESC LIMIT %d OFFSET %d",
$status,
$limit,
$offset
)
);
}
/**
* Count carts by status.
*
* @param string $status Status.
* @return int
*/
public static function count_carts_by_status( $status ) {
global $wpdb;
$table = $wpdb->prefix . MAPLE_CARTS_TABLE;
return (int) $wpdb->get_var(
$wpdb->prepare( "SELECT COUNT(*) FROM {$table} WHERE status = %s", $status )
);
}
/**
* Get all email templates.
*
* @param bool $active_only Only active templates.
* @return array
*/
public static function get_email_templates( $active_only = false ) {
global $wpdb;
$table = $wpdb->prefix . MAPLE_CARTS_EMAILS_TABLE;
$where = $active_only ? 'WHERE is_active = 1' : '';
return $wpdb->get_results( "SELECT * FROM {$table} {$where} ORDER BY sort_order ASC" );
}
/**
* Get email template by ID.
*
* @param int $id Template ID.
* @return object|null
*/
public static function get_email_template( $id ) {
global $wpdb;
$table = $wpdb->prefix . MAPLE_CARTS_EMAILS_TABLE;
return $wpdb->get_row( $wpdb->prepare( "SELECT * FROM {$table} WHERE id = %d", $id ) );
}
/**
* Save email template.
*
* @param array $data Template data.
* @return int|false Template ID or false.
*/
public static function save_email_template( $data ) {
global $wpdb;
$table = $wpdb->prefix . MAPLE_CARTS_EMAILS_TABLE;
if ( ! empty( $data['id'] ) ) {
$id = $data['id'];
unset( $data['id'] );
$wpdb->update( $table, $data, [ 'id' => $id ] );
return $id;
} else {
$wpdb->insert( $table, $data );
return $wpdb->insert_id;
}
}
/**
* Delete email template.
*
* @param int $id Template ID.
* @return bool
*/
public static function delete_email_template( $id ) {
global $wpdb;
$table = $wpdb->prefix . MAPLE_CARTS_EMAILS_TABLE;
return $wpdb->delete( $table, [ 'id' => $id ] ) !== false;
}
/**
* Schedule emails for a cart.
*
* @param int $cart_id Cart ID.
*/
public static function schedule_emails( $cart_id ) {
global $wpdb;
$log_table = $wpdb->prefix . MAPLE_CARTS_EMAIL_LOG_TABLE;
$cart = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM {$wpdb->prefix}" . MAPLE_CARTS_TABLE . " WHERE id = %d", $cart_id ) );
if ( ! $cart || $cart->unsubscribed ) {
return;
}
$templates = self::get_email_templates( true );
if ( empty( $templates ) ) {
return;
}
$base_time = $cart->abandoned_at ? $cart->abandoned_at : current_time( 'mysql' );
// Get all existing scheduled emails for this cart in one query.
$existing_template_ids = $wpdb->get_col(
$wpdb->prepare(
"SELECT email_template_id FROM {$log_table} WHERE cart_id = %d",
$cart_id
)
);
$existing_set = array_flip( $existing_template_ids );
foreach ( $templates as $template ) {
// Skip if already scheduled.
if ( isset( $existing_set[ $template->id ] ) ) {
continue;
}
// Calculate scheduled time.
$delay_seconds = self::get_delay_seconds( $template->delay_value, $template->delay_unit );
$scheduled_at = gmdate( 'Y-m-d H:i:s', strtotime( $base_time ) + $delay_seconds );
$wpdb->insert(
$log_table,
[
'cart_id' => $cart_id,
'email_template_id' => $template->id,
'scheduled_at' => $scheduled_at,
'status' => 'pending',
]
);
}
}
/**
* Get delay in seconds.
*
* @param int $value Delay value.
* @param string $unit Delay unit.
* @return int
*/
private static function get_delay_seconds( $value, $unit ) {
switch ( $unit ) {
case 'minutes':
return $value * 60;
case 'hours':
return $value * 3600;
case 'days':
return $value * 86400;
default:
return $value * 3600;
}
}
/**
* Get pending emails to send.
*
* @return array
*/
public static function get_pending_emails() {
global $wpdb;
$log_table = $wpdb->prefix . MAPLE_CARTS_EMAIL_LOG_TABLE;
$carts_table = $wpdb->prefix . MAPLE_CARTS_TABLE;
$email_table = $wpdb->prefix . MAPLE_CARTS_EMAILS_TABLE;
$now = current_time( 'mysql' );
return $wpdb->get_results(
$wpdb->prepare(
"SELECT l.*, c.email, c.customer_name, c.cart_contents, c.cart_total, c.currency,
c.session_id, c.coupon_code as cart_coupon, c.billing_data,
e.subject, e.body, e.include_coupon, e.coupon_type, e.coupon_amount, e.coupon_expires_days
FROM {$log_table} l
JOIN {$carts_table} c ON l.cart_id = c.id
JOIN {$email_table} e ON l.email_template_id = e.id
WHERE l.status = 'pending'
AND l.scheduled_at <= %s
AND c.status = 'abandoned'
AND c.unsubscribed = 0
ORDER BY l.scheduled_at ASC
LIMIT 50",
$now
)
);
}
/**
* Update email log status.
*
* @param int $id Log ID.
* @param string $status Status.
* @param array $extra Extra data.
*/
public static function update_email_log( $id, $status, $extra = [] ) {
global $wpdb;
$table = $wpdb->prefix . MAPLE_CARTS_EMAIL_LOG_TABLE;
$data = array_merge( [ 'status' => $status ], $extra );
if ( 'sent' === $status ) {
$data['sent_at'] = current_time( 'mysql' );
}
$wpdb->update( $table, $data, [ 'id' => $id ] );
}
/**
* Cancel pending emails for a cart.
*
* @param int $cart_id Cart ID.
*/
public static function cancel_pending_emails( $cart_id ) {
global $wpdb;
$table = $wpdb->prefix . MAPLE_CARTS_EMAIL_LOG_TABLE;
$wpdb->update(
$table,
[ 'status' => 'cancelled' ],
[ 'cart_id' => $cart_id, 'status' => 'pending' ]
);
}
/**
* Get cart statistics.
*
* @param string $period Period (7days, 30days, all).
* @return array
*/
public static function get_stats( $period = '30days' ) {
global $wpdb;
$table = $wpdb->prefix . MAPLE_CARTS_TABLE;
$date_filter = '';
if ( '7days' === $period ) {
$date_filter = "AND created_at >= DATE_SUB(NOW(), INTERVAL 7 DAY)";
} elseif ( '30days' === $period ) {
$date_filter = "AND created_at >= DATE_SUB(NOW(), INTERVAL 30 DAY)";
}
$stats = [
'abandoned' => 0,
'recovered' => 0,
'lost' => 0,
'abandoned_value' => 0,
'recovered_value' => 0,
'recovery_rate' => 0,
];
$results = $wpdb->get_results(
"SELECT status, COUNT(*) as count, SUM(cart_total) as total
FROM {$table}
WHERE 1=1 {$date_filter}
GROUP BY status"
);
foreach ( $results as $row ) {
if ( 'abandoned' === $row->status ) {
$stats['abandoned'] = (int) $row->count;
$stats['abandoned_value'] = (float) $row->total;
} elseif ( 'recovered' === $row->status ) {
$stats['recovered'] = (int) $row->count;
$stats['recovered_value'] = (float) $row->total;
} elseif ( 'lost' === $row->status ) {
$stats['lost'] = (int) $row->count;
}
}
$total_actionable = $stats['abandoned'] + $stats['recovered'] + $stats['lost'];
if ( $total_actionable > 0 ) {
$stats['recovery_rate'] = round( ( $stats['recovered'] / $total_actionable ) * 100, 1 );
}
return $stats;
}
/**
* Cleanup old data.
* Runs daily, processes in batches to prevent memory issues.
*/
public static function cleanup_old_data() {
global $wpdb;
// Prevent overlapping runs.
if ( get_transient( 'maple_carts_cleanup_running' ) ) {
return;
}
set_transient( 'maple_carts_cleanup_running', 1, 3600 ); // 1 hour lock.
$days = (int) Maple_Carts::get_option( 'delete_after_days', 90 );
$carts_table = $wpdb->prefix . MAPLE_CARTS_TABLE;
$log_table = $wpdb->prefix . MAPLE_CARTS_EMAIL_LOG_TABLE;
$batch_size = 500;
// Delete old carts in batches.
$wpdb->query(
$wpdb->prepare(
"DELETE FROM {$carts_table} WHERE created_at < DATE_SUB(NOW(), INTERVAL %d DAY) LIMIT %d",
$days,
$batch_size
)
);
// Delete orphaned email logs in batches.
$wpdb->query(
"DELETE l FROM {$log_table} l
LEFT JOIN {$carts_table} c ON l.cart_id = c.id
WHERE c.id IS NULL
LIMIT {$batch_size}"
);
// Delete expired coupons created by this plugin (batched).
$expired_coupons = $wpdb->get_col(
$wpdb->prepare(
"SELECT ID FROM {$wpdb->posts}
WHERE post_type = 'shop_coupon'
AND ID IN (
SELECT post_id FROM {$wpdb->postmeta}
WHERE meta_key = '_maple_carts_coupon' AND meta_value = '1'
)
AND ID IN (
SELECT post_id FROM {$wpdb->postmeta}
WHERE meta_key = 'date_expires' AND meta_value < UNIX_TIMESTAMP()
)
LIMIT %d",
$batch_size
)
);
foreach ( $expired_coupons as $coupon_id ) {
wp_delete_post( $coupon_id, true );
}
// Delete unused coupons older than 30 days (never used, cart may have converted another way).
$unused_old_coupons = $wpdb->get_col(
$wpdb->prepare(
"SELECT p.ID FROM {$wpdb->posts} p
INNER JOIN {$wpdb->postmeta} pm_maple ON p.ID = pm_maple.post_id
AND pm_maple.meta_key = '_maple_carts_coupon' AND pm_maple.meta_value = '1'
INNER JOIN {$wpdb->postmeta} pm_created ON p.ID = pm_created.post_id
AND pm_created.meta_key = '_maple_carts_created_at' AND pm_created.meta_value < %d
INNER JOIN {$wpdb->postmeta} pm_usage ON p.ID = pm_usage.post_id
AND pm_usage.meta_key = 'usage_count' AND pm_usage.meta_value = '0'
WHERE p.post_type = 'shop_coupon'
LIMIT %d",
strtotime( '-30 days' ),
$batch_size
)
);
foreach ( $unused_old_coupons as $coupon_id ) {
wp_delete_post( $coupon_id, true );
}
delete_transient( 'maple_carts_cleanup_running' );
}
/**
* Delete a cart.
*
* @param int $cart_id Cart ID.
* @return bool
*/
public static function delete_cart( $cart_id ) {
global $wpdb;
// Delete email logs first.
$wpdb->delete( $wpdb->prefix . MAPLE_CARTS_EMAIL_LOG_TABLE, [ 'cart_id' => $cart_id ] );
// Delete cart.
return $wpdb->delete( $wpdb->prefix . MAPLE_CARTS_TABLE, [ 'id' => $cart_id ] ) !== false;
}
}
// Register cron interval early.
add_filter( 'cron_schedules', [ 'Maple_Carts_DB', 'add_cron_interval' ] );