· 5 years ago · Aug 11, 2020, 06:50 AM
1<?php
2
3if ( ! defined( 'ABSPATH' ) ) {
4 exit; // Exit if accessed directly
5}
6
7//
8// NOTES:
9// A subscription purchase creates a subscription and a 'complete' transaction
10// A subscription purchase with a trial creates a subscription and a 'pending' transaction
11// A one-off purchase creates a 'complete' transaction and no subscription
12// "Subscription is linked to Stripe, transaction is linked to membership"
13//
14
15class WPF_MemberPress extends WPF_Integrations_Base {
16
17 /**
18 * Gets things started
19 *
20 * @access public
21 * @return void
22 */
23
24 public function init() {
25
26 $this->slug = 'memberpress';
27
28 // WPF Settings
29 add_filter( 'wpf_meta_field_groups', array( $this, 'add_meta_field_group' ), 20 );
30 add_filter( 'wpf_meta_fields', array( $this, 'prepare_meta_fields' ), 10 );
31
32 // MemberPress admin tools
33 add_action( 'mepr-product-options-tabs', array( $this, 'output_product_nav_tab' ) );
34 add_action( 'mepr-product-options-pages', array( $this, 'output_product_content_tab' ) );
35 add_action( 'save_post', array( $this, 'save_meta_box_data' ) );
36
37 // Completed purchase / status changes
38 add_filter( 'wpf_user_register', array( $this, 'user_register' ), 10, 2 );
39 add_action( 'mepr_subscription_transition_status', array( $this, 'subscription_status_changed' ), 10, 3 );
40
41 // New account / transcation stuff
42 //add_action( 'mepr-signup', array( $this, 'apply_tags_checkout' ) ); // It is used for processing the signup form before the logic progresses on to 'the_content'
43 //add_action( 'mepr-event-transaction-completed', array( $this, 'apply_tags_checkout' ) ); // Capture first normal payment after trial period on MemberPress Transaction
44 add_action( 'mepr-txn-status-complete', array( $this, 'apply_tags_checkout' ) ); // Called after completed payment
45
46 // Recurring transcation stuff
47 add_action( 'mepr-event-recurring-transaction-failed', array( $this, 'recurrring_transaction_failed' ) );
48 add_action( 'mepr-event-recurring-transaction-completed', array( $this, 'recurrring_transaction_completed' ) );
49 add_action( 'mepr-event-transaction-expired', array( $this, 'transaction_expired' ), 20 ); // 20 so MP can set the subscription status
50
51 // Corporate Accounts addon
52 add_action( 'mepr-txn-status-complete', array( $this, 'corporate_accounts_tagging' ) );
53
54 // Profile updates (bidirectional)
55 add_action( 'mepr_save_account', array( $this, 'save_account' ) );
56 add_filter( 'wpf_user_update', array( $this, 'user_update' ), 10, 2 );
57 add_action( 'wpf_pulled_user_meta', array( $this, 'pulled_user_meta' ), 10, 2 );
58
59 // Add user to membership when tag-link tags are applied
60 add_action( 'wpf_tags_modified', array( $this, 'add_to_membership' ), 10, 2 );
61
62 // Coupons
63 add_action( 'add_meta_boxes', array( $this, 'add_coupon_meta_box' ), 20, 2 );
64
65 // Batch operations
66 add_filter( 'wpf_export_options', array( $this, 'export_options' ) );
67 add_action( 'wpf_batch_memberpress_init', array( $this, 'batch_init_subscriptions' ) );
68 add_action( 'wpf_batch_memberpress', array( $this, 'batch_step_subscriptions' ) );
69
70 add_action( 'wpf_batch_memberpress_transactions_init', array( $this, 'batch_init_transactions' ) );
71 add_action( 'wpf_batch_memberpress_transactions', array( $this, 'batch_step_transactions' ) );
72
73 add_action( 'wpf_batch_memberpress_memberships_init', array( $this, 'batch_init_memberships' ) );
74 add_action( 'wpf_batch_memberpress_memberships', array( $this, 'batch_step_memberships' ) );
75
76 }
77
78 /**
79 * Adds a user to a membership level if a "link" tag is applied
80 *
81 * @access public
82 * @return void
83 */
84
85 public function add_to_membership( $user_id, $user_tags ) {
86
87 $linked_products = get_posts(
88 array(
89 'post_type' => 'memberpressproduct',
90 'nopaging' => true,
91 'fields' => 'ids',
92 'meta_query' => array(
93 array(
94 'key' => 'wpf-settings-memberpress',
95 'compare' => 'EXISTS',
96 ),
97 ),
98 )
99 );
100
101 if ( empty( $linked_products ) ) {
102 return;
103 }
104
105 // Update role based on user tags
106 foreach ( $linked_products as $product_id ) {
107
108 $settings = get_post_meta( $product_id, 'wpf-settings-memberpress', true );
109
110 if ( empty( $settings ) || empty( $settings['tag_link'] ) ) {
111 continue;
112 }
113
114 $tag_id = $settings['tag_link'][0];
115
116 $mepr_user = new MeprUser( $user_id );
117
118 if ( in_array( $tag_id, $user_tags ) && ! $mepr_user->is_already_subscribed_to( $product_id ) ) {
119
120 // Auto enroll
121 wpf_log( 'info', $user_id, 'User auto-enrolled in <a href="' . admin_url( 'post.php?post=' . $product_id . '&action=edit' ) . '" target="_blank">' . get_the_title( $product_id ) . '</a> by linked tag <strong>' . wp_fusion()->user->get_tag_label( $tag_id ) . '</strong>' );
122
123 // Create the MemberPress transaction
124 $txn = new MeprTransaction();
125 $txn->user_id = $user_id;
126 $txn->product_id = $product_id;
127 $txn->txn_type = 'subscription_confirmation';
128 $txn->gateway = 'manual';
129 $txn->created_at = current_time( 'mysql' );
130
131 $product = new MeprProduct( $txn->product_id );
132
133 // Can't use $txn->create_free_transaction( $txn ); since it forces a redirect, so copied the code from MeprTransaction
134 if ( $product->period_type != 'lifetime' ) { // A free recurring subscription? Nope - let's make it lifetime for free here folks
135
136 $expires_at = MeprUtils::db_lifetime();
137
138 } else {
139 $product_expiration = $product->get_expires_at( strtotime( $txn->created_at ) );
140
141 if ( is_null( $product_expiration ) ) {
142 $expires_at = MeprUtils::db_lifetime();
143 } else {
144 $expires_at = MeprUtils::ts_to_mysql_date( $product_expiration, 'Y-m-d 23:59:59' );
145 }
146 }
147
148 $txn->trans_num = MeprTransaction::generate_trans_num();
149 $txn->status = 'pending';
150 $txn->txn_type = 'payment';
151 $txn->gateway = 'free';
152 $txn->expires_at = $expires_at;
153
154 // This will only work before maybe_cancel_old_sub is run
155 $upgrade = $txn->is_upgrade();
156 $downgrade = $txn->is_downgrade();
157
158 $event_txn = $txn->maybe_cancel_old_sub();
159 $txn->status = 'complete';
160 $txn->store();
161
162 $free_gateway = new MeprBaseStaticGateway( 'free', __( 'Free', 'memberpress' ), __( 'Free', 'memberpress' ) );
163
164 if ( $upgrade ) {
165
166 $free_gateway->upgraded_sub( $txn, $event_txn );
167
168 } elseif ( $downgrade ) {
169
170 $free_gateway->downgraded_sub( $txn, $event_txn );
171
172 }
173
174 MeprUtils::send_signup_notices( $txn );
175 MeprEvent::record( 'transaction-completed', $txn ); // Delete this if we use $free_gateway->send_transaction_receipt_notices later
176 MeprEvent::record( 'non-recurring-transaction-completed', $txn ); // Delete this if we use $free_gateway->send_transaction_receipt_notices later
177
178 } elseif ( ! in_array( $tag_id, $user_tags ) && $mepr_user->is_already_subscribed_to( $product_id ) ) {
179
180 // Auto un-enroll
181 wpf_log( 'info', $user_id, 'User unenrolled from <a href="' . admin_url( 'post.php?post=' . $product_id . '&action=edit' ) . '" target="_blank">' . get_the_title( $product_id ) . '</a> by linked tag <strong>' . wp_fusion()->user->get_tag_label( $tag_id ) . '</strong>' );
182
183 $transactions = $mepr_user->active_product_subscriptions( 'transactions' );
184
185 foreach ( $transactions as $transaction ) {
186
187 if ( $transaction->product_id == $product_id && $transaction->gateway == 'free' ) {
188
189 remove_action( 'mepr-event-transaction-expired', array( $this, 'transaction_expired' ), 20 ); // no need to apply Expired tags
190
191 $transaction->destroy();
192
193 add_action( 'mepr-event-transaction-expired', array( $this, 'transaction_expired' ), 20 );
194
195 }
196 }
197 }
198 }
199
200 }
201
202 /**
203 * Formats special fields for sending
204 *
205 * @access public
206 * @return array User meta
207 */
208
209 private function format_fields( $user_meta, $remove_empty = false ) {
210
211 if ( empty( $user_meta ) ) {
212 return $user_meta;
213 }
214
215 $contact_fields = wp_fusion()->settings->get( 'contact_fields' );
216 $mepr_options = MeprOptions::fetch();
217
218 foreach ( $user_meta as $key => $value ) {
219
220 // Convert checkboxes to an array of their labels (not values)
221 if ( is_array( $value ) && isset( $contact_fields[ $key ] ) && isset( $contact_fields[ $key ]['type'] ) && $contact_fields[ $key ]['type'] == 'checkboxes' ) {
222
223 $value_labels = array();
224
225 foreach ( $mepr_options->custom_fields as $field_object ) {
226
227 if ( $field_object->field_key == $key ) {
228
229 foreach ( $field_object->options as $option ) {
230
231 if ( isset( $value[ $option->option_value ] ) ) {
232
233 $value_labels[] = $option->option_name;
234
235 }
236 }
237 }
238 }
239
240 $user_meta[ $key ] = $value_labels;
241
242 } elseif ( isset( $contact_fields[ $key ] ) && isset( $contact_fields[ $key ]['type'] ) && $contact_fields[ $key ]['type'] == 'radios' ) {
243
244 foreach ( $mepr_options->custom_fields as $field_object ) {
245
246 if ( $field_object->field_key == $key ) {
247
248 foreach ( $field_object->options as $option ) {
249
250 if ( $option->option_value == $value ) {
251
252 $user_meta[ $key ] = $option->option_name;
253
254 }
255 }
256 }
257 }
258 }
259 }
260
261 // Possibly clear out empty checkboxes if it's a MP form
262 if ( $remove_empty ) {
263
264 foreach ( $mepr_options->custom_fields as $field_object ) {
265
266 if ( $field_object->show_in_account == true && ! isset( $user_meta[ $field_object->field_key ] ) ) {
267
268 $user_meta[ $field_object->field_key ] = null;
269
270 }
271 }
272 }
273
274 return $user_meta;
275
276 }
277
278 /**
279 * Triggered when new member is added
280 *
281 * @access public
282 * @return array Post data
283 */
284
285 public function user_register( $post_data, $user_id ) {
286
287 $field_map = array(
288 'user_first_name' => 'first_name',
289 'user_last_name' => 'last_name',
290 'mepr_user_password' => 'user_pass',
291 );
292
293 $post_data = $this->map_meta_fields( $post_data, $field_map );
294 $post_data = $this->format_fields( $post_data );
295
296 return $post_data;
297
298 }
299
300 /**
301 * Triggered when MemberPress account is saved
302 *
303 * @access public
304 * @return void
305 */
306
307 public function save_account( $user ) {
308
309 // Modify post data so user_update knows to remove empty fields
310 $_POST['from'] = 'profile';
311
312 wp_fusion()->user->push_user_meta( $user->ID, $_POST );
313
314 }
315
316 /**
317 * Adjusts field formatting for custom MemberPress fields
318 *
319 * @access public
320 * @return array User meta
321 */
322
323 public function user_update( $user_meta, $user_id ) {
324
325 if ( isset( $user_meta['from'] ) && $user_meta['from'] == 'profile' ) {
326 $remove_empty = true;
327 } else {
328 $remove_empty = false;
329 }
330
331 $user_meta = $this->format_fields( $user_meta, $remove_empty );
332
333 $field_map = array(
334 'mepr-new-password' => 'user_pass',
335 );
336
337 $user_meta = $this->map_meta_fields( $user_meta, $field_map );
338
339 return $user_meta;
340
341 }
342
343 /**
344 * Loads array type fields into array format
345 *
346 * @access public
347 * @return array User Meta
348 */
349
350 public function pulled_user_meta( $user_meta, $user_id ) {
351
352 $contact_fields = wp_fusion()->settings->get( 'contact_fields' );
353 $mepr_options = MeprOptions::fetch();
354
355 foreach ( $mepr_options->custom_fields as $field_object ) {
356
357 if ( ! empty( $user_meta[ $field_object->field_key ] ) && $field_object->field_type == 'checkboxes' ) {
358
359 $loaded_value = explode( ',', $user_meta[ $field_object->field_key ] );
360 $new_value = array();
361
362 foreach ( $field_object->options as $option ) {
363
364 if ( in_array( $option->option_name, $loaded_value ) ) {
365 $new_value[ $option->option_value ] = 'on';
366 }
367 }
368
369 $user_meta[ $field_object->field_key ] = $new_value;
370
371 } elseif ( ! empty( $user_meta[ $field_object->field_key ] ) && $field_object->field_type == 'radios' ) {
372
373 foreach ( $field_object->options as $option ) {
374
375 if ( $user_meta[ $field_object->field_key ] == $option->option_name ) {
376
377 $user_meta[ $field_object->field_key ] = $option->option_value;
378
379 }
380 }
381 }
382 }
383
384 // Remove MemberPress subscription fields from updating wp_usermeta
385 $fields = array(
386 'mepr_membership_level',
387 'mepr_reg_date',
388 'mepr_expiration',
389 'mepr_payment_method',
390 );
391
392 foreach ( $fields as $field ) {
393
394 if ( isset( $user_meta[ $field ] ) ) {
395 unset( $user_meta[ $field ] );
396 }
397 }
398
399 return $user_meta;
400
401 }
402
403 /**
404 * Triggered when payment for membership / product is complete (for one-time or free billing)
405 *
406 * @access public
407 * @return void
408 */
409
410 public function apply_tags_checkout( $event ) {
411
412 // The mepr-signup hook passes a transaction already
413 if ( is_a( $event, 'MeprTransaction' ) ) {
414 $txn = $event;
415 } else {
416 $txn = $event->get_data();
417 }
418
419 if ( 'complete' != $txn->status ) {
420 return;
421 }
422
423 // No need to run this twice if another action fires
424 remove_action( 'mepr-signup', array( $this, 'apply_tags_checkout' ) );
425 remove_action( 'mepr-event-transaction-completed', array( $this, 'apply_tags_checkout' ) );
426 remove_action( 'mepr-txn-status-complete', array( $this, 'apply_tags_checkout' ) );
427
428 // Log the action
429
430 $action = 'unknown action';
431
432 if ( doing_action( 'mepr-signup' ) ) {
433 $action = 'mepr-signup';
434 } elseif ( doing_action( 'mepr-event-transaction-completed' ) ) {
435 $action = 'mepr-event-transaction-completed';
436 } elseif ( doing_action( 'mepr-txn-status-complete' ) ) {
437 $action = 'mepr-txn-status-complete';
438 }
439
440 // Logger
441 wpf_log( 'info', $txn->user_id, 'New MemberPress transaction <a href="' . admin_url( 'admin.php?page=memberpress-trans&action=edit&id=' . $txn->id ) . '" target="_blank">#' . $txn->id . '</a> (' . $action . ')' );
442
443 //
444 // Get meta fields
445 //
446
447 $payment_method = $txn->payment_method();
448 $product_id = $txn->product_id;
449
450 $update_data = array(
451 'mepr_membership_level' => get_the_title( $product_id ),
452 'mepr_reg_date' => $txn->created_at,
453 'mepr_payment_method' => $payment_method->name,
454 );
455
456 // Add expiration only if applicable
457 if ( strtotime( $txn->expires_at ) >= 0 ) {
458 $update_data['mepr_expiration'] = $txn->expires_at;
459 }
460
461 // Coupons
462 if ( ! empty( $txn->coupon_id ) ) {
463 $update_data['mepr_coupon'] = get_the_title( $txn->coupon_id );
464 }
465
466 // Push all meta as well to get any updated custom field values during upgrades
467 $user_meta = array_map( array( wp_fusion()->user, 'map_user_meta' ), get_user_meta( $txn->user_id ) );
468
469 $update_data = array_merge( $user_meta, $update_data );
470
471 wp_fusion()->user->push_user_meta( $txn->user_id, $update_data );
472
473 //
474 // Update tags based on the product purchased
475 //
476
477 $apply_tags = array();
478
479 $settings = get_post_meta( $txn->product_id, 'wpf-settings-memberpress', true );
480
481 if ( ! empty( $settings ) ) {
482
483 if ( ! empty( $settings['apply_tags_registration'] ) ) {
484 $apply_tags = array_merge( $apply_tags, $settings['apply_tags_registration'] );
485 }
486
487 if ( ! empty( $settings['tag_link'] ) ) {
488 $apply_tags = array_merge( $apply_tags, $settings['tag_link'] );
489 }
490
491 if ( ! empty( $settings['apply_tags_payment_failed'] ) ) {
492
493 // Remove any failed tags
494 remove_action( 'wpf_tags_modified', array( $this, 'add_to_membership' ), 10, 2 );
495
496 wp_fusion()->user->remove_tags( $settings['apply_tags_payment_failed'], $txn->user_id );
497
498 add_action( 'wpf_tags_modified', array( $this, 'add_to_membership' ), 10, 2 );
499
500 }
501
502 // If this transaction was against a subscription that had a trial, and is no longer in a trial, consider it "converted"
503 $subscription = $txn->subscription();
504
505 if ( false !== $subscription && true == $subscription->trial ) {
506
507 // Figure out if it's the first real payment
508
509 $first_payment = false;
510
511 if ( $subscription->trial_amount > 0.00 && $subscription->txn_count == 2 ) {
512 $first_payment = true;
513 } elseif ( $subscription->trial_amount == 0.00 && $subscription->txn_count == 1 ) {
514 $first_payment = true;
515 }
516
517 if ( true == $first_payment && ! empty( $settings['apply_tags_converted'] ) ) {
518
519 $apply_tags = array_merge( $apply_tags, $settings['apply_tags_converted'] );
520
521 }
522 }
523 }
524
525 // Coupons
526 if ( ! empty( $txn->coupon_id ) ) {
527
528 $coupon_settings = get_post_meta( $txn->coupon_id, 'wpf-settings', true );
529
530 if ( ! empty( $coupon_settings ) && ! empty( $coupon_settings['apply_tags_coupon'] ) ) {
531 $apply_tags = array_merge( $apply_tags, $coupon_settings['apply_tags_coupon'] );
532 }
533 }
534
535 // Corporate accounts
536 $corporate_account = get_user_meta( $txn->user_id, 'mpca_corporate_account_id', true );
537
538 if ( ! empty( $corporate_account ) && ! empty( $settings['apply_tags_corporate_accounts'] ) ) {
539 $apply_tags = array_merge( $apply_tags, $coupon_settings['apply_tags_corporate_accounts'] );
540 }
541
542 if ( ! empty( $apply_tags ) ) {
543
544 // Prevent looping when tags are applied
545 remove_action( 'wpf_tags_modified', array( $this, 'add_to_membership' ), 10, 2 );
546
547 wp_fusion()->user->apply_tags( $apply_tags, $txn->user_id );
548
549 add_action( 'wpf_tags_modified', array( $this, 'add_to_membership' ), 10, 2 );
550
551 }
552
553 }
554
555 /**
556 * Applies tags when a recurring transaction fails
557 *
558 * @access public
559 * @return void
560 */
561
562 public function recurrring_transaction_failed( $event ) {
563
564 $txn = $event->get_data();
565
566 $settings = get_post_meta( $txn->product_id, 'wpf-settings-memberpress', true );
567
568 remove_action( 'wpf_tags_modified', array( $this, 'add_to_membership' ), 10, 2 );
569
570 // A payment failure removes them from the membership so we need to prevent the linked tag from re-enrolling them
571
572 if ( ! empty( $settings['tag_link'] ) ) {
573 wp_fusion()->user->remove_tags( $settings['tag_link'], $txn->user_id );
574 }
575
576 if ( ! empty( $settings ) && ! empty( $settings['apply_tags_payment_failed'] ) ) {
577 wp_fusion()->user->apply_tags( $settings['apply_tags_payment_failed'], $txn->user_id );
578 }
579
580 add_action( 'wpf_tags_modified', array( $this, 'add_to_membership' ), 10, 2 );
581
582 }
583
584
585 /**
586 * Removes tags when a recurring transaction is complete
587 *
588 * @access public
589 * @return void
590 */
591
592 public function recurrring_transaction_completed( $event ) {
593
594 $txn = $event->get_data();
595
596 $settings = get_post_meta( $txn->product_id, 'wpf-settings-memberpress', true );
597
598 if ( ! empty( $settings ) && ! empty( $settings['apply_tags_payment_failed'] ) ) {
599 wp_fusion()->user->remove_tags( $settings['apply_tags_payment_failed'], $txn->user_id );
600 }
601
602 if ( ! empty( $settings['apply_tags_expired'] ) ) {
603 wp_fusion()->user->remove_tags( $settings['apply_tags_expired'], $txn->user_id );
604 }
605
606 }
607
608 /**
609 * Apply expired tags
610 *
611 * @access public
612 * @return void
613 */
614
615 public function transaction_expired( $event, $sub_status = false ) {
616
617 $txn = $event->get_data();
618
619 $subscription = $txn->subscription();
620
621 if ( strtotime( $txn->expires_at ) <= time() && ( empty( $subscription ) || $subscription->is_expired() ) ) {
622
623 $settings = get_post_meta( $txn->product_id, 'wpf-settings-memberpress', true );
624
625 if ( empty( $settings ) ) {
626 return;
627 }
628
629 wpf_log( 'info', $txn->user_id, 'Transaction <a href="' . admin_url( 'admin.php?page=memberpress-trans&action=edit&id=' . $txn->id ) . '" target="_blank">#' . $txn->id . '</a> expired for product <a href="' . get_edit_post_link( $txn->product_id ) . '" target="_blank">' . get_the_title( $txn->product_id ) . '</a>.', array( 'source' => 'memberpress' ) );
630
631 remove_action( 'wpf_tags_modified', array( $this, 'add_to_membership' ), 10, 2 );
632
633 if ( ! empty( $settings['tag_link'] ) ) {
634 wp_fusion()->user->remove_tags( $settings['tag_link'], $txn->user_id );
635 }
636
637 if ( ! empty( $settings['remove_tags'] ) ) {
638 wp_fusion()->user->remove_tags( $settings['apply_tags_registration'], $txn->user_id );
639 }
640
641 if ( ! empty( $settings['apply_tags_expired'] ) ) {
642 wp_fusion()->user->apply_tags( $settings['apply_tags_expired'], $txn->user_id );
643 }
644
645 add_action( 'wpf_tags_modified', array( $this, 'add_to_membership' ), 10, 2 );
646
647 }
648
649 }
650
651 /**
652 * Apply tags for corporate / sub-accounts
653 *
654 * @access public
655 * @return void
656 */
657
658 public function corporate_accounts_tagging( $txn ) {
659
660 if ( 'sub_account' == $txn->txn_type ) {
661
662 $settings = get_post_meta( $txn->product_id, 'wpf-settings-memberpress', true );
663
664 if ( ! empty( $settings['apply_tags_corporate_accounts'] ) ) {
665 wp_fusion()->user->apply_tags( $settings['apply_tags_corporate_accounts'], $txn->user_id );
666 }
667 }
668
669 }
670
671 /**
672 * Triggered when a subscription status is changed
673 *
674 * @access public
675 * @return void
676 */
677
678 public function subscription_status_changed( $old_status, $new_status, $subscription ) {
679
680 error_log('subscription status changed from ' . $old_status . ' to ' . $new_status);
681
682 // Don't run on pending subscriptions
683 if ( 'pending' == $new_status ) {
684 return;
685 }
686
687 // Sometimes during registration MP goes from Active to Active
688 if ( $old_status == $new_status ) {
689 return;
690 }
691
692 // Don't require the checkout callback
693 remove_action( 'mepr-signup', array( $this, 'apply_tags_checkout' ) );
694 remove_action( 'mepr-event-transaction-completed', array( $this, 'apply_tags_checkout' ) );
695 remove_action( 'mepr-txn-status-complete', array( $this, 'apply_tags_checkout' ) );
696
697 // Get subscription data
698 $data = $subscription->get_values();
699
700 wpf_log( 'info', $data['user_id'], 'MemberPress subscription <a href="' . admin_url( 'admin.php?page=memberpress-subscriptions&action=edit&id=' . $subscription->id ) . '" target="_blank">#' . $subscription->id . '</a> status changed from <strong>' . ucwords( $old_status ) . '</strong> to <strong>' . ucwords( $new_status ) . '</strong>' );
701
702 // Get WPF settings
703 $settings = get_post_meta( $data['product_id'], 'wpf-settings-memberpress', true );
704
705 if ( empty( $settings ) ) {
706 $settings = array();
707 }
708
709 $defaults = array(
710 'apply_tags_registration' => array(),
711 'remove_tags' => false,
712 'tag_link' => array(),
713 'apply_tags_cancelled' => array(),
714 'apply_tags_expired' => array(),
715 'apply_tags_payment_failed' => array(),
716 'apply_tags_corporate_accounts' => array(),
717 'apply_tags_trial' => array(),
718 'apply_tags_converted' => array(),
719 );
720
721 $settings = wp_parse_args( $settings, $defaults );
722
723 $apply_tags = array();
724
725 $remove_tags = array();
726
727 // New subscriptions
728 if ( 'active' == $new_status ) {
729
730 // Apply tags
731 $apply_tags = array_merge( $apply_tags, $settings['apply_tags_registration'], $settings['tag_link'] );
732
733 // Remove cancelled / expired tags
734 $remove_tags = array_merge( $remove_tags, $settings['apply_tags_cancelled'], $settings['apply_tags_expired'] );
735
736 // Update data
737 $payment_method = $subscription->payment_method();
738
739 $update_data = array(
740 'mepr_reg_date' => $data['created_at'],
741 'mepr_payment_method' => $payment_method->name,
742 'mepr_membership_level' => get_the_title( $data['product_id'] ),
743 );
744
745 // Add expiration only if applicable
746 if ( strtotime( $subscription->get_expires_at() ) >= 0 ) {
747 $update_data['mepr_expiration'] = date( 'Y-m-d H:i:s', $subscription->get_expires_at() );
748 }
749
750 // Sync trial duration
751 if ( $subscription->trial ) {
752 $update_data['mepr_trial_duration'] = $subscription->trial_days;
753 }
754
755 // Coupon used
756 if ( ! empty( $subscription->coupon_id ) ) {
757
758 $update_data['mepr_coupon'] = get_the_title( $subscription->coupon_id );
759
760 $coupon_settings = get_post_meta( $subscription->coupon_id, 'wpf-settings', true );
761
762 if ( ! empty( $coupon_settings ) && ! empty( $coupon_settings['apply_tags_coupon'] ) ) {
763 $apply_tags = array_merge( $apply_tags, $coupon_settings['apply_tags_coupon'] );
764 }
765 }
766
767 // Get all meta as well to get any updated custom field values during upgrades
768 $user_meta = array_map( array( wp_fusion()->user, 'map_user_meta' ), get_user_meta( $data['user_id'] ) );
769
770 $update_data = array_merge( $user_meta, $update_data );
771
772 wp_fusion()->user->push_user_meta( $data['user_id'], $update_data );
773
774 }
775
776 // Other status changes
777 if ( $subscription->is_expired() && ! in_array( $new_status, array( 'active', 'pending' ) ) ) {
778
779 // Expired subscription
780 $remove_tags = array_merge( $remove_tags, $settings['tag_link'] );
781 $apply_tags = array_merge( $apply_tags, $settings['apply_tags_expired'] );
782
783 if ( ! empty( $settings['remove_tags'] ) ) {
784 $remove_tags = array_merge( $remove_tags, $settings['apply_tags_registration'] );
785 }
786 } elseif ( 'cancelled' == $new_status ) {
787
788 // Cancelled subscription
789 $apply_tags = array_merge( $apply_tags, $settings['apply_tags_cancelled'] );
790
791 } elseif ( $subscription->in_trial() ) {
792
793 // If is in a trial and isn't cancelled / expired
794 $apply_tags = array_merge( $apply_tags, $settings['apply_tags_trial'] );
795
796 }
797
798 // Prevent looping when tags are modified
799 remove_action( 'wpf_tags_modified', array( $this, 'add_to_membership' ), 10, 2 );
800
801 // Remove any tags
802 if ( ! empty( $remove_tags ) ) {
803 wp_fusion()->user->remove_tags( $remove_tags, $data['user_id'] );
804 }
805
806 // Apply any tags
807 if ( ! empty( $apply_tags ) ) {
808 wp_fusion()->user->apply_tags( $apply_tags, $data['user_id'] );
809 }
810
811 add_action( 'wpf_tags_modified', array( $this, 'add_to_membership' ), 10, 2 );
812
813 }
814
815 /**
816 * Adds MemberPress field group to meta fields list
817 *
818 * @access public
819 * @return array Field groups
820 */
821
822 public function add_meta_field_group( $field_groups ) {
823
824 if ( ! isset( $field_groups['memberpress'] ) ) {
825 $field_groups['memberpress'] = array(
826 'title' => 'MemberPress',
827 'fields' => array(),
828 );
829 }
830
831 return $field_groups;
832
833 }
834
835 /**
836 * Sets field labels and types for Custom MemberPress fields
837 *
838 * @access public
839 * @return array Meta fields
840 */
841
842 public function prepare_meta_fields( $meta_fields ) {
843
844 $mepr_options = MeprOptions::fetch();
845 $mepr_fields = array_merge( $mepr_options->custom_fields, $mepr_options->address_fields );
846
847 foreach ( $mepr_fields as $field_object ) {
848 $meta_fields[ $field_object->field_key ] = array(
849 'label' => $field_object->field_name,
850 'type' => $field_object->field_type,
851 'group' => 'memberpress',
852 );
853
854 if ( $field_object->field_key == 'mepr-address-country' ) {
855 $meta_fields[ $field_object->field_key ]['type'] = 'country';
856 }
857
858 if ( $field_object->field_key == 'mepr-address-state' ) {
859 $meta_fields[ $field_object->field_key ]['type'] = 'state';
860 }
861 }
862
863 $meta_fields['mepr_membership_level'] = array(
864 'label' => 'Membership Level Name',
865 'type' => 'text',
866 'group' => 'memberpress',
867 );
868
869 $meta_fields['mepr_reg_date'] = array(
870 'label' => 'Registration Date',
871 'type' => 'date',
872 'group' => 'memberpress',
873 );
874
875 $meta_fields['mepr_expiration'] = array(
876 'label' => 'Expiration Date',
877 'type' => 'date',
878 'group' => 'memberpress',
879 );
880
881 $meta_fields['mepr_trial_duration'] = array(
882 'label' => 'Trial Duration (days)',
883 'type' => 'text',
884 'group' => 'memberpress',
885 );
886
887 $meta_fields['mepr_payment_method'] = array(
888 'label' => 'Payment Method',
889 'type' => 'text',
890 'group' => 'memberpress',
891 );
892
893 $meta_fields['mepr_coupon'] = array(
894 'label' => 'Coupon Used',
895 'type' => 'text',
896 'group' => 'memberpress',
897 );
898
899 return $meta_fields;
900
901 }
902
903
904 /**
905 * Outputs <li> nav item for membership level configuration
906 *
907 * @access public
908 * @return mixed
909 */
910
911 public function output_product_nav_tab( $product ) {
912
913 echo '<a class="nav-tab main-nav-tab" href="#" id="wp-fusion">WP Fusion</a>';
914
915 }
916
917 /**
918 * Outputs tabbed content area for WPF membership settings
919 *
920 * @access public
921 * @return mixed
922 */
923
924 public function output_product_content_tab( $product ) {
925
926 echo '<div class="product_options_page wp-fusion">';
927
928 echo '<div class="product-options-panel">';
929
930 echo '<p>';
931
932 printf( __( 'For more information on these settings, %1$ssee our documentation%2$s.', 'wp-fusion' ), '<a href="https://wpfusion.com/documentation/membership/memberpress/" target="_blank">', '</a>' );
933
934 echo '</p>';
935
936 wp_nonce_field( 'wpf_meta_box_memberpress', 'wpf_meta_box_memberpress_nonce' );
937
938 $settings = array(
939 'apply_tags_registration' => array(),
940 'remove_tags' => false,
941 'tag_link' => array(),
942 'apply_tags_cancelled' => array(),
943 'apply_tags_expired' => array(),
944 'apply_tags_payment_failed' => array(),
945 'apply_tags_corporate_accounts' => array(),
946 'apply_tags_trial' => array(),
947 'apply_tags_converted' => array(),
948 );
949
950 if ( get_post_meta( $product->ID, 'wpf-settings-memberpress', true ) ) {
951 $settings = array_merge( $settings, get_post_meta( $product->ID, 'wpf-settings-memberpress', true ) );
952 }
953
954 echo '<label><strong>' . __( 'Apply Tags', 'wp-fusion' ) . ':</strong></label><br />';
955
956 $args = array(
957 'setting' => $settings['apply_tags_registration'],
958 'meta_name' => 'wpf-settings-memberpress',
959 'field_id' => 'apply_tags_registration',
960 'no_dupes' => array( 'tag_link' ),
961 );
962
963 wpf_render_tag_multiselect( $args );
964
965 echo '<br /><span class="description"><small>' . sprintf( __( 'These tags will be applied to the customer in %s upon registering for this membership.', 'wp-fusion' ), wp_fusion()->crm->name ) . '</small></span>';
966
967 echo '<br /><br /><input class="checkbox" type="checkbox" id="wpf-remove-tags-memberpress" name="wpf-settings-memberpress[remove_tags]" value="1" ' . checked( $settings['remove_tags'], 1, false ) . ' />';
968 echo '<label for="wpf-remove-tags-memberpress">' . __( 'Remove original tags (above) when the membership expires.', 'wp-fusion' ) . '.</label>';
969
970 echo '<br /><br /><label><strong>' . __( 'Link with Tag', 'wp-fusion' ) . ':</strong></label><br >';
971
972 $args = array(
973 'setting' => $settings['tag_link'],
974 'meta_name' => 'wpf-settings-memberpress',
975 'field_id' => 'tag_link',
976 'placeholder' => 'Select Tag',
977 'limit' => 1,
978 'no_dupes' => array( 'apply_tags_registration', 'apply_tags_cancelled' ),
979 );
980
981 wpf_render_tag_multiselect( $args );
982
983 echo '<br/><span class="description"><small>' . sprintf( __( 'This tag will be applied in %1$s when a member is registered. Likewise, if this tag is applied to a user from within %2$s, they will be automatically enrolled in this membership. If the tag is removed they will be removed from the membership.', 'wp-fusion' ), wp_fusion()->crm->name, wp_fusion()->crm->name ) . '</small></span><br />';
984
985 echo '<br /><label><strong>' . __( 'Apply Tags - Cancelled', 'wp-fusion' ) . ':</strong></label><br />';
986
987 $args = array(
988 'setting' => $settings['apply_tags_cancelled'],
989 'meta_name' => 'wpf-settings-memberpress',
990 'field_id' => 'apply_tags_cancelled',
991 'no_dupes' => array( 'tag_link' ),
992 );
993
994 wpf_render_tag_multiselect( $args );
995
996 echo '<br /><span class="description"><small>' . __( 'Apply these tags when a subscription is cancelled. Happens when an admin or user cancels a subscription, or if the payment gateway has canceled the subscription due to too many failed payments (will be removed if the membership is resumed).', 'wp-fusion' ) . '</small></span>';
997
998 echo '<br /><br /><label><strong>' . __( 'Apply Tags - Expired', 'wp-fusion' ) . ':</strong></label><br />';
999
1000 $args = array(
1001 'setting' => $settings['apply_tags_expired'],
1002 'meta_name' => 'wpf-settings-memberpress',
1003 'field_id' => 'apply_tags_expired',
1004 );
1005
1006 wpf_render_tag_multiselect( $args );
1007
1008 echo '<br /><span class="description"><small>' . __( 'Apply these tags when a membership expires (will be removed if the membership is resumed).', 'wp-fusion' ) . '</small></span>';
1009
1010 echo '<br /><br /><label><strong>' . __( 'Apply Tags - Payment Failed', 'wp-fusion' ) . ':</strong></label><br />';
1011
1012 $args = array(
1013 'setting' => $settings['apply_tags_payment_failed'],
1014 'meta_name' => 'wpf-settings-memberpress',
1015 'field_id' => 'apply_tags_payment_failed',
1016 );
1017
1018 wpf_render_tag_multiselect( $args );
1019
1020 echo '<br /><span class="description"><small>' . __( 'Apply these tags when a recurring payment fails (will be removed if a payment is made).', 'wp-fusion' ) . '</small></span>';
1021
1022 echo '<br /><br /><label><strong>' . __( 'Apply Tags - Trial', 'wp-fusion' ) . ':</strong></label><br />';
1023
1024 $args = array(
1025 'setting' => $settings['apply_tags_trial'],
1026 'meta_name' => 'wpf-settings-memberpress',
1027 'field_id' => 'apply_tags_trial',
1028 );
1029
1030 wpf_render_tag_multiselect( $args );
1031
1032 echo '<br /><span class="description"><small>' . __( 'Apply these tags when a subscription is created in a trial status.', 'wp-fusion' ) . '</small></span>';
1033
1034 echo '<br /><br /><label><strong>' . __( 'Apply Tags - Converted', 'wp-fusion' ) . ':</strong></label><br />';
1035
1036 $args = array(
1037 'setting' => $settings['apply_tags_converted'],
1038 'meta_name' => 'wpf-settings-memberpress',
1039 'field_id' => 'apply_tags_converted',
1040 );
1041
1042 wpf_render_tag_multiselect( $args );
1043
1044 echo '<br /><span class="description"><small>' . __( 'Apply these tags when a trial converts to a normal subscription.', 'wp-fusion' ) . '</small></span>';
1045
1046 // Corporate accounts addon
1047 if ( defined( 'MPCA_PLUGIN_NAME' ) ) {
1048
1049 echo '<br /><br /><label><strong>' . __( 'Apply Tags - Corporate Accounts', 'wp-fusion' ) . ':</strong></label><br />';
1050
1051 $args = array(
1052 'setting' => $settings['apply_tags_corporate_accounts'],
1053 'meta_name' => 'wpf-settings-memberpress',
1054 'field_id' => 'apply_tags_corporate_accounts',
1055 );
1056
1057 wpf_render_tag_multiselect( $args );
1058
1059 echo '<br /><span class="description"><small>' . __( 'Apply these tags to members added as sub-accounts to this account.', 'wp-fusion' ) . '</small></span>';
1060
1061 }
1062
1063 do_action( 'wpf_memberpress_meta_box', $settings, $product );
1064
1065 echo '</div>';
1066
1067 echo '</div>';
1068
1069 }
1070
1071 /**
1072 * Saves data captured in the new interfaces to a post meta field for the membership
1073 *
1074 * @access public
1075 * @return void
1076 */
1077
1078 public function save_meta_box_data( $post_id ) {
1079
1080 // Check if our nonce is set.
1081 if ( ! isset( $_POST['wpf_meta_box_memberpress_nonce'] ) || ! wp_verify_nonce( $_POST['wpf_meta_box_memberpress_nonce'], 'wpf_meta_box_memberpress' ) || $_POST['post_type'] == 'revision' ) {
1082 return;
1083 }
1084
1085 // If this is an autosave, our form has not been submitted, so we don't want to do anything.
1086 if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) {
1087 return;
1088 }
1089
1090 if ( $_POST['post_type'] == 'memberpressproduct' ) {
1091
1092 // Memberships
1093 if ( isset( $_POST['wpf-settings-memberpress'] ) ) {
1094 $data = $_POST['wpf-settings-memberpress'];
1095 } else {
1096 $data = array();
1097 }
1098
1099 // Update the meta field in the database.
1100 update_post_meta( $post_id, 'wpf-settings-memberpress', $data );
1101
1102 } elseif ( $_POST['post_type'] == 'memberpresscoupon' ) {
1103
1104 // Coupons
1105 if ( isset( $_POST['wpf-settings'] ) ) {
1106 $data = $_POST['wpf-settings'];
1107 } else {
1108 $data = array();
1109 }
1110
1111 // Update the meta field in the database.
1112 update_post_meta( $post_id, 'wpf-settings', $data );
1113
1114 }
1115
1116 }
1117
1118 /**
1119 * Adds meta box
1120 *
1121 * @access public
1122 * @return mixed
1123 */
1124
1125 public function add_coupon_meta_box( $post_id, $data ) {
1126
1127 add_meta_box( 'wpf-memberpress-meta', 'WP Fusion - Coupon Settings', array( $this, 'meta_box_callback' ), 'memberpresscoupon' );
1128
1129 }
1130
1131
1132 /**
1133 * Displays meta box content
1134 *
1135 * @access public
1136 * @return mixed
1137 */
1138
1139 public function meta_box_callback( $post ) {
1140
1141 $settings = array(
1142 'apply_tags_coupon' => array(),
1143 );
1144
1145 if ( get_post_meta( $post->ID, 'wpf-settings', true ) ) {
1146 $settings = array_merge( $settings, get_post_meta( $post->ID, 'wpf-settings', true ) );
1147 }
1148
1149 wp_nonce_field( 'wpf_meta_box_memberpress', 'wpf_meta_box_memberpress_nonce' );
1150
1151 echo '<table class="form-table"><tbody>';
1152
1153 echo '<tr>';
1154
1155 echo '<th scope="row"><label for="tag_link">Apply tags:</label></th>';
1156 echo '<td>';
1157
1158 $args = array(
1159 'setting' => $settings['apply_tags_coupon'],
1160 'meta_name' => 'wpf-settings',
1161 'field_id' => 'apply_tags_coupon',
1162 );
1163
1164 wpf_render_tag_multiselect( $args );
1165
1166 echo '<span class="description">These tags will be applied when this coupon is used.</span>';
1167 echo '</td>';
1168
1169 echo '</tr>';
1170
1171 echo '</tbody></table>';
1172
1173 }
1174
1175 /**
1176 * //
1177 * // BATCH TOOLS
1178 * //
1179 **/
1180
1181 /**
1182 * Adds Memberpress checkbox to available export options
1183 *
1184 * @access public
1185 * @return array Options
1186 */
1187
1188 public function export_options( $options ) {
1189
1190 $options['memberpress'] = array(
1191 'label' => 'MemberPress subscriptions meta',
1192 'title' => 'subscriptions',
1193 'tooltip' => __( 'Syncs the registration date, expiration date, and membership level name for all existing MemberPress subscriptions. Does not modify tags or create new contact records.', 'wp-fusion' ),
1194 );
1195
1196 $options['memberpress_transactions'] = array(
1197 'label' => 'MemberPress transactions meta',
1198 'title' => 'transactions',
1199 'tooltip' => __( 'Syncs the registration date, expiration date, payment method, and membership level name for all existing MemberPress transactions. Does not modify tags or create new contact records.', 'wp-fusion' ),
1200 );
1201
1202 $options['memberpress_memberships'] = array(
1203 'label' => 'MemberPress memberships statuses',
1204 'title' => 'memberships',
1205 'tooltip' => __( 'Updates the tags for all members based on their current membership status. Does not create new contact records.', 'wp-fusion' ),
1206 );
1207
1208 return $options;
1209
1210 }
1211
1212 /**
1213 * Counts total number of members to be processed
1214 *
1215 * @access public
1216 * @return array Members
1217 */
1218
1219 public function batch_init_subscriptions() {
1220
1221 $subscriptions_db = MeprSubscription::get_all();
1222 $subscriptions = array();
1223
1224 foreach ( $subscriptions_db as $subscription ) {
1225 $subscriptions[] = $subscription->id;
1226 }
1227
1228 wpf_log( 'info', 0, 'Beginning <strong>MemberPress subscriptions meta</strong> batch operation on ' . count( $subscriptions ) . ' subscriptions', array( 'source' => 'batch-process' ) );
1229
1230 return $subscriptions;
1231
1232 }
1233
1234 /**
1235 * Processes member actions in batches
1236 *
1237 * @access public
1238 * @return void
1239 */
1240
1241 public function batch_step_subscriptions( $subscription_id ) {
1242
1243 $subscription = new MeprSubscription( $subscription_id );
1244
1245 $user_id = $subscription->user_id;
1246
1247 $data = array(
1248 'mepr_reg_date' => $subscription->created_at,
1249 'mepr_expiration' => date( 'Y-m-d H:i:s', $subscription->get_expires_at() ),
1250 'mepr_membership_level' => get_the_title( $subscription->product_id ),
1251 );
1252
1253 if ( ! empty( $user_id ) ) {
1254 wp_fusion()->user->push_user_meta( $user_id, $data );
1255 }
1256
1257 }
1258
1259 /**
1260 * Counts total number of members to be processed
1261 *
1262 * @access public
1263 * @return array Members
1264 */
1265
1266 public function batch_init_transactions() {
1267
1268 $transactions_db = MeprTransaction::get_all();
1269 $transactions = array();
1270
1271 foreach ( $transactions_db as $transaction ) {
1272 $transactions[] = $transaction->id;
1273 }
1274
1275 wpf_log( 'info', 0, 'Beginning <strong>MemberPress transactions meta</strong> batch operation on ' . count( $transactions ) . ' transactions', array( 'source' => 'batch-process' ) );
1276
1277 return $transactions;
1278
1279 }
1280
1281 /**
1282 * Processes member actions in batches
1283 *
1284 * @access public
1285 * @return void
1286 */
1287
1288 public function batch_step_transactions( $transaction_id ) {
1289
1290 $txn = new MeprTransaction( $transaction_id );
1291
1292 $user_id = $txn->user_id;
1293
1294 $payment_method = $txn->payment_method();
1295 $product_id = $txn->product_id;
1296
1297 $update_data = array(
1298 'mepr_membership_level' => get_the_title( $product_id ),
1299 'mepr_reg_date' => $txn->created_at,
1300 'mepr_payment_method' => $payment_method->name,
1301 );
1302
1303 // Add expiration only if applicable
1304 if ( strtotime( $txn->expires_at ) >= 0 ) {
1305 $update_data['mepr_expiration'] = $txn->expires_at;
1306 }
1307
1308 // Coupons
1309 if ( ! empty( $txn->coupon_id ) ) {
1310 $update_data['mepr_coupon'] = get_the_title( $txn->coupon_id );
1311 }
1312
1313 if ( ! empty( $user_id ) ) {
1314 wp_fusion()->user->push_user_meta( $user_id, $update_data );
1315 }
1316
1317 }
1318
1319 /**
1320 * Counts total number of members to be processed
1321 *
1322 * @access public
1323 * @return array Members
1324 */
1325
1326 public function batch_init_memberships() {
1327
1328 $members = MeprUser::all( 'ids' );
1329
1330 wpf_log( 'info', 0, 'Beginning <strong>MemberPress memberships statuses</strong> batch operation on ' . count( $members ) . ' members', array( 'source' => 'batch-process' ) );
1331
1332 return $members;
1333
1334 }
1335
1336 /**
1337 * Processes member actions in batches
1338 *
1339 * @access public
1340 * @return void
1341 */
1342
1343 public function batch_step_memberships( $member_id ) {
1344
1345 $product_ids = array();
1346
1347 $member = new MeprUser( $member_id );
1348 $subscriptions = array_unique( $member->current_and_prior_subscriptions() );
1349
1350 // Get products from subscriptions
1351
1352 if ( ! empty( $subscriptions ) ) {
1353 $product_ids = array_merge( $product_ids, $subscriptions );
1354 }
1355
1356 // Get products from transactions
1357
1358 $transactions = $member->transactions();
1359
1360 if ( ! empty( $transactions ) ) {
1361
1362 foreach ( $transactions as $transaction ) {
1363
1364 if ( ! in_array( $transaction->product_id, $product_ids ) ) {
1365 $product_ids[] = $transaction->product_id;
1366 }
1367
1368 }
1369
1370 }
1371
1372 if ( empty( $product_ids ) ) {
1373 return;
1374 }
1375
1376 $apply_tags = array();
1377
1378 foreach ( $product_ids as $product_id ) {
1379
1380 $settings = get_post_meta( $product_id, 'wpf-settings-memberpress', true );
1381
1382 if ( $member->is_already_subscribed_to( $product_id ) ) {
1383
1384 if ( ! empty( $settings['apply_tags_registration'] ) ) {
1385 $apply_tags = array_merge( $apply_tags, $settings['apply_tags_registration'] );
1386 }
1387
1388 if ( ! empty( $settings['tag_link'] ) ) {
1389 $apply_tags = array_merge( $apply_tags, $settings['tag_link'] );
1390 }
1391 } else {
1392
1393 if ( ! empty( $settings['remove_tags'] ) ) {
1394 wp_fusion()->user->remove_tags( $settings['apply_tags_registration'], $member_id );
1395 }
1396 }
1397 }
1398
1399 if ( ! empty( $apply_tags ) ) {
1400
1401 wp_fusion()->user->apply_tags( $apply_tags, $member_id );
1402
1403 }
1404
1405 }
1406
1407
1408}
1409
1410new WPF_MemberPress();
1411