Skip to content

Commit

Permalink
Merge remote-tracking branch 'strangerstudios/dev' into user-fields-p…
Browse files Browse the repository at this point in the history
…hp-ui
  • Loading branch information
dparker1005 committed Nov 21, 2024
2 parents 144376a + 28fe841 commit f0eb0a9
Show file tree
Hide file tree
Showing 102 changed files with 15,786 additions and 621 deletions.
13 changes: 13 additions & 0 deletions CHANGELOG.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,17 @@
== Changelog ==
= 3.3.2 - 2024-11-21 =
* ENHANCEMENT: Updated the "Checkout Spam Protection" security setting to also check for spam when trying to apply invalid discount codes. #3208 (@ideadude, @dparker1005)
* ENHANCEMENT: Added a new filter `pmpro_registered_reports` for registering reports. #3200 (@mircobabini)
* ENHANCEMENT: Now showing the Stripe API version being used by PMPro in site health instead of on the Payment Gateways settings page. #3204 (@dparker1005)
* BUG FIX/ENHANCEMENT: Fixed an issue where Stripe error messages at checkout would not show at the bottom of the checkout page. #3194 (@andrewlimaza)
* BUG FIX/ENHANCEMENT: Now setting the discount code for an order during the `saveOrder()` method at checkout and when updating an order in the WP admin. #3190 (@dparker1005)
* BUG FIX/ENHANCEMENT: Improved the logic to get random order codes to help prevent duplicate order codes. #3191 (@dparker1005)
* BUG FIX: Fixed `doing_it_wrong` notices for strings that are localized before the `init` hook which started showing after sites updated to WordPress version 6.7. #3200 (@mircobabini)
* BUG FIX: Fixed an issue where the value '0' for a user field would be overwritten with the field's default value when displayed in the checkout form. #3189 (@dparker1005)
* BUG FIX: Fixed an issue where IP addresses may not be detected correctly when using the `pmpro_get_ip()` function. #3192 (@andrewlimaza)
* BUG FIX: Now ensuring that the `SITENAME` constant is not already defined before defining it. #3196 (@dparker1005)
* BUG FIX: Fixed a fatal error that would occur on the confirmation page if an invalid user ID is set on the order object being shown. #3207 (@dparker1005)

= 3.3.1 - 2024-10-24 =
* ENHANCEMENT: Added a Subscriptions List Table to allow admins to view, manage, and link subscriptions from the WordPress admin. #2828 (@dparker1005, @kimcoleman)
* ENHANCEMENT: Discount codes can now be set to only allow one use per user. #3175 (@dparker1005)
Expand Down
10 changes: 5 additions & 5 deletions adminpages/orders.php
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,11 @@
}
}

// Set the discount code.
if( isset( $_REQUEST['discount_code_id'] ) ) {
$order->discount_code_id = intval( $_REQUEST['discount_code_id'] );
}

// check nonce for saving
$nonceokay = true;
if ( empty( $_REQUEST['pmpro_orders_nonce'] ) || ! check_admin_referer( 'save', 'pmpro_orders_nonce' ) ) {
Expand All @@ -195,11 +200,6 @@
$pmpro_msg = __( 'Error saving order.', 'paid-memberships-pro' );
$pmpro_msgt = 'error';
}

