feat: initial implementation of WooList phpList Integration plugin v1.0.0
- phpList REST API wrapper with subscriber get-or-create + list assignment - WooCommerce Settings tab (5 sections: connection, orders, signup, newsletter) - Test Connection button via admin-post action - Hooks for order completed/cancelled and user_register events - [woolist_newsletter] shortcode with jQuery AJAX, fixed & auto-generated coupons - Responsive front-end form styles and JS with loading/success/error states Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
201
woolist-phplist/includes/class-woolist-shortcode.php
Normal file
201
woolist-phplist/includes/class-woolist-shortcode.php
Normal file
@@ -0,0 +1,201 @@
|
||||
<?php
|
||||
/**
|
||||
* [woolist_newsletter] shortcode and AJAX handler.
|
||||
*
|
||||
* @package WooList
|
||||
*/
|
||||
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
class WooList_Shortcode {
|
||||
|
||||
/** @var WooList_API */
|
||||
private WooList_API $api;
|
||||
|
||||
public function __construct( WooList_API $api ) {
|
||||
$this->api = $api;
|
||||
|
||||
add_shortcode( 'woolist_newsletter', [ $this, 'render_shortcode' ] );
|
||||
|
||||
// Register AJAX handlers for logged-in and guest visitors.
|
||||
add_action( 'wp_ajax_woolist_newsletter_submit', [ $this, 'handle_ajax' ] );
|
||||
add_action( 'wp_ajax_nopriv_woolist_newsletter_submit', [ $this, 'handle_ajax' ] );
|
||||
|
||||
// Enqueue front-end assets.
|
||||
add_action( 'wp_enqueue_scripts', [ $this, 'enqueue_assets' ] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueue CSS and JS for the newsletter form.
|
||||
*/
|
||||
public function enqueue_assets(): void {
|
||||
wp_enqueue_style(
|
||||
'woolist-public',
|
||||
WOOLIST_URL . 'assets/css/woolist-public.css',
|
||||
[],
|
||||
WOOLIST_VERSION
|
||||
);
|
||||
|
||||
wp_enqueue_script(
|
||||
'woolist-public',
|
||||
WOOLIST_URL . 'assets/js/woolist-public.js',
|
||||
[ 'jquery' ],
|
||||
WOOLIST_VERSION,
|
||||
true
|
||||
);
|
||||
|
||||
wp_localize_script(
|
||||
'woolist-public',
|
||||
'woolist',
|
||||
[
|
||||
'ajaxurl' => admin_url( 'admin-ajax.php' ),
|
||||
'nonce' => wp_create_nonce( 'woolist_newsletter_nonce' ),
|
||||
'i18n' => [
|
||||
'subscribing' => __( 'Subscribing…', 'woolist-phplist' ),
|
||||
'subscribe' => __( 'Subscribe', 'woolist-phplist' ),
|
||||
'error' => __( 'Something went wrong. Please try again.', 'woolist-phplist' ),
|
||||
],
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the [woolist_newsletter] shortcode.
|
||||
*
|
||||
* @return string HTML output.
|
||||
*/
|
||||
public function render_shortcode(): string {
|
||||
if ( get_option( 'woolist_sync_newsletter' ) !== 'yes' ) {
|
||||
if ( defined( 'WP_DEBUG' ) && WP_DEBUG && current_user_can( 'manage_options' ) ) {
|
||||
return '<!-- WooList: newsletter shortcode is disabled in settings -->';
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
ob_start();
|
||||
?>
|
||||
<div class="woolist-newsletter-wrap">
|
||||
<form id="woolist-newsletter-form" novalidate>
|
||||
<div class="woolist-form-row">
|
||||
<input
|
||||
type="email"
|
||||
name="woolist_email"
|
||||
class="woolist-email-input"
|
||||
placeholder="<?php esc_attr_e( 'Your email address', 'woolist-phplist' ); ?>"
|
||||
required
|
||||
/>
|
||||
<button type="submit" class="woolist-submit-btn">
|
||||
<?php esc_html_e( 'Subscribe', 'woolist-phplist' ); ?>
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
<div class="woolist-response" style="display:none;" aria-live="polite"></div>
|
||||
</div>
|
||||
<?php
|
||||
return ob_get_clean();
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle AJAX newsletter subscription submission.
|
||||
*/
|
||||
public function handle_ajax(): void {
|
||||
// 1. Verify nonce.
|
||||
if ( ! check_ajax_referer( 'woolist_newsletter_nonce', 'nonce', false ) ) {
|
||||
wp_send_json_error( [ 'message' => __( 'Security check failed. Please refresh and try again.', 'woolist-phplist' ) ], 403 );
|
||||
}
|
||||
|
||||
// 2. Validate email.
|
||||
$email = isset( $_POST['woolist_email'] ) ? sanitize_email( wp_unslash( $_POST['woolist_email'] ) ) : '';
|
||||
if ( ! is_email( $email ) ) {
|
||||
wp_send_json_error( [ 'message' => __( 'Please enter a valid email address.', 'woolist-phplist' ) ], 400 );
|
||||
}
|
||||
|
||||
// 3. Get list ID.
|
||||
$list_id = (int) get_option( 'woolist_newsletter_list_id', 0 );
|
||||
if ( $list_id < 1 ) {
|
||||
wp_send_json_error( [ 'message' => __( 'Newsletter is not configured. Please contact the site administrator.', 'woolist-phplist' ) ], 500 );
|
||||
}
|
||||
|
||||
// 4. Subscribe email to phpList.
|
||||
$result = $this->api->subscribe_email_to_list( $email, $list_id );
|
||||
if ( ! $result['success'] ) {
|
||||
wp_send_json_error( [ 'message' => __( 'Could not subscribe your email. Please try again later.', 'woolist-phplist' ) ], 500 );
|
||||
}
|
||||
|
||||
// 5. Handle coupon generation.
|
||||
$coupon_code = '';
|
||||
$thankyou_msg = get_option( 'woolist_newsletter_thankyou', __( 'Thank you for subscribing!', 'woolist-phplist' ) );
|
||||
|
||||
if ( get_option( 'woolist_newsletter_enable_coupon' ) === 'yes' ) {
|
||||
$coupon_mode = get_option( 'woolist_coupon_mode', 'fixed' );
|
||||
|
||||
if ( $coupon_mode === 'fixed' ) {
|
||||
$coupon_code = sanitize_text_field( get_option( 'woolist_coupon_fixed_code', '' ) );
|
||||
} else {
|
||||
$coupon_code = $this->generate_coupon( $email );
|
||||
}
|
||||
}
|
||||
|
||||
// Replace {coupon} placeholder in the thank-you message.
|
||||
$thankyou_msg = str_replace( '{coupon}', esc_html( $coupon_code ), $thankyou_msg );
|
||||
|
||||
wp_send_json_success(
|
||||
[
|
||||
'message' => wp_kses_post( $thankyou_msg ),
|
||||
'coupon' => $coupon_code,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a unique WooCommerce percentage coupon for the subscriber.
|
||||
*
|
||||
* @param string $email Subscriber email.
|
||||
* @return string Generated coupon code, or empty string on failure.
|
||||
*/
|
||||
private function generate_coupon( string $email ): string {
|
||||
if ( ! class_exists( 'WC_Coupon' ) ) {
|
||||
error_log( '[WooList] WC_Coupon class not available; cannot generate coupon.' );
|
||||
return '';
|
||||
}
|
||||
|
||||
$discount_pct = (int) get_option( 'woolist_coupon_discount_pct', 10 );
|
||||
$expiry_days = (int) get_option( 'woolist_coupon_expiry_days', 30 );
|
||||
$coupon_code = 'WOOLIST-' . strtoupper( substr( md5( $email . time() ), 0, 8 ) );
|
||||
|
||||
$expiry_date = '';
|
||||
if ( $expiry_days > 0 ) {
|
||||
$expiry_date = gmdate( 'Y-m-d', strtotime( '+' . $expiry_days . ' days' ) );
|
||||
}
|
||||
|
||||
$post_id = wp_insert_post(
|
||||
[
|
||||
'post_title' => $coupon_code,
|
||||
'post_name' => $coupon_code,
|
||||
'post_status' => 'publish',
|
||||
'post_type' => 'shop_coupon',
|
||||
'post_excerpt' => 'WooList newsletter signup coupon for ' . $email,
|
||||
],
|
||||
true
|
||||
);
|
||||
|
||||
if ( is_wp_error( $post_id ) ) {
|
||||
error_log( '[WooList] Failed to create coupon post: ' . $post_id->get_error_message() );
|
||||
return '';
|
||||
}
|
||||
|
||||
// Set coupon meta via WC functions.
|
||||
update_post_meta( $post_id, 'discount_type', 'percent' );
|
||||
update_post_meta( $post_id, 'coupon_amount', (string) $discount_pct );
|
||||
update_post_meta( $post_id, 'usage_limit', '1' );
|
||||
update_post_meta( $post_id, 'usage_limit_per_user', '1' );
|
||||
update_post_meta( $post_id, 'individual_use', 'yes' );
|
||||
update_post_meta( $post_id, 'customer_email', [ $email ] );
|
||||
|
||||
if ( $expiry_date ) {
|
||||
update_post_meta( $post_id, 'date_expires', strtotime( $expiry_date ) );
|
||||
}
|
||||
|
||||
return $coupon_code;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user