Paid Memberships Pro - Version 1.8.6

Version Description

  • BUG: The pmpro_membership_level_profile_fields_update() function now only fires on the personal_options_update and edit_user_profile_update hooks instead of the profile_update hook since this function expects to be called from the your-profile or edit-user page.
  • FEATURE: Added the ability to print invoices/orders from the Memberships -> Orders page in the dashboard. (Thanks, AgResources)
  • FEATURE: Added the ability to email invoices/orders from the Memberships -> Orders page in the dashboard. (Thanks, AgResources)
  • ENHANCEMENT: Added pmpro_before_change_membership_level action. Runs before the subscriptions are cancelled and before the SQL executes to change a user's level. Passes $level_id and $user_id.
  • ENHANCEMENT: Updated Stripe Web Hook handler to save event into global $pmpro_stripe_event so filters can act on it.
  • ENHANCEMENT: Added $pmpro_level as a second parameter for the pmpro_setup_new_user filter. (Thanks, David Crabill)
  • ENHANCEMENT: Added Vietnamese language pack. (Thanks, Alex Training Center)
Download this release

Release Info

Developer strangerstudios
Plugin Icon 128x128 Paid Memberships Pro
Version 1.8.6
Comparing to
See all releases

Code changes from version 1.8.5.5 to 1.8.6

Files changed (48) hide show
  1. adminpages/orders-print.php +39 -0
  2. adminpages/orders.php +149 -87
  3. adminpages/templates/orders-print.php +71 -0
  4. classes/class.pmproemail.php +51 -0
  5. classes/gateways/class.pmprogateway_payflowpro.php +42 -0
  6. classes/gateways/class.pmprogateway_stripe.php +8 -4
  7. css/frontend.css +1 -4
  8. email/billable_invoice.html +6 -0
  9. includes/currencies.php +11 -1
  10. includes/functions.php +72 -17
  11. includes/profile.php +2 -1
  12. includes/services.php +24 -0
  13. languages/email/vi_VN/admin_change.html +7 -0
  14. languages/email/vi_VN/admin_change_admin.html +5 -0
  15. languages/email/vi_VN/billing.html +16 -0
  16. languages/email/vi_VN/billing_admin.html +17 -0
  17. languages/email/vi_VN/billing_failure.html +11 -0
  18. languages/email/vi_VN/billing_failure_admin.html +11 -0
  19. languages/email/vi_VN/cancel.html +3 -0
  20. languages/email/vi_VN/cancel_admin.html +8 -0
  21. languages/email/vi_VN/checkout_check.html +17 -0
  22. languages/email/vi_VN/checkout_check_admin.html +17 -0
  23. languages/email/vi_VN/checkout_express.html +14 -0
  24. languages/email/vi_VN/checkout_express_admin.html +14 -0
  25. languages/email/vi_VN/checkout_free.html +8 -0
  26. languages/email/vi_VN/checkout_free_admin.html +8 -0
  27. languages/email/vi_VN/checkout_freetrial.html +19 -0
  28. languages/email/vi_VN/checkout_freetrial_admin.html +19 -0
  29. languages/email/vi_VN/checkout_paid.html +23 -0
  30. languages/email/vi_VN/checkout_paid_admin.html +23 -0
  31. languages/email/vi_VN/checkout_trial.html +23 -0
  32. languages/email/vi_VN/checkout_trial_admin.html +23 -0
  33. languages/email/vi_VN/credit_card_expiring.html +13 -0
  34. languages/email/vi_VN/default.html +1 -0
  35. languages/email/vi_VN/footer.html +4 -0
  36. languages/email/vi_VN/header.html +1 -0
  37. languages/email/vi_VN/invoice.html +19 -0
  38. languages/email/vi_VN/membership_expired.html +7 -0
  39. languages/email/vi_VN/membership_expiring.html +6 -0
  40. languages/email/vi_VN/trial_ending.html +8 -0
  41. languages/pmpro-vi_VN.mo +0 -0
  42. languages/pmpro-vi_VN.po +4556 -0
  43. pages/checkout.php +8 -4
  44. paid-memberships-pro.php +2 -2
  45. preheaders/checkout.php +1 -1
  46. readme.txt +22 -2
  47. services/ipnhandler.php +1 -1
  48. services/stripe-webhook.php +32 -31
adminpages/orders-print.php ADDED
@@ -0,0 +1,39 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Order - Print View
4
+ *
5
+ * Gets the order and displays the print view template.
6
+ *
7
+ * @since 1.8.6
8
+ */
9
+
10
+ //only admins can get this
11
+ if ( ! function_exists( "current_user_can" ) || ( ! current_user_can( "manage_options" ) && ! current_user_can( "pmpro_ordersprint" ) ) ) {
12
+ die( __( "You do not have permissions to perform this action.", "pmpro" ) );
13
+ }
14
+
15
+ // Do we have an order ID?
16
+ if ( empty( $_REQUEST['order'] ) ) {
17
+ wp_redirect( admin_url( 'admin..php?page=pmpro-orders' ) );
18
+ exit;
19
+ }
20
+
21
+ // Get order and membership level.
22
+ $order = new MemberOrder($_REQUEST['order']);
23
+ $level = pmpro_getLevel($order->membership_id);
24
+
25
+ // Load template
26
+ if ( file_exists( get_stylesheet_directory() . '/paid-memberships-pro/pages/orders-print.php' ) ) {
27
+ $template = get_stylesheet_directory() . '/paid-memberships-pro/pages/orders-print.php';
28
+ } elseif ( file_exists( get_template_directory() . '/paid-memberships-pro/pages/orders-print.php' ) ) {
29
+ $template = get_template_directory() . '/paid-memberships-pro/pages/orders-print.php';
30
+ } else {
31
+ $template = PMPRO_DIR . '/adminpages/templates/orders-print.php';
32
+ }
33
+
34
+ require_once( $template );
35
+ ?>
36
+ <script>
37
+ window.print();
38
+ </script>
39
+
adminpages/orders.php CHANGED
@@ -57,6 +57,109 @@
57
  else
58
  $status = "";
59
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
60
 
61
  //deleting?
62
  if(!empty($_REQUEST['delete']))
@@ -81,8 +184,6 @@
81
 
82
  $thisyear = date("Y");
83
 
84
-
85
-
86
  //this array stores fields that should be read only
87
  $read_only_fields = apply_filters("pmpro_orders_read_only_fields", array("code", "payment_transaction_id", "subscription_transaction_id"));
88
 
@@ -564,7 +665,42 @@
564
  </form>
565
 
566
  <?php } else { ?>
567
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
568
  <form id="posts-filter" method="get" action="">
569
  <h2>
570
  <?php _e('Orders', 'pmpro');?>
@@ -646,8 +782,7 @@
646
 
647
  <?php
648
  //Note: only orders belonging to current levels can be filtered. There is no option for orders belonging to deleted levels
649
- $levels = pmpro_getAllLevels();
650
-
651
  ?>
652
  <select id="l" name="l">
653
  <?php foreach($levels as $level) { ?>
@@ -781,85 +916,6 @@
781
  </p>
782
 
783
  <?php
784
- //some vars for the search
785
- if(isset($_REQUEST['pn']))
786
- $pn = intval($_REQUEST['pn']);
787
- else
788
- $pn = 1;
789
-
790
- if(isset($_REQUEST['limit']))
791
- $limit = intval($_REQUEST['limit']);
792
- else
793
- {
794
- /**
795
- * Filter to set the default number of items to show per page
796
- * on the Orders page in the admin.
797
- *
798
- * @since 1.8.4.5
799
- *
800
- * @param int $limit The number of items to show per page.
801
- */
802
- $limit = apply_filters('pmpro_orders_per_page', 15);
803
- }
804
-
805
- $end = $pn * $limit;
806
- $start = $end - $limit;
807
-
808
- //filters
809
- if($filter == "all" || !$filter)
810
- $condition = "1=1";
811
- elseif($filter == "within-a-date-range")
812
- {
813
- $start_date = $start_year."-".$start_month."-".$start_day;
814
- $end_date = $end_year."-".$end_month."-".$end_day;
815
-
816
- //add times to dates
817
- $start_date = $start_date . " 00:00:00";
818
- $end_date = $end_date . " 23:59:59";
819
-
820
- $condition = "timestamp BETWEEN '".esc_sql($start_date)."' AND '".esc_sql($end_date)."'";
821
- }
822
- elseif($filter == "predefined-date-range")
823
- {
824
- if($predefined_date == "Last Month")
825
- {
826
- $start_date = date("Y-m-d", strtotime("first day of last month", current_time("timestamp")));
827
- $end_date = date("Y-m-d", strtotime("last day of last month", current_time("timestamp")));
828
- }
829
- elseif($predefined_date == "This Month")
830
- {
831
- $start_date = date("Y-m-d", strtotime("first day of this month", current_time("timestamp")));
832
- $end_date = date("Y-m-d", strtotime("last day of this month", current_time("timestamp")));
833
- }
834
- elseif($predefined_date == "This Year")
835
- {
836
- $year = date('Y');
837
- $start_date = date("Y-m-d", strtotime("first day of January $year", current_time("timestamp")));
838
- $end_date = date("Y-m-d", strtotime("last day of December $year", current_time("timestamp")));
839
- }
840
-
841
- elseif($predefined_date == "Last Year")
842
- {
843
- $year = date('Y') - 1;
844
- $start_date = date("Y-m-d", strtotime("first day of January $year", current_time("timestamp")));
845
- $end_date = date("Y-m-d", strtotime("last day of December $year", current_time("timestamp")));
846
- }
847
-
848
- //add times to dates
849
- $start_date = $start_date . " 00:00:00";
850
- $end_date = $end_date . " 23:59:59";
851
-
852
- $condition = "timestamp BETWEEN '".esc_sql($start_date)."' AND '".esc_sql($end_date)."'";
853
- }
854
- elseif($filter == "within-a-level")
855
- {
856
- $condition = "membership_id = " . esc_sql($l);
857
- }
858
- elseif($filter == "within-a-status")
859
- {
860
- $condition = "status = '" . esc_sql($status) . "' ";
861
- }
862
-
863
  //string search
864
  if($s)
865
  {
@@ -904,7 +960,7 @@
904
  <?php
905
  }
906
  ?>
907
- <table class="widefat">
908
  <thead>
909
  <tr class="thead">
910
  <th><?php _e('ID', 'pmpro');?></th>
@@ -921,6 +977,8 @@
921
  <th></th>
922
  <th></th>
923
  <th></th>
 
 
924
  </tr>
925
  </thead>
926
  <tbody id="orders" class="list:order orders-list">
@@ -997,6 +1055,12 @@
997
  <td align="center">
998
  <a href="javascript:askfirst('<?php echo str_replace("'", "\'", sprintf(__("Deleting orders is permanent and can affect active users. Are you sure you want to delete order %s?", "pmpro"), str_replace("'", "", $order->code)));?>', 'admin.php?page=pmpro-orders&delete=<?php echo $order->id;?>'); void(0);"><?php _e('delete', 'pmpro');?></a>
999
  </td>
 
 
 
 
 
 
1000
  </tr>
1001
  <?php
1002
  }
@@ -1013,7 +1077,6 @@
1013
  </tbody>
1014
  </table>
1015
  </form>
1016
-
1017
  <?php
1018
  //add normal args
1019
  $pagination_url = add_query_arg($url_params, get_admin_url(NULL, "/admin.php?page=pmpro-orders"));
@@ -1021,7 +1084,6 @@
1021
  ?>
1022
 
1023
  <?php } ?>
1024
-
1025
  <?php
1026
  require_once(dirname(__FILE__) . "/admin_footer.php");
1027
  ?>
57
  else
58
  $status = "";
59
 
60
+ //some vars for the search
61
+ if(isset($_REQUEST['pn']))
62
+ $pn = intval($_REQUEST['pn']);
63
+ else
64
+ $pn = 1;
65
+
66
+ if(isset($_REQUEST['limit']))
67
+ $limit = intval($_REQUEST['limit']);
68
+ else
69
+ {
70
+ /**
71
+ * Filter to set the default number of items to show per page
72
+ * on the Orders page in the admin.
73
+ *
74
+ * @since 1.8.4.5
75
+ *
76
+ * @param int $limit The number of items to show per page.
77
+ */
78
+ $limit = apply_filters('pmpro_orders_per_page', 15);
79
+ }
80
+
81
+ $end = $pn * $limit;
82
+ $start = $end - $limit;
83
+
84
+ //filters
85
+ if(empty($filter) || $filter === "all")
86
+ {
87
+ $condition = "1=1";
88
+ $filter = "all";
89
+ }
90
+ elseif($filter == "within-a-date-range")
91
+ {
92
+ $start_date = $start_year."-".$start_month."-".$start_day;
93
+ $end_date = $end_year."-".$end_month."-".$end_day;
94
+
95
+ //add times to dates
96
+ $start_date = $start_date . " 00:00:00";
97
+ $end_date = $end_date . " 23:59:59";
98
+
99
+ $condition = "timestamp BETWEEN '".esc_sql($start_date)."' AND '".esc_sql($end_date)."'";
100
+ }
101
+ elseif($filter == "predefined-date-range")
102
+ {
103
+ if($predefined_date == "Last Month")
104
+ {
105
+ $start_date = date("Y-m-d", strtotime("first day of last month", current_time("timestamp")));
106
+ $end_date = date("Y-m-d", strtotime("last day of last month", current_time("timestamp")));
107
+ }
108
+ elseif($predefined_date == "This Month")
109
+ {
110
+ $start_date = date("Y-m-d", strtotime("first day of this month", current_time("timestamp")));
111
+ $end_date = date("Y-m-d", strtotime("last day of this month", current_time("timestamp")));
112
+ }
113
+ elseif($predefined_date == "This Year")
114
+ {
115
+ $year = date('Y');
116
+ $start_date = date("Y-m-d", strtotime("first day of January $year", current_time("timestamp")));
117
+ $end_date = date("Y-m-d", strtotime("last day of December $year", current_time("timestamp")));
118
+ }
119
+
120
+ elseif($predefined_date == "Last Year")
121
+ {
122
+ $year = date('Y') - 1;
123
+ $start_date = date("Y-m-d", strtotime("first day of January $year", current_time("timestamp")));
124
+ $end_date = date("Y-m-d", strtotime("last day of December $year", current_time("timestamp")));
125
+ }
126
+
127
+ //add times to dates
128
+ $start_date = $start_date . " 00:00:00";
129
+ $end_date = $end_date . " 23:59:59";
130
+
131
+ $condition = "timestamp BETWEEN '".esc_sql($start_date)."' AND '".esc_sql($end_date)."'";
132
+ }
133
+ elseif($filter == "within-a-level")
134
+ {
135
+ $condition = "membership_id = " . esc_sql($l);
136
+ }
137
+ elseif($filter == "within-a-status")
138
+ {
139
+ $condition = "status = '" . esc_sql($status) . "' ";
140
+ }
141
+
142
+ //emailing?
143
+ if(!empty($_REQUEST['email']) && !empty($_REQUEST['order']))
144
+ {
145
+ $email = new PMProEmail();
146
+ $user = get_user_by('email', $_REQUEST['email']);
147
+ $order = new MemberOrder($_REQUEST['order']);
148
+ if($email->sendBillableInvoiceEmail($user, $order))
149
+ {
150
+ $pmpro_msg = __("Invoice emailed successfully.", "pmpro");
151
+ $pmpro_msgt = "success";
152
+ }
153
+ else
154
+ {
155
+ $pmpro_msg = __("Error emailing invoice.", "pmpro");
156
+ $pmpro_msgt = "error";
157
+ }
158
+
159
+ //clean up so we stay on the orders list view
160
+ unset($_REQUEST['order']);
161
+ $order = NULL;
162
+ }
163
 
164
  //deleting?
165
  if(!empty($_REQUEST['delete']))
184
 
185
  $thisyear = date("Y");
186
 
 
 
187
  //this array stores fields that should be read only
188
  $read_only_fields = apply_filters("pmpro_orders_read_only_fields", array("code", "payment_transaction_id", "subscription_transaction_id"));
189
 
665
  </form>
666
 
667
  <?php } else { ?>
668
+ <?php
669
+ /**
670
+ * Code to handle emailing billable invoices.
671
+ *
672
+ * @since 1.8.6
673
+ */
674
+ ?>
675
+ <script>
676
+ // Update fields in email modal.
677
+ jQuery(document).ready(function($) {
678
+ var order, order_id;
679
+ $('.email_link').click(function() {
680
+ order_id = $(this).data('order');
681
+ $('input[name=order]').val(order_id);
682
+ // Get email address from order ID
683
+ data = {
684
+ action: 'pmpro_get_order_json',
685
+ order_id: order_id
686
+ };
687
+ $.post(ajaxurl, data, function(response) {
688
+ order = JSON.parse(response);
689
+ $('input[name=email]').val(order.Email);
690
+ });
691
+ });
692
+ });
693
+ </script>
694
+ <?php add_thickbox(); ?>
695
+ <div id="email_invoice" style="display:none;">
696
+ <h3><?php _e('Email Invoice', 'pmpro'); ?></h3>
697
+ <form method="post" action="">
698
+ <input type="hidden" name="order" value="" />
699
+ <?php _e('Send an invoice for this order to: ', 'pmpro'); ?>
700
+ <input type="text" value="" name="email" />
701
+ <button class="button button-primary alignright"><?php _e('Send Email', 'pmpro'); ?></button>
702
+ </form>
703
+ </div>
704
  <form id="posts-filter" method="get" action="">
705
  <h2>
706
  <?php _e('Orders', 'pmpro');?>
782
 
783
  <?php
784
  //Note: only orders belonging to current levels can be filtered. There is no option for orders belonging to deleted levels
785
+ $levels = pmpro_getAllLevels(true, true);
 
786
  ?>
787
  <select id="l" name="l">
788
  <?php foreach($levels as $level) { ?>
916
  </p>
917
 
918
  <?php
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
919
  //string search
920
  if($s)
921
  {
960
  <?php
961
  }
962
  ?>
963
+ <table class="widefat">
964
  <thead>
965
  <tr class="thead">
966
  <th><?php _e('ID', 'pmpro');?></th>
977
  <th></th>
978
  <th></th>
979
  <th></th>
980
+ <th></th>
981
+ <th></th>
982
  </tr>
983
  </thead>
984
  <tbody id="orders" class="list:order orders-list">
1055
  <td align="center">
1056
  <a href="javascript:askfirst('<?php echo str_replace("'", "\'", sprintf(__("Deleting orders is permanent and can affect active users. Are you sure you want to delete order %s?", "pmpro"), str_replace("'", "", $order->code)));?>', 'admin.php?page=pmpro-orders&delete=<?php echo $order->id;?>'); void(0);"><?php _e('delete', 'pmpro');?></a>
1057
  </td>
1058
+ <td align="center">
1059
+ <a href="admin-ajax.php?action=pmpro_orders_print_view&order=<?php echo $order->id; ?>" target="_blank"><?php _e('print', 'pmpro');?></a>
1060
+ </td>
1061
+ <td align="center">
1062
+ <a href="#TB_inline?width=600&height=200&inlineId=email_invoice" class="thickbox email_link" data-order="<?php echo $order->id; ?>"><?php _e('email', 'pmpro');?></a>
1063
+ </td>
1064
  </tr>
1065
  <?php
1066
  }
1077
  </tbody>
1078
  </table>
1079
  </form>
 
1080
  <?php
1081
  //add normal args
1082
  $pagination_url = add_query_arg($url_params, get_admin_url(NULL, "/admin.php?page=pmpro-orders"));
1084
  ?>
1085
 
1086
  <?php } ?>
 
1087
  <?php
1088
  require_once(dirname(__FILE__) . "/admin_footer.php");
1089
  ?>
adminpages/templates/orders-print.php ADDED
@@ -0,0 +1,71 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Template for Order Print Views
4
+ *
5
+ * @since 1.8.6
6
+ */
7
+ ?>
8
+ <table style="width:600px;margin-left:auto;margin-right:auto;">
9
+ <thead>
10
+ <tr>
11
+ <td rowspan="2" style="width:80%;">
12
+ <h2><?php bloginfo( 'sitename' ); ?></h2>
13
+ </td>
14
+ <td><?php echo __('Invoice #: ', 'pmpro') . '&nbsp;' . $order->code; ?></td>
15
+ </tr>
16
+ <tr>
17
+ <td>
18
+ <?php echo __( 'Date:', 'pmpro' ) . '&nbsp;' . date( 'Y-m-d', $order->timestamp ) ?>
19
+ </td>
20
+ </tr>
21
+ <?php if(!empty($order->billing->name)): ?>
22
+ <tr>
23
+ <td>
24
+ <strong><?php _e( 'Bill to:', 'pmpro' ); ?></strong><br>
25
+ <?php
26
+ echo pmpro_formatAddress(
27
+ $order->billing->name,
28
+ $order->billing->street,
29
+ "",
30
+ $order->billing->city,
31
+ $order->billing->state,
32
+ $order->billing->zip,
33
+ $order->billing->country,
34
+ $order->billing->phone
35
+ );
36
+ ?>
37
+ <?php endif; ?>
38
+ </td>
39
+ </tr>
40
+ </thead>
41
+ <tbody>
42
+ <tr>
43
+ <td colspan="2">
44
+ <table style="width:100%;border-width:1px;border-style:solid;border-collapse:collapse;">
45
+ <tr style="border-width:1px;border-style:solid;border-collapse:collapse;">
46
+ <th style="text-align:center;border-width:1px;border-style:solid;border-collapse:collapse;"><?php _e('ID', 'pmpro'); ?></th>
47
+ <th style="border-width:1px;border-style:solid;border-collapse:collapse;"><?php _e('Item', 'pmpro'); ?></th>
48
+ <th style="border-width:1px;border-style:solid;border-collapse:collapse;"><?php _e('Price', 'pmpro'); ?></th>
49
+ </tr>
50
+ <tr style="border-width:1px;border-style:solid;border-collapse:collapse;">
51
+ <td style="text-align:center;border-width:1px;border-style:solid;border-collapse:collapse;"><?php echo $level->id; ?></td>
52
+ <td style="border-width:1px;border-style:solid;border-collapse:collapse;"><?php echo $level->name; ?></td>
53
+ <td style="text-align:right;"><?php echo $order->subtotal; ?></td>
54
+ </tr>
55
+ <tr style="border-width:1px;border-style:solid;border-collapse:collapse;">
56
+ <th colspan="2" style="text-align:right;border-width:1px;border-style:solid;border-collapse:collapse;"><?php _e('Subtotal', 'pmpro'); ?></th>
57
+ <td style="text-align:right;border-width:1px;border-style:solid;border-collapse:collapse;"><?php echo $order->subtotal; ?></td>
58
+ </tr>
59
+ <tr style="border-width:1px;border-style:solid;border-collapse:collapse;">
60
+ <th colspan="2" style="text-align:right;border-width:1px;border-style:solid;border-collapse:collapse;"><?php _e('Tax', 'pmpro'); ?></th>
61
+ <td style="text-align:right;border-width:1px;border-style:solid;border-collapse:collapse;"><?php echo $order->tax; ?></td>
62
+ </tr>
63
+ <tr style="border-width:1px;border-style:solid;border-collapse:collapse;">
64
+ <th colspan="2" style="text-align:right;border-width:1px;border-style:solid;border-collapse:collapse;"><?php _e('Total', 'pmpro'); ?></th>
65
+ <th style="text-align:right;border-width:1px;border-style:solid;border-collapse:collapse;"><?php echo pmpro_formatPrice($order->total); ?></th>
66
+ </tr>
67
+ </table>
68
+ </td>
69
+ </tr>
70
+ </tbody>
71
+ </table>
classes/class.pmproemail.php CHANGED
@@ -821,4 +821,55 @@
821
 
822
  return $this->sendEmail();
823
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
824
  }
821
 
822
  return $this->sendEmail();
823
  }
824
+
825
+ /**
826
+ * Send billable invoice email.
827
+ *
828
+ * @since 1.8.6
829
+ *
830
+ * @param WP_User $user
831
+ * @param MemberOrder $order
832
+ *
833
+ * @return bool Whether the email was sent successfully.
834
+ */
835
+ function sendBillableInvoiceEmail($user = NULL, $order = NULL)
836
+ {
837
+ global $current_user;
838
+
839
+ if(!$user)
840
+ $user = $current_user;
841
+
842
+ if(!$user || !$order)
843
+ return false;
844
+
845
+ $level = pmpro_getLevel($order->membership_id);
846
+
847
+ $this->email = $user->user_email;
848
+ $this->subject = __('Invoice for Order #: ', 'pmpro') . $order->code;
849
+ $this->template = "billable_invoice";
850
+
851
+ // Load invoice template
852
+ if ( file_exists( get_stylesheet_directory() . '/paid-memberships-pro/pages/orders-print.php' ) ) {
853
+ $template = get_stylesheet_directory() . '/paid-memberships-pro/pages/orders-print.php';
854
+ } elseif ( file_exists( get_template_directory() . '/paid-memberships-pro/pages/orders-print.php' ) ) {
855
+ $template = get_template_directory() . '/paid-memberships-pro/pages/orders-print.php';
856
+ } else {
857
+ $template = PMPRO_DIR . '/adminpages/templates/orders-print.php';
858
+ }
859
+
860
+ ob_start();
861
+ require_once( $template );
862
+
863
+ $invoice = ob_get_contents();
864
+ ob_end_clean();
865
+
866
+ $this->data = array(
867
+ 'order_code' => $order->code,
868
+ 'login_link' => wp_login_url(pmpro_url("account")),
869
+ 'invoice_link' => wp_login_url(pmpro_url("invoice", "?invoice=" . $order->code)),
870
+ 'invoice' => $invoice
871
+ );
872
+
873
+ return $this->sendEmail();
874
+ }
875
  }
classes/gateways/class.pmprogateway_payflowpro.php CHANGED
@@ -242,6 +242,13 @@
242
  $nvpStr .= "&CITY=" . $order->billing->city . "&STATE=" . $order->billing->state . "&BILLTOCOUNTRY=" . $order->billing->country . "&ZIP=" . $order->billing->zip . "&PHONENUM=" . $order->billing->phone;
243
  }
244
 
 
 
 
 
 
 
 
245
  //for debugging, let's attach this to the class object
246
  $this->nvpStr = $nvpStr;
247
 
@@ -268,6 +275,13 @@
268
  //paypal profile stuff
269
  $nvpStr="&ORIGID=" . $authorization_id;
270
 
 
 
 
 
 
 
 
271
  $this->httpParsedResponseAr = $this->PPHttpPost('V', $nvpStr);
272
 
273
  if("0" == strtoupper($this->httpParsedResponseAr["RESULT"])) {
@@ -316,6 +330,13 @@
316
  $nvpStr .= "&CITY=" . $order->billing->city . "&STATE=" . $order->billing->state . "&BILLTOCOUNTRY=" . $order->billing->country . "&ZIP=" . $order->billing->zip . "&PHONENUM=" . $order->billing->phone;
317
  }
318
 
 
 
 
 
 
 
 
319
  $this->nvpStr = $nvpStr;
320
  $this->httpParsedResponseAr = $this->PPHttpPost('S', $nvpStr);
321
 
@@ -432,6 +453,13 @@
432
  $nvpStr .= "&CITY=" . $order->billing->city . "&STATE=" . $order->billing->state . "&BILLTOCOUNTRY=" . $order->billing->country . "&ZIP=" . $order->billing->zip . "&PHONENUM=" . $order->billing->phone;
433
  }
434
 
 
 
 
 
 
 
 
435
  $this->nvpStr = $nvpStr;
436
  $this->httpParsedResponseAr = $this->PPHttpPost('R', $nvpStr);
437
 
@@ -474,6 +502,13 @@
474
  $nvpStr .= "&CITY=" . $order->billing->city . "&STATE=" . $order->billing->state . "&BILLTOCOUNTRY=" . $order->billing->country . "&ZIP=" . $order->billing->zip . "&PHONENUM=" . $order->billing->phone;
475
  }
476
 
 
 
 
 
 
 
 
477
  $this->nvpStr = $nvpStr;
478
  $this->httpParsedResponseAr = $this->PPHttpPost('R', $nvpStr);
479
 
@@ -499,6 +534,13 @@
499
  //paypal profile stuff
500
  $nvpStr = "&ORIGPROFILEID=" . $order->subscription_transaction_id . "&ACTION=C";
501
 
 
 
 
 
 
 
 
502
  $this->nvpStr = $nvpStr;
503
  $this->httpParsedResponseAr = $this->PPHttpPost('R', $nvpStr);
504
 
242
  $nvpStr .= "&CITY=" . $order->billing->city . "&STATE=" . $order->billing->state . "&BILLTOCOUNTRY=" . $order->billing->country . "&ZIP=" . $order->billing->zip . "&PHONENUM=" . $order->billing->phone;
243
  }
244
 
245
+ /**
246
+ * Filter NVP string
247
+ *
248
+ * @since 1.8.5.6
249
+ */
250
+ $nvpStr = apply_filters('pmpro_payflow_authorize_nvpstr', $nvpStr, $this);
251
+
252
  //for debugging, let's attach this to the class object
253
  $this->nvpStr = $nvpStr;
254
 
275
  //paypal profile stuff
276
  $nvpStr="&ORIGID=" . $authorization_id;
277
 
278
+ /**
279
+ * Filter NVP string
280
+ *
281
+ * @since 1.8.5.6
282
+ */
283
+ $nvpStr = apply_filters('pmpro_payflow_void_nvpstr', $nvpStr, $this);
284
+
285
  $this->httpParsedResponseAr = $this->PPHttpPost('V', $nvpStr);
286
 
287
  if("0" == strtoupper($this->httpParsedResponseAr["RESULT"])) {
330
  $nvpStr .= "&CITY=" . $order->billing->city . "&STATE=" . $order->billing->state . "&BILLTOCOUNTRY=" . $order->billing->country . "&ZIP=" . $order->billing->zip . "&PHONENUM=" . $order->billing->phone;
331
  }
332
 
333
+ /**
334
+ * Filter NVP string
335
+ *
336
+ * @since 1.8.5.6
337
+ */
338
+ $nvpStr = apply_filters('pmpro_payflow_charge_nvpstr', $nvpStr, $this);
339
+
340
  $this->nvpStr = $nvpStr;
341
  $this->httpParsedResponseAr = $this->PPHttpPost('S', $nvpStr);
342
 
453
  $nvpStr .= "&CITY=" . $order->billing->city . "&STATE=" . $order->billing->state . "&BILLTOCOUNTRY=" . $order->billing->country . "&ZIP=" . $order->billing->zip . "&PHONENUM=" . $order->billing->phone;
454
  }
455
 
456
+ /**
457
+ * Filter NVP string
458
+ *
459
+ * @since 1.8.5.6
460
+ */
461
+ $nvpStr = apply_filters('pmpro_payflow_subscribe_nvpstr', $nvpStr, $this);
462
+
463
  $this->nvpStr = $nvpStr;
464
  $this->httpParsedResponseAr = $this->PPHttpPost('R', $nvpStr);
465
 
502
  $nvpStr .= "&CITY=" . $order->billing->city . "&STATE=" . $order->billing->state . "&BILLTOCOUNTRY=" . $order->billing->country . "&ZIP=" . $order->billing->zip . "&PHONENUM=" . $order->billing->phone;
503
  }
504
 
505
+ /**
506
+ * Filter NVP string
507
+ *
508
+ * @since 1.8.5.6
509
+ */
510
+ $nvpStr = apply_filters('pmpro_payflow_update_nvpstr', $nvpStr, $this);
511
+
512
  $this->nvpStr = $nvpStr;
513
  $this->httpParsedResponseAr = $this->PPHttpPost('R', $nvpStr);
514
 
534
  //paypal profile stuff
535
  $nvpStr = "&ORIGPROFILEID=" . $order->subscription_transaction_id . "&ACTION=C";
536
 
537
+ /**
538
+ * Filter NVP string
539
+ *
540
+ * @since 1.8.5.6
541
+ */
542
+ $nvpStr = apply_filters('pmpro_payflow_cancel_nvpstr', $nvpStr, $this);
543
+
544
  $this->nvpStr = $nvpStr;
545
  $this->httpParsedResponseAr = $this->PPHttpPost('R', $nvpStr);
546
 
classes/gateways/class.pmprogateway_stripe.php CHANGED
@@ -231,8 +231,7 @@
231
  {
232
  //build array for creating token
233
  var args = {
234
- number: jQuery('#AccountNumber').val(),
235
- cvc: jQuery('#CVV').val(),
236
  exp_month: jQuery('#ExpirationMonth').val(),
237
  exp_year: jQuery('#ExpirationYear').val()
238
  <?php
@@ -251,6 +250,11 @@
251
  ?>
252
  };
253
 
 
 
 
 
 
254
  if (jQuery('#bfirstname').length && jQuery('#blastname').length)
255
  args['name'] = jQuery.trim(jQuery('#bfirstname').val() + ' ' + jQuery('#blastname').val());
256
 
@@ -283,7 +287,7 @@
283
  // insert the token into the form so it gets submitted to the server
284
  form$.append("<input type='hidden' name='stripeToken' value='" + token + "'/>");
285
 
286
- console.log(response);
287
 
288
  //insert fields for other card fields
289
  if(jQuery('#CardType[name=CardType]').length)
@@ -1268,7 +1272,7 @@
1268
  }
1269
  catch (Exception $e)
1270
  {
1271
- $order->error = __("Error creating plan with Stripe:", "pmpro") . $e->getMessage();
1272
  $order->shorterror = $order->error;
1273
  return false;
1274
  }
231
  {
232
  //build array for creating token
233
  var args = {
234
+ number: jQuery('#AccountNumber').val(),
 
235
  exp_month: jQuery('#ExpirationMonth').val(),
236
  exp_year: jQuery('#ExpirationYear').val()
237
  <?php
250
  ?>
251
  };
252
 
253
+ //add CVC if not blank
254
+ if(jQuery('#CVV').val().length)
255
+ args['cvc'] = jQuery('#CVV').val();
256
+
257
+ //add first and last name if not blank
258
  if (jQuery('#bfirstname').length && jQuery('#blastname').length)
259
  args['name'] = jQuery.trim(jQuery('#bfirstname').val() + ' ' + jQuery('#blastname').val());
260
 
287
  // insert the token into the form so it gets submitted to the server
288
  form$.append("<input type='hidden' name='stripeToken' value='" + token + "'/>");
289
 
290
+ //console.log(response);
291
 
292
  //insert fields for other card fields
293
  if(jQuery('#CardType[name=CardType]').length)
1272
  }
1273
  catch (Exception $e)
1274
  {
1275
+ $order->error = __("Error getting subscription with Stripe:", "pmpro") . $e->getMessage();
1276
  $order->shorterror = $order->error;
1277
  return false;
1278
  }
css/frontend.css CHANGED
@@ -67,7 +67,7 @@ form.pmpro_form label {float: left; margin: 3px 10px 0 0; width: 200px; font-wei
67
  form.pmpro_form label.pmpro_normal {float: none; margin: 0 0 0 0; width: auto; font-weight: normal; text-align: auto;}
68
  .pmpro_clickable {cursor: pointer;}
69
  form.pmpro_form .likelabel {font-weight: bold; }
70
- form.pmpro_form .input, form.pmpro_form textarea, .input, form.pmpro_form select {border: 1px solid #AAA; display: inline-block; margin: 0 3px 0 0; padding: 3px; width: auto; max-width: 60%; }
71
  form.pmpro_form textarea {font-family: Arial, Helvetica, sans-serif; font-size: 12px; }
72
  form.pmpro_form select {margin: 2px 0 0 0 ; font-size: 12px;}
73
  form.pmpro_form .lite {color: #666; }
@@ -177,9 +177,6 @@ li.pmpro_more {list-style-type: none; text-align: center; margin-left: -20px; pa
177
 
178
  .pmpro_small {font-size: .8em;}
179
 
180
- /* Fixes for Twentyfiften and other themes. */
181
- #your-profile input {display: inline-block; width: auto; } /* front end profile via Theme My Login */
182
-
183
  @media (max-width:768px){
184
  #pmpro_levels_table {border: none; }
185
  #pmpro_levels_table th {display: none; }
67
  form.pmpro_form label.pmpro_normal {float: none; margin: 0 0 0 0; width: auto; font-weight: normal; text-align: auto;}
68
  .pmpro_clickable {cursor: pointer;}
69
  form.pmpro_form .likelabel {font-weight: bold; }
70
+ form.pmpro_form .input, form.pmpro_form textarea, form.pmpro_form select {border: 1px solid #AAA; display: inline-block; margin: 0 3px 0 0; padding: 3px; width: auto; max-width: 60%; }
71
  form.pmpro_form textarea {font-family: Arial, Helvetica, sans-serif; font-size: 12px; }
72
  form.pmpro_form select {margin: 2px 0 0 0 ; font-size: 12px;}
73
  form.pmpro_form .lite {color: #666; }
177
 
178
  .pmpro_small {font-size: .8em;}
179
 
 
 
 
180
  @media (max-width:768px){
181
  #pmpro_levels_table {border: none; }
182
  #pmpro_levels_table th {display: none; }
email/billable_invoice.html ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
1
+ <p>Thank you for your membership to !!sitename!!. Below is your invoice for order #: !!order_code!!</p>
2
+
3
+ !!invoice!!
4
+
5
+ <p>Log in to your membership account here: !!login_link!!</p>
6
+ <p>To view an online version of this invoice, click here: !!invoice_link!!</p>
includes/currencies.php CHANGED
@@ -3,12 +3,22 @@
3
  global $pmpro_currencies, $pmpro_default_currency;
4
  $pmpro_default_currency = apply_filters("pmpro_default_currency", "USD");
5
 
 
 
 
 
 
 
 
 
 
 
6
  $pmpro_currencies = array(
7
  'USD' => __('US Dollars (&#36;)', 'pmpro'),
8
  'EUR' => array(
9
  'name' => __('Euros (&euro;)', 'pmpro'),
10
  'symbol' => '&euro;',
11
- 'position' => 'right'
12
  ),
13
  'GBP' => array(
14
  'name' => __('Pounds Sterling (&pound;)', 'pmpro'),
3
  global $pmpro_currencies, $pmpro_default_currency;
4
  $pmpro_default_currency = apply_filters("pmpro_default_currency", "USD");
5
 
6
+ function pmpro_euro_position_from_locale($position = 'right')
7
+ {
8
+ $locale = get_locale();
9
+ if(strpos($locale, 'en_') === 0)
10
+ {
11
+ $position = 'left';
12
+ }
13
+ return $position;
14
+ }
15
+
16
  $pmpro_currencies = array(
17
  'USD' => __('US Dollars (&#36;)', 'pmpro'),
18
  'EUR' => array(
19
  'name' => __('Euros (&euro;)', 'pmpro'),
20
  'symbol' => '&euro;',
21
+ 'position' => apply_filters("pmpro_euro_position", pmpro_euro_position_from_locale())
22
  ),
23
  'GBP' => array(
24
  'name' => __('Pounds Sterling (&pound;)', 'pmpro'),
includes/functions.php CHANGED
@@ -486,18 +486,28 @@ function pmpro_showRequiresMembershipMessage()
486
  }
487
  }
488
 
489
- /* pmpro_hasMembershipLevel() checks if the passed user is a member of the passed level
 
490
  *
 
491
  * $level may either be the ID or name of the desired membership_level. (or an array of such)
492
  * If $user_id is omitted, the value will be retrieved from $current_user.
493
  *
494
- * Return values:
495
- * Success returns boolean true.
496
- * Failure returns a string containing the error message.
 
 
 
 
 
 
 
 
497
  */
498
  function pmpro_hasMembershipLevel($levels = NULL, $user_id = NULL)
499
  {
500
- global $current_user, $all_membership_levels, $wpdb;
501
 
502
  $return = false;
503
 
@@ -530,13 +540,18 @@ function pmpro_hasMembershipLevel($levels = NULL, $user_id = NULL)
530
 
531
  if(empty($membership_levels))
532
  {
533
- //user has no levels just check if 0, L, or -L was sent in one of the levels
534
  if(in_array(0, $levels, true) || in_array("0", $levels))
535
  $return = true;
536
  elseif(in_array("L", $levels) || in_array("l", $levels))
537
  $return = (!empty($user_id) && $user_id == $current_user->ID);
538
  elseif(in_array("-L", $levels) || in_array("-l", $levels))
539
- $return = (empty($user_id) || $user_id != $current_user->ID);
 
 
 
 
 
540
  }
541
  else
542
  {
@@ -554,9 +569,9 @@ function pmpro_hasMembershipLevel($levels = NULL, $user_id = NULL)
554
  if(empty($user_id) || $user_id != $current_user->ID)
555
  $return = true;
556
  }
557
- elseif($level == "0")
558
  {
559
- continue; //user with levels so not a "non-member"
560
  }
561
  else
562
  {
@@ -667,6 +682,20 @@ function pmpro_changeMembershipLevel($level, $user_id = NULL, $old_level_status
667
  }
668
  }
669
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
670
  //should we cancel their gateway subscriptions?
671
  $pmpro_cancel_previous_subscriptions = true;
672
  if(isset($_REQUEST['cancel_membership']) && $_REQUEST['cancel_membership'] == false)
@@ -749,19 +778,20 @@ function pmpro_changeMembershipLevel($level, $user_id = NULL, $old_level_status
749
  }
750
  }
751
 
752
- //get level id
753
- if(is_array($level))
754
- $level_id = $level['membership_id']; //custom level
755
- else
756
- $level_id = $level; //just id
757
-
758
  //remove cached level
759
  global $all_membership_levels;
760
  unset($all_membership_levels[$user_id]);
761
 
762
  //update user data and call action
763
  pmpro_set_current_user();
764
- do_action("pmpro_after_change_membership_level", $level_id, $user_id); //$level is the $level_id here
 
 
 
 
 
 
 
765
  return true;
766
  }
767
 
@@ -1367,6 +1397,18 @@ function pmpro_getMembershipLevelForUser($user_id = NULL, $force = false)
1367
  JOIN {$wpdb->pmpro_memberships_users} AS mu ON (l.id = mu.membership_id)
1368
  WHERE mu.user_id = $user_id AND mu.status = 'active'
1369
  LIMIT 1");
 
 
 
 
 
 
 
 
 
 
 
 
1370
  return $all_membership_levels[$user_id];
1371
  }
1372
  }
@@ -1397,7 +1439,8 @@ function pmpro_getMembershipLevelsForUser($user_id = NULL, $include_inactive = f
1397
  $user_id = intval($user_id);
1398
 
1399
  global $wpdb;
1400
- return $wpdb->get_results("SELECT
 
1401
  l.id AS ID,
1402
  l.id as id,
1403
  mu.id as subscription_id,
@@ -1418,6 +1461,18 @@ function pmpro_getMembershipLevelsForUser($user_id = NULL, $include_inactive = f
1418
  FROM {$wpdb->pmpro_membership_levels} AS l
1419
  JOIN {$wpdb->pmpro_memberships_users} AS mu ON (l.id = mu.membership_id)
1420
  WHERE mu.user_id = $user_id".($include_inactive?"":" AND mu.status = 'active'"));
 
 
 
 
 
 
 
 
 
 
 
 
1421
  }
1422
 
1423
  /* pmpro_getLevel() returns the level object for a level
486
  }
487
  }
488
 
489
+ /**
490
+ * Function to check if a user has specified membership levels.
491
  *
492
+ * pmpro_hasMembershipLevel() checks if the passed user is a member of the passed level
493
  * $level may either be the ID or name of the desired membership_level. (or an array of such)
494
  * If $user_id is omitted, the value will be retrieved from $current_user.
495
  *
496
+ * Return values:
497
+ * * Success returns boolean true.
498
+ * * Failure returns a string containing the error message.
499
+ *
500
+ * @since 1.8.5 Added 'e' option for expired members.
501
+ * @since 1.0.0
502
+ *
503
+ * @param mixed $levels The levels to check.
504
+ * @param int $user_id The user ID to check.
505
+ *
506
+ * @return bool Result of membership query.
507
  */
508
  function pmpro_hasMembershipLevel($levels = NULL, $user_id = NULL)
509
  {
510
+ global $current_user, $wpdb;
511
 
512
  $return = false;
513
 
540
 
541
  if(empty($membership_levels))
542
  {
543
+ //user has no levels just check if 0, L, -1, or e was sent in one of the levels
544
  if(in_array(0, $levels, true) || in_array("0", $levels))
545
  $return = true;
546
  elseif(in_array("L", $levels) || in_array("l", $levels))
547
  $return = (!empty($user_id) && $user_id == $current_user->ID);
548
  elseif(in_array("-L", $levels) || in_array("-l", $levels))
549
+ $return = (empty($user_id) || $user_id != $current_user->ID);
550
+ elseif(in_array("E", $levels) || in_array("e", $levels)) {
551
+ $sql = "SELECT id FROM $wpdb->pmpro_memberships_users WHERE user_id=$user_id AND status='expired' LIMIT 1";
552
+ $expired = $wpdb->get_var($sql);
553
+ return !empty($expired);
554
+ }
555
  }
556
  else
557
  {
569
  if(empty($user_id) || $user_id != $current_user->ID)
570
  $return = true;
571
  }
572
+ elseif($level == "0" || strtoupper($level) == "E")
573
  {
574
+ continue; //user with levels so not a "non-member" or expired
575
  }
576
  else
577
  {
682
  }
683
  }
684
 
685
+ //get level id
686
+ if(is_array($level))
687
+ $level_id = $level['membership_id']; //custom level
688
+ else
689
+ $level_id = $level; //just id
690
+
691
+ /**
692
+ * Action to run before the membership level changes.
693
+ *
694
+ * @param int $level_id ID of the level changed to.
695
+ * @param int $user_id ID of the user changed.
696
+ */
697
+ do_action("pmpro_before_change_membership_level", $level_id, $user_id);
698
+
699
  //should we cancel their gateway subscriptions?
700
  $pmpro_cancel_previous_subscriptions = true;
701
  if(isset($_REQUEST['cancel_membership']) && $_REQUEST['cancel_membership'] == false)
778
  }
779
  }
780
 
 
 
 
 
 
 
781
  //remove cached level
782
  global $all_membership_levels;
783
  unset($all_membership_levels[$user_id]);
784
 
785
  //update user data and call action
786
  pmpro_set_current_user();
787
+
788
+ /**
789
+ * Action to run after the membership level changes.
790
+ *
791
+ * @param int $level_id ID of the level changed to.
792
+ * @param int $user_id ID of the user changed.
793
+ */
794
+ do_action("pmpro_after_change_membership_level", $level_id, $user_id);
795
  return true;
796
  }
797
 
1397
  JOIN {$wpdb->pmpro_memberships_users} AS mu ON (l.id = mu.membership_id)
1398
  WHERE mu.user_id = $user_id AND mu.status = 'active'
1399
  LIMIT 1");
1400
+
1401
+ /**
1402
+ * pmpro_get_membership_level_for_user filter.
1403
+ *
1404
+ * Filters the returned level.
1405
+ *
1406
+ * @since 1.8.5.4
1407
+ *
1408
+ * @param object $level Level object.
1409
+ */
1410
+ $all_membership_levels[$user_id] = apply_filters('pmpro_get_membership_level_for_user', $all_membership_levels[$user_id]);
1411
+
1412
  return $all_membership_levels[$user_id];
1413
  }
1414
  }
1439
  $user_id = intval($user_id);
1440
 
1441
  global $wpdb;
1442
+
1443
+ $levels = $wpdb->get_results("SELECT
1444
  l.id AS ID,
1445
  l.id as id,
1446
  mu.id as subscription_id,
1461
  FROM {$wpdb->pmpro_membership_levels} AS l
1462
  JOIN {$wpdb->pmpro_memberships_users} AS mu ON (l.id = mu.membership_id)
1463
  WHERE mu.user_id = $user_id".($include_inactive?"":" AND mu.status = 'active'"));
1464
+ /**
1465
+ * pmpro_get_membership_levels_for_user filter.
1466
+ *
1467
+ * Filters the returned levels.
1468
+ *
1469
+ * @since 1.8.5.4
1470
+ *
1471
+ * @param array $levels Array of level objects.
1472
+ */
1473
+ $levels = apply_filters('pmpro_get_membership_levels_for_user', $levels);
1474
+
1475
+ return $levels;
1476
  }
1477
 
1478
  /* pmpro_getLevel() returns the level object for a level
includes/profile.php CHANGED
@@ -344,4 +344,5 @@ function pmpro_membership_level_profile_fields_update()
344
  }
345
  add_action( 'show_user_profile', 'pmpro_membership_level_profile_fields' );
346
  add_action( 'edit_user_profile', 'pmpro_membership_level_profile_fields' );
347
- add_action( 'profile_update', 'pmpro_membership_level_profile_fields_update' );
 
344
  }
345
  add_action( 'show_user_profile', 'pmpro_membership_level_profile_fields' );
346
  add_action( 'edit_user_profile', 'pmpro_membership_level_profile_fields' );
347
+ add_action( 'personal_options_update', 'pmpro_membership_level_profile_fields_update' );
348
+ add_action( 'edit_user_profile_update', 'pmpro_membership_level_profile_fields_update' );
includes/services.php CHANGED
@@ -69,6 +69,30 @@ function pmpro_wp_ajax_orders_csv()
69
  }
70
  add_action('wp_ajax_orders_csv', 'pmpro_wp_ajax_orders_csv');
71
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
72
  function pmpro_update_level_order() {
73
  echo pmpro_setOption('level_order');
74
  exit;
69
  }
70
  add_action('wp_ajax_orders_csv', 'pmpro_wp_ajax_orders_csv');
71
 
72
+ /**
73
+ * Load the Orders print view.
74
+ *
75
+ * @since 1.8.6
76
+ */
77
+ function pmpro_orders_print_view() {
78
+ require_once(dirname(__FILE__) . "/../adminpages/orders-print.php");
79
+ exit;
80
+ }
81
+ add_action('wp_ajax_pmpro_orders_print_view', 'pmpro_orders_print_view');
82
+
83
+ /**
84
+ * Get order JSON.
85
+ *
86
+ * @since 1.8.6
87
+ */
88
+ function pmpro_get_order_json() {
89
+ $order_id = $_REQUEST['order_id'];
90
+ $order = new MemberOrder($order_id);
91
+ echo json_encode($order);
92
+ exit;
93
+ }
94
+ add_action('wp_ajax_pmpro_get_order_json', 'pmpro_get_order_json');
95
+
96
  function pmpro_update_level_order() {
97
  echo pmpro_setOption('level_order');
98
  exit;
languages/email/vi_VN/admin_change.html ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
1
+ <p>Admin của !!sitename!! đã thay đổi kiểu thành viên của bạn.</p>
2
+
3
+ <p>!!membership_change!!.</p>
4
+
5
+ <p>Nếu bạn không yêu cầu thay đổi kiểu thành viên và muốn biết thêm thông tin xin vui lòng liên hệ với chúng tôi tại !!siteemail!!</p>
6
+
7
+ <p>Đăng nhập vào tài khoản thành viên của bạn ở đây: !!login_link!!</p>
languages/email/vi_VN/admin_change_admin.html ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
1
+ <p>Admin của !!sitename!! đã thay đổi kiểu thành viên cho !!name!!.</p>
2
+
3
+ <p>!!membership_change!!.</p>
4
+
5
+ <p>Đăng nhập với WordPress admin ở đây: !!login_link!!</p>
languages/email/vi_VN/billing.html ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <p>Thông tin thanh toán của bạn tại !!sitename!! đã thay đổi.</p>
2
+
3
+ <p>Tài khoản: !!display_name!! (!!user_email!!)</p>
4
+ <p>
5
+ Thông tin thanh toán:<br />
6
+ !!billing_address!!
7
+ </p>
8
+
9
+ <p>
10
+ !!cardtype!!: !!accountnumber!!<br />
11
+ Hết hạn: !!expirationmonth!!/!!expirationyear!!
12
+ </p>
13
+
14
+ <p>Nếu bạn không yêu cầu thay đổi thông tin thanh toán xin vui lòng liên hệ với chúng tôi tại !!siteemail!!</p>
15
+
16
+ <p>Đăng nhập vào tài khoản thành viên của bạn ở đây: !!login_link!!</p>
languages/email/vi_VN/billing_admin.html ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <p> cho !!display_name!! tại !!sitename!! đã thay đổi.</p>
2
+
3
+ <p>Tài khoản: !!display_name!! (!!user_email!!)</p>
4
+ <p>
5
+ Thông tin thanh toán:<br />
6
+ !!billing_name!!<br />
7
+ !!billing_street!!<br />
8
+ !!billing_city!!, !!billing_state!! !!billing_zip!! !!billing_country!!
9
+ !!billing_phone!!
10
+ </p>
11
+
12
+ <p>
13
+ !!cardtype!!: !!accountnumber!!<br />
14
+ Hết hạn: !!expirationmonth!!/!!expirationyear!!
15
+ </p>
16
+
17
+ <p>Đăng nhập vào bảng điều khiển WordPressZ ở đây: !!login_link!!</p>
languages/email/vi_VN/billing_failure.html ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ <p>Việc thanh toán tiền cho đăng ký học này tại !!sitename!! đã thất bại. <strong> Vui lòng click vào link dưới đây để đăng nhập và cập nhật thông tin thanh toán để tránh bị ngừng tài khoản. !!login_link!!</strong></p>
2
+
3
+ <p>Tài khoản: !!display_name!! (!!user_email!!)</p>
4
+ <p>Các thông tin tài khoản gần đây nhất mà chúng tôi có trong hồ sơ là:</p>
5
+
6
+ <p>!!billing_address!!</p>
7
+
8
+ <p>
9
+ !!cardtype!!: !!accountnumber!!<br />
10
+ Hết hạn: !!expirationmonth!!/!!expirationyear!!
11
+ </p>
languages/email/vi_VN/billing_failure_admin.html ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ <p>Thanh toán thất bại</p>
2
+
3
+ <p>Tài khoản: !!display_name!! (!!user_email!!)</p>
4
+ <p>Các thông tin tài khoản gần đây nhất mà chúng tôi có trong hồ sơ là is:</p>
5
+
6
+ <p>!!billing_address!!</p>
7
+
8
+ <p>
9
+ !!cardtype!!: !!accountnumber!!<br />
10
+ Hết hạn: !!expirationmonth!!/!!expirationyear!!
11
+ </p>
languages/email/vi_VN/cancel.html ADDED
@@ -0,0 +1,3 @@
 
 
 
1
+ <p>Quyền thành viên của bạn !!sitename!! đã bị hủy bỏ.</p>
2
+
3
+ <p>Nếu bạn không yêu cầu hủy bỏ và muốn biết thêm thông tin xin vui lòng liên hệ với chúng tôi tại !!siteemail!!</p>
languages/email/vi_VN/cancel_admin.html ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
1
+ <p>Quyên thành viên của !!user_login!! tại !!sitename!! đã bi hủy.</p>
2
+
3
+ <p>Tài khoản: !!display_name!! (!!user_email!!)</p>
4
+ <p>Kiểu thành viên: !!membership_level_name!!</p>
5
+ <p>Ngày bắt đầu: !!startdate!!</p>
6
+ <p>Ngày hủy bỏ: !!enddate!!</p>
7
+
8
+ <p>Đăng nhập để vào phần quản trị của WordPress của bạn ở đây: !!login_link!!</p>
languages/email/vi_VN/checkout_check.html ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <p>Cám ơn bạn đã đăng ký thành viên tại !!sitename!!. Tài khoản thành viên của bạn được kích hoạt ngay bây giờ.</p>
2
+
3
+ !!instructions!!
4
+
5
+ <p>Dưới đây là chi tiết về tài khoản thành viên của bạn và biên nhận của hóa đơn cho việc tạo thành viên lần đầu của bạn.</p>
6
+
7
+ <p>Tài khoản: !!display_name!! (!!user_email!!)</p>
8
+ <p>Kiểu thành viên !!membership_level_name!!</p>
9
+ <p>Phí thành viên: !!membership_cost!!</p>
10
+ !!membership_expiration!! !!discount_code!!
11
+
12
+ <p>
13
+ Hóa đơn số!!invoice_id!! on !!invoice_date!!<br />
14
+ Hóa Đơn Tổng: !!invoice_total!!
15
+ </p>
16
+
17
+ <p>Đăng nhập để vào tài khoản của bạn ở đây: !!login_link!!</p>
languages/email/vi_VN/checkout_check_admin.html ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <p>Có một thành viên mới thanh toán tại !!sitename!!.</p>
2
+
3
+ <p><strong>Họ đã chọn thanh toán bằng đánh dấu vào.</strong></p>
4
+
5
+ <p>Dưới đây là chi tiết về tài khoản của thành viên mới và biên nhận cho hóa đơn thành viên lần đầu.</p>
6
+
7
+ <p>Tài khoản: !!display_name!! (!!user_email!!)</p>
8
+ <p>Kiểu thành viên: !!membership_level_name!!</p>
9
+ <p>Phí thành viên: !!membership_cost!!</p>
10
+ !!membership_expiration!! !!discount_code!!
11
+
12
+ <p>
13
+ Số hóa đơn!!invoice_id!! on !!invoice_date!!<br />
14
+ Hóa đơn tổng: $!!invoice_total!!
15
+ </p>
16
+
17
+ <p>Đăng nhập để vào tài khoản của bạn ở đây: !!login_link!!</p>
languages/email/vi_VN/checkout_express.html ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <p>Cảm ơn bạn đã đang ký thành viên tại !!sitename!!. Tài kh oản than9hf viên của bạn sẽ được kishs hoạt ngay.</p>
2
+ <p>Dưới đây là chi tiết về tài khoản thành viên của bạn và biên nhận cho hóa đơn cho thành viên lần đầu của bạn.</p>
3
+
4
+ <p>Tài khoản: !!display_name!! (!!user_email!!)</p>
5
+ <p>Kiểu thành viên: !!membership_level_name!!</p>
6
+ <p>Phí thành viên: !!membership_cost!!</p>
7
+ !!membership_expiration!! !!discount_code!!
8
+
9
+ <p>
10
+ Số hóa đơn!!invoice_id!! on !!invoice_date!!<br />
11
+ Hóa đơn tổng: !!invoice_total!!
12
+ </p>
13
+
14
+ <p>Đăng nhập để vào tài khoản của bạn ở đây: !!login_link!!</p>
languages/email/vi_VN/checkout_express_admin.html ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <p>Có một thành viên mới thanh toán tại !!sitename!!.</p>
2
+ <p>Dưới đây là chi tiết về tài khoản thành viên của bạn và biên nhận cho hóa đơn cho thành viên lần đầu của bạn.</p>
3
+
4
+ <p>Tài khoản: !!display_name!! (!!user_email!!)</p>
5
+ <p>Kiểu thành viên: !!membership_level_name!!</p>
6
+ <p>Phí thành viên: !!membership_cost!!</p>
7
+ !!membership_expiration!! !!discount_code!!
8
+
9
+ <p>
10
+ Số hóa đơn!!invoice_id!! on !!invoice_date!!<br />
11
+ Hóa đơn tổng: !!invoice_total!!
12
+ </p>
13
+
14
+ <p>Đăng nhập để vào tài khoản của bạn ở đây: !!login_link!!</p>
languages/email/vi_VN/checkout_free.html ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
1
+ <p>Cảm ơn bạn đã đang ký thành viên tại !!sitename!!. Tài khoản thành viên của bạn sẽ được kích hoạt ngay.</p>
2
+ <p>Below are details about your membership Tài khoản.</p>
3
+
4
+ <p>Tài khoản: !!display_name!! (!!user_email!!)</p>
5
+ <p>Kiểu thành viên: !!membership_level_name!!</p>
6
+ !!membership_expiration!! !!discount_code!!
7
+
8
+ <p>Đăng nhập để vào tài khoản của bạn ở đây: !!login_link!!</p>
languages/email/vi_VN/checkout_free_admin.html ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
1
+ <p>Có một thành viên mới thanh toán tại !!sitename!!.</p>
2
+ <p>Dưới đây là chi tiết về tài khoản thành viên của bạn.</p>
3
+
4
+ <p>Tài khoản: !!display_name!! (!!user_email!!)</p>
5
+ <p>Kiểu thành viên: !!membership_level_name!!</p>
6
+ !!membership_expiration!! !!discount_code!!
7
+
8
+ <p>Đăng nhập để vào tài khoản của bạn ở đây: !!login_link!!</p>
languages/email/vi_VN/checkout_freetrial.html ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <p>Cảm ơn bạn đã đang ký thành viên tại !!sitename!!. Tài kh oản thành viên của bạn sẽ được kích hoạt ngay.</p>
2
+ <p>Dưới đây là chi tiết về tài khoản thành viên của bạn.</p>
3
+
4
+ <p>Tài khoản: !!display_name!! (!!user_email!!)</p>
5
+ <p>Kiểu thành viên !!membership_level_name!!</p>
6
+ <p>Phí thành viên: !!membership_cost!!</p>
7
+ !!membership_expiration!! !!discount_code!!
8
+
9
+ <p>
10
+ Thông t in thanh toán trong hồ sơ:<br />
11
+ !!billing_address!!
12
+ </p>
13
+
14
+ <p>
15
+ !!cardtype!!: !!accountnumber!!<br />
16
+ Hết hạn: !!expirationmonth!!/!!expirationyear!!
17
+ </p>
18
+
19
+ <p>Đăng nhập để vào tài khoản của bạn ở đây: !!login_link!!</p>
languages/email/vi_VN/checkout_freetrial_admin.html ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <p>Có một thành viên mới thanh toán tại !!sitename!!.</p>
2
+ <p>Dưới đây là chi tiết về tài khoản thành viên của bạn và biên nhận cho hóa đơn cho thành viên lần đầu của bạn.</p>
3
+
4
+ <p>Tài khoản: !!display_name!! (!!user_email!!)</p>
5
+ <p>Kiểu thành viên: !!membership_level_name!!</p>
6
+ <p>Phí thành viên: !!membership_cost!!</p>
7
+ !!membership_expiration!! !!discount_code!!
8
+
9
+ <p>
10
+ Thông t in thanh toán trong hồ sơ:<br />
11
+ !!billing_address!!
12
+ </p>
13
+
14
+ <p>
15
+ !!cardtype!!: !!accountnumber!!<br />
16
+ Hết hạn: !!expirationmonth!!/!!expirationyear!!
17
+ </p>
18
+
19
+ <p>Đăng nhập để vào tài khoản của bạn ở đây: !!login_link!!</p>
languages/email/vi_VN/checkout_paid.html ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <p>Cảm ơn bạn đã đang ký thành viên tại !!sitename!!. Tài kh oản thành viên của bạn sẽ được kích hoạt ngay.</p>
2
+ <p>Dưới đây là chi tiết về tài khoản thành viên của bạn và biên nhận cho hóa đơn cho thành viên lần đầu của bạn.</p>
3
+
4
+ <p>Tài khoản: !!display_name!! (!!user_email!!)</p>
5
+ <p>Kiểu thành viên: !!membership_level_name!!</p>
6
+ <p>Phí thành viên: !!membership_cost!!</p>
7
+ !!membership_expiration!! !!discount_code!!
8
+
9
+ <p>
10
+ Số hóa đơn!!invoice_id!! trong !!invoice_date!!<br />
11
+ Hóa đơn tổng: !!invoice_total!!
12
+ </p>
13
+ <p>
14
+ Thông t in thanh toán:<br />
15
+ !!billing_address!!
16
+ </p>
17
+
18
+ <p>
19
+ !!cardtype!!: !!accountnumber!!<br />
20
+ Expires: !!expirationmonth!!/!!expirationyear!!
21
+ </p>
22
+
23
+ <p>Đăng nhập để vào tài khoản của bạn ở đây: !!login_link!!</p>
languages/email/vi_VN/checkout_paid_admin.html ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <p>Có một thành viên mới thanh toán tại !!sitename!!.</p>
2
+ <p>Dưới đây là chi tiết về tài khoản thành viên của bạn và biên nhận cho hóa đơn cho thành viên lần đầu của bạn.</p>
3
+
4
+ <p>Tài khoản: !!display_name!! (!!user_email!!)</p>
5
+ <p>Kiểu thành viên: !!membership_level_name!!</p>
6
+ <p>Phí thành viên: !!membership_cost!!</p>
7
+ !!membership_expiration!! !!discount_code!!
8
+
9
+ <p>
10
+ Số hóa đơn!!invoice_id!! trong !!invoice_date!!<br />
11
+ Hóa đơn tổng: !!invoice_total!!
12
+ </p>
13
+ <p>
14
+ Thông tin thanh toán:<br />
15
+ !!billing_address!!
16
+ </p>
17
+
18
+ <p>
19
+ !!cardtype!!: !!accountnumber!!<br />
20
+ Hết hạn: !!expirationmonth!!/!!expirationyear!!
21
+ </p>
22
+
23
+ <p>Đăng nhập để vào tài khoản của bạn ở đây: !!login_link!!</p>
languages/email/vi_VN/checkout_trial.html ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ 7<p>Cảm ơn bạn đã đang ký thành viên tại !!sitename!!. Tài kh oản thành viên của bạn sẽ được kích hoạt ngay.</p>
2
+ <p>Dưới đây là chi tiết về tài khoản thành viên của bạn và biên nhận cho hóa đơn cho thành viên lần đầu của bạn.</p>
3
+
4
+ <p>Tài khoản: !!display_name!! (!!user_email!!)</p>
5
+ <p>Kiểu thành viên: !!membership_level_name!!</p>
6
+ <p>Phí thành viên: !!membership_cost!!</p>
7
+ !!membership_expiration!! !!discount_code!!
8
+
9
+ <p>
10
+ Số hóa đơn!!invoice_id!! trong !!invoice_date!!<br />
11
+ Hóa đơn tổng: !!invoice_total!!
12
+ </p>
13
+ <p>
14
+ Thông tin thanh toán:<br />
15
+ !!billing_address!!
16
+ </p>
17
+
18
+ <p>
19
+ !!cardtype!!: !!accountnumber!!<br />
20
+ Hết hạn: !!expirationmonth!!/!!expirationyear!!
21
+ </p>
22
+
23
+ <p>Log in to your membership account here: !!login_link!!</p>
languages/email/vi_VN/checkout_trial_admin.html ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <p>Có một thành viên mới thanh toán tại !!sitename!!.</p>
2
+ <p>Dưới đây là chi tiết về tài khoản thành viên của bạn và biên nhận cho hóa đơn cho thành viên lần đầu của bạn.</p>
3
+
4
+ <p>Tài khoản: !!display_name!! (!!user_email!!)</p>
5
+ <p>Kiểu thành viên: !!membership_level_name!!</p>
6
+ <p>Phí thành viên: !!membership_cost!!</p>
7
+ !!membership_expiration!! !!discount_code!!
8
+
9
+ <p>
10
+ Số hóa đơn!!invoice_id!! on !!invoice_date!!<br />
11
+ Hóa đơn tổng: !!invoice_total!!
12
+ </p>
13
+ <p>
14
+ Thông tin thanh toán:<br />
15
+ !!billing_address!!
16
+ </p>
17
+
18
+ <p>
19
+ !!cardtype!!: !!accountnumber!!<br />
20
+ Hết hạn: !!expirationmonth!!/!!expirationyear!!
21
+ </p>
22
+
23
+ <p>Đăng nhập để vào tài khoản của bạn ở đây: !!login_link!!</p>
languages/email/vi_VN/credit_card_expiring.html ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <p>Phương thức thanh toán cho tài khoản thành viên tại !!sitename!! sẽ sớm hết hạn. <strong>Vui lòng click vào link dưới đây để đăng nhập và cập nhật thông tin thanh toán của bạn để tránh bị ngừng tài khoản. !!login_link!!</strong></p>
2
+
3
+ <p>Tài khoản: !!display_name!! (!!user_email!!)</p>
4
+ <p>Các thông tin tài khoản gần đây nhất mà chúng tôi có trong hồ sơ is:</p>
5
+
6
+ <p>!!billing_name!!</br />
7
+ !!billing_address!!
8
+ </p>
9
+
10
+ <p>
11
+ !!cardtype!!: !!accountnumber!!<br />
12
+ Hết hạn: !!expirationmonth!!/!!expirationyear!!
13
+ </p>
languages/email/vi_VN/default.html ADDED
@@ -0,0 +1 @@
 
1
+ !!body!!
languages/email/vi_VN/footer.html ADDED
@@ -0,0 +1,4 @@
 
 
 
 
1
+ <p>
2
+ Trân trọng,<br />
3
+ !!sitename!!
4
+ </p>
languages/email/vi_VN/header.html ADDED
@@ -0,0 +1 @@
 
1
+ <p>Kính gửi !!name!!,</p>
languages/email/vi_VN/invoice.html ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <p>Cảm ơn bạn đã đang ký thành viên tại !!sitename!!. Dưới đây là hóa đơn cho đăng ký loại thành viên gần đây nhất của bạn.</p>
2
+
3
+ <p>Tài khoản: !!display_name!! (!!user_email!!)</p>
4
+ <p>
5
+ Số hóa đơn!!invoice_id!! trong !!invoice_date!!<br />
6
+ Hóa đơn tổng: !!invoice_total!!
7
+ </p>
8
+ <p>
9
+ Thông tin thanh toán:<br />
10
+ !!billing_address!!
11
+ </p>
12
+
13
+ <p>
14
+ !!cardtype!!: !!accountnumber!!<br />
15
+ Hết hạn: !!expirationmonth!!/!!expirationyear!!
16
+ </p>
17
+
18
+ <p>Đăng nhập để vào tài khoản của bạn ở đây: !!login_link!!</p>
19
+ <p>Để xem một phiên bản trực tuyến của hoá đơn này, click vào đây: !!invoice_link!!</p>
languages/email/vi_VN/membership_expired.html ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
1
+ <p>Quyền thành viên tại !!sitename!! đã hết hạn.</p>
2
+
3
+ <p>Cám ơn bạn đã ủng hộ.</p>
4
+
5
+ <p>Xem các loại hội viên hiện tại của chúng tôi tại đây: !!levels_link!!</p>
6
+
7
+ <p>Đăng nhập để quản lý tài khoản của bạn ở đây: !!login_link!!</p>
languages/email/vi_VN/membership_expiring.html ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
1
+ <p>Cảm ơn bạn đã đang ký thành viên tại !!sitename!!. Đây chỉ là nhắc nhở rằng quyền thành viên của bạn sẽ kết thúc vào ngày !!enddate!!.</p>
2
+
3
+ <p>Tài khoản: !!display_name!! (!!user_email!!)</p>
4
+ <p>Kiểu thành viên: !!membership_level_name!!</p>
5
+
6
+ <p>Đăng nhập để vào tài khoản của bạn ở đây: !!login_link!!</p>
languages/email/vi_VN/trial_ending.html ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
1
+ <p>Cảm ơn bạn đã đang ký thành viên tại !!sitename!!. Thời gian dùng thử của bạn đang kết thucd !!trial_end!!.</p>
2
+
3
+ <p>Tài khoản: !!display_name!! (!!user_email!!)</p>
4
+ <p>Kiểu thành viên: !!membership_level_name!!</p>
5
+
6
+ <p>Lệ phí của bạn sẽ được thay đổi từ !!trial_amount!! tới !!billing_amount!! mỗi !!cycle_number!! !!cycle_period!!(s).</p>
7
+
8
+ <p>Đăng nhập để vào tài khoản của bạn ở đây: !!login_link!!</p>
languages/pmpro-vi_VN.mo ADDED
Binary file
languages/pmpro-vi_VN.po ADDED
@@ -0,0 +1,4556 @@