// also update the discount code if needed
if( isset( $_REQUEST['discount_code_id'] ) ) {
$order->updateDiscountCode( intval( $_REQUEST['discount_code_id'] ) );
}
} else {
// order passed?
// Checking orderby as order could be the order ID or whether the List Table should be sorted ascending or descending.
Expand Down
18 changes: 10 additions & 8 deletions adminpages/reports/logins.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,19 @@
PMPro Report
Title: Logins
Slug: login
For each report, add a line like:
global $pmpro_reports;
$pmpro_reports['slug'] = 'Title';
For each report, also write two functions:
For each report, write three functions:
* pmpro_report_{slug}_register() to register the widget (slug and title).
* pmpro_report_{slug}_widget() to show up on the report homepage.
* pmpro_report_{slug}_page() to show up when users click on the report page widget.
*/
global $pmpro_reports;
$pmpro_reports['login'] = __('Visits, Views, and Logins', 'paid-memberships-pro');
function pmpro_report_login_register( $pmpro_reports ) {
$pmpro_reports['login'] = __( 'Visits, Views, and Logins', 'paid-memberships-pro' );

return $pmpro_reports;
}

add_filter( 'pmpro_registered_reports', 'pmpro_report_login_register' );

function pmpro_report_login_widget() {
global $wpdb, $pmpro_reports;
Expand Down
16 changes: 9 additions & 7 deletions adminpages/reports/members-per-level.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,18 @@
Title: Members per Level
Slug: members_per_level
For each report, add a line like:
global $pmpro_reports;
$pmpro_reports['slug'] = 'Title';
For each report, also write two functions:
For each report, write three functions:
* pmpro_report_{slug}_register() to register the widget (slug and title).
* pmpro_report_{slug}_widget() to show up on the report homepage.
* pmpro_report_{slug}_page() to show up when users click on the report page widget.
*/
global $pmpro_reports;
$pmpro_reports['members_per_level'] = __('Active Members Per Level', 'paid-memberships-pro' );
function pmpro_report_members_per_level_register( $pmpro_reports ) {
$pmpro_reports['members_per_level'] = __('Active Members Per Level', 'paid-memberships-pro' );

return $pmpro_reports;
}

add_filter( 'pmpro_registered_reports', 'pmpro_report_members_per_level_register' );

// Enqueue Google Visualization JS on report page
function pmpro_report_members_per_level_init() {
Expand Down
16 changes: 9 additions & 7 deletions adminpages/reports/memberships.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,18 @@
Title: Membership Stats
Slug: memberships
For each report, add a line like:
global $pmpro_reports;
$pmpro_reports['slug'] = 'Title';
For each report, also write two functions:
For each report, write three functions:
* pmpro_report_{slug}_register() to register the widget (slug and title).
* pmpro_report_{slug}_widget() to show up on the report homepage.
* pmpro_report_{slug}_page() to show up when users click on the report page widget.
*/
global $pmpro_reports;
$pmpro_reports['memberships'] = __( 'Membership Stats', 'paid-memberships-pro' );
function pmpro_report_memberships_register( $pmpro_reports ) {
$pmpro_reports['memberships'] = __( 'Membership Stats', 'paid-memberships-pro' );

return $pmpro_reports;
}

add_filter( 'pmpro_registered_reports', 'pmpro_report_memberships_register' );

// queue Google Visualization JS on report page
function pmpro_report_memberships_init() {
Expand Down
25 changes: 14 additions & 11 deletions adminpages/reports/sales.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,23 @@
Title: Sales
Slug: sales
For each report, add a line like:
global $pmpro_reports;
$pmpro_reports['slug'] = 'Title';
For each report, also write two functions:
For each report, write three functions:
* pmpro_report_{slug}_register() to register the widget (slug and title).
* pmpro_report_{slug}_widget() to show up on the report homepage.
* pmpro_report_{slug}_page() to show up when users click on the report page widget.
*/
global $pmpro_reports;
$gateway_environment = get_option( "pmpro_gateway_environment");
if($gateway_environment == "sandbox")
$pmpro_reports['sales'] = __('Sales and Revenue (Testing/Sandbox)', 'paid-memberships-pro' );
else
$pmpro_reports['sales'] = __('Sales and Revenue', 'paid-memberships-pro' );
function pmpro_report_sales_register( $pmpro_reports ) {
$gateway_environment = get_option( "pmpro_gateway_environment" );
if ( $gateway_environment == "sandbox" ) {
$pmpro_reports['sales'] = __( 'Sales and Revenue (Testing/Sandbox)', 'paid-memberships-pro' );
} else {
$pmpro_reports['sales'] = __( 'Sales and Revenue', 'paid-memberships-pro' );
}

return $pmpro_reports;
}

add_filter( 'pmpro_registered_reports', 'pmpro_report_sales_register' );

//queue Google Visualization JS on report page
function pmpro_report_sales_init()
Expand Down
111 changes: 63 additions & 48 deletions adminpages/subscriptions.php
Original file line number Diff line number Diff line change
Expand Up @@ -243,56 +243,71 @@ class="page-title-action pmpro-has-icon pmpro-has-icon-visibility">
}
?>
<form action="" method="post">
<table class="form-table">
<tbody>
<tr>
<th scope="row"><?php esc_html_e( 'ID', 'paid-memberships-pro' ); ?></th>
<td><?php echo esc_html( $subscription->get_id() ); ?></td>
</tr>
<tr>
<th scope="row"><?php esc_html_e( 'Subscription Transaction ID', 'paid-memberships-pro' ); ?></th>
<td><?php echo esc_html( $subscription->get_subscription_transaction_id() ); ?></td>
</tr>
<tr>
<th scope="row"><?php esc_html_e( 'Gateway', 'paid-memberships-pro' ); ?></th>
<td><?php echo esc_html( $subscription->get_gateway() ); ?></td>
</tr>
<tr>
<th scope="row"><?php esc_html_e( 'Gateway Environment', 'paid-memberships-pro' ); ?></th>
<td><?php echo esc_html( $subscription->get_gateway_environment() ); ?></td>
</tr>
<tr>
<th scope="row"><?php esc_html_e( 'User ID', 'paid-memberships-pro' ); ?></th>
<td>
<input type="text" name="user_id" value="<?php echo esc_attr( $user_id ); ?>" />
</td>
</tr>
<tr>
<th scope="row"><?php esc_html_e( 'Membership Level', 'paid-memberships-pro' ); ?></th>
<td>
<?php
// Get all membership levels.
$levels = pmpro_getAllLevels( true, true );
<div class="pmpro_section" data-visibility="shown" data-activated="true">
<div class="pmpro_section_toggle">
<button class="pmpro_section-toggle-button" type="button" aria-expanded="true">
<span class="dashicons dashicons-arrow-up-alt2"></span>
<?php esc_html_e( 'Subscription Information', 'paid-memberships-pro' ); ?>
</button>
</div>
<div class="pmpro_section_inside">
<table class="form-table">
<tbody>
<tr>
<th scope="row"><?php esc_html_e( 'ID', 'paid-memberships-pro' ); ?></th>
<td><?php echo esc_html( $subscription->get_id() ); ?></td>
</tr>
<tr>
<th scope="row"><?php esc_html_e( 'Subscription Transaction ID', 'paid-memberships-pro' ); ?></th>
<td>
<?php echo esc_html( $subscription->get_subscription_transaction_id() ); ?>
<p class="description"><?php esc_html_e( 'Generated by the gateway. Useful to cross reference subscriptions.', 'paid-memberships-pro' ); ?></p>
</td>
</tr>
<tr>
<th scope="row"><?php esc_html_e( 'Gateway', 'paid-memberships-pro' ); ?></th>
<td><?php echo esc_html( $subscription->get_gateway() ); ?></td>
</tr>
<tr>
<th scope="row"><?php esc_html_e( 'Gateway Environment', 'paid-memberships-pro' ); ?></th>
<td><?php echo esc_html( $subscription->get_gateway_environment() ); ?></td>
</tr>
<tr>
<th scope="row"><?php esc_html_e( 'User ID', 'paid-memberships-pro' ); ?></th>
<td>
<input type="text" name="user_id" value="<?php echo esc_attr( $user_id ); ?>" />
</td>
</tr>
<tr>
<th scope="row"><?php esc_html_e( 'Membership Level', 'paid-memberships-pro' ); ?></th>
<td>
<?php
// Get all membership levels.
$levels = pmpro_getAllLevels( true, true );

// Display a dropdown of membership levels.
?>
<select name="membership_level_id">
<?php
foreach ( $levels as $level ) {
// Display a dropdown of membership levels.
?>
<option value="<?php echo esc_attr( $level->id ); ?>" <?php selected( $level->id, $membership_level_id ); ?>><?php echo esc_html( $level->name ); ?></option>
<?php
}
?>
</select>
</td>
</tr>
</tbody>
</table>
<?php wp_nonce_field( 'edit', 'pmpro_subscriptions_nonce' ); ?>
<input type="hidden" name="id" value="<?php echo esc_attr( $subscription->get_id() ); ?>" />
<input type="hidden" name="action" value="edit" />
<input type="submit" class="button button-primary" value="<?php esc_attr_e( 'Update Subscription', 'paid-memberships-pro' ); ?>" />
<select name="membership_level_id">
<?php
foreach ( $levels as $level ) {
?>
<option value="<?php echo esc_attr( $level->id ); ?>" <?php selected( $level->id, $membership_level_id ); ?>><?php echo esc_html( $level->name ); ?></option>
<?php
}
?>
</select>
</td>
</tr>
</tbody>
</table>
</div> <!-- end pmpro_section_inside -->
</div> <!-- end pmpro_section -->
<p class="submit">
<?php wp_nonce_field( 'edit', 'pmpro_subscriptions_nonce' ); ?>
<input type="hidden" name="id" value="<?php echo esc_attr( $subscription->get_id() ); ?>" />
<input type="hidden" name="action" value="edit" />
<input type="submit" class="button button-primary" value="<?php esc_attr_e( 'Update Subscription', 'paid-memberships-pro' ); ?>" />
</p>
</form>
<?php
} elseif ( ! empty( $subscription ) ) {
Expand Down
4 changes: 4 additions & 0 deletions classes/class-pmpro-site-health.php
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,10 @@ public function get_gateway() {
$api = $stripe->using_api_keys();
$connect = $stripe->has_connect_credentials();

if ( defined( 'PMPRO_STRIPE_API_VERSION' ) ) {
$gateway_text .= ' (' . __( 'API Version', 'paid-memberships-pro' ) . ': ' . PMPRO_STRIPE_API_VERSION . ')';
}

if ( $legacy ) {
$gateway_text .= ' (' . __( 'Legacy Keys', 'paid-memberships-pro' ) . ')';
return $gateway_text . ' [' . $gateway . ':legacy-keys]';
Expand Down
5 changes: 3 additions & 2 deletions classes/class.memberorder.php
Original file line number Diff line number Diff line change
Expand Up @@ -1573,7 +1573,7 @@ function saveOrder()
* Get a random code to use as the order code.
*/
function getRandomCode() {
global $wpdb;
global $wpdb, $current_user;

// We mix this with the seed to make sure we get unique codes.
static $count = 0;
Expand All @@ -1589,7 +1589,8 @@ function getRandomCode() {
}

while( empty( $code ) ) {
$scramble = md5( $auth_code . microtime() . $secure_auth_code . $count );
$current_user_id = ! empty( $current_user->ID ) ? $current_user->ID : 0;
$scramble = md5( $auth_code . microtime() . $secure_auth_code . $current_user_id . $count );
$code = substr( $scramble, 0, 10 );
$code = apply_filters( 'pmpro_random_code', $code, $this ); //filter
$check = $wpdb->get_var( $wpdb->prepare( "SELECT id FROM $wpdb->pmpro_membership_orders WHERE code = %s LIMIT 1", $code ) );
Expand Down
2 changes: 1 addition & 1 deletion classes/gateways/class.pmprogateway_paypalexpress.php
Original file line number Diff line number Diff line change
Expand Up @@ -826,7 +826,7 @@ function subscribe(&$order)
// create email
$pmproemail = new PMProEmail();
$body = '<p>' . __( "There was a potential issue while setting the 'Profile Start Date' for a user's subscription at checkout. PayPal does not allow one to set a Profile Start Date further than 1 year out. Typically, this is not an issue, but sometimes a combination of custom code or add ons for PMPro (e.g. the Prorating or Auto-renewal Checkbox add ons) will try to set a Profile Start Date out past 1 year in order to respect an existing user's original expiration date before they checked out. The user's information is below. PMPro has allowed the checkout and simply restricted the Profile Start Date to 1 year out with a possible additional free Trial of up to 1 year. You should double check this information to determine if maybe the user has overpaid or otherwise needs to be addressed. If you get many of these emails, you should consider adjusting your custom code to avoid these situations.", 'paid-memberships-pro' ) . '</p>';
$body .= '<p>' . sprintf( __( 'User: %1$s<br />Email: %2$s<br />Membership Level: %3$s<br />Order #: %4$s<br />Original Profile Start Date: %5$s<br />Adjusted Profile Start Date: %6$s<br />Trial Period: %7$s<br />Trial Frequency: %8$s<br />', 'paid-memberships-pro' ), $order->user->user_nicename, $order->user->user_email, $level->name, $order->code, date( 'c', $original_start_date ), $one_year_out_date, $trial_period, $trial_frequency ) . '</p>';
$body .= '<p>' . sprintf( __( 'User: %1$s<br />Email: %2$s<br />Membership Level: %3$s<br />Order #: %4$s<br />Original Profile Start Date: %5$s<br />Adjusted Profile Start Date: %6$s<br />Trial Period: %7$s<br />Trial Frequency: %8$s<br />', 'paid-memberships-pro' ), $order->user->user_nicename, $order->user->user_email, $level->name, $order->code, $original_start_date , $one_year_out_date, $trial_period, $trial_frequency ) . '</p>';
$pmproemail->template = 'profile_start_date_limit_check';
$pmproemail->subject = sprintf( __( 'Profile Start Date Issue Detected and Fixed at %s', 'paid-memberships-pro' ), get_bloginfo( 'name' ) );
$pmproemail->data = array( 'body' => $body );
Expand Down
29 changes: 7 additions & 22 deletions classes/gateways/class.pmprogateway_stripe.php
Original file line number Diff line number Diff line change
Expand Up @@ -528,10 +528,6 @@ public static function pmpro_payment_option_fields( $values, $gateway ) {
<h2><?php esc_html_e( 'Other Stripe Settings', 'paid-memberships-pro' ); ?></h2>
</td>
</tr>
<tr class="gateway gateway_stripe" <?php if ( $gateway != "stripe" ) { ?>style="display: none;"<?php } ?>>
<th><?php esc_html_e( 'Stripe API Version', 'paid-memberships-pro' ); ?></th>
<td><code><?php echo esc_html( PMPRO_STRIPE_API_VERSION ); ?></code></td>
</tr>
<tr class="gateway gateway_stripe" <?php if ( $gateway != "stripe" ) { ?>style="display: none;"<?php } ?>>
<th scope="row" valign="top">
<label for="stripe_payment_flow"><?php esc_html_e( 'Payment Flow', 'paid-memberships-pro' ); ?></label>
Expand Down Expand Up @@ -3369,25 +3365,14 @@ private function cancelSubscriptionAtGateway( $subscription, $preserve_local_mem
return false;
}

// If this is already cancelled, return true.
if ( ! empty( $subscription->canceled_at ) ) {
return true;
}

// Make sure we get the customer for this subscription.
$order = new MemberOrder();
$order->getLastMemberOrderBySubscriptionTransactionID( $subscription->id );

// No order?
if ( empty( $order ) ) {
//lets cancel anyway, but this is suspicious
$r = $subscription->cancel();

return true;
// Get the PMPro subscription.
$pmpro_subscription = PMPro_Subscription::get_subscription_from_subscription_transaction_id( $subscription->id, 'stripe', get_option( 'pmpro_gateway_environment', 'sandbox' ) );
if ( empty( $pmpro_subscription ) ) {
return false;
}

// Okay have an order, so get customer so we can cancel invoices too
$customer = $this->update_customer_at_checkout( $order );
$customer = $this->get_customer_for_user( $pmpro_subscription->get_user_id() );

// Get open invoices.
$invoices = Stripe_Invoice::all(['customer' => $customer->id, 'status' => 'open']);
Expand All @@ -3405,12 +3390,11 @@ private function cancelSubscriptionAtGateway( $subscription, $preserve_local_mem

// Sometimes we don't want to cancel the local membership when Stripe sends its webhook.
if ( $preserve_local_membership ) {
$this->ignoreCancelWebhookForThisSubscription( $subscription->id, $order->user_id );
$this->ignoreCancelWebhookForThisSubscription( $subscription->id, $pmpro_subscription->get_user_id() );
}

// Cancel
$r = $subscription->cancel();

return true;
} catch ( \Throwable $e ) {
return false;
Expand Down Expand Up @@ -3779,6 +3763,7 @@ private function create_payment_intent( &$order ) {
$params = array(
'customer' => $order->stripe_customer->id,
'payment_method' => $order->payment_method_id,
'payment_method_types' => array( 'card' ),
'amount' => $this->convert_price_to_unit_amount( $amount ),
'currency' => $pmpro_currency,
'confirmation_method' => 'manual',
Expand Down
Loading

0 comments on commit f0eb0a9

Please sign in to comment.