468 lines
15 KiB
PHP
468 lines
15 KiB
PHP
<?php
|
|
/**
|
|
* Paid Memberships Pro Sensor helper
|
|
*
|
|
* @since 5.5.2
|
|
*
|
|
* @package wsal
|
|
* @subpackage wsal-paid-memberships-pro
|
|
*/
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace WSAL\WP_Sensors\Helpers;
|
|
|
|
use WSAL\Helpers\WP_Helper;
|
|
use WSAL\PMP_Addon_Member_Edit_Panel\PMP_Addon_Member_Edit_Panel;
|
|
|
|
// Exit if accessed directly.
|
|
if ( ! defined( 'ABSPATH' ) ) {
|
|
exit;
|
|
}
|
|
|
|
if ( ! class_exists( '\WSAL\WP_Sensors\Helpers\Paid_Memberships_Pro_Helper' ) ) {
|
|
|
|
/**
|
|
* Helper Sensor class for Paid Memberships Pro.
|
|
*
|
|
* @package wsal
|
|
* @subpackage sensors-helpers
|
|
*
|
|
* @since 5.5.2
|
|
*/
|
|
class Paid_Memberships_Pro_Helper {
|
|
|
|
/**
|
|
* Class cache to store the state of the plugin.
|
|
*
|
|
* @var bool
|
|
*
|
|
* @since 5.5.2
|
|
*/
|
|
private static $plugin_active = null;
|
|
|
|
/**
|
|
* Class cache to store the state of the plugin for sensors.
|
|
*
|
|
* @var bool
|
|
*
|
|
* @since 5.5.2
|
|
*/
|
|
private static $plugin_active_for_sensors = null;
|
|
|
|
/**
|
|
* List of plugin event IDs for Paid Memberships Pro
|
|
*
|
|
* @var int[]
|
|
*
|
|
* @since 5.5.2
|
|
*/
|
|
private static $plugin_events = array( 9501, 9502, 9503, 9504, 9505, 9506, 9507, 9508, 9509 );
|
|
|
|
/**
|
|
* Confirms we can load alerts if plugin is active
|
|
*
|
|
* @return bool
|
|
*
|
|
* @since 5.5.2
|
|
*/
|
|
public static function load_alerts_for_sensor(): bool {
|
|
if ( null === self::$plugin_active_for_sensors ) {
|
|
self::$plugin_active_for_sensors = WP_Helper::is_plugin_active( 'paid-memberships-pro/paid-memberships-pro.php' );
|
|
}
|
|
|
|
return self::$plugin_active_for_sensors;
|
|
}
|
|
|
|
/**
|
|
* Register a custom event object within WSAL.
|
|
*
|
|
* @param array $objects array of objects current registered within WSAL.
|
|
*
|
|
* @since 5.5.2
|
|
*/
|
|
public static function wsal_paid_memberships_pro_add_custom_event_objects( $objects ) {
|
|
$new_objects = array(
|
|
'pmpro_membership_levels' => esc_html__( 'Paid Memberships Pro - Membership Levels', 'wp-security-audit-log' ),
|
|
'pmpro_members' => esc_html__( 'Paid Memberships Pro - Members', 'wp-security-audit-log' ),
|
|
'pmpro_subscriptions' => esc_html__( 'Paid Memberships Pro - Subscriptions', 'wp-security-audit-log' ),
|
|
'pmpro_orders' => esc_html__( 'Paid Memberships Pro - Orders', 'wp-security-audit-log' ),
|
|
);
|
|
|
|
$objects = array_merge( $objects, $new_objects );
|
|
|
|
return $objects;
|
|
}
|
|
|
|
/**
|
|
* Check if Paid Memberships Pro is active.
|
|
*
|
|
* @return bool
|
|
*
|
|
* @since 5.5.2
|
|
*/
|
|
public static function is_pmp_active() {
|
|
if ( null === self::$plugin_active ) {
|
|
if ( \function_exists( 'pmpro_getAllLevels' ) ) {
|
|
self::$plugin_active = true;
|
|
} else {
|
|
self::$plugin_active = false;
|
|
}
|
|
}
|
|
|
|
return self::$plugin_active;
|
|
}
|
|
|
|
/**
|
|
* Returns the events associated with Paid Memberships Pro.
|
|
*
|
|
* @since 5.5.2
|
|
*/
|
|
public static function get_plugin_events(): array {
|
|
return self::$plugin_events;
|
|
}
|
|
|
|
/**
|
|
* Determine if the 'Disable New Signups' setting for a PMP membership has changed between old and new values, and if so return a string indicating the change.
|
|
*
|
|
* @param int $old_value - The old value obtained from pmpro_getLevel(). Format: [allow_signups] => 0 OR 1.
|
|
* @param string $new_value - The new value obtained from $_REQUEST. Format: [disable_signups] => yes OR NULL.
|
|
*
|
|
* @return string - Returns as string with the change, if detected, otherwise returns an empty string.
|
|
*
|
|
* @since 5.5.2
|
|
*/
|
|
public static function pmp_enable_signup_setting_change( $old_value, $new_value ): string {
|
|
$old_allow_signups_str = (string) $old_value;
|
|
$new_disable_signups_str = (string) $new_value;
|
|
|
|
$change_str = '';
|
|
|
|
// Signups: disabled => enabled.
|
|
if ( ( '0' === $old_allow_signups_str || '' === $old_allow_signups_str ) && ( '' === $new_disable_signups_str ) ) {
|
|
$change_str = 'disabled => enabled';
|
|
}
|
|
|
|
// Signups: enabled => disabled.
|
|
if ( ( '1' === $old_allow_signups_str ) && ( 'yes' === $new_disable_signups_str ) ) {
|
|
$change_str = 'enabled => disabled';
|
|
}
|
|
|
|
return $change_str;
|
|
}
|
|
|
|
/**
|
|
* Format membership changes for display in the event list in wp-admin.
|
|
*
|
|
* @param string $value - Meta value.
|
|
* @param string $expression - Meta expression including the surrounding percentage chars.
|
|
* @param array $configuration - formatter configuration.
|
|
* @param int|null $occurrence_id - Occurrence ID. Only present if the event was already written to the database. Default null.
|
|
*
|
|
* ! $occurrence_id is not the event id, it's the id of the record in the wsal_occurrences table.
|
|
*
|
|
* @return string - The formatted string to be displayed in the event list in wp-admin.
|
|
*
|
|
* @since 5.5.2
|
|
*/
|
|
public static function wsal_pmp_format_membership_changes( $value, $expression, $configuration, $occurrence_id ) {
|
|
if ( '%PMPMembershipChanges%' === $expression || '%PMPOrderChanges%' === $expression ) {
|
|
// Remove <strong> tags from start and end.
|
|
$value = preg_replace( '/^<strong>(.*)<\/strong>$/s', '$1', $value );
|
|
|
|
// Replace all <br> with proper <br>.
|
|
$value = str_replace( '<br>', '<br>', $value );
|
|
|
|
/**
|
|
* Wrap the value after the first colon in <b>...</b> for each old => new value.
|
|
* matches[1]: label, matches[2]: 'old => new' value.
|
|
*/
|
|
$value = preg_replace_callback(
|
|
'/<br>\s*([^:]+):\s*([^<]+)(?=<br>|$)/',
|
|
function ( $matches ) {
|
|
return '<br>' . $matches[1] . ': <b>' . $matches[2] . '</b>';
|
|
},
|
|
$value
|
|
);
|
|
}
|
|
|
|
return $value;
|
|
}
|
|
|
|
/**
|
|
* Get the gateway names for Paid Memberships Pro orders when we only have free levels available.
|
|
*
|
|
* @return string - The gateway name.
|
|
*
|
|
* @since 5.5.2
|
|
*/
|
|
public static function get_pmp_free_levels_gateway() {
|
|
return \pmpro_onlyFreeLevels() ? \__( 'Default', 'wp-security-audit-log' ) : \__( 'Testing Only', 'wp-security-audit-log' );
|
|
}
|
|
|
|
/**
|
|
* Build a readable string to indicate a time period. E.g. "3 Months", "1 Year", "Never".
|
|
*
|
|
* @param int|string $number - The number for the period label.
|
|
* @param int|string $period - The period label (e.g. Week, Month, Year). Sometimes this may be 0.
|
|
|
|
* @return string
|
|
*
|
|
* @since 5.5.2
|
|
*/
|
|
public static function build_membership_time_period_string( $number, $period ): string {
|
|
$number = (int) $number;
|
|
|
|
$periods = array(
|
|
'Hour' => \_n( 'hour', 'hours', $number, 'wp-security-audit-log' ),
|
|
'Day' => \_n( 'day', 'days', $number, 'wp-security-audit-log' ),
|
|
'Week' => \_n( 'week', 'weeks', $number, 'wp-security-audit-log' ),
|
|
'Month' => \_n( 'month', 'months', $number, 'wp-security-audit-log' ),
|
|
'Year' => \_n( 'year', 'years', $number, 'wp-security-audit-log' ),
|
|
);
|
|
|
|
if ( 0 === (int) $number || empty( $period ) || ! isset( $periods[ $period ] ) ) {
|
|
return \__( 'Never', 'wp-security-audit-log' );
|
|
}
|
|
|
|
return $number . ' ' . $periods[ $period ];
|
|
}
|
|
|
|
/**
|
|
* Build a readable string for billing cycles of memberships. E.g. "One-time", "3 Months", "1 Year".
|
|
*
|
|
* @param int|string $cycle_number - The number for the period of the billing cycle.
|
|
* @param int|string $cycle_period - The period label of the billing cycle.
|
|
* @param bool $is_recurring - Whether the membership is recurring or not.
|
|
* @param float|string $billing_amount - The billing amount for the membership.
|
|
*
|
|
* @return string
|
|
*
|
|
* @since 5.5.2
|
|
*/
|
|
public static function build_membership_billing_cycle_string( $cycle_number, $cycle_period, $is_recurring, $billing_amount ) {
|
|
$string = \__( 'One-time', 'wp-security-audit-log' );
|
|
|
|
if ( null !== $cycle_number && null !== $cycle_period ) {
|
|
if ( true === $is_recurring && $billing_amount > 0 && $cycle_number > 0 ) {
|
|
$string = self::build_membership_time_period_string( $cycle_number, $cycle_period );
|
|
}
|
|
}
|
|
|
|
return $string;
|
|
}
|
|
|
|
/**
|
|
* Build a readable string to explain the trial period of a membership.
|
|
*
|
|
* @param float|string $trial_amount - the price of the trial.
|
|
* @param int|string $trial_limit - the number of payments covered by the trial price.
|
|
* @param bool $is_custom_trial - whether the membership has a custom trial or not.
|
|
*
|
|
* @return string
|
|
*
|
|
* @since 5.5.2
|
|
*/
|
|
public static function build_membership_trial_string( $trial_amount, $trial_limit, $is_custom_trial ) {
|
|
$string = \__( 'Disabled', 'wp-security-audit-log' );
|
|
|
|
if ( true === $is_custom_trial ) {
|
|
$price = (float) $trial_amount;
|
|
|
|
if ( (float) $trial_amount <= 0 ) {
|
|
$price = \__( 'Free', 'wp-security-audit-log' );
|
|
}
|
|
|
|
if ( (int) $trial_limit > 0 ) {
|
|
$string = $price . ' ' . \__( 'for', 'wp-security-audit-log' ) . ' ' . (int) $trial_limit . ' ' . \_n( 'payment', 'payments', (int) $trial_limit, 'wp-security-audit-log' );
|
|
}
|
|
}
|
|
|
|
return $string;
|
|
}
|
|
|
|
/**
|
|
* Get the values of a membership level by its ID, and return an array of current saved values.
|
|
*
|
|
* @param int|string $membership_id - The membership level ID.
|
|
*
|
|
* @return array - Array of current membership values.
|
|
*
|
|
* @since 5.5.2
|
|
*/
|
|
public static function extract_current_membership_values( $membership_id ): array {
|
|
|
|
$data = array();
|
|
|
|
$level_values = \pmpro_getLevel( $membership_id );
|
|
|
|
$is_recurring = \pmpro_isLevelRecurring( $level_values );
|
|
$is_custom_trial = \pmpro_isLevelTrial( $level_values );
|
|
|
|
if ( $level_values ) {
|
|
$data = array(
|
|
'name' => $level_values->name,
|
|
'description' => $level_values->description,
|
|
'confirmation' => $level_values->confirmation,
|
|
'initial_payment' => $level_values->initial_payment,
|
|
'billing_amount' => $level_values->billing_amount,
|
|
'cycle_period' => self::build_membership_billing_cycle_string( $level_values->cycle_number, $level_values->cycle_period, $is_recurring, $level_values->billing_amount ),
|
|
'billing_limit' => $level_values->billing_limit,
|
|
'trial' => self::build_membership_trial_string( $level_values->trial_amount, $level_values->trial_limit, $is_custom_trial ),
|
|
'expiration_period' => self::build_membership_time_period_string( $level_values->expiration_number, $level_values->expiration_period ),
|
|
'allow_signups' => $level_values->allow_signups ?? null,
|
|
);
|
|
}
|
|
|
|
return $data;
|
|
}
|
|
|
|
/**
|
|
* Compare 2 arrays, returning an array of changed fields with old => new values.
|
|
*
|
|
* @param array $old_values - Old values array.
|
|
* @param array $new_values - New values array.
|
|
*
|
|
* @return array - Array of changed fields with old => new values.
|
|
*
|
|
* @since 5.5.2
|
|
*/
|
|
public static function get_array_changed_fields( $old_values, $new_values ): array {
|
|
$changed_fields = array();
|
|
|
|
// Get the intersection of keys.
|
|
$common_keys = array_intersect( array_keys( $old_values ), array_keys( $new_values ) );
|
|
|
|
foreach ( $common_keys as $key ) {
|
|
// Compare as strings for consistency.
|
|
$old = (string) $old_values[ $key ];
|
|
$new = (string) $new_values[ $key ];
|
|
|
|
if ( $old !== $new ) {
|
|
$changed_fields[ $key ] = $old . ' => ' . $new;
|
|
}
|
|
}
|
|
|
|
return $changed_fields;
|
|
}
|
|
|
|
/**
|
|
* Convert an array of changed PMP fields into a string for display in the main wsal event list.
|
|
*
|
|
* @param array $changed_fields_array - An array with a list of recently changed PMP fields.
|
|
*
|
|
* @return string - A string listing the changed fields with old => new values.
|
|
*
|
|
* @since 5.5.2
|
|
*/
|
|
public static function convert_changed_fields_array_into_string( $changed_fields_array ): string {
|
|
$changed_fields_string = '';
|
|
|
|
if ( empty( $changed_fields_array ) || ! is_array( $changed_fields_array ) ) {
|
|
return $changed_fields_string;
|
|
}
|
|
|
|
foreach ( $changed_fields_array as $field => $change ) {
|
|
if ( ! empty( $change ) ) {
|
|
$field_label = ucfirst( str_replace( '_', ' ', $field ) );
|
|
$changed_fields_string .= '<br>' . $field_label . ': ' . $change;
|
|
}
|
|
}
|
|
|
|
return $changed_fields_string;
|
|
}
|
|
|
|
/**
|
|
* Compare old and new membership values, returning a string of changed fields with old => new values.
|
|
*
|
|
* @param array $old_values - The old values of a Paid Memberships Pro membership level.
|
|
* @param array $new_values - The new values of a Paid Memberships Pro membership level.
|
|
*
|
|
* @return string - A string listing the changed fields with old => new values.
|
|
*
|
|
* @since 5.5.2
|
|
*/
|
|
public static function compare_membership_values_change_after_update( $old_values, $new_values ): string {
|
|
|
|
$changed_fields = self::get_array_changed_fields( $old_values, $new_values );
|
|
|
|
$changed_fields['signups'] = self::pmp_enable_signup_setting_change( $old_values['allow_signups'], $new_values['disable_signups'] );
|
|
|
|
$changed_fields_string = self::convert_changed_fields_array_into_string( $changed_fields );
|
|
|
|
return $changed_fields_string;
|
|
}
|
|
|
|
/**
|
|
* Get the array of an order by its ID, and return an array of current saved values.
|
|
*
|
|
* @param int $order_id - The Paid Memberships Pro order ID.
|
|
*
|
|
* @return array - An array of the values found in the database for this order.
|
|
*
|
|
* @since 5.5.2
|
|
*/
|
|
public static function get_pmp_order_by_id( $order_id ): array {
|
|
global $wpdb;
|
|
|
|
// Direct MOST RECENT and non cached DB query required for accurate before/after change detection.
|
|
// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching
|
|
$old_order = $wpdb->get_row(
|
|
$wpdb->prepare(
|
|
"SELECT * FROM {$wpdb->base_prefix}pmpro_membership_orders WHERE id = %d",
|
|
$order_id
|
|
)
|
|
);
|
|
|
|
if ( ! $old_order ) {
|
|
return array();
|
|
}
|
|
|
|
$fallback_gateway = self::get_pmp_free_levels_gateway();
|
|
|
|
// We need to covert this which is in string format, to a unix timestamp.
|
|
$old_timestamp_converted = strtotime( $old_order->timestamp );
|
|
|
|
$old_order_array = array(
|
|
'user_id' => $old_order->user_id ?? null,
|
|
'membership_id' => $old_order->membership_id ?? null,
|
|
'subtotal' => $old_order->subtotal ?? null,
|
|
'tax' => $old_order->tax ?? null,
|
|
'total' => $old_order->total ?? null,
|
|
'payment_type' => $old_order->payment_type ?? null,
|
|
'card_type' => $old_order->cardtype ?? null,
|
|
'account_number' => $old_order->accountnumber ?? null,
|
|
'expiration_month' => $old_order->expirationmonth ?? null,
|
|
'expiration_year' => $old_order->expirationyear ?? null,
|
|
'status' => $old_order->status ?? null,
|
|
'gateway' => $old_order->gateway ? $old_order->gateway : $fallback_gateway,
|
|
'gateway_environment' => $old_order->gateway_environment ?? null,
|
|
'order_date' => $old_order->timestamp ? \date_i18n( \get_option( 'date_format' ) . ' H:i:s', $old_timestamp_converted ) : null,
|
|
'notes' => $old_order->notes ?? null,
|
|
);
|
|
|
|
return $old_order_array;
|
|
}
|
|
|
|
/**
|
|
* Add Edit Member Panel for WP Activity Log.
|
|
* This panel can be found in wp-admin: Memberships > Members > Edit Member
|
|
*
|
|
* @param array $panels currently existing panels.
|
|
*
|
|
* @return array Modified panels array with our panel added.
|
|
*
|
|
* @since 5.5.2
|
|
*/
|
|
public static function wsal_paid_memberships_pro_include_member_panel( $panels ) {
|
|
|
|
// If the class exists, add a panel.
|
|
if ( class_exists( 'WSAL\PMP_Addon_Member_Edit_Panel\PMP_Addon_Member_Edit_Panel' ) && class_exists( 'PMPro_Member_Edit_Panel' ) ) {
|
|
$panels[] = new PMP_Addon_Member_Edit_Panel();
|
|
}
|
|
|
|
return $panels;
|
|
}
|
|
}
|
|
}
|