225 lines
5.4 KiB
PHP
225 lines
5.4 KiB
PHP
<?php
|
||||
|
|
/**
|
|||
|
|
* Utility functions.
|
|||
|
|
*
|
|||
|
|
* @package WC_Email_Discount_Manager
|
|||
|
|
*/
|
|||
|
|
|
|||
|
|
if ( ! defined( 'ABSPATH' ) ) {
|
|||
|
|
exit;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Static helper methods shared between admin and frontend.
|
|||
|
|
*/
|
|||
|
|
class WC_EDM_Helpers {
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Find the best matching email assignment for an email address.
|
|||
|
|
*
|
|||
|
|
* Match priority:
|
|||
|
|
* 1. Exact email match (john@example.com)
|
|||
|
|
* 2. Domain match (@company.com)
|
|||
|
|
* 3. Partial match (john)
|
|||
|
|
*
|
|||
|
|
* @param string $email Email address.
|
|||
|
|
* @return object|null Matching row from the emails table, or null.
|
|||
|
|
*/
|
|||
|
|
public static function find_email_match( $email ) {
|
|||
|
|
$email = strtolower( trim( (string) $email ) );
|
|||
|
|
|
|||
|
|
if ( '' === $email || ! is_email( $email ) ) {
|
|||
|
|
return null;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
$assignments = WC_EDM_DB::get_active_emails();
|
|||
|
|
|
|||
|
|
if ( empty( $assignments ) ) {
|
|||
|
|
return null;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
$domain_match = null;
|
|||
|
|
$partial_match = null;
|
|||
|
|
|
|||
|
|
foreach ( $assignments as $assignment ) {
|
|||
|
|
$pattern = strtolower( trim( $assignment->email_pattern ) );
|
|||
|
|
|
|||
|
|
if ( '' === $pattern ) {
|
|||
|
|
continue;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 1. Exact match wins immediately.
|
|||
|
|
if ( $pattern === $email ) {
|
|||
|
|
return $assignment;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 2. Domain match: pattern starts with '@'.
|
|||
|
|
if ( null === $domain_match && 0 === strpos( $pattern, '@' ) ) {
|
|||
|
|
if ( substr( $email, -strlen( $pattern ) ) === $pattern ) {
|
|||
|
|
$domain_match = $assignment;
|
|||
|
|
}
|
|||
|
|
continue;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 3. Partial match: pattern occurs anywhere in the email.
|
|||
|
|
if ( null === $partial_match && false === strpos( $pattern, '@' ) && false !== strpos( $email, $pattern ) ) {
|
|||
|
|
$partial_match = $assignment;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if ( $domain_match ) {
|
|||
|
|
return $domain_match;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return $partial_match;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Get all product categories.
|
|||
|
|
*
|
|||
|
|
* @return WP_Term[]
|
|||
|
|
*/
|
|||
|
|
public static function get_product_categories() {
|
|||
|
|
$terms = get_terms(
|
|||
|
|
array(
|
|||
|
|
'taxonomy' => 'product_cat',
|
|||
|
|
'hide_empty' => false,
|
|||
|
|
'orderby' => 'name',
|
|||
|
|
'order' => 'ASC',
|
|||
|
|
)
|
|||
|
|
);
|
|||
|
|
|
|||
|
|
return is_wp_error( $terms ) ? array() : $terms;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Get every shipping method instance across all zones (including "Rest of the world").
|
|||
|
|
*
|
|||
|
|
* @return array Map of method key ("flat_rate:3") => array{ label, zone, method }.
|
|||
|
|
*/
|
|||
|
|
public static function get_all_shipping_methods() {
|
|||
|
|
$methods = array();
|
|||
|
|
|
|||
|
|
if ( ! class_exists( 'WC_Shipping_Zones' ) ) {
|
|||
|
|
return $methods;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
$zones = WC_Shipping_Zones::get_zones();
|
|||
|
|
|
|||
|
|
// Zone 0 = "Locations not covered by your other zones".
|
|||
|
|
$rest_of_world = new WC_Shipping_Zone( 0 );
|
|||
|
|
$zones[] = array(
|
|||
|
|
'zone_name' => $rest_of_world->get_zone_name(),
|
|||
|
|
'shipping_methods' => $rest_of_world->get_shipping_methods(),
|
|||
|
|
);
|
|||
|
|
|
|||
|
|
foreach ( $zones as $zone ) {
|
|||
|
|
$zone_name = $zone['zone_name'];
|
|||
|
|
|
|||
|
|
foreach ( $zone['shipping_methods'] as $method ) {
|
|||
|
|
$key = $method->id . ':' . $method->get_instance_id();
|
|||
|
|
|
|||
|
|
$methods[ $key ] = array(
|
|||
|
|
'key' => $key,
|
|||
|
|
'label' => $method->get_title(),
|
|||
|
|
'zone' => $zone_name,
|
|||
|
|
'method' => $method->id,
|
|||
|
|
);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return $methods;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Get all published WooCommerce coupon codes.
|
|||
|
|
*
|
|||
|
|
* @return string[]
|
|||
|
|
*/
|
|||
|
|
public static function get_coupon_codes() {
|
|||
|
|
$posts = get_posts(
|
|||
|
|
array(
|
|||
|
|
'post_type' => 'shop_coupon',
|
|||
|
|
'post_status' => 'publish',
|
|||
|
|
'posts_per_page' => 500,
|
|||
|
|
'orderby' => 'title',
|
|||
|
|
'order' => 'ASC',
|
|||
|
|
)
|
|||
|
|
);
|
|||
|
|
|
|||
|
|
$codes = array();
|
|||
|
|
|
|||
|
|
foreach ( $posts as $post ) {
|
|||
|
|
$codes[] = wc_format_coupon_code( $post->post_title );
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return array_values( array_unique( $codes ) );
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Get a human-readable description of an email assignment's target.
|
|||
|
|
*
|
|||
|
|
* @param object $assignment Row from the emails table.
|
|||
|
|
* @return string
|
|||
|
|
*/
|
|||
|
|
public static function describe_assignment( $assignment ) {
|
|||
|
|
$extra = ! empty( $assignment->free_shipping )
|
|||
|
|
? ' + ' . __( 'free shipping', 'wc-email-discount' )
|
|||
|
|
: '';
|
|||
|
|
|
|||
|
|
return self::describe_assignment_type( $assignment ) . $extra;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Describe the discount part of an assignment (without extras).
|
|||
|
|
*
|
|||
|
|
* @param object $assignment Row from the emails table.
|
|||
|
|
* @return string
|
|||
|
|
*/
|
|||
|
|
private static function describe_assignment_type( $assignment ) {
|
|||
|
|
switch ( $assignment->assignment_type ) {
|
|||
|
|
case 'group':
|
|||
|
|
$group = $assignment->group_id ? WC_EDM_DB::get_group( (int) $assignment->group_id ) : null;
|
|||
|
|
if ( $group ) {
|
|||
|
|
/* translators: 1: group name, 2: discount percent */
|
|||
|
|
return sprintf( __( '%1$s (%2$s%%)', 'wc-email-discount' ), $group->name, wc_format_localized_decimal( $group->discount_percent ) );
|
|||
|
|
}
|
|||
|
|
return __( 'Group missing', 'wc-email-discount' );
|
|||
|
|
|
|||
|
|
case 'coupon':
|
|||
|
|
/* translators: %s: coupon code */
|
|||
|
|
return sprintf( __( 'Coupon: %s', 'wc-email-discount' ), $assignment->coupon_code );
|
|||
|
|
|
|||
|
|
case 'custom':
|
|||
|
|
/* translators: %s: discount percent */
|
|||
|
|
return sprintf( __( '%s%% off entire cart', 'wc-email-discount' ), wc_format_localized_decimal( $assignment->custom_discount ) );
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return '';
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Sanitize a discount percent value into the 0–100 range.
|
|||
|
|
*
|
|||
|
|
* @param mixed $value Raw value.
|
|||
|
|
* @return float
|
|||
|
|
*/
|
|||
|
|
public static function sanitize_percent( $value ) {
|
|||
|
|
$value = (float) wc_format_decimal( wp_unslash( $value ) );
|
|||
|
|
return max( 0, min( 100, $value ) );
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Build an admin URL for the plugin page.
|
|||
|
|
*
|
|||
|
|
* @param array $args Extra query args (tab, action, id, ...).
|
|||
|
|
* @return string
|
|||
|
|
*/
|
|||
|
|
public static function admin_url( $args = array() ) {
|
|||
|
|
return add_query_arg(
|
|||
|
|
array_merge( array( 'page' => 'wc-email-discounts' ), $args ),
|
|||
|
|
admin_url( 'admin.php' )
|
|||
|
|
);
|
|||
|
|
}
|
|||
|
|
}
|