Paid Memberships Pro - Version 1.8.6.1

Version Description

  • SECURITY: Removed debug code from the PayPal IPN Handler script that was causing invalid IPN requests to process as if they were valid. (Thanks, Francois Harvey)
Download this release

Release Info

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

Code changes from version 1.8.6.2 to 1.8.6.1

adminpages/functions.php CHANGED
@@ -55,7 +55,8 @@ function pmpro_checkLevelForStripeCompatibility($level = NULL)
55
  $level = $wpdb->get_row("SELECT * FROM $wpdb->pmpro_membership_levels WHERE id = '" . esc_sql($level) . "' LIMIT 1");
56
 
57
  //check this level
58
- if($level->billing_limit > 0)
 
59
  {
60
  return false;
61
  }
55
  $level = $wpdb->get_row("SELECT * FROM $wpdb->pmpro_membership_levels WHERE id = '" . esc_sql($level) . "' LIMIT 1");
56
 
57
  //check this level
58
+ if(($level->cycle_number > 0 && $level->cycle_period == "Day") ||
59
+ $level->billing_limit > 0)
60
  {
61
  return false;
62
  }
adminpages/reports.php CHANGED
@@ -1,66 +1,62 @@
1
- <?php
2
- global $pmpro_reports;
3
-
4
- require_once(dirname(__FILE__) . "/admin_header.php");
5
-
6
- //default view, report widgets
7
- if(empty($_REQUEST['report']))
8
- {
9
- //wrapper
10
- ?>
11
- <div id="dashboard-widgets-wrap">
12
- <div id="dashboard-widgets" class="metabox-holder pmpro_reports-holder">
13
- <div id="postbox-container-1" class="postbox-container">
14
- <div id="normal-sortables" class="meta-box-sortables ui-sortable">
15
- <?php
16
-
17
- //report widgets
18
- $count = 0;
19
- $nreports = count($pmpro_reports);
20
- $split = false;
21
- foreach($pmpro_reports as $report => $title)
22
- {
23
- //make sure title is translated (since these are set before translations happen)
24
- $title = __($title, "pmpro");
25
-
26
- //put half of the report widgets in postbox-container-2
27
- if(!$split && $count++ > $nreports/2)
28
- {
29
- $split = true;
30
- ?>
31
- </div></div><div id="postbox-container-2" class="postbox-container"><div id="side-sortables" class="meta-box-sortables ui-sortable">
32
- <?php
33
- }
34
- ?>
35
- <div id="pmpro_report_<?php echo $report; ?>" class="postbox pmpro_clickable" onclick="location.href='<?php echo admin_url("admin.php?page=pmpro-reports&report=" . $report);?>';">
36
- <h3 class="hndle"><span><?php echo $title; ?></span></h3>
37
- <div class="inside">
38
- <?php call_user_func("pmpro_report_" . $report . "_widget"); ?>
39
- <p style="text-align:center;">
40
- <a class="button button-primary" href="<?php echo admin_url("admin.php?page=pmpro-reports&report=" . $report);?>"><?php _e('Details', 'pmpro');?></a>
41
- </p>
42
- </div>
43
- </div>
44
- <?php
45
- }
46
-
47
- //end wrapper
48
- ?>
49
- </div>
50
- </div>
51
- </div>
52
- <?php
53
- }
54
- else
55
- {
56
- //view a single report
57
- $report = sanitize_text_field($_REQUEST['report']);
58
- call_user_func("pmpro_report_" . $report . "_page");
59
- ?>
60
- <hr />
61
- <a class="button button-primary" href="<?php echo admin_url("admin.php?page=pmpro-reports");?>"><?php _e('Back to Reports Dashboard', 'pmpro');?></a>
62
- <?php
63
- }
64
-
65
- require_once(dirname(__FILE__) . "/admin_footer.php");
66
  ?>
1
+ <?php
2
+ global $pmpro_reports;
3
+
4
+ require_once(dirname(__FILE__) . "/admin_header.php");
5
+
6
+ //default view, report widgets
7
+ if(empty($_REQUEST['report']))
8
+ {
9
+ //wrapper
10
+ ?>
11
+ <div id="dashboard-widgets-wrap">
12
+ <div id="dashboard-widgets" class="metabox-holder pmpro_reports-holder columns-2">
13
+ <div id="postbox-container-1" class="postbox-container">
14
+ <div id="normal-sortables" class="meta-box-sortables ui-sortable">
15
+ <?php
16
+
17
+ //report widgets
18
+ $count = 0;
19
+ $nreports = count($pmpro_reports);
20
+ $split = false;
21
+ foreach($pmpro_reports as $report => $title)
22
+ {
23
+ //make sure title is translated (since these are set before translations happen)
24
+ $title = __($title, "pmpro");
25
+
26
+ //put half of the report widgets in postbox-container-2
27
+ if(!$split && $count++ > $nreports/2)
28
+ {
29
+ $split = true;
30
+ ?>
31
+ </div></div><div id="postbox-container-2" class="postbox-container"><div id="side-sortables" class="meta-box-sortables ui-sortable">
32
+ <?php
33
+ }
34
+ ?>
35
+ <div id="pmpro_report_<?php echo $report; ?>" class="postbox pmpro_clickable" onclick="location.href='<?php echo admin_url("admin.php?page=pmpro-reports&report=" . $report);?>';">
36
+ <h3 class="hndle"><span><?php echo $title; ?></span></h3>
37
+ <div class="inside">
38
+ <?php call_user_func("pmpro_report_" . $report . "_widget"); ?>
39
+ <div style="margin-top:10px;border-top: 1px solid #ddd; padding-top: 10px; text-align:center;">
40
+ <a class="button button-primary" href="<?php echo admin_url("admin.php?page=pmpro-reports&report=" . $report);?>"><?php _e('Details', 'pmpro');?></a>
41
+ </div>
42
+ </div>
43
+ </div>
44
+ <?php
45
+ }
46
+
47
+ //end wrapper
48
+ ?>
49
+ </div>
50
+ </div>
51
+ </div>
52
+ <?php
53
+ }
54
+ else
55
+ {
56
+ //view a single report
57
+ $report = sanitize_text_field($_REQUEST['report']);
58
+ call_user_func("pmpro_report_" . $report . "_page");
59
+ }
60
+
61
+ require_once(dirname(__FILE__) . "/admin_footer.php");
 
 
 
 
62
  ?>
adminpages/reports/login.php CHANGED
@@ -23,38 +23,22 @@ function pmpro_report_login_widget()
23
  $views = get_option("pmpro_views", array("today"=>0, "thisday"=>date("Y-m-d", $now), "alltime"=>0, "month"=>0, "thismonth"=>date("n", $now)));
24
  $logins = get_option("pmpro_logins", array("today"=>0, "thisday"=>date("Y-m-d", $now), "alltime"=>0, "month"=>0, "thismonth"=>date("n", $now)));
25
  ?>
26
- <span id="pmpro_report_login">
27
- <table class="wp-list-table widefat fixed striped">
28
- <thead>
29
- <tr>
30
- <th scope="col">&nbsp;</th>
31
- <th scope="col"><?php _e('Visits','pmpro'); ?></th>
32
- <th scope="col"><?php _e('Views','pmpro'); ?></th>
33
- <th scope="col"><?php _e('Logins','pmpro'); ?></th>
34
- </tr>
35
- </thead>
36
- <tbody>
37
- <tr>
38
- <th scope="row"><?php _e('Today','pmpro'); ?></th>
39
- <td><?php echo number_format_i18n($visits['today']); ?></td>
40
- <td><?php echo number_format_i18n($views['today']); ?></td>
41
- <td><?php echo number_format_i18n($logins['today']);?></td>
42
- </tr>
43
- <tr>
44
- <th scope="row"><?php _e('This Month','pmpro'); ?></th>
45
- <td><?php echo number_format_i18n($visits['month']); ?></td>
46
- <td><?php echo number_format_i18n($views['month']); ?></td>
47
- <td><?php echo number_format_i18n($logins['month']); ?></td>
48
- </tr>
49
- <tr>
50
- <th scope="row"><?php _e('All Time','pmpro'); ?></th>
51
- <td><?php echo number_format_i18n($visits['alltime']); ?></td>
52
- <td><?php echo number_format_i18n($views['alltime']);?></td>
53
- <td><?php echo number_format_i18n($logins['alltime']); ?></td>
54
- </tr>
55
- </tbody>
56
- </table>
57
- </span>
58
  <?php
59
  }
60
 
23
  $views = get_option("pmpro_views", array("today"=>0, "thisday"=>date("Y-m-d", $now), "alltime"=>0, "month"=>0, "thismonth"=>date("n", $now)));
24
  $logins = get_option("pmpro_logins", array("today"=>0, "thisday"=>date("Y-m-d", $now), "alltime"=>0, "month"=>0, "thismonth"=>date("n", $now)));
25
  ?>
26
+ <div style="width: 33%; float: left;">
27
+ <p><?php _e('Visits Today', 'pmpro')?>: <?php echo $visits['today'];?></p>
28
+ <p><?php _e('Visits This Month', 'pmpro')?>: <?php echo $visits['month'];?></p>
29
+ <p><?php _e('Visits All Time', 'pmpro')?>: <?php echo $visits['alltime'];?></p>
30
+ </div>
31
+ <div style="width: 33%; float: left;">
32
+ <p><?php _e('Views Today', 'pmpro')?>: <?php echo $views['today'];?></p>
33
+ <p><?php _e('Views This Month', 'pmpro')?>: <?php echo $views['month'];?></p>
34
+ <p><?php _e('Views All Time', 'pmpro')?>: <?php echo $views['alltime'];?></p>
35
+ </div>
36
+ <div style="width: 33%; float: left;">
37
+ <p><?php _e('Logins Today', 'pmpro')?>: <?php echo $logins['today'];?></p>
38
+ <p><?php _e('Logins This Month', 'pmpro')?>: <?php echo $logins['month'];?></p>
39
+ <p><?php _e('Logins All Time', 'pmpro')?>: <?php echo $logins['alltime'];?></p>
40
+ </div>
41
+ <div class="clear"></div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
42
  <?php
43
  }
44
 
adminpages/reports/memberships.php CHANGED
@@ -1,624 +1,660 @@
1
- <?php
2
- /*
3
- PMPro Report
4
- Title: Membership Stats
5
- Slug: memberships
6
-
7
- For each report, add a line like:
8
- global $pmpro_reports;
9
- $pmpro_reports['slug'] = 'Title';
10
-
11
- For each report, also write two functions:
12
- * pmpro_report_{slug}_widget() to show up on the report homepage.
13
- * pmpro_report_{slug}_page() to show up when users click on the report page widget.
14
- */
15
-
16
- global $pmpro_reports;
17
-
18
- $pmpro_reports['memberships'] = __('Membership Stats', 'pmpro');
19
-
20
- //queue Google Visualization JS on report page
21
- function pmpro_report_memberships_init() {
22
- if(is_admin() && isset($_REQUEST['report']) && $_REQUEST['report'] == "memberships" && isset($_REQUEST['page']) && $_REQUEST['page'] == "pmpro-reports")
23
- wp_enqueue_script("jsapi", "https://www.google.com/jsapi");
24
- }
25
- add_action( 'init', 'pmpro_report_memberships_init' );
26
-
27
-
28
- //widget
29
- function pmpro_report_memberships_widget() {
30
- global $wpdb;
31
- ?>
32
- <span id="pmpro_report_memberships">
33
- <table class="wp-list-table widefat fixed striped">
34
- <thead>
35
- <tr>
36
- <th scope="col">&nbsp;</th>
37
- <th scope="col"><?php _e('Signups','pmpro'); ?></th>
38
- <th scope="col"><?php _e('Cancellations','pmpro'); ?></th>
39
- </tr>
40
- </thead>
41
- <tbody>
42
- <tr>
43
- <th scope="row"><?php _e('Today','pmpro'); ?></th>
44
- <td><?php echo number_format_i18n(pmpro_getSignups('today')); ?></td>
45
- <td><?php echo number_format_i18n(pmpro_getCancellations('today')); ?></td>
46
- </tr>
47
- <tr>
48
- <th scope="row"><?php _e('This Month','pmpro'); ?></th>
49
- <td><?php echo number_format_i18n(pmpro_getSignups('this month')); ?></td>
50
- <td><?php echo number_format_i18n(pmpro_getCancellations('this month')); ?></td>
51
- </tr>
52
- <tr>
53
- <th scope="row"><?php _e('This Year','pmpro'); ?></th>
54
- <td><?php echo number_format_i18n(pmpro_getSignups('this year')); ?></td>
55
- <td><?php echo number_format_i18n(pmpro_getCancellations('this year')); ?></td>
56
- </tr>
57
- <tr>
58
- <th scope="row"><?php _e('All Time','pmpro'); ?></th>
59
- <td><?php echo number_format_i18n(pmpro_getSignups('all time')); ?></td>
60
- <td><?php echo number_format_i18n(pmpro_getCancellations('all time')); ?></td>
61
- </tr>
62
- </tbody>
63
- </table>
64
- </span>
65
- <?php
66
- }
67
-
68
- function pmpro_report_memberships_page()
69
- {
70
- global $wpdb, $pmpro_currency_symbol;
71
-
72
- //get values from form
73
- if(isset($_REQUEST['type']))
74
- $type = sanitize_text_field($_REQUEST['type']);
75
- else
76
- $type = "signup_v_cancel";
77
-
78
- if(isset($_REQUEST['period']))
79
- $period = sanitize_text_field($_REQUEST['period']);
80
- else
81
- $period = "monthly";
82
-
83
- if(isset($_REQUEST['month']))
84
- $month = intval($_REQUEST['month']);
85
- else
86
- $month = date("n");
87
-
88
- $thisyear = date("Y");
89
- if(isset($_REQUEST['year']))
90
- $year = intval($_REQUEST['year']);
91
- else
92
- $year = date("Y");
93
-
94
- if(isset($_REQUEST['level']))
95
- $l = intval($_REQUEST['level']);
96
- else
97
- $l = "";
98
-
99
- //calculate start date and how to group dates returned from DB
100
- if($period == "daily")
101
- {
102
- $startdate = $year . '-' . substr("0" . $month, strlen($month) - 1, 2) . '-01';
103
- $enddate = $year . '-' . substr("0" . $month, strlen($month) - 1, 2) . '-32';
104
- $date_function = 'DAY';
105
- }
106
- elseif($period == "monthly")
107
- {
108
- $startdate = $year . '-01-01';
109
- $enddate = strval(intval($year)+1) . '-01-01';
110
- $date_function = 'MONTH';
111
- }
112
- elseif($period == "annual")
113
- {
114
- $startdate = '1960-01-01'; //all time
115
- $date_function = 'YEAR';
116
- }
117
-
118
- //testing or live data
119
- $gateway_environment = pmpro_getOption("gateway_environment");
120
-
121
- //get data
122
- if ( $type === "signup_v_cancel" ) {
123
- $sqlQuery = "SELECT $date_function(startdate) as date, COUNT(DISTINCT user_id) as signups
124
- FROM $wpdb->pmpro_memberships_users WHERE startdate >= '" . $startdate . "' ";
125
-
126
- if(!empty($enddate))
127
- $sqlQuery .= "AND startdate < '" . $enddate . "' ";
128
- }
129
- if ( $type === "mrr_ltv" ) {
130
- // Get total revenue, number of months in system, and date
131
- if ( $period == 'annual' )
132
- $sqlQuery = "SELECT SUM(total) as total, COUNT(DISTINCT MONTH(timestamp)) as months, $date_function(timestamp) as date
133
- FROM $wpdb->pmpro_membership_orders WHERE status NOT IN('refunded', 'review', 'token')
134
- AND timestamp >= '" . $startdate . "' AND gateway_environment = '" . esc_sql($gateway_environment) . "' ";
135
-
136
- if ( $period == 'monthly' )
137
- $sqlQuery = "SELECT SUM(total) as total, $date_function(timestamp) as date
138
- FROM $wpdb->pmpro_membership_orders WHERE status NOT IN('refunded', 'review', 'token')
139
- AND timestamp >= '" . $startdate . "' AND gateway_environment = '" . esc_sql($gateway_environment) . "' ";
140
-
141
- if(!empty($enddate))
142
- $sqlQuery .= "AND timestamp < '" . $enddate . "' ";
143
- }
144
-
145
- if(!empty($l))
146
- $sqlQuery .= "AND membership_id IN(" . $l . ") ";
147
-
148
- $sqlQuery .= " GROUP BY date ORDER BY date ";
149
-
150
- $dates = $wpdb->get_results($sqlQuery);
151
-
152
- //fill in blanks in dates
153
- $cols = array();
154
- if($period == "daily")
155
- {
156
- $lastday = date("t", strtotime($startdate, current_time("timestamp")));
157
-
158
- for($i = 1; $i <= $lastday; $i++)
159
- {
160
- // Signups vs. Cancellations
161
- if ( $type === "signup_v_cancel" ) {
162
- $cols[$i] = new stdClass();
163
- $cols[$i]->signups = 0;
164
- foreach($dates as $date)
165
- {
166
- if( $date->date == $i ) {
167
- $cols[$i]->signups = $date->signups;
168
- }
169
- }
170
- }
171
- }
172
- }
173
- elseif($period == "monthly")
174
- {
175
- for($i = 1; $i < 13; $i++)
176
- {
177
- // Signups vs. Cancellations
178
- if ( $type === "signup_v_cancel" ) {
179
- $cols[$i] = new stdClass();
180
- $cols[$i]->date = $i;
181
- $cols[$i]->signups = 0;
182
- foreach($dates as $date)
183
- {
184
- if( $date->date == $i ) {
185
- $cols[$i]->date = $date->date;
186
- $cols[$i]->signups = $date->signups;
187
- }
188
- }
189
- }
190
-
191
- // MRR & LTV
192
- if ( $type === "mrr_ltv" ) {
193
- $cols[$i] = new stdClass();
194
- $cols[$i]->date = $i;
195
- $cols[$i]->months = 1;
196
- foreach($dates as $date)
197
- {
198
- if( $date->date == $i ) {
199
- $cols[$i]->total = $date->total;
200
- }
201
- }
202
- }
203
- }
204
- }
205
- elseif($period == "annual") //annual
206
- {
207
- }
208
-
209
- $dates = ( ! empty( $cols ) ) ? $cols : $dates;
210
-
211
- // Signups vs. cancellations
212
- if ( $type === "signup_v_cancel" )
213
- {
214
- $sqlQuery = "SELECT $date_function(mu1.modified) as date, COUNT(DISTINCT mu1.user_id) as cancellations
215
- FROM $wpdb->pmpro_memberships_users mu1
216
- LEFT JOIN $wpdb->pmpro_memberships_users mu2 ON mu1.user_id = mu2.user_id AND
217
- mu2.modified > mu1.enddate AND
218
- DATE_ADD(mu1.modified, INTERVAL 1 DAY) > mu2.startdate
219
- WHERE mu1.status = 'inactive'
220
- AND mu2.id IS NULL
221
- AND mu1.startdate >= '" . $startdate . "'
222
- AND mu1.startdate < '" . $enddate . "' ";
223
-
224
- //restrict by level
225
- if(!empty($l))
226
- $sqlQuery .= "AND membership_id IN(" . $l . ") ";
227
-
228
- $sqlQuery .= " GROUP BY date ORDER BY date ";
229
-
230
- $cdates = $wpdb->get_results($sqlQuery, OBJECT_K);
231
-
232
- foreach( $dates as &$date )
233
- {
234
- if(!empty($cdates) && !empty($cdates[$date->date]))
235
- $date->cancellations = $cdates[$date->date]->cancellations;
236
- else
237
- $date->cancellations = 0;
238
- }
239
- }
240
-
241
- // MRR & LTV
242
- if ( $type === "mrr_ltv" && count( $dates ) === 1 ) {
243
- $dummy_date = new stdClass();
244
- $dummy_date->total = 0;
245
- $dummy_date->months = 0;
246
- $dummy_date->date = $dates[0]->date - 1;
247
- array_unshift( $dates, $dummy_date ); // Add to beginning
248
- }
249
- ?>
250
- <form id="posts-filter" method="get" action="">
251
- <h2>
252
- <?php _e('Membership Stats', 'pmpro');?>
253
- </h2>
254
- <ul class="subsubsub">
255
- <li>
256
- <?php _ex('Show', 'Dropdown label, e.g. Show Daily Revenue for January', 'pmpro')?>
257
- <select id="period" name="period">
258
- <option value="daily" <?php selected($period, "daily");?>><?php _e('Daily', 'pmpro');?></option>
259
- <option value="monthly" <?php selected($period, "monthly");?>><?php _e('Monthly', 'pmpro');?></option>
260
- <option value="annual" <?php selected($period, "annual");?>><?php _e('Annual', 'pmpro');?></option>
261
- </select>
262
- <select id="type" name="type">
263
- <option value="signup_v_cancel" <?php selected($type, "signup_v_cancel");?>><?php _e('Signups vs. Cancellations', 'pmpro');?></option>
264
- <?php /*
265
- <option value="mrr_ltv" <?php selected($type, "mrr_ltv");?>><?php _e('MRR & LTV', 'pmpro');?></option>
266
- */ ?>
267
- </select>
268
- <span id="for"><?php _ex('for', 'Dropdown label, e.g. Show Daily Revenue for January', 'pmpro')?></span>
269
- <select id="month" name="month">
270
- <?php for($i = 1; $i < 13; $i++) { ?>
271
- <option value="<?php echo $i;?>" <?php selected($month, $i);?>><?php echo date("F", mktime(0, 0, 0, $i, 2));?></option>
272
- <?php } ?>
273
- </select>
274
- <select id="year" name="year">
275
- <?php for($i = $thisyear; $i > 2007; $i--) { ?>
276
- <option value="<?php echo $i;?>" <?php selected($year, $i);?>><?php echo $i;?></option>
277
- <?php } ?>
278
- </select>
279
- <span id="for"><?php _ex('for', 'Dropdown label, e.g. Show Daily Revenue for January', 'pmpro')?></span>
280
- <select name="level">
281
- <option value="" <?php if(!$l) { ?>selected="selected"<?php } ?>><?php _e('All Levels', 'pmpro');?></option>
282
- <?php
283
- $levels = $wpdb->get_results("SELECT id, name FROM $wpdb->pmpro_membership_levels ORDER BY name");
284
- foreach($levels as $level)
285
- {
286
- ?>
287
- <option value="<?php echo $level->id?>" <?php if($l == $level->id) { ?>selected="selected"<?php } ?>><?php echo $level->name?></option>
288
- <?php
289
- }
290
- ?>
291
- </select>
292
-
293
- <input type="hidden" name="page" value="pmpro-reports" />
294
- <input type="hidden" name="report" value="memberships" />
295
- <input type="submit" value="<?php _ex('Generate Report', 'Submit button value.', 'pmpro');?>" />
296
- </li>
297
- </ul>
298
-
299
- <div id="chart_div" style="clear: both; width: 100%; height: 500px;"></div>
300
-
301
- <script>
302
- //update month/year when period dropdown is changed
303
- jQuery(document).ready(function() {
304
- jQuery('#period').change(function() {
305
- pmpro_ShowMonthOrYear();
306
- });
307
- });
308
-
309
- function pmpro_ShowMonthOrYear()
310
- {
311
- var period = jQuery('#period').val();
312
- if(period == 'daily')
313
- {
314
- jQuery('#for').show();
315
- jQuery('#month').show();
316
- jQuery('#year').show();
317
- }
318
- else if(period == 'monthly')
319
- {
320
- jQuery('#for').show();
321
- jQuery('#month').hide();
322
- jQuery('#year').show();
323
- }
324
- else
325
- {
326
- jQuery('#for').hide();
327
- jQuery('#month').hide();
328
- jQuery('#year').hide();
329
- }
330
- }
331
-
332
- pmpro_ShowMonthOrYear();
333
-
334
- //draw the chart
335
- google.load("visualization", "1", {packages:["corechart"]});
336
- google.setOnLoadCallback(drawChart);
337
- function drawChart() {
338
-
339
- var data = google.visualization.arrayToDataTable([
340
- <?php if ( $type === "signup_v_cancel" ) : // Signups vs. cancellations ?>
341
- ['<?php echo $date_function;?>', 'Signups', 'Cancellations'],
342
- <?php foreach($dates as $key => $value) { ?>
343
- ['<?php if($period == "monthly") echo date("M", mktime(0,0,0,$value->date,2)); else if($period == "daily") echo $key; else echo $value->date;?>', <?php echo $value->signups; ?>, <?php echo $value->cancellations; ?>],
344
- <?php } ?>
345
- <?php endif; ?>
346
-
347
- <?php if ( $type === "mrr_ltv" ) : // Signups vs. cancellations ?>
348
- ['<?php echo $date_function;?>', 'MRR', 'LTV'],
349
- <?php foreach($dates as $key => $value) { ?>
350
- ['<?php if($period == "monthly") echo date("M", mktime(0,0,0,$value->date,2)); else if($period == "daily") echo $key; else echo $value->date;?>', <?php echo (($mrr = $value->total / $value->months) && $mrr != 0) ? $mrr : 0; ?>, <?php echo pmpro_getLTV($period, NULL, $mrr ); ?>],
351
- <?php } ?>
352
- <?php endif; ?>
353
- ]);
354
-
355
- var options = {
356
- colors: ['#0099c6', '#dc3912'],
357
- hAxis: {title: '<?php echo $date_function;?>', titleTextStyle: {color: 'black'}, maxAlternation: 1},
358
- vAxis: {color: 'green', titleTextStyle: {color: '#51a351'}},
359
- };
360
-
361
- <?php if ( $type === "signup_v_cancel" ) : // Signups vs. cancellations ?>
362
- var chart = new google.visualization.ColumnChart(document.getElementById('chart_div'));
363
- <?php elseif ( $type === "mrr_ltv" ) : // MRR & LTV ?>
364
-
365
- <?php
366
- //prefix or suffix?
367
- if(pmpro_getCurrencyPosition() == "right")
368
- $position = "suffix";
369
- else
370
- $position = "prefix";
371
- ?>
372
-
373
- var formatter = new google.visualization.NumberFormat({<?php echo $position;?>: '<?php echo html_entity_decode($pmpro_currency_symbol);?>'});
374
- formatter.format(data, 2);
375
- var formatter = new google.visualization.NumberFormat({<?php echo $position;?>: '<?php echo html_entity_decode($pmpro_currency_symbol);?>'});
376
- formatter.format(data, 1);
377
-
378
- var chart = new google.visualization.LineChart(document.getElementById('chart_div'));
379
- <?php endif; ?>
380
- chart.draw(data, options);
381
- }
382
- </script>
383
-
384
- </form>
385
- <?php
386
- }
387
-
388
-
389
-
390
- /*
391
- Other code required for your reports. This file is loaded every time WP loads with PMPro enabled.
392
- */
393
-
394
- //get signups
395
- function pmpro_getSignups($period = false, $levels = 'all')
396
- {
397
- //check for a transient
398
- $cache = get_transient( 'pmpro_report_memberships_signups' );
399
- if( ! empty( $cache ) && ! empty( $cache[$period] ) && ! empty( $cache[$period][$levels] ) )
400
- return $cache[$period][$levels];
401
-
402
- //a sale is an order with status = success
403
- if( $period == 'today' )
404
- $startdate = date(' Y-m-d' );
405
- elseif( $period == 'this month')
406
- $startdate = date( 'Y-m' ) . '-01';
407
- elseif( $period == 'this year')
408
- $startdate = date( 'Y' ) . '-01-01';
409
- else
410
- $startdate = '';
411
-
412
-
413
- //build query
414
- global $wpdb;
415
-
416
- $sqlQuery = "SELECT COUNT(DISTINCT user_id) FROM $wpdb->pmpro_memberships_users WHERE startdate >= '" . $startdate . "' ";
417
-
418
- //restrict by level
419
- if(!empty($levels) && $levels != 'all')
420
- $sqlQuery .= "AND membership_id IN(" . $levels . ") ";
421
-
422
- $signups = $wpdb->get_var($sqlQuery);
423
-
424
- //save in cache
425
- if(!empty($cache) && !empty($cache[$period]))
426
- $cache[$period][$levels] = $signups;
427
- elseif(!empty($cache))
428
- $cache[$period] = array($levels => $signups);
429
- else
430
- $cache = array($period => array($levels => $signups));
431
-
432
- set_transient("pmpro_report_memberships_signups", $cache, 3600*24);
433
-
434
- return $signups;
435
- }
436
-
437
- //get cancellations
438
- function pmpro_getCancellations($period = false, $levels = 'all')
439
- {
440
- //check for a transient
441
- $cache = get_transient( 'pmpro_report_memberships_cancellations' );
442
- if( ! empty( $cache ) && ! empty( $cache[$period] ) && ! empty( $cache[$period][$levels] ) )
443
- return $cache[$period][$levels];
444
-
445
- //figure out start date
446
- if( $period == 'today' )
447
- $startdate = date(' Y-m-d' );
448
- elseif( $period == 'this month')
449
- $startdate = date( 'Y-m' ) . '-01';
450
- elseif( $period == 'this year')
451
- $startdate = date( 'Y' ) . '-01-01';
452
- else
453
- $startdate = '';
454
-
455
- $startdate_plus_one = strtotime( $startdate . + ' + 1 day', current_time("timestamp") );
456
-
457
- /*
458
- build query.
459
- cancellations are marked in the memberships users table with status = 'inactive'
460
- we try to ignore cancellations when the user gets a new level with 24 hours (probably an upgrade or downgrade)
461
- */
462
- global $wpdb;
463
-
464
- //$sqlQuery = "SELECT mu1.user_id, mu2.user_id FROM $wpdb->pmpro_memberships_users mu1 LEFT JOIN $wpdb->pmpro_memberships_users mu2 ON mu1.user_id = mu2.user_id AND mu2.status = 'inactive' AND mu2.startdate > mu1.startdate";
465
- $sqlQuery = "SELECT COUNT(mu1.id)
466
- FROM $wpdb->pmpro_memberships_users mu1
467
- LEFT JOIN $wpdb->pmpro_memberships_users mu2 ON mu1.user_id = mu2.user_id AND
468
- mu2.modified > mu1.enddate AND
469
- DATE_ADD(mu1.modified, INTERVAL 1 DAY) > mu2.startdate
470
- WHERE mu1.status = 'inactive'
471
- AND mu2.id IS NULL
472
- AND mu1.startdate >= '" . $startdate . "' ";
473
-
474
- //restrict by level
475
- if(!empty($levels) && $levels != 'all')
476
- $sqlQuery .= "AND membership_id IN(" . $levels . ") ";
477
-
478
- $cancellations = $wpdb->get_var($sqlQuery);
479
-
480
- //save in cache
481
- if(!empty($cache) && !empty($cache[$period]) && is_array($cache[$period]))
482
- $cache[$period][$levels] = $cancellations;
483
- elseif(!empty($cache))
484
- $cache[$period] = array($levels => $cancellations);
485
- else
486
- $cache = array($period => array($levels => $cancellations));
487
-
488
- set_transient("pmpro_report_memberships_cancellations", $cache, 3600*24);
489
-
490
- return $cancellations;
491
- }
492
-
493
- //get MRR
494
- function pmpro_getMRR($period, $levels = 'all')
495
- {
496
- //check for a transient
497
- //$cache = get_transient("pmpro_report_mrr");
498
- if(!empty($cache) && !empty($cache[$period]) && !empty($cache[$period][$levels]))
499
- return $cache[$period][$levels];
500
-
501
- //a sale is an order with status NOT IN refunded, review, token, error
502
- if($period == "this month")
503
- $startdate = date("Y-m") . "-01";
504
- elseif($period == "this year")
505
- $startdate = date("Y") . "-01-01";
506
- else
507
- $startdate = "";
508
-
509
- $gateway_environment = pmpro_getOption("gateway_environment");
510
-
511
- //build query
512
- global $wpdb;
513
- // Get total revenue
514
- $sqlQuery = "SELECT SUM(total) FROM $wpdb->pmpro_membership_orders WHERE status NOT IN('refunded', 'review', 'token', 'error') AND timestamp >= '" . $startdate . "' AND gateway_environment = '" . esc_sql($gateway_environment) . "' ";
515
-
516
- //restrict by level
517
- if(!empty($levels) && $levels != 'all') {
518
- $sqlQuery .= "AND membership_id IN(" . $levels . ") ";
519
- }
520
-
521
- $revenue = $wpdb->get_var($sqlQuery);
522
-
523
- //when was the first order
524
- $first_order_timestamp = $wpdb->get_var("SELECT UNIX_TIMESTAMP(`timestamp`) FROM $wpdb->pmpro_membership_orders WHERE `timestamp` IS NOT NULL AND `timestamp` > '0000-00-00 00:00:00' ORDER BY `timestamp` LIMIT 1");
525
-
526
- //if we don't have a timestamp, we can't do this
527
- if(empty($first_order_timestamp))
528
- return false;
529
-
530
- //how many months ago was the first order
531
- $months = $wpdb->get_var("SELECT PERIOD_DIFF('" . date("Ym") . "', '" . date("Ym", $first_order_timestamp) . "')");
532
-
533
- /* this works in PHP 5.3+ without using MySQL to get the diff
534
- $date1 = new DateTime(date("Y-m-d", $first_order_timestamp));
535
- $date2 = new DateTime(date("Y-m-d"));
536
- $interval = $date1->diff($date2);
537
- $years = intval($interval->format('%y'));
538
- $months = $years*12 + intval($interval->format('%m'));
539
- */
540
-
541
- if($months > 0)
542
- $mrr = $revenue / $months;
543
- else
544
- $mrr = 0;
545
-
546
- //save in cache
547
- if(!empty($cache) && !empty($cache[$period]))
548
- $cache[$period][$levels] = $mrr;
549
- elseif(!empty($cache))
550
- $cache[$period] = array($levels => $mrr);
551
- else
552
- $cache = array($period => array($levels => $mrr));
553
-
554
- set_transient("pmpro_report_mrr", $cache, 3600*24);
555
-
556
- return $mrr;
557
- }
558
-
559
- //get Cancellation Rate
560
- function pmpro_getCancellationRate($period, $levels = 'all')
561
- {
562
- //check for a transient
563
- $cache = get_transient("pmpro_report_cancellation_rate");
564
- if(!empty($cache) && !empty($cache[$period]) && !empty($cache[$period][$levels]))
565
- return $cache[$period][$levels];
566
-
567
- $signups = pmpro_getSignups($period, $levels);
568
- $cancellations = pmpro_getCancellations($period, $levels);
569
-
570
- if(empty($signups))
571
- return false;
572
-
573
- $rate = number_format(($cancellations / $signups)*100, 2);
574
-
575
- //save in cache
576
- if(!empty($cache) && !empty($cache[$period]))
577
- $cache[$period][$levels] = $rate;
578
- elseif(!empty($cache))
579
- $cache[$period] = array($levels => $rate);
580
- else
581
- $cache = array($period => array($levels => $rate));
582
-
583
- set_transient("pmpro_report_cancellation_rate", $cache, 3600*24);
584
-
585
- return $rate;
586
- }
587
-
588
- //get LTV
589
- function pmpro_getLTV($period, $levels = 'all', $mrr = NULL, $signups = NULL, $cancellation_rate = NULL)
590
- {
591
- if(empty($mrr))
592
- $mrr = pmpro_getMRR($period, $levels);
593
- if(empty($signups))
594
- $signups = pmpro_getSignups($period, $levels);
595
- if(empty($cancellation_rate))
596
- $cancellation_rate = pmpro_getCancellationRate($period, $levels);
597
-
598
- //average monthly spend
599
- if(empty($signups))
600
- return false;
601
-
602
- if($signups > 0)
603
- $ams = $mrr / $signups;
604
- else
605
- $ams = 0;
606
-
607
- if($cancellation_rate > 0)
608
- $ltv = $ams * (1/$cancellation_rate);
609
- else
610
- $ltv = $ams;
611
-
612
- return $ltv;
613
- }
614
-
615
- //delete transients when an order goes through
616
- function pmpro_report_memberships_delete_transients()
617
- {
618
- delete_transient("pmpro_report_mrr");
619
- delete_transient("pmpro_report_cancellation_rate");
620
- delete_transient("pmpro_report_memberships_cancellations");
621
- delete_transient("pmpro_report_memberships_signups");
622
- }
623
- add_action("pmpro_after_checkout", "pmpro_report_memberships_delete_transients");
624
- add_action("pmpro_updated_order", "pmpro_report_memberships_delete_transients");
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ PMPro Report
4
+ Title: Membership Stats
5
+ Slug: memberships
6
+
7
+ For each report, add a line like:
8
+ global $pmpro_reports;
9
+ $pmpro_reports['slug'] = 'Title';
10
+
11
+ For each report, also write two functions:
12
+ * pmpro_report_{slug}_widget() to show up on the report homepage.
13
+ * pmpro_report_{slug}_page() to show up when users click on the report page widget.
14
+ */
15
+
16
+ global $pmpro_reports;
17
+
18
+ $pmpro_reports['memberships'] = __('Membership Stats', 'pmpro');
19
+
20
+ //queue Google Visualization JS on report page
21
+ function pmpro_report_memberships_init() {
22
+ if(is_admin() && isset($_REQUEST['report']) && $_REQUEST['report'] == "memberships" && isset($_REQUEST['page']) && $_REQUEST['page'] == "pmpro-reports")
23
+ wp_enqueue_script("jsapi", "https://www.google.com/jsapi");
24
+ }
25
+ add_action( 'init', 'pmpro_report_memberships_init' );
26
+
27
+
28
+ //widget
29
+ function pmpro_report_memberships_widget() {
30
+ global $wpdb, $pmpro_currency_symbol;
31
+ ?>
32
+ <style type="text/css">
33
+ #pmpro_report_memberships .section-label {
34
+ margin: 15px 0;
35
+ font-size: 18px;
36
+ text-align: left;
37
+ display: block;
38
+ }
39
+
40
+ #pmpro_report_memberships .section-label:first-child {
41
+ margin-top: 0;
42
+ }
43
+
44
+ #pmpro_report_memberships div {text-align: center;}
45
+ #pmpro_report_memberships em {display: block; font-style: normal; font-size: 2em; margin: 5px; line-height: 26px;}
46
+ </style>
47
+ <span id="pmpro_report_memberships">
48
+ <label class="section-label"><?php _e('Signups', 'pmpro');?>:</label>
49
+ <div style="width: 25%; float: left;">
50
+ <label><?php _e('All Time', 'pmpro');?></label>
51
+ <em><?php echo pmpro_getSignups( 'all time' ); ?></em>
52
+ </div>
53
+ <div style="width: 25%; float: left;">
54
+ <label><?php _e('This Year', 'pmpro');?></label>
55
+ <em><?php echo pmpro_getSignups( 'this year' ); ?></em>
56
+ </div>
57
+ <div style="width: 25%; float: left;">
58
+ <label><?php _e('This Month', 'pmpro');?></label>
59
+ <em><?php echo pmpro_getSignups( 'this month' ); ?></em>
60
+ </div>
61
+ <div style="width: 25%; float: left;">
62
+ <label><?php _e('Today', 'pmpro');?></label>
63
+ <em><?php echo pmpro_getSignups( 'today' ); ?></em>
64
+ </div>
65
+ <div class="clear"></div>
66
+
67
+ <label class="section-label"><?php _e('Cancellations', 'pmpro');?>:</label>
68
+ <div style="width: 25%; float: left;">
69
+ <label><?php _e('All Time', 'pmpro');?></label>
70
+ <em><?php echo pmpro_getCancellations( 'all time' ); ?></em>
71
+ </div>
72
+ <div style="width: 25%; float: left;">
73
+ <label><?php _e('This Year', 'pmpro');?></label>
74
+ <em><?php echo pmpro_getCancellations( 'this year' ); ?></em>
75
+ </div>
76
+ <div style="width: 25%; float: left;">
77
+ <label><?php _e('This Month', 'pmpro');?></label>
78
+ <em><?php echo pmpro_getCancellations( 'this month' ); ?></em>
79
+ </div>
80
+ <div style="width: 25%; float: left;">
81
+ <label><?php _e('Today', 'pmpro');?></label>
82
+ <em><?php echo pmpro_getCancellations( 'today' ); ?></em>
83
+ </div>
84
+ <div class="clear"></div>
85
+
86
+ <label class="section-label"><?php _e('Other Stats', 'pmpro');?>:</label>
87
+ <div style="width: 33%; float: left;">
88
+ <label><?php _e('Monthly Recurring Revenue (MRR)', 'pmpro');?></label>
89
+ <em><?php echo pmpro_formatPrice(pmpro_getMRR( 'all time' )); ?></em>
90
+ </div>
91
+ <div style="width: 33%; float: left;">
92
+ <label><?php _e('Cancellation Rate', 'pmpro');?></label>
93
+ <em><?php echo pmpro_getCancellationRate('all time' ); ?>%</em>
94
+ </div>
95
+ <div style="width: 33%; float: left;">
96
+ <label><?php _e('Lifetime Value (LTV)', 'pmpro');?></label>
97
+ <em><?php echo pmpro_formatPrice(pmpro_getLTV('all time')); ?></em>
98
+ </div>
99
+ <div class="clear"></div>
100
+ </span>
101
+ <?php
102
+ }
103
+
104
+ function pmpro_report_memberships_page()
105
+ {
106
+ global $wpdb, $pmpro_currency_symbol;
107
+
108
+ //get values from form
109
+ if(isset($_REQUEST['type']))
110
+ $type = sanitize_text_field($_REQUEST['type']);
111
+ else
112
+ $type = "signup_v_cancel";
113
+
114
+ if(isset($_REQUEST['period']))
115
+ $period = sanitize_text_field($_REQUEST['period']);
116
+ else
117
+ $period = "monthly";
118
+
119
+ if(isset($_REQUEST['month']))
120
+ $month = intval($_REQUEST['month']);
121
+ else
122
+ $month = date("n");
123
+
124
+ $thisyear = date("Y");
125
+ if(isset($_REQUEST['year']))
126
+ $year = intval($_REQUEST['year']);
127
+ else
128
+ $year = date("Y");
129
+
130
+ if(isset($_REQUEST['level']))
131
+ $l = intval($_REQUEST['level']);
132
+ else
133
+ $l = "";
134
+
135
+ //calculate start date and how to group dates returned from DB
136
+ if($period == "daily")
137
+ {
138
+ $startdate = $year . '-' . substr("0" . $month, strlen($month) - 1, 2) . '-01';
139
+ $enddate = $year . '-' . substr("0" . $month, strlen($month) - 1, 2) . '-32';
140
+ $date_function = 'DAY';
141
+ }
142
+ elseif($period == "monthly")
143
+ {
144
+ $startdate = $year . '-01-01';
145
+ $enddate = strval(intval($year)+1) . '-01-01';
146
+ $date_function = 'MONTH';
147
+ }
148
+ elseif($period == "annual")
149
+ {
150
+ $startdate = '1960-01-01'; //all time
151
+ $date_function = 'YEAR';
152
+ }
153
+
154
+ //testing or live data
155
+ $gateway_environment = pmpro_getOption("gateway_environment");
156
+
157
+ //get data
158
+ if ( $type === "signup_v_cancel" ) {
159
+ $sqlQuery = "SELECT $date_function(startdate) as date, COUNT(DISTINCT user_id) as signups
160
+ FROM $wpdb->pmpro_memberships_users WHERE startdate >= '" . $startdate . "' ";
161
+
162
+ if(!empty($enddate))
163
+ $sqlQuery .= "AND startdate < '" . $enddate . "' ";
164
+ }
165
+ if ( $type === "mrr_ltv" ) {
166
+ // Get total revenue, number of months in system, and date
167
+ if ( $period == 'annual' )
168
+ $sqlQuery = "SELECT SUM(total) as total, COUNT(DISTINCT MONTH(timestamp)) as months, $date_function(timestamp) as date
169
+ FROM $wpdb->pmpro_membership_orders WHERE status NOT IN('refunded', 'review', 'token')
170
+ AND timestamp >= '" . $startdate . "' AND gateway_environment = '" . esc_sql($gateway_environment) . "' ";
171
+
172
+ if ( $period == 'monthly' )
173
+ $sqlQuery = "SELECT SUM(total) as total, $date_function(timestamp) as date
174
+ FROM $wpdb->pmpro_membership_orders WHERE status NOT IN('refunded', 'review', 'token')
175
+ AND timestamp >= '" . $startdate . "' AND gateway_environment = '" . esc_sql($gateway_environment) . "' ";
176
+
177
+ if(!empty($enddate))
178
+ $sqlQuery .= "AND timestamp < '" . $enddate . "' ";
179
+ }
180
+
181
+ if(!empty($l))
182
+ $sqlQuery .= "AND membership_id IN(" . $l . ") ";
183
+
184
+ $sqlQuery .= " GROUP BY date ORDER BY date ";
185
+
186
+ $dates = $wpdb->get_results($sqlQuery);
187
+
188
+ //fill in blanks in dates
189
+ $cols = array();
190
+ if($period == "daily")
191
+ {
192
+ $lastday = date("t", strtotime($startdate, current_time("timestamp")));
193
+
194
+ for($i = 1; $i <= $lastday; $i++)
195
+ {
196
+ // Signups vs. Cancellations
197
+ if ( $type === "signup_v_cancel" ) {
198
+ $cols[$i] = new stdClass();
199
+ $cols[$i]->signups = 0;
200
+ foreach($dates as $date)
201
+ {
202
+ if( $date->date == $i ) {
203
+ $cols[$i]->signups = $date->signups;
204
+ }
205
+ }
206
+ }
207
+ }
208
+ }
209
+ elseif($period == "monthly")
210
+ {
211
+ for($i = 1; $i < 13; $i++)
212
+ {
213
+ // Signups vs. Cancellations
214
+ if ( $type === "signup_v_cancel" ) {
215
+ $cols[$i] = new stdClass();
216
+ $cols[$i]->date = $i;
217
+ $cols[$i]->signups = 0;
218
+ foreach($dates as $date)
219
+ {
220
+ if( $date->date == $i ) {
221
+ $cols[$i]->date = $date->date;
222
+ $cols[$i]->signups = $date->signups;
223
+ }
224
+ }
225
+ }
226
+
227
+ // MRR & LTV
228
+ if ( $type === "mrr_ltv" ) {
229
+ $cols[$i] = new stdClass();
230
+ $cols[$i]->date = $i;
231
+ $cols[$i]->months = 1;
232
+ foreach($dates as $date)
233
+ {
234
+ if( $date->date == $i ) {
235
+ $cols[$i]->total = $date->total;
236
+ }
237
+ }
238
+ }
239
+ }
240
+ }
241
+ elseif($period == "annual") //annual
242
+ {
243
+ }
244
+
245
+ $dates = ( ! empty( $cols ) ) ? $cols : $dates;
246
+
247
+ // Signups vs. cancellations
248
+ if ( $type === "signup_v_cancel" )
249
+ {
250
+ $sqlQuery = "SELECT $date_function(mu1.modified) as date, COUNT(DISTINCT mu1.user_id) as cancellations
251
+ FROM $wpdb->pmpro_memberships_users mu1
252
+ LEFT JOIN $wpdb->pmpro_memberships_users mu2 ON mu1.user_id = mu2.user_id AND
253
+ mu2.modified > mu1.enddate AND
254
+ DATE_ADD(mu1.modified, INTERVAL 1 DAY) > mu2.startdate
255
+ WHERE mu1.status = 'inactive'
256
+ AND mu2.id IS NULL
257
+ AND mu1.startdate >= '" . $startdate . "'
258
+ AND mu1.startdate < '" . $enddate . "' ";
259
+
260
+ //restrict by level
261
+ if(!empty($l))
262
+ $sqlQuery .= "AND membership_id IN(" . $l . ") ";
263
+
264
+ $sqlQuery .= " GROUP BY date ORDER BY date ";
265
+
266
+ $cdates = $wpdb->get_results($sqlQuery, OBJECT_K);
267
+
268
+ foreach( $dates as &$date )
269
+ {
270
+ if(!empty($cdates[$date->date]))
271
+ $date->cancellations = $cdates[$date->date]->cancellations;
272
+ else
273
+ $date->cancellations = 0;
274
+ }
275
+ }
276
+
277
+ // MRR & LTV
278
+ if ( $type === "mrr_ltv" && count( $dates ) === 1 ) {
279
+ $dummy_date = new stdClass();
280
+ $dummy_date->total = 0;
281
+ $dummy_date->months = 0;
282
+ $dummy_date->date = $dates[0]->date - 1;
283
+ array_unshift( $dates, $dummy_date ); // Add to beginning
284
+ }
285
+ ?>
286
+ <form id="posts-filter" method="get" action="">
287
+ <h2>
288
+ <?php _e('Membership Stats', 'pmpro');?>
289
+ </h2>
290
+ <ul class="subsubsub">
291
+ <li>
292
+ <?php _ex('Show', 'Dropdown label, e.g. Show Daily Revenue for January', 'pmpro')?>
293
+ <select id="period" name="period">
294
+ <option value="daily" <?php selected($period, "daily");?>><?php _e('Daily', 'pmpro');?></option>
295
+ <option value="monthly" <?php selected($period, "monthly");?>><?php _e('Monthly', 'pmpro');?></option>
296
+ <option value="annual" <?php selected($period, "annual");?>><?php _e('Annual', 'pmpro');?></option>
297
+ </select>
298
+ <select id="type" name="type">
299
+ <option value="signup_v_cancel" <?php selected($type, "signup_v_cancel");?>><?php _e('Signups vs. Cancellations', 'pmpro');?></option>
300
+ <?php /*
301
+ <option value="mrr_ltv" <?php selected($type, "mrr_ltv");?>><?php _e('MRR & LTV', 'pmpro');?></option>
302
+ */ ?>
303
+ </select>
304
+ <span id="for"><?php _ex('for', 'Dropdown label, e.g. Show Daily Revenue for January', 'pmpro')?></span>
305
+ <select id="month" name="month">
306
+ <?php for($i = 1; $i < 13; $i++) { ?>
307
+ <option value="<?php echo $i;?>" <?php selected($month, $i);?>><?php echo date("F", mktime(0, 0, 0, $i, 2));?></option>
308
+ <?php } ?>
309
+ </select>
310
+ <select id="year" name="year">
311
+ <?php for($i = $thisyear; $i > 2007; $i--) { ?>
312
+ <option value="<?php echo $i;?>" <?php selected($year, $i);?>><?php echo $i;?></option>
313
+ <?php } ?>
314
+ </select>
315
+ <span id="for"><?php _ex('for', 'Dropdown label, e.g. Show Daily Revenue for January', 'pmpro')?></span>
316
+ <select name="level">
317
+ <option value="" <?php if(!$l) { ?>selected="selected"<?php } ?>><?php _e('All Levels', 'pmpro');?></option>
318
+ <?php
319
+ $levels = $wpdb->get_results("SELECT id, name FROM $wpdb->pmpro_membership_levels ORDER BY name");
320
+ foreach($levels as $level)
321
+ {
322
+ ?>
323
+ <option value="<?php echo $level->id?>" <?php if($l == $level->id) { ?>selected="selected"<?php } ?>><?php echo $level->name?></option>
324
+ <?php
325
+ }
326
+ ?>
327
+ </select>
328
+
329
+ <input type="hidden" name="page" value="pmpro-reports" />
330
+ <input type="hidden" name="report" value="memberships" />
331
+ <input type="submit" value="<?php _ex('Generate Report', 'Submit button value.', 'pmpro');?>" />
332
+ </li>
333
+ </ul>
334
+
335
+ <div id="chart_div" style="clear: both; width: 100%; height: 500px;"></div>
336
+
337
+ <script>
338
+ //update month/year when period dropdown is changed
339
+ jQuery(document).ready(function() {
340
+ jQuery('#period').change(function() {
341
+ pmpro_ShowMonthOrYear();
342
+ });
343
+ });
344
+
345
+ function pmpro_ShowMonthOrYear()
346
+ {
347
+ var period = jQuery('#period').val();
348
+ if(period == 'daily')
349
+ {
350
+ jQuery('#for').show();
351
+ jQuery('#month').show();
352
+ jQuery('#year').show();
353
+ }
354
+ else if(period == 'monthly')
355
+ {
356
+ jQuery('#for').show();
357
+ jQuery('#month').hide();
358
+ jQuery('#year').show();
359
+ }
360
+ else
361
+ {
362
+ jQuery('#for').hide();
363
+ jQuery('#month').hide();
364
+ jQuery('#year').hide();
365
+ }
366
+ }
367
+
368
+ pmpro_ShowMonthOrYear();
369
+
370
+ //draw the chart
371
+ google.load("visualization", "1", {packages:["corechart"]});
372
+ google.setOnLoadCallback(drawChart);
373
+ function drawChart() {
374
+
375
+ var data = google.visualization.arrayToDataTable([
376
+ <?php if ( $type === "signup_v_cancel" ) : // Signups vs. cancellations ?>
377
+ ['<?php echo $date_function;?>', 'Signups', 'Cancellations'],
378
+ <?php foreach($dates as $key => $value) { ?>
379
+ ['<?php if($period == "monthly") echo date("M", mktime(0,0,0,$value->date,2)); else if($period == "daily") echo $key; else echo $value->date;?>', <?php echo $value->signups; ?>, <?php echo $value->cancellations; ?>],
380
+ <?php } ?>
381
+ <?php endif; ?>
382
+
383
+ <?php if ( $type === "mrr_ltv" ) : // Signups vs. cancellations ?>
384
+ ['<?php echo $date_function;?>', 'MRR', 'LTV'],
385
+ <?php foreach($dates as $key => $value) { ?>
386
+ ['<?php if($period == "monthly") echo date("M", mktime(0,0,0,$value->date,2)); else if($period == "daily") echo $key; else echo $value->date;?>', <?php echo (($mrr = $value->total / $value->months) && $mrr != 0) ? $mrr : 0; ?>, <?php echo pmpro_getLTV($period, NULL, $mrr ); ?>],
387
+ <?php } ?>
388
+ <?php endif; ?>
389
+ ]);
390
+
391
+ var options = {
392
+ colors: ['#0099c6', '#dc3912'],
393
+ hAxis: {title: '<?php echo $date_function;?>', titleTextStyle: {color: 'black'}, maxAlternation: 1},
394
+ vAxis: {color: 'green', titleTextStyle: {color: '#51a351'}},
395
+ };
396
+
397
+ <?php if ( $type === "signup_v_cancel" ) : // Signups vs. cancellations ?>
398
+ var chart = new google.visualization.ColumnChart(document.getElementById('chart_div'));
399
+ <?php elseif ( $type === "mrr_ltv" ) : // MRR & LTV ?>
400
+
401
+ <?php
402
+ //prefix or suffix?
403
+ if(pmpro_getCurrencyPosition() == "right")
404
+ $position = "suffix";
405
+ else
406
+ $position = "prefix";
407
+ ?>
408
+
409
+ var formatter = new google.visualization.NumberFormat({<?php echo $position;?>: '<?php echo html_entity_decode($pmpro_currency_symbol);?>'});
410
+ formatter.format(data, 2);
411
+ var formatter = new google.visualization.NumberFormat({<?php echo $position;?>: '<?php echo html_entity_decode($pmpro_currency_symbol);?>'});
412
+ formatter.format(data, 1);
413
+
414
+ var chart = new google.visualization.LineChart(document.getElementById('chart_div'));
415
+ <?php endif; ?>
416
+ chart.draw(data, options);
417
+ }
418
+ </script>
419
+
420
+ </form>
421
+ <?php
422
+ }
423
+
424
+
425
+
426
+ /*
427
+ Other code required for your reports. This file is loaded every time WP loads with PMPro enabled.
428
+ */
429
+
430
+ //get signups
431
+ function pmpro_getSignups($period = false, $levels = 'all')
432
+ {
433
+ //check for a transient
434
+ $cache = get_transient( 'pmpro_report_memberships_signups' );
435
+ if( ! empty( $cache ) && ! empty( $cache[$period] ) && ! empty( $cache[$period][$levels] ) )
436
+ return $cache[$period][$levels];
437
+
438
+ //a sale is an order with status = success
439
+ if( $period == 'today' )
440
+ $startdate = date(' Y-m-d' );
441
+ elseif( $period == 'this month')
442
+ $startdate = date( 'Y-m' ) . '-01';
443
+ elseif( $period == 'this year')
444
+ $startdate = date( 'Y' ) . '-01-01';
445
+ else
446
+ $startdate = '';
447
+
448
+
449
+ //build query
450
+ global $wpdb;
451
+
452
+ $sqlQuery = "SELECT COUNT(DISTINCT user_id) FROM $wpdb->pmpro_memberships_users WHERE startdate >= '" . $startdate . "' ";
453
+
454
+ //restrict by level
455
+ if(!empty($levels) && $levels != 'all')
456
+ $sqlQuery .= "AND membership_id IN(" . $levels . ") ";
457
+
458
+ $signups = $wpdb->get_var($sqlQuery);
459
+
460
+ //save in cache
461
+ if(!empty($cache) && !empty($cache[$period]))
462
+ $cache[$period][$levels] = $signups;
463
+ elseif(!empty($cache))
464
+ $cache[$period] = array($levels => $signups);
465
+ else
466
+ $cache = array($period => array($levels => $signups));
467
+
468
+ set_transient("pmpro_report_memberships_signups", $cache, 3600*24);
469
+
470
+ return $signups;
471
+ }
472
+
473
+ //get cancellations
474
+ function pmpro_getCancellations($period = false, $levels = 'all')
475
+ {
476
+ //check for a transient
477
+ $cache = get_transient( 'pmpro_report_memberships_cancellations' );
478
+ if( ! empty( $cache ) && ! empty( $cache[$period] ) && ! empty( $cache[$period][$levels] ) )
479
+ return $cache[$period][$levels];
480
+
481
+ //figure out start date
482
+ if( $period == 'today' )
483
+ $startdate = date(' Y-m-d' );
484
+ elseif( $period == 'this month')
485
+ $startdate = date( 'Y-m' ) . '-01';
486
+ elseif( $period == 'this year')
487
+ $startdate = date( 'Y' ) . '-01-01';
488
+ else
489
+ $startdate = '';
490
+
491
+ $startdate_plus_one = strtotime( $startdate . + ' + 1 day', current_time("timestamp") );
492
+
493
+ /*
494
+ build query.
495
+ cancellations are marked in the memberships users table with status = 'inactive'
496
+ we try to ignore cancellations when the user gets a new level with 24 hours (probably an upgrade or downgrade)
497
+ */
498
+ global $wpdb;
499
+
500
+ //$sqlQuery = "SELECT mu1.user_id, mu2.user_id FROM $wpdb->pmpro_memberships_users mu1 LEFT JOIN $wpdb->pmpro_memberships_users mu2 ON mu1.user_id = mu2.user_id AND mu2.status = 'inactive' AND mu2.startdate > mu1.startdate";
501
+ $sqlQuery = "SELECT COUNT(mu1.id)
502
+ FROM $wpdb->pmpro_memberships_users mu1
503
+ LEFT JOIN $wpdb->pmpro_memberships_users mu2 ON mu1.user_id = mu2.user_id AND
504
+ mu2.modified > mu1.enddate AND
505
+ DATE_ADD(mu1.modified, INTERVAL 1 DAY) > mu2.startdate
506
+ WHERE mu1.status = 'inactive'
507
+ AND mu2.id IS NULL
508
+ AND mu1.startdate >= '" . $startdate . "' ";
509
+
510
+ //restrict by level
511
+ if(!empty($levels) && $levels != 'all')
512
+ $sqlQuery .= "AND membership_id IN(" . $levels . ") ";
513
+
514
+ $cancellations = $wpdb->get_var($sqlQuery);
515
+
516
+ //save in cache
517
+ if(!empty($cache) && !empty($cache[$period]) && is_array($cache[$period]))
518
+ $cache[$period][$levels] = $cancellations;
519
+ elseif(!empty($cache))
520
+ $cache[$period] = array($levels => $cancellations);
521
+ else
522
+ $cache = array($period => array($levels => $cancellations));
523
+
524
+ set_transient("pmpro_report_memberships_cancellations", $cache, 3600*24);
525
+
526
+ return $cancellations;
527
+ }
528
+
529
+ //get MRR
530
+ function pmpro_getMRR($period, $levels = 'all')
531
+ {
532
+ //check for a transient
533
+ //$cache = get_transient("pmpro_report_mrr");
534
+ if(!empty($cache) && !empty($cache[$period]) && !empty($cache[$period][$levels]))
535
+ return $cache[$period][$levels];
536
+
537
+ //a sale is an order with status NOT IN refunded, review, token, error
538
+ if($period == "this month")
539
+ $startdate = date("Y-m") . "-01";
540
+ elseif($period == "this year")
541
+ $startdate = date("Y") . "-01-01";
542
+ else
543
+ $startdate = "";
544
+
545
+ $gateway_environment = pmpro_getOption("gateway_environment");
546
+
547
+ //build query
548
+ global $wpdb;
549
+ // Get total revenue
550
+ $sqlQuery = "SELECT SUM(total) FROM $wpdb->pmpro_membership_orders WHERE status NOT IN('refunded', 'review', 'token', 'error') AND timestamp >= '" . $startdate . "' AND gateway_environment = '" . esc_sql($gateway_environment) . "' ";
551
+
552
+ //restrict by level
553
+ if(!empty($levels) && $levels != 'all') {
554
+ $sqlQuery .= "AND membership_id IN(" . $levels . ") ";
555
+ }
556
+
557
+ $revenue = $wpdb->get_var($sqlQuery);
558
+
559
+ //when was the first order
560
+ $first_order_timestamp = $wpdb->get_var("SELECT UNIX_TIMESTAMP(`timestamp`) FROM $wpdb->pmpro_membership_orders WHERE `timestamp` IS NOT NULL AND `timestamp` > '0000-00-00 00:00:00' ORDER BY `timestamp` LIMIT 1");
561
+
562
+ //if we don't have a timestamp, we can't do this
563
+ if(empty($first_order_timestamp))
564
+ return false;
565
+
566
+ //how many months ago was the first order
567
+ $months = $wpdb->get_var("SELECT PERIOD_DIFF('" . date("Ym") . "', '" . date("Ym", $first_order_timestamp) . "')");
568
+
569
+ /* this works in PHP 5.3+ without using MySQL to get the diff
570
+ $date1 = new DateTime(date("Y-m-d", $first_order_timestamp));
571
+ $date2 = new DateTime(date("Y-m-d"));
572
+ $interval = $date1->diff($date2);
573
+ $years = intval($interval->format('%y'));
574
+ $months = $years*12 + intval($interval->format('%m'));
575
+ */
576
+
577
+ if($months > 0)
578
+ $mrr = $revenue / $months;
579
+ else
580
+ $mrr = 0;
581
+
582
+ //save in cache
583
+ if(!empty($cache) && !empty($cache[$period]))
584
+ $cache[$period][$levels] = $mrr;
585
+ elseif(!empty($cache))
586
+ $cache[$period] = array($levels => $mrr);
587
+ else
588
+ $cache = array($period => array($levels => $mrr));
589
+
590
+ set_transient("pmpro_report_mrr", $cache, 3600*24);
591
+
592
+ return $mrr;
593
+ }
594
+
595
+ //get Cancellation Rate
596
+ function pmpro_getCancellationRate($period, $levels = 'all')
597
+ {
598
+ //check for a transient
599
+ $cache = get_transient("pmpro_report_cancellation_rate");
600
+ if(!empty($cache) && !empty($cache[$period]) && !empty($cache[$period][$levels]))
601
+ return $cache[$period][$levels];
602
+
603
+ $signups = pmpro_getSignups($period, $levels);
604
+ $cancellations = pmpro_getCancellations($period, $levels);
605
+
606
+ if(empty($signups))
607
+ return false;
608
+
609
+ $rate = number_format(($cancellations / $signups)*100, 2);
610
+
611
+ //save in cache
612
+ if(!empty($cache) && !empty($cache[$period]))
613
+ $cache[$period][$levels] = $rate;
614
+ elseif(!empty($cache))
615
+ $cache[$period] = array($levels => $rate);
616
+ else
617
+ $cache = array($period => array($levels => $rate));
618
+
619
+ set_transient("pmpro_report_cancellation_rate", $cache, 3600*24);
620
+
621
+ return $rate;
622
+ }
623
+
624
+ //get LTV
625
+ function pmpro_getLTV($period, $levels = 'all', $mrr = NULL, $signups = NULL, $cancellation_rate = NULL)
626
+ {
627
+ if(empty($mrr))
628
+ $mrr = pmpro_getMRR($period, $levels);
629
+ if(empty($signups))
630
+ $signups = pmpro_getSignups($period, $levels);
631
+ if(empty($cancellation_rate))
632
+ $cancellation_rate = pmpro_getCancellationRate($period, $levels);
633
+
634
+ //average monthly spend
635
+ if(empty($signups))
636
+ return false;
637
+
638
+ if($signups > 0)
639
+ $ams = $mrr / $signups;
640
+ else
641
+ $ams = 0;
642
+
643
+ if($cancellation_rate > 0)
644
+ $ltv = $ams * (1/$cancellation_rate);
645
+ else
646
+ $ltv = $ams;
647
+
648
+ return $ltv;
649
+ }
650
+
651
+ //delete transients when an order goes through
652
+ function pmpro_report_memberships_delete_transients()
653
+ {
654
+ delete_transient("pmpro_report_mrr");
655
+ delete_transient("pmpro_report_cancellation_rate");
656
+ delete_transient("pmpro_report_memberships_cancellations");
657
+ delete_transient("pmpro_report_memberships_signups");
658
+ }
659
+ add_action("pmpro_after_checkout", "pmpro_report_memberships_delete_transients");
660
+ add_action("pmpro_updated_order", "pmpro_report_memberships_delete_transients");
adminpages/reports/sales.php CHANGED
@@ -1,415 +1,406 @@
1
- <?php
2
- /*
3
- PMPro Report
4
- Title: Sales
5
- Slug: sales
6
-
7
- For each report, add a line like:
8
- global $pmpro_reports;
9
- $pmpro_reports['slug'] = 'Title';
10
-
11
- For each report, also write two functions:
12
- * pmpro_report_{slug}_widget() to show up on the report homepage.
13
- * pmpro_report_{slug}_page() to show up when users click on the report page widget.
14
- */
15
- global $pmpro_reports;
16
- $gateway_environment = pmpro_getOption("gateway_environment");
17
- if($gateway_environment == "sandbox")
18
- $pmpro_reports['sales'] = __('Sales and Revenue (Testing/Sandbox)', 'pmpro');
19
- else
20
- $pmpro_reports['sales'] = __('Sales and Revenue', 'pmpro');
21
-
22
- //queue Google Visualization JS on report page
23
- function pmpro_report_sales_init()
24
- {
25
- if(is_admin() && isset($_REQUEST['report']) && $_REQUEST['report'] == "sales" && isset($_REQUEST['page']) && $_REQUEST['page'] == "pmpro-reports")
26
- {
27
- wp_enqueue_script("jsapi", "https://www.google.com/jsapi");
28
- }
29
- }
30
- add_action("init", "pmpro_report_sales_init");
31
-
32
- //widget
33
- function pmpro_report_sales_widget()
34
- {
35
- global $wpdb;
36
- ?>
37
- <style>
38
- #pmpro_report_sales tbody td:last-child {text-align: right; }
39
- </style>
40
- <span id="pmpro_report_sales">
41
- <table class="wp-list-table widefat fixed striped">
42
- <thead>
43
- <tr>
44
- <th scope="col">&nbsp;</th>
45
- <th scope="col"><?php _e('Sales','pmpro'); ?></th>
46
- <th scope="col"><?php _e('Revenue','pmpro'); ?></th>
47
- </tr>
48
- </thead>
49
- <tbody>
50
- <tr>
51
- <th scope="row"><?php _e('Today','pmpro'); ?></th>
52
- <td><?php echo number_format_i18n(pmpro_getSales("today")); ?></td>
53
- <td><?php echo pmpro_formatPrice(pmpro_getRevenue("today"));?></td>
54
- </tr>
55
- <tr>
56
- <th scope="row"><?php _e('This Month','pmpro'); ?></th>
57
- <td><?php echo number_format_i18n(pmpro_getSales("this month")); ?></td>
58
- <td><?php echo pmpro_formatPrice(pmpro_getRevenue("this month"));?></td>
59
- </tr>
60
- <tr>
61
- <th scope="row"><?php _e('This Year','pmpro'); ?></th>
62
- <td><?php echo number_format_i18n(pmpro_getSales("this year")); ?></td>
63
- <td><?php echo pmpro_formatPrice(pmpro_getRevenue("this year"));?></td>
64
- </tr>
65
- <tr>
66
- <th scope="row"><?php _e('All Time','pmpro'); ?></th>
67
- <td><?php echo number_format_i18n(pmpro_getSales("all time")); ?></td>
68
- <td><?php echo pmpro_formatPrice(pmpro_getRevenue("all time"));?></td>
69
- </tr>
70
- </tbody>
71
- </table>
72
- </span>
73
- <?php
74
- }
75
-
76
- function pmpro_report_sales_page()
77
- {
78
- global $wpdb, $pmpro_currency_symbol, $pmpro_currency, $pmpro_currencies;
79
-
80
- //get values from form
81
- if(isset($_REQUEST['type']))
82
- $type = sanitize_text_field($_REQUEST['type']);
83
- else
84
- $type = "revenue";
85
-
86
- if($type == "sales")
87
- $type_function = "COUNT";
88
- else
89
- $type_function = "SUM";
90
-
91
- if(isset($_REQUEST['period']))
92
- $period = sanitize_text_field($_REQUEST['period']);
93
- else
94
- $period = "daily";
95
-
96
- if(isset($_REQUEST['month']))
97
- $month = intval($_REQUEST['month']);
98
- else
99
- $month = date("n", current_time('timestamp'));
100
-
101
- $thisyear = date("Y", current_time('timestamp'));
102
- if(isset($_REQUEST['year']))
103
- $year = intval($_REQUEST['year']);
104
- else
105
- $year = $thisyear;
106
-
107
- if(isset($_REQUEST['level']))
108
- $l = intval($_REQUEST['level']);
109
- else
110
- $l = "";
111
-
112
- //calculate start date and how to group dates returned from DB
113
- if($period == "daily")
114
- {
115
- $startdate = $year . '-' . substr("0" . $month, strlen($month) - 1, 2) . '-01';
116
- $enddate = $year . '-' . substr("0" . $month, strlen($month) - 1, 2) . '-32';
117
- $date_function = 'DAY';
118
- }
119
- elseif($period == "monthly")
120
- {
121
- $startdate = $year . '-01-01';
122
- $enddate = strval(intval($year)+1) . '-01-01';
123
- $date_function = 'MONTH';
124
- }
125
- else
126
- {
127
- $startdate = '1960-01-01'; //all time
128
- $date_function = 'YEAR';
129
- }
130
-
131
- //testing or live data
132
- $gateway_environment = pmpro_getOption("gateway_environment");
133
-
134
- //get data
135
- $sqlQuery = "SELECT $date_function(timestamp) as date, $type_function(total) as value FROM $wpdb->pmpro_membership_orders WHERE timestamp >= '" . $startdate . "' AND status NOT IN('refunded', 'review', 'token', 'error') AND gateway_environment = '" . esc_sql($gateway_environment) . "' ";
136
-
137
- if(!empty($enddate))
138
- $sqlQuery .= "AND timestamp < '" . $enddate . "' ";
139
-
140
- if(!empty($l))
141
- $sqlQuery .= "AND membership_id IN(" . $l . ") ";
142
-
143
- $sqlQuery .= " GROUP BY date ORDER BY date ";
144
-
145
- $dates = $wpdb->get_results($sqlQuery);
146
-
147
- //fill in blanks in dates
148
- $cols = array();
149
- if($period == "daily")
150
- {
151
- $lastday = date("t", strtotime($startdate, current_time("timestamp")));
152
-
153
- for($i = 1; $i <= $lastday; $i++)
154
- {
155
- $cols[$i] = 0;
156
- foreach($dates as $date)
157
- {
158
- if($date->date == $i)
159
- $cols[$i] = $date->value;
160
- }
161
- }
162
- }
163
- elseif($period == "monthly")
164
- {
165
- for($i = 1; $i < 13; $i++)
166
- {
167
- $cols[$i] = 0;
168
- foreach($dates as $date)
169
- {
170
- if($date->date == $i)
171
- $cols[$i] = $date->value;
172
- }
173
- }
174
- }
175
- else //annual
176
- {
177
- //get min and max years
178
- $min = 9999;
179
- $max = 0;
180
- foreach($dates as $date)
181
- {
182
- $min = min($min, $date->date);
183
- $max = max($max, $date->date);
184
- }
185
-
186
- for($i = $min; $i <= $max; $i++)
187
- {
188
- foreach($dates as $date)
189
- {
190
- if($date->date == $i)
191
- $cols[$i] = $date->value;
192
- }
193
- }
194
- }
195
- ?>
196
- <form id="posts-filter" method="get" action="">
197
- <h2>
198
- <?php _e('Sales and Revenue', 'pmpro');?>
199
- </h2>
200
-
201
- <div class="tablenav top">
202
- <?php _ex('Show', 'Dropdown label, e.g. Show Daily Revenue for January', 'pmpro')?>
203
- <select id="period" name="period">
204
- <option value="daily" <?php selected($period, "daily");?>><?php _e('Daily', 'pmpro');?></option>
205
- <option value="monthly" <?php selected($period, "monthly");?>><?php _e('Monthly', 'pmpro');?></option>
206
- <option value="annual" <?php selected($period, "annual");?>><?php _e('Annual', 'pmpro');?></option>
207
- </select>
208
- <select name="type">
209
- <option value="revenue" <?php selected($type, "revenue");?>><?php _e('Revenue', 'pmpro');?></option>
210
- <option value="sales" <?php selected($type, "sales");?>><?php _e('Sales', 'pmpro');?></option>
211
- </select>
212
- <span id="for"><?php _ex('for', 'Dropdown label, e.g. Show Daily Revenue for January', 'pmpro')?></span>
213
- <select id="month" name="month">
214
- <?php for($i = 1; $i < 13; $i++) { ?>
215
- <option value="<?php echo $i;?>" <?php selected($month, $i);?>><?php echo date("F", mktime(0, 0, 0, $i, 2));?></option>
216
- <?php } ?>
217
- </select>
218
- <select id="year" name="year">
219
- <?php for($i = $thisyear; $i > 2007; $i--) { ?>
220
- <option value="<?php echo $i;?>" <?php selected($year, $i);?>><?php echo $i;?></option>
221
- <?php } ?>
222
- </select>
223
- <span id="for"><?php _ex('for', 'Dropdown label, e.g. Show Daily Revenue for January', 'pmpro')?></span>
224
- <select name="level">
225
- <option value="" <?php if(!$l) { ?>selected="selected"<?php } ?>><?php _e('All Levels', 'pmpro');?></option>
226
- <?php
227
- $levels = $wpdb->get_results("SELECT id, name FROM $wpdb->pmpro_membership_levels ORDER BY name");
228
- foreach($levels as $level)
229
- {
230
- ?>
231
- <option value="<?php echo $level->id?>" <?php if($l == $level->id) { ?>selected="selected"<?php } ?>><?php echo $level->name?></option>
232
- <?php
233
- }
234
- ?>
235
- </select>
236
-
237
- <input type="hidden" name="page" value="pmpro-reports" />
238
- <input type="hidden" name="report" value="sales" />
239
- <input type="submit" class="button action" value="<?php _ex('Generate Report', 'Submit button value.', 'pmpro');?>" />
240
- </div>
241
-
242
- <div id="chart_div" style="clear: both; width: 100%; height: 500px;"></div>
243
-
244
- <script>
245
- //update month/year when period dropdown is changed
246
- jQuery(document).ready(function() {
247
- jQuery('#period').change(function() {
248
- pmpro_ShowMonthOrYear();
249
- });
250
- });
251
-
252
- function pmpro_ShowMonthOrYear()
253
- {
254
- var period = jQuery('#period').val();
255
- if(period == 'daily')
256
- {
257
- jQuery('#for').show();
258
- jQuery('#month').show();
259
- jQuery('#year').show();
260
- }
261
- else if(period == 'monthly')
262
- {
263
- jQuery('#for').show();
264
- jQuery('#month').hide();
265
- jQuery('#year').show();
266
- }
267
- else
268
- {
269
- jQuery('#for').hide();
270
- jQuery('#month').hide();
271
- jQuery('#year').hide();
272
- }
273
- }
274
-
275
- pmpro_ShowMonthOrYear();
276
-
277
- //draw the chart
278
- google.load("visualization", "1", {packages:["corechart"]});
279
- google.setOnLoadCallback(drawChart);
280
- function drawChart() {
281
-
282
- var data = google.visualization.arrayToDataTable([
283
- ['<?php echo $date_function;?>', '<?php echo ucwords($type);?>'],
284
- <?php foreach($cols as $date => $value) { ?>
285
- ['<?php if($period == "monthly") echo date("M", mktime(0,0,0,$date,2)); else echo $date;?>', <?php echo $value;?>],
286
- <?php } ?>
287
- ]);
288
-
289
- var options = {
290
- colors: ['#51a351', '#387038'],
291
- hAxis: {title: '<?php echo $date_function;?>', titleTextStyle: {color: 'black'}, maxAlternation: 1},
292
- vAxis: {color: 'green', titleTextStyle: {color: '#51a351'}},
293
- };
294
-
295
- <?php
296
- if($type != "sales")
297
- {
298
- if(pmpro_getCurrencyPosition() == "right")
299
- $position = "suffix";
300
- else
301
- $position = "prefix";
302
- ?>
303
- var formatter = new google.visualization.NumberFormat({<?php echo $position;?>: '<?php echo html_entity_decode($pmpro_currency_symbol);?>'});
304
- formatter.format(data, 1);
305
- <?php
306
- }
307
- ?>
308
-
309
- var chart = new google.visualization.ColumnChart(document.getElementById('chart_div'));
310
- chart.draw(data, options);
311
- }
312
- </script>
313
-
314
- </form>
315
- <?php
316
- }
317
-
318
- /*
319
- Other code required for your reports. This file is loaded every time WP loads with PMPro enabled.
320
- */
321
-
322
- //get sales
323
- function pmpro_getSales($period, $levels = NULL)
324
- {
325
- //check for a transient
326
- $cache = get_transient("pmpro_report_sales");
327
- if(!empty($cache) && !empty($cache[$period]) && !empty($cache[$period][$levels]))
328
- return $cache[$period][$levels];
329
-
330
- //a sale is an order with status NOT IN('refunded', 'review', 'token', 'error')
331
- if($period == "today")
332
- $startdate = date("Y-m-d", current_time('timestamp'));
333
- elseif($period == "this month")
334
- $startdate = date("Y-m", current_time('timestamp')) . "-01";
335
- elseif($period == "this year")
336
- $startdate = date("Y", current_time('timestamp')) . "-01-01";
337
- else
338
- $startdate = "";
339
-
340
- $gateway_environment = pmpro_getOption("gateway_environment");
341
-
342
- //build query
343
- global $wpdb;
344
- $sqlQuery = "SELECT COUNT(*) FROM $wpdb->pmpro_membership_orders WHERE status NOT IN('refunded', 'review', 'token', 'error') AND timestamp >= '" . $startdate . "' AND gateway_environment = '" . esc_sql($gateway_environment) . "' ";
345
-
346
- //restrict by level
347
- if(!empty($levels))
348
- $sqlQuery .= "AND membership_id IN(" . $levels . ") ";
349
-
350
- $sales = $wpdb->get_var($sqlQuery);
351
-
352
- //save in cache
353
- if(!empty($cache) && !empty($cache[$period]))
354
- $cache[$period][$levels] = $sales;
355
- elseif(!empty($cache))
356
- $cache[$period] = array($levels => $sales);
357
- else
358
- $cache = array($period => array($levels => $sales));
359
-
360
- set_transient("pmpro_report_sales", $cache, 3600*24);
361
-
362
- return $sales;
363
- }
364
-
365
- //get revenue
366
- function pmpro_getRevenue($period, $levels = NULL)
367
- {
368
- //check for a transient
369
- $cache = get_transient("pmpro_report_revenue");
370
- if(!empty($cache) && !empty($cache[$period]) && !empty($cache[$period][$levels]))
371
- return $cache[$period][$levels];
372
-
373
- //a sale is an order with status NOT IN('refunded', 'review', 'token', 'error')
374
- if($period == "today")
375
- $startdate = date("Y-m-d", current_time('timestamp'));
376
- elseif($period == "this month")
377
- $startdate = date("Y-m", current_time('timestamp')) . "-01";
378
- elseif($period == "this year")
379
- $startdate = date("Y", current_time('timestamp')) . "-01-01";
380
- else
381
- $startdate = "";
382
-
383
- $gateway_environment = pmpro_getOption("gateway_environment");
384
-
385
- //build query
386
- global $wpdb;
387
- $sqlQuery = "SELECT SUM(total) FROM $wpdb->pmpro_membership_orders WHERE status NOT IN('refunded', 'review', 'token', 'error') AND timestamp >= '" . $startdate . "' AND gateway_environment = '" . esc_sql($gateway_environment) . "' ";
388
-
389
- //restrict by level
390
- if(!empty($levels))
391
- $sqlQuery .= "AND membership_id IN(" . $levels . ") ";
392
-
393
- $revenue = $wpdb->get_var($sqlQuery);
394
-
395
- //save in cache
396
- if(!empty($cache) && !empty($cache[$period]))
397
- $cache[$period][$levels] = $revenue;
398
- elseif(!empty($cache))
399
- $cache[$period] = array($levels => $revenue);
400
- else
401
- $cache = array($period => array($levels => $revenue));
402
-
403
- set_transient("pmpro_report_revenue", $cache, 3600*24);
404
-
405
- return $revenue;
406
- }
407
-
408
- //delete transients when an order goes through
409
- function pmpro_report_sales_delete_transients()
410
- {
411
- delete_transient("pmpro_report_sales");
412
- delete_transient("pmpro_report_revenue");
413
- }
414
- add_action("pmpro_after_checkout", "pmpro_report_sales_delete_transients");
415
- add_action("pmpro_updated_order", "pmpro_report_sales_delete_transients");
1
+ <?php
2
+ /*
3
+ PMPro Report
4
+ Title: Sales
5
+ Slug: sales
6
+
7
+ For each report, add a line like:
8
+ global $pmpro_reports;
9
+ $pmpro_reports['slug'] = 'Title';
10
+
11
+ For each report, also write two functions:
12
+ * pmpro_report_{slug}_widget() to show up on the report homepage.
13
+ * pmpro_report_{slug}_page() to show up when users click on the report page widget.
14
+ */
15
+ global $pmpro_reports;
16
+ $gateway_environment = pmpro_getOption("gateway_environment");
17
+ if($gateway_environment == "sandbox")
18
+ $pmpro_reports['sales'] = __('Sales and Revenue (Testing/Sandbox)', 'pmpro');
19
+ else
20
+ $pmpro_reports['sales'] = __('Sales and Revenue', 'pmpro');
21
+
22
+ //queue Google Visualization JS on report page
23
+ function pmpro_report_sales_init()
24
+ {
25
+ if(is_admin() && isset($_REQUEST['report']) && $_REQUEST['report'] == "sales" && isset($_REQUEST['page']) && $_REQUEST['page'] == "pmpro-reports")
26
+ {
27
+ wp_enqueue_script("jsapi", "https://www.google.com/jsapi");
28
+ }
29
+ }
30
+ add_action("init", "pmpro_report_sales_init");
31
+
32
+ //widget
33
+ function pmpro_report_sales_widget()
34
+ {
35
+ global $wpdb;
36
+ ?>
37
+ <style>
38
+ #pmpro_report_sales div {text-align: center;}
39
+ #pmpro_report_sales em {display: block; font-style: normal; font-size: 2em; margin: 5px;}
40
+ </style>
41
+ <span id="#pmpro_report_sales">
42
+ <div style="width: 25%; float: left;">
43
+ <em><?php echo pmpro_getSales("all time");?></em>
44
+ <label>All Time</label>
45
+ <em><?php echo pmpro_formatPrice(pmpro_getRevenue("all time"));?></em>
46
+ </div>
47
+ <div style="width: 25%; float: left;">
48
+ <em><?php echo pmpro_getSales("this year");?></em>
49
+ <label>This Year</label>
50
+ <em><?php echo pmpro_formatPrice(pmpro_getRevenue("this year"));?></em>
51
+ </div>
52
+ <div style="width: 25%; float: left;">
53
+ <em><?php echo pmpro_getSales("this month");?></em>
54
+ <label>This Month</label>
55
+ <em><?php echo pmpro_formatPrice(pmpro_getRevenue("this month"));?></em>
56
+ </div>
57
+ <div style="width: 25%; float: left;">
58
+ <em><?php echo pmpro_getSales("today");?></em>
59
+ <label>Today</label>
60
+ <em><?php echo pmpro_formatPrice(pmpro_getRevenue("today"));?></em>
61
+ </div>
62
+ <div class="clear"></div>
63
+ </span>
64
+ <?php
65
+ }
66
+
67
+ function pmpro_report_sales_page()
68
+ {
69
+ global $wpdb, $pmpro_currency_symbol, $pmpro_currency, $pmpro_currencies;
70
+
71
+ //get values from form
72
+ if(isset($_REQUEST['type']))
73
+ $type = sanitize_text_field($_REQUEST['type']);
74
+ else
75
+ $type = "revenue";
76
+
77
+ if($type == "sales")
78
+ $type_function = "COUNT";
79
+ else
80
+ $type_function = "SUM";
81
+
82
+ if(isset($_REQUEST['period']))
83
+ $period = sanitize_text_field($_REQUEST['period']);
84
+ else
85
+ $period = "daily";
86
+
87
+ if(isset($_REQUEST['month']))
88
+ $month = intval($_REQUEST['month']);
89
+ else
90
+ $month = date("n", current_time('timestamp'));
91
+
92
+ $thisyear = date("Y", current_time('timestamp'));
93
+ if(isset($_REQUEST['year']))
94
+ $year = intval($_REQUEST['year']);
95
+ else
96
+ $year = $thisyear;
97
+
98
+ if(isset($_REQUEST['level']))
99
+ $l = intval($_REQUEST['level']);
100
+ else
101
+ $l = "";
102
+
103
+ //calculate start date and how to group dates returned from DB
104
+ if($period == "daily")
105
+ {
106
+ $startdate = $year . '-' . substr("0" . $month, strlen($month) - 1, 2) . '-01';
107
+ $enddate = $year . '-' . substr("0" . $month, strlen($month) - 1, 2) . '-32';
108
+ $date_function = 'DAY';
109
+ }
110
+ elseif($period == "monthly")
111
+ {
112
+ $startdate = $year . '-01-01';
113
+ $enddate = strval(intval($year)+1) . '-01-01';
114
+ $date_function = 'MONTH';
115
+ }
116
+ else
117
+ {
118
+ $startdate = '1960-01-01'; //all time
119
+ $date_function = 'YEAR';
120
+ }
121
+
122
+ //testing or live data
123
+ $gateway_environment = pmpro_getOption("gateway_environment");
124
+
125
+ //get data
126
+ $sqlQuery = "SELECT $date_function(timestamp) as date, $type_function(total) as value FROM $wpdb->pmpro_membership_orders WHERE timestamp >= '" . $startdate . "' AND status NOT IN('refunded', 'review', 'token', 'error') AND gateway_environment = '" . esc_sql($gateway_environment) . "' ";
127
+
128
+ if(!empty($enddate))
129
+ $sqlQuery .= "AND timestamp < '" . $enddate . "' ";
130
+
131
+ if(!empty($l))
132
+ $sqlQuery .= "AND membership_id IN(" . $l . ") ";
133
+
134
+ $sqlQuery .= " GROUP BY date ORDER BY date ";
135
+
136
+ $dates = $wpdb->get_results($sqlQuery);
137
+
138
+ //fill in blanks in dates
139
+ $cols = array();
140
+ if($period == "daily")
141
+ {
142
+ $lastday = date("t", strtotime($startdate, current_time("timestamp")));
143
+
144
+ for($i = 1; $i <= $lastday; $i++)
145
+ {
146
+ $cols[$i] = 0;
147
+ foreach($dates as $date)
148
+ {
149
+ if($date->date == $i)
150
+ $cols[$i] = $date->value;
151
+ }
152
+ }
153
+ }
154
+ elseif($period == "monthly")
155
+ {
156
+ for($i = 1; $i < 13; $i++)
157
+ {
158
+ $cols[$i] = 0;
159
+ foreach($dates as $date)
160
+ {
161
+ if($date->date == $i)
162
+ $cols[$i] = $date->value;
163
+ }
164
+ }
165
+ }
166
+ else //annual
167
+ {
168
+ //get min and max years
169
+ $min = 9999;
170
+ $max = 0;
171
+ foreach($dates as $date)
172
+ {
173
+ $min = min($min, $date->date);
174
+ $max = max($max, $date->date);
175
+ }
176
+
177
+ for($i = $min; $i <= $max; $i++)
178
+ {
179
+ foreach($dates as $date)
180
+ {
181
+ if($date->date == $i)
182
+ $cols[$i] = $date->value;
183
+ }
184
+ }
185
+ }
186
+ ?>
187
+ <form id="posts-filter" method="get" action="">
188
+ <h2>
189
+ <?php _e('Sales and Revenue', 'pmpro');?>
190
+ </h2>
191
+
192
+ <div class="tablenav top">
193
+ <?php _ex('Show', 'Dropdown label, e.g. Show Daily Revenue for January', 'pmpro')?>
194
+ <select id="period" name="period">
195
+ <option value="daily" <?php selected($period, "daily");?>><?php _e('Daily', 'pmpro');?></option>
196
+ <option value="monthly" <?php selected($period, "monthly");?>><?php _e('Monthly', 'pmpro');?></option>
197
+ <option value="annual" <?php selected($period, "annual");?>><?php _e('Annual', 'pmpro');?></option>
198
+ </select>
199
+ <select name="type">
200
+ <option value="revenue" <?php selected($type, "revenue");?>><?php _e('Revenue', 'pmpro');?></option>
201
+ <option value="sales" <?php selected($type, "sales");?>><?php _e('Sales', 'pmpro');?></option>
202
+ </select>
203
+ <span id="for"><?php _ex('for', 'Dropdown label, e.g. Show Daily Revenue for January', 'pmpro')?></span>
204
+ <select id="month" name="month">
205
+ <?php for($i = 1; $i < 13; $i++) { ?>
206
+ <option value="<?php echo $i;?>" <?php selected($month, $i);?>><?php echo date("F", mktime(0, 0, 0, $i, 2));?></option>
207
+ <?php } ?>
208
+ </select>
209
+ <select id="year" name="year">
210
+ <?php for($i = $thisyear; $i > 2007; $i--) { ?>
211
+ <option value="<?php echo $i;?>" <?php selected($year, $i);?>><?php echo $i;?></option>
212
+ <?php } ?>
213
+ </select>
214
+ <span id="for"><?php _ex('for', 'Dropdown label, e.g. Show Daily Revenue for January', 'pmpro')?></span>
215
+ <select name="level">
216
+ <option value="" <?php if(!$l) { ?>selected="selected"<?php } ?>><?php _e('All Levels', 'pmpro');?></option>
217
+ <?php
218
+ $levels = $wpdb->get_results("SELECT id, name FROM $wpdb->pmpro_membership_levels ORDER BY name");
219
+ foreach($levels as $level)
220
+ {
221
+ ?>
222
+ <option value="<?php echo $level->id?>" <?php if($l == $level->id) { ?>selected="selected"<?php } ?>><?php echo $level->name?></option>
223
+ <?php
224
+ }
225
+ ?>
226
+ </select>
227
+
228
+ <input type="hidden" name="page" value="pmpro-reports" />
229
+ <input type="hidden" name="report" value="sales" />
230
+ <input type="submit" class="button action" value="<?php _ex('Generate Report', 'Submit button value.', 'pmpro');?>" />
231
+ </div>
232
+
233
+ <div id="chart_div" style="clear: both; width: 100%; height: 500px;"></div>
234
+
235
+ <script>
236
+ //update month/year when period dropdown is changed
237
+ jQuery(document).ready(function() {
238
+ jQuery('#period').change(function() {
239
+ pmpro_ShowMonthOrYear();
240
+ });
241
+ });
242
+
243
+ function pmpro_ShowMonthOrYear()
244
+ {
245
+ var period = jQuery('#period').val();
246
+ if(period == 'daily')
247
+ {
248
+ jQuery('#for').show();
249
+ jQuery('#month').show();
250
+ jQuery('#year').show();
251
+ }
252
+ else if(period == 'monthly')
253
+ {
254
+ jQuery('#for').show();
255
+ jQuery('#month').hide();
256
+ jQuery('#year').show();
257
+ }
258
+ else
259
+ {
260
+ jQuery('#for').hide();
261
+ jQuery('#month').hide();
262
+ jQuery('#year').hide();
263
+ }
264
+ }
265
+
266
+ pmpro_ShowMonthOrYear();
267
+
268
+ //draw the chart
269
+ google.load("visualization", "1", {packages:["corechart"]});
270
+ google.setOnLoadCallback(drawChart);
271
+ function drawChart() {
272
+
273
+ var data = google.visualization.arrayToDataTable([
274
+ ['<?php echo $date_function;?>', '<?php echo ucwords($type);?>'],
275
+ <?php foreach($cols as $date => $value) { ?>
276
+ ['<?php if($period == "monthly") echo date("M", mktime(0,0,0,$date,2)); else echo $date;?>', <?php echo $value;?>],
277
+ <?php } ?>
278
+ ]);
279
+
280
+ var options = {
281
+ colors: ['#51a351', '#387038'],
282
+ hAxis: {title: '<?php echo $date_function;?>', titleTextStyle: {color: 'black'}, maxAlternation: 1},
283
+ vAxis: {color: 'green', titleTextStyle: {color: '#51a351'}},
284
+ };
285
+
286
+ <?php
287
+ if($type != "sales")
288
+ {
289
+ if(pmpro_getCurrencyPosition() == "right")
290
+ $position = "suffix";
291
+ else
292
+ $position = "prefix";
293
+ ?>
294
+ var formatter = new google.visualization.NumberFormat({<?php echo $position;?>: '<?php echo html_entity_decode($pmpro_currency_symbol);?>'});
295
+ formatter.format(data, 1);
296
+ <?php
297
+ }
298
+ ?>
299
+
300
+ var chart = new google.visualization.ColumnChart(document.getElementById('chart_div'));
301
+ chart.draw(data, options);
302
+ }
303
+ </script>
304
+
305
+ </form>
306
+ <?php
307
+ }
308
+
309
+ /*
310
+ Other code required for your reports. This file is loaded every time WP loads with PMPro enabled.
311
+ */
312
+
313
+ //get sales
314
+ function pmpro_getSales($period, $levels = NULL)
315
+ {
316
+ //check for a transient
317
+ $cache = get_transient("pmpro_report_sales");
318
+ if(!empty($cache) && !empty($cache[$period]) && !empty($cache[$period][$levels]))
319
+ return $cache[$period][$levels];
320
+
321
+ //a sale is an order with status NOT IN('refunded', 'review', 'token', 'error')
322
+ if($period == "today")
323
+ $startdate = date("Y-m-d", current_time('timestamp'));
324
+ elseif($period == "this month")
325
+ $startdate = date("Y-m", current_time('timestamp')) . "-01";
326
+ elseif($period == "this year")
327
+ $startdate = date("Y", current_time('timestamp')) . "-01-01";
328
+ else
329
+ $startdate = "";
330
+
331
+ $gateway_environment = pmpro_getOption("gateway_environment");
332
+
333
+ //build query
334
+ global $wpdb;
335
+ $sqlQuery = "SELECT COUNT(*) FROM $wpdb->pmpro_membership_orders WHERE status NOT IN('refunded', 'review', 'token', 'error') AND timestamp >= '" . $startdate . "' AND gateway_environment = '" . esc_sql($gateway_environment) . "' ";
336
+
337
+ //restrict by level
338
+ if(!empty($levels))
339
+ $sqlQuery .= "AND membership_id IN(" . $levels . ") ";
340
+
341
+ $sales = $wpdb->get_var($sqlQuery);
342
+
343
+ //save in cache
344
+ if(!empty($cache) && !empty($cache[$period]))
345
+ $cache[$period][$levels] = $sales;
346
+ elseif(!empty($cache))
347
+ $cache[$period] = array($levels => $sales);
348
+ else
349
+ $cache = array($period => array($levels => $sales));
350
+
351
+ set_transient("pmpro_report_sales", $cache, 3600*24);
352
+
353
+ return $sales;
354
+ }
355
+
356
+ //get revenue
357
+ function pmpro_getRevenue($period, $levels = NULL)
358
+ {
359
+ //check for a transient
360
+ $cache = get_transient("pmpro_report_revenue");
361
+ if(!empty($cache) && !empty($cache[$period]) && !empty($cache[$period][$levels]))
362
+ return $cache[$period][$levels];
363
+
364
+ //a sale is an order with status NOT IN('refunded', 'review', 'token', 'error')
365
+ if($period == "today")
366
+ $startdate = date("Y-m-d", current_time('timestamp'));
367
+ elseif($period == "this month")
368
+ $startdate = date("Y-m", current_time('timestamp')) . "-01";
369
+ elseif($period == "this year")
370
+ $startdate = date("Y", current_time('timestamp')) . "-01-01";
371
+ else
372
+ $startdate = "";
373
+
374
+ $gateway_environment = pmpro_getOption("gateway_environment");
375
+
376
+ //build query
377
+ global $wpdb;
378
+ $sqlQuery = "SELECT SUM(total) FROM $wpdb->pmpro_membership_orders WHERE status NOT IN('refunded', 'review', 'token', 'error') AND timestamp >= '" . $startdate . "' AND gateway_environment = '" . esc_sql($gateway_environment) . "' ";
379
+
380
+ //restrict by level
381
+ if(!empty($levels))
382
+ $sqlQuery .= "AND membership_id IN(" . $levels . ") ";
383
+
384
+ $revenue = $wpdb->get_var($sqlQuery);
385
+
386
+ //save in cache
387
+ if(!empty($cache) && !empty($cache[$period]))
388
+ $cache[$period][$levels] = $revenue;
389
+ elseif(!empty($cache))
390
+ $cache[$period] = array($levels => $revenue);
391
+ else
392
+ $cache = array($period => array($levels => $revenue));
393
+
394
+ set_transient("pmpro_report_revenue", $cache, 3600*24);
395
+
396
+ return $revenue;
397
+ }
398
+
399
+ //delete transients when an order goes through
400
+ function pmpro_report_sales_delete_transients()
401
+ {
402
+ delete_transient("pmpro_report_sales");
403
+ delete_transient("pmpro_report_revenue");
404
+ }
405
+ add_action("pmpro_after_checkout", "pmpro_report_sales_delete_transients");
406
+ add_action("pmpro_updated_order", "pmpro_report_sales_delete_transients");
 
 
 
 
 
 
 
 
 
adminpages/templates/orders-email.php DELETED
@@ -1,71 +0,0 @@
1
- <?php
2
- /**
3
- * Template for Email Invoices
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>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
adminpages/templates/orders-print.php CHANGED
@@ -1,99 +1,71 @@
1
  <?php
2
  /**
3
- * Template for Print Invoices
4
  *
5
  * @since 1.8.6
6
  */
7
  ?>
8
- <!doctype html>
9
- <html lang="en">
10
- <head>
11
- <meta charset="UTF-8">
12
- <style>
13
- .main, .header {
14
- display: block;
15
- }
16
- .right {
17
- display: inline-block;
18
- float: right;
19
- }
20
- .alignright {
21
- text-align: right;
22
- }
23
- .aligncenter {
24
- text-align: center;
25
- }
26
- .invoice, .invoice tr, .invoice th, .invoice td {
27
- border: 1px solid;
28
- border-collapse: collapse;
29
- padding: 4px;
30
- }
31
- .invoice {
32
- width: 100%;
33
- }
34
- @media screen {
35
- body {
36
- max-width: 50%;
37
- margin: 0 auto;
38
- }
39
- }
40
- </style>
41
- </head>
42
- <body>
43
- <header class="header">
44
- <div>
45
  <h2><?php bloginfo( 'sitename' ); ?></h2>
46
- </div>
47
- <div class="right">
48
- <table>
49
- <tr>
50
- <td><?php echo __('Invoice #: ', 'pmpro') . '&nbsp;' . $order->code; ?></td>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
51
  </tr>
52
- <tr>
53
- <td>
54
- <?php echo __( 'Date:', 'pmpro' ) . '&nbsp;' . date( 'Y-m-d', $order->timestamp ) ?>
55
- </td>
 
 
 
 
 
 
 
 
 
 
 
 
56
  </tr>
57
  </table>
58
- </div>
59
- </header>
60
- <main class="main">
61
- <p>
62
- <?php echo pmpro_formatAddress(
63
- $order->billing->name,
64
- $order->billing->address1,
65
- $order->billing->address2,
66
- $order->billing->city,
67
- $order->billing->state,
68
- $order->billing->zip,
69
- $order->billing->country,
70
- $order->billing->phone
71
- ); ?>
72
- </p>
73
- <table class="invoice">
74
- <tr>
75
- <th><?php _e('ID', 'pmpro'); ?></th>
76
- <th><?php _e('Item', 'pmpro'); ?></th>
77
- <th><?php _e('Price', 'pmpro'); ?></th>
78
- </tr>
79
- <tr>
80
- <td class="aligncenter"><?php echo $level->id; ?></td>
81
- <td><?php echo $level->name; ?></td>
82
- <td class="alignright"><?php echo $order->subtotal; ?></td>
83
- </tr>
84
- <tr>
85
- <th colspan="2" class="alignright"><?php _e('Subtotal', 'pmpro'); ?></th>
86
- <td class="alignright"><?php echo $order->subtotal; ?></td>
87
- </tr>
88
- <tr>
89
- <th colspan="2" class="alignright"><?php _e('Tax', 'pmpro'); ?></th>
90
- <td class="alignright"><?php echo $order->tax; ?></td>
91
- </tr>
92
- <tr>
93
- <th colspan="2" class="alignright"><?php _e('Total', 'pmpro'); ?></th>
94
- <th class="alignright"><?php echo pmpro_formatPrice( $order->total ); ?></th>
95
- </tr>
96
- </table>
97
- </main>
98
- </body>
99
- </html>
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
@@ -849,12 +849,12 @@
849
  $this->template = "billable_invoice";
850
 
851
  // Load invoice template
852
- if ( file_exists( get_stylesheet_directory() . '/paid-memberships-pro/pages/orders-email.php' ) ) {
853
- $template = get_stylesheet_directory() . '/paid-memberships-pro/pages/orders-email.php';
854
- } elseif ( file_exists( get_template_directory() . '/paid-memberships-pro/pages/orders-email.php' ) ) {
855
- $template = get_template_directory() . '/paid-memberships-pro/pages/orders-email.php';
856
  } else {
857
- $template = PMPRO_DIR . '/adminpages/templates/orders-email.php';
858
  }
859
 
860
  ob_start();
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();
css/admin.css CHANGED
@@ -1,122 +1,118 @@
1
- /* icon */
2
- #wp-admin-bar-paid-memberships-pro .ab-item .ab-icon:before {
3
- font-family: "dashicons";
4
- content: "\f307";
5
- }
6
- .pmpro_admin tr td .dashicons {padding-top: 5px; }
7
-
8
- /* header/etc */
9
- .pmpro_admin {background: url(../images/Paid-Memberships-Pro_watermark.png) bottom right no-repeat !important; padding: 1em 0 70px 0; }
10
-
11
- .pmpro_admin .pmpro_banner h2 {float: left; }
12
- .pmpro_admin .pmpro_banner .pmpro_meta {float: left; margin: 26px 0 0 0; font-size: 12px; }
13
- .pmpro_admin .pmpro_banner .pmpro_meta .pmpro_tag-blue {margin: 0 0 0 5px; }
14
- .pmpro_admin .pmpro_banner .pmpro_logo {float: left; margin: 0 1em 0 0; width: 350px; height: 75px; }
15
- .pmpro_admin .pmpro_banner ul.pmpro_menu {clear: both; border: 1px solid #CCC; border-radius: 5px; -moz-border-radius: 5px; background: #FFF; }
16
- .pmpro_admin .pmpro_banner ul.pmpro_menu li {display: inline-block; margin: 10px 0; padding: 0px 10px; border-right: 1px solid #CCC; }
17
- .pmpro_admin .pmpro_banner ul.pmpro_menu li a, .pmpro_admin .pmpro_banner ul.pmpro_menu li a:link {color: #1e0741; text-decoration: none; }
18
- .pmpro_admin .pmpro_banner ul.pmpro_menu li a:hover {text-decoration: underline; color: #412f5b; }
19
-
20
- .pmpro_admin .pmpro_tag-grey {display: inline-block; font-size: 11px; font-weight: bold; position: relative; padding: 2px 5px; border: 1px solid #CCC; background: whiteSmoke; border-radius: 10px; -moz-border-radius: 10px; -webkit-border-radius: 10px; }
21
- .pmpro_admin .pmpro_tag-blue {display: inline-block; font-size: 11px; font-weight: bold; position: relative; padding: 2px 5px; border: 1px solid #2997c8; border-radius: 10px; -moz-border-radius: 10px; -webkit-border-radius: 10px; background: #2997c8; color: #FFF; text-decoration: none; }
22
- .pmpro_admin a.pmpro_tag-blue:hover {background: #77a02e; border: 1px solid #77a02e; }
23
-
24
- .pmpro_admin .topborder {border-top: 1px solid #CCC; margin-top: 1em; padding-top: 1em; }
25
- .pmpro_admin #editorcontainer #description {width: 100%; height: 180px; }
26
- .pmpro_admin .widefat {margin-top: 1em; }
27
-
28
- /* checkboxes */
29
- .checkbox_box {width: 300px; background: #FFFFFF; border: 1px solid #CCC;}
30
- .checkbox_box div {border-bottom: 1px solid #CCC; padding: 3px;}
31
- .checkbox_box .clickable {cursor: pointer;}
32
- .checkbox_box .clickable:hover {background: #FFC;}
33
-
34
- /* levels */
35
- tr.pmpro_gray td {color: #AAA;}
36
- tr td.level_name a {font-size: 115%; font-weight: bold; }
37
- .membership-levels tr {background: #fff;}
38
- .membership-levels tr.alternate {background: #f9f9f9;}
39
- .membership-levels tr.ui-sortable-helper {border: 1px solid #2997C8;}
40
- tr.testclass {border: 3px solid #2997C8; background: #2997C8;}
41
-
42
- /* settings */
43
- tr.pmpro_settings_divider td {padding: 5px; margin: 0; color: #555; border-top: 1px solid #CCC; border-bottom: 1px solid #CCC;}
44
- tr.pmpro_settings_divider td:before {content: "- ";}
45
- tr.pmpro_settings_divider td:after {content: " -";}
46
-
47
- /* messages */
48
- .pmpro_message {background-color: #D5E4F7; background-image: url(../images/icon_information.gif); background-position: 3px 5px; background-repeat: no-repeat; margin: .5em 0; padding: 6px 6px 6px 25px; color: #345395; font-size: 11px; font-weight: bold; line-height: 1.3em; }
49
-
50
- .pmpro_success {background-color: #CFEECA; background-image: url(../images/icon_success.gif); color: #208A1B; }
51
- .pmpro_error {background-color: #F9D6CB; background-image: url(../images/icon_error.gif); color: #E36154; }
52
- .pmpro_alert {background-color: #FFF6CC; background-image: url(../images/icon_alert.gif); color: #CF8516; }
53
-
54
- .pmpro_message a {color: #345395; }
55
- .pmpro_success a {color: #208A1B; }
56
- .pmpro_error a {color: #E36154; }
57
- .pmpro_alert a {color: #CF8516; }
58
-
59
- /* highlighted trs */
60
- tr.pmpro_message {background-image: none;}
61
- tr.pmpro_success {background-image: none;}
62
- tr.pmpro_error {background-image: none;}
63
- tr.pmpro_alert {background-image: none;}
64
-
65
- /* discount levels */
66
- .pmpro_discount_levels {border: 1px solid #CCC;}
67
- .pmpro_discount_levels div {padding: 5px; border: 1px solid #CCC;}
68
- .pmpro_discount_levels div div {margin-top: 5px; background: #F5F5F5;}
69
-
70
- /* pagination */
71
- div.pmpro_pagination {padding: 3px; margin: 5px 0px 5px 0px; font-size: 10px; float: right; }
72
- div.pmpro_pagination a {padding: 2px 5px 2px 5px; margin: 1px; border: 1px solid #666; text-decoration: none; /* no underline */ color: #666; background: #EEE; }
73
- div.pmpro_pagination a:hover, div.pmpro_pagination a:active {background: #FFF; }
74
- div.pmpro_pagination span.current {border: 1px solid #FFF; color: #FFF; background: #666; padding: 2px 5px 2px 5px; margin: 1px; font-weight: bold; }
75
- div.pmpro_pagination span.disabled {padding: 2px 5px 2px 5px; margin: 2px; border: 1px solid #BBB; color: #BBB; background: #EFEFEF;}
76
-
77
- p.pmpro_meta_notice {font-size: .8em; padding-top: 5px; border-top: 1px solid #CCC;}
78
-
79
- /* add ons */
80
- .pmpro_admin .widgets-holder-wrap {clear: both; margin-top: 20px; padding: 0 8px; }
81
- .pmpro_admin .widgets-holder-wrap .widget {float: left; width: 32%; margin: 0 1% 1% 0; position: relative; }
82
- .pmpro_admin .widgets-holder-wrap p.description {padding: 0; }
83
- .pmpro_admin .widgets-holder-wrap .widget-top {height: auto; cursor: default; }
84
- .pmpro_admin .widgets-holder-wrap .widget-inside {display: block; height: 130px; overflow: hidden; }
85
- .pmpro_admin .widgets-holder-wrap .widget-inside p {height: 80px; overflow: hidden; }
86
- .pmpro_admin #pmpro-gists.widgets-holder-wrap .widget-inside, .pmpro_admin #pmpro-gists.widgets-holder-wrap .widget-inside p {height: auto; }
87
- .pmpro_admin .widgets-holder-wrap .widget-title { }
88
- .pmpro_admin .widgets-holder-wrap .widget-title h4 { }
89
- .pmpro_admin .widgets-holder-wrap .widget-title .status-label {display: block; float: left; margin: 0 5px 0 0; width: 10px;
90
- height: 10px; overflow: hidden; border-radius: 10px; -moz-border-radius: 10px; -webkit-border-radius: 10px; border: 1px solid #DFDFDF; text-indent: -9999em; }
91
- .pmpro_admin .widgets-holder-wrap .disabled .widget-title .status-label {background: #F00; }
92
- .pmpro_admin .widgets-holder-wrap .enabled .widget-title .status-label {background: #0C0; }
93
-
94
- .pmpro_admin .widgets-holder-wrap .widget-title .version {position: absolute; top: 13px; right: 10px; }
95
- .pmpro_admin .widgets-holder-wrap .widget-inside .addon-thumb {width: 100px; height: 100px; float: right; margin: 10px 0 0 10px; border: 1px solid #DFDFDF; background: #FFF; padding: 2px;}
96
-
97
- /*@media (min-width: 1200px) {
98
- .auto-fold .pmpro_admin .widgets-holder-wrap .widget-inside, .auto-fold .pmpro_admin .widgets-holder-wrap .widget-inside p {height: auto; }
99
- }
100
- */
101
- @media (max-width:900px) {
102
- .auto-fold .pmpro_admin .widgets-holder-wrap .widget {float: none; width: 100%; }
103
- .auto-fold .pmpro_admin .widgets-holder-wrap .widget-inside, .auto-fold .pmpro_admin .widgets-holder-wrap .widget-inside p {height: auto; }
104
- }
105
-
106
- /* misc */
107
- .pmpro_lite {color: #AAA;}
108
- .pmpro_pad20 {padding: 20px !important;}
109
- .pmpro_red {color: #CC0000;}
110
- .pmpro_green {color: #00AA00;}
111
- .ssp_description #description {width: 100%;}
112
- .top0em {margin-top: 0;}
113
- h2.nav-tab-wrapper {margin-bottom: 1em; }
114
-
115
- /* reports */
116
- .pmpro_reports-holder { }
117
- .pmpro_clickable {cursor: pointer;}
118
- .js .postbox.pmpro_clickable h3 {cursor: pointer;}
119
- .pmpro_reports-holder .wp-list-table tbody td {font-size: 1.2rem; font-weight: bold; }
120
- @media screen and (max-width: 782px) {
121
- .pmpro_reports-holder tr:not(.inline-edit-row):not(.no-items) td:not(.check-column) {display: table-cell; }
122
- }
1
+ /* icon */
2
+ #wp-admin-bar-paid-memberships-pro .ab-item .ab-icon:before {
3
+ font-family: "dashicons";
4
+ content: "\f307";
5
+ }
6
+ .pmpro_admin tr td .dashicons {padding-top: 5px; }
7
+
8
+ /* header/etc */
9
+ .pmpro_admin {background: url(../images/Paid-Memberships-Pro_watermark.png) bottom right no-repeat !important; padding: 1em 0 70px 0; }
10
+
11
+ .pmpro_admin .pmpro_banner h2 {float: left; }
12
+ .pmpro_admin .pmpro_banner .pmpro_meta {float: left; margin: 26px 0 0 0; font-size: 12px; }
13
+ .pmpro_admin .pmpro_banner .pmpro_meta .pmpro_tag-blue {margin: 0 0 0 5px; }
14
+ .pmpro_admin .pmpro_banner .pmpro_logo {float: left; margin: 0 1em 0 0; width: 350px; height: 75px; }
15
+ .pmpro_admin .pmpro_banner ul.pmpro_menu {clear: both; border: 1px solid #CCC; border-radius: 5px; -moz-border-radius: 5px; background: #FFF; }
16
+ .pmpro_admin .pmpro_banner ul.pmpro_menu li {display: inline-block; margin: 10px 0; padding: 0px 10px; border-right: 1px solid #CCC; }
17
+ .pmpro_admin .pmpro_banner ul.pmpro_menu li a, .pmpro_admin .pmpro_banner ul.pmpro_menu li a:link {color: #1e0741; text-decoration: none; }
18
+ .pmpro_admin .pmpro_banner ul.pmpro_menu li a:hover {text-decoration: underline; color: #412f5b; }
19
+
20
+ .pmpro_admin .pmpro_tag-grey {display: inline-block; font-size: 11px; font-weight: bold; position: relative; padding: 2px 5px; border: 1px solid #CCC; background: whiteSmoke; border-radius: 10px; -moz-border-radius: 10px; -webkit-border-radius: 10px; }
21
+ .pmpro_admin .pmpro_tag-blue {display: inline-block; font-size: 11px; font-weight: bold; position: relative; padding: 2px 5px; border: 1px solid #2997c8; border-radius: 10px; -moz-border-radius: 10px; -webkit-border-radius: 10px; background: #2997c8; color: #FFF; text-decoration: none; }
22
+ .pmpro_admin a.pmpro_tag-blue:hover {background: #77a02e; border: 1px solid #77a02e; }
23
+
24
+ .pmpro_admin .topborder {border-top: 1px solid #CCC; margin-top: 1em; padding-top: 1em; }
25
+ .pmpro_admin #editorcontainer #description {width: 100%; height: 180px; }
26
+ .pmpro_admin .widefat {margin-top: 1em; }
27
+
28
+ /* checkboxes */
29
+ .checkbox_box {width: 300px; background: #FFFFFF; border: 1px solid #CCC;}
30
+ .checkbox_box div {border-bottom: 1px solid #CCC; padding: 3px;}
31
+ .checkbox_box .clickable {cursor: pointer;}
32
+ .checkbox_box .clickable:hover {background: #FFC;}
33
+
34
+ /* levels */
35
+ tr.pmpro_gray td {color: #AAA;}
36
+ tr td.level_name a {font-size: 115%; font-weight: bold; }
37
+ .membership-levels tr {background: #fff;}
38
+ .membership-levels tr.alternate {background: #f9f9f9;}
39
+ .membership-levels tr.ui-sortable-helper {border: 1px solid #2997C8;}
40
+ tr.testclass {border: 3px solid #2997C8; background: #2997C8;}
41
+
42
+ /* settings */
43
+ tr.pmpro_settings_divider td {padding: 5px; margin: 0; color: #555; border-top: 1px solid #CCC; border-bottom: 1px solid #CCC;}
44
+ tr.pmpro_settings_divider td:before {content: "- ";}
45
+ tr.pmpro_settings_divider td:after {content: " -";}
46
+
47
+ /* messages */
48
+ .pmpro_message {background-color: #D5E4F7; background-image: url(../images/icon_information.gif); background-position: 3px 5px; background-repeat: no-repeat; margin: .5em 0; padding: 6px 6px 6px 25px; color: #345395; font-size: 11px; font-weight: bold; line-height: 1.3em; }
49
+
50
+ .pmpro_success {background-color: #CFEECA; background-image: url(../images/icon_success.gif); color: #208A1B; }
51
+ .pmpro_error {background-color: #F9D6CB; background-image: url(../images/icon_error.gif); color: #E36154; }
52
+ .pmpro_alert {background-color: #FFF6CC; background-image: url(../images/icon_alert.gif); color: #CF8516; }
53
+
54
+ .pmpro_message a {color: #345395; }
55
+ .pmpro_success a {color: #208A1B; }
56
+ .pmpro_error a {color: #E36154; }
57
+ .pmpro_alert a {color: #CF8516; }
58
+
59
+ /* highlighted trs */
60
+ tr.pmpro_message {background-image: none;}
61
+ tr.pmpro_success {background-image: none;}
62
+ tr.pmpro_error {background-image: none;}
63
+ tr.pmpro_alert {background-image: none;}
64
+
65
+ /* discount levels */
66
+ .pmpro_discount_levels {border: 1px solid #CCC;}
67
+ .pmpro_discount_levels div {padding: 5px; border: 1px solid #CCC;}
68
+ .pmpro_discount_levels div div {margin-top: 5px; background: #F5F5F5;}
69
+
70
+ /* pagination */
71
+ div.pmpro_pagination {padding: 3px; margin: 5px 0px 5px 0px; font-size: 10px; float: right; }
72
+ div.pmpro_pagination a {padding: 2px 5px 2px 5px; margin: 1px; border: 1px solid #666; text-decoration: none; /* no underline */ color: #666; background: #EEE; }
73
+ div.pmpro_pagination a:hover, div.pmpro_pagination a:active {background: #FFF; }
74
+ div.pmpro_pagination span.current {border: 1px solid #FFF; color: #FFF; background: #666; padding: 2px 5px 2px 5px; margin: 1px; font-weight: bold; }
75
+ div.pmpro_pagination span.disabled {padding: 2px 5px 2px 5px; margin: 2px; border: 1px solid #BBB; color: #BBB; background: #EFEFEF;}
76
+
77
+ p.pmpro_meta_notice {font-size: .8em; padding-top: 5px; border-top: 1px solid #CCC;}
78
+
79
+ /* add ons */
80
+ .pmpro_admin .widgets-holder-wrap {clear: both; margin-top: 20px; padding: 0 8px; }
81
+ .pmpro_admin .widgets-holder-wrap .widget {float: left; width: 32%; margin: 0 1% 1% 0; position: relative; }
82
+ .pmpro_admin .widgets-holder-wrap p.description {padding: 0; }
83
+ .pmpro_admin .widgets-holder-wrap .widget-top {height: auto; cursor: default; }
84
+ .pmpro_admin .widgets-holder-wrap .widget-inside {display: block; height: 130px; overflow: hidden; }
85
+ .pmpro_admin .widgets-holder-wrap .widget-inside p {height: 80px; overflow: hidden; }
86
+ .pmpro_admin #pmpro-gists.widgets-holder-wrap .widget-inside, .pmpro_admin #pmpro-gists.widgets-holder-wrap .widget-inside p {height: auto; }
87
+ .pmpro_admin .widgets-holder-wrap .widget-title { }
88
+ .pmpro_admin .widgets-holder-wrap .widget-title h4 { }
89
+ .pmpro_admin .widgets-holder-wrap .widget-title .status-label {display: block; float: left; margin: 0 5px 0 0; width: 10px;
90
+ height: 10px; overflow: hidden; border-radius: 10px; -moz-border-radius: 10px; -webkit-border-radius: 10px; border: 1px solid #DFDFDF; text-indent: -9999em; }
91
+ .pmpro_admin .widgets-holder-wrap .disabled .widget-title .status-label {background: #F00; }
92
+ .pmpro_admin .widgets-holder-wrap .enabled .widget-title .status-label {background: #0C0; }
93
+
94
+ .pmpro_admin .widgets-holder-wrap .widget-title .version {position: absolute; top: 13px; right: 10px; }
95
+ .pmpro_admin .widgets-holder-wrap .widget-inside .addon-thumb {width: 100px; height: 100px; float: right; margin: 10px 0 0 10px; border: 1px solid #DFDFDF; background: #FFF; padding: 2px;}
96
+
97
+ /*@media (min-width: 1200px) {
98
+ .auto-fold .pmpro_admin .widgets-holder-wrap .widget-inside, .auto-fold .pmpro_admin .widgets-holder-wrap .widget-inside p {height: auto; }
99
+ }
100
+ */
101
+ @media (max-width:900px) {
102
+ .auto-fold .pmpro_admin .widgets-holder-wrap .widget {float: none; width: 100%; }
103
+ .auto-fold .pmpro_admin .widgets-holder-wrap .widget-inside, .auto-fold .pmpro_admin .widgets-holder-wrap .widget-inside p {height: auto; }
104
+ }
105
+
106
+ /* misc */
107
+ .pmpro_lite {color: #AAA;}
108
+ .pmpro_pad20 {padding: 20px !important;}
109
+ .pmpro_red {color: #CC0000;}
110
+ .pmpro_green {color: #00AA00;}
111
+ .ssp_description #description {width: 100%;}
112
+ .top0em {margin-top: 0;}
113
+ h2.nav-tab-wrapper {margin-bottom: 1em; }
114
+
115
+ /* reports */
116
+ .pmpro_reports-holder { }
117
+ .pmpro_clickable {cursor: pointer;}
118
+ .js .postbox.pmpro_clickable h3 {cursor: pointer;}
 
 
 
 
includes/content.php CHANGED
@@ -323,7 +323,7 @@ function pmpro_membership_content_filter($content, $skipcheck = false)
323
  $pmpro_content_message_post = '</div>';
324
 
325
  $sr_search = array("!!levels!!", "!!referrer!!");
326
- $sr_replace = array(pmpro_implodeToEnglish($post_membership_levels_names), site_url($_SERVER['REQUEST_URI']));
327
 
328
  //get the correct message to show at the bottom
329
  if(is_feed())
@@ -472,16 +472,10 @@ add_action('wp', 'pmpro_hide_pages_redirect');
472
  *
473
  * @since 1.8.5.4
474
  */
475
- function pmpro_post_classes( $classes, $class, $post_id ) {
476
-
477
- $post = get_post($post_id);
478
-
479
- if(empty($post))
480
- return $classes;
481
-
482
  $post_levels = array();
483
  $post_levels = pmpro_has_membership_access($post->ID,NULL,true);
484
-
485
  if(!empty($post_levels))
486
  {
487
  if(!empty($post_levels[1]))
@@ -495,42 +489,4 @@ function pmpro_post_classes( $classes, $class, $post_id ) {
495
  }
496
  return $classes;
497
  }
498
- add_filter( 'post_class', 'pmpro_post_classes', 10, 3 );
499
-
500
- /**
501
- * Adds custom classes to the array of body classes.
502
- * Same as the above, but acts on the "queried object" instead of the post global.
503
- *
504
- * pmpro-body-level-required = this post requires at least one level
505
- * pmpro-body-level-1 = this post requires level 1
506
- * pmpro-body-has-access = this post is usually locked, but the current user has access to this post
507
- *
508
- * @param array $classes Classes for the body element.
509
- * @return array
510
- *
511
- * @since 1.8.6.1
512
- */
513
- function pmpro_body_classes( $classes ) {
514
-
515
- $post = get_queried_object();
516
-
517
- if(empty($post))
518
- return $classes;
519
-
520
- $post_levels = array();
521
- $post_levels = pmpro_has_membership_access($post->ID,NULL,true);
522
-
523
- if(!empty($post_levels))
524
- {
525
- if(!empty($post_levels[1]))
526
- {
527
- $classes[] = 'pmpro-body-level-required';
528
- foreach($post_levels[1] as $post_level)
529
- $classes[] = 'pmpro-body-level-' . $post_level[0];
530
- }
531
- if(!empty($post_levels[0]) && $post_levels[0] == true)
532
- $classes[] = 'pmpro-body-has-access';
533
- }
534
- return $classes;
535
- }
536
- add_filter( 'body_class', 'pmpro_body_classes' );
323
  $pmpro_content_message_post = '</div>';
324
 
325
  $sr_search = array("!!levels!!", "!!referrer!!");
326
+ $sr_replace = array(pmpro_implodeToEnglish($post_membership_levels_names), $_SERVER['REQUEST_URI']);
327
 
328
  //get the correct message to show at the bottom
329
  if(is_feed())
472
  *
473
  * @since 1.8.5.4
474
  */
475
+ function pmpro_post_classes( $classes ) {
476
+ global $post;
 
 
 
 
 
477
  $post_levels = array();
478
  $post_levels = pmpro_has_membership_access($post->ID,NULL,true);
 
479
  if(!empty($post_levels))
480
  {
481
  if(!empty($post_levels[1]))
489
  }
490
  return $classes;
491
  }
492
+ add_filter( 'post_class', 'pmpro_post_classes' );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
includes/functions.php CHANGED
@@ -1,2116 +1,2116 @@
1
- <?php
2
- /****************************************************************
3
-
4
- IMPORTANT. PLEASE READ.
5
-
6
- DO NOT EDIT THIS FILE or any other file in the /wp-content/plugins/paid-memberships-pro/ directory.
7
- Doing so could break the PMPro plugin and/or keep you from upgrading this plugin in the future.
8
- We regularly release updates to the plugin, including important security fixes and new features.
9
- You want to be able to upgrade.
10
-
11
- If you were asked to insert code into "your functions.php file", it was meant that you edit the functions.php
12
- in the root folder of your active theme. e.g. /wp-content/themes/twentytwelve/functions.php
13
- You can also create a custom plugin to place customization code into. Instructions are here:
14
- http://www.paidmembershipspro.com/2012/08/create-a-plugin-for-pmpro-customizations/
15
-
16
- Further documentation for customizing Paid Memberships Pro can be found here:
17
- http://www.paidmembershipspro.com/documentation/
18
-
19
- ****************************************************************/
20
- if(!function_exists("sornot"))
21
- {
22
- function sornot($t, $n)
23
- {
24
- if($n == 1)
25
- return $t;
26
- else
27
- return $t . "s";
28
- }
29
- }
30
-
31
- //set up wpdb for the tables we need
32
- function pmpro_setDBTables()
33
- {
34
- global $wpdb;
35
- $wpdb->hide_errors();
36
- $wpdb->pmpro_membership_levels = $wpdb->prefix . 'pmpro_membership_levels';
37
- $wpdb->pmpro_memberships_users = $wpdb->prefix . 'pmpro_memberships_users';
38
- $wpdb->pmpro_memberships_categories = $wpdb->prefix . 'pmpro_memberships_categories';
39
- $wpdb->pmpro_memberships_pages = $wpdb->prefix . 'pmpro_memberships_pages';
40
- $wpdb->pmpro_membership_orders = $wpdb->prefix . 'pmpro_membership_orders';
41
- $wpdb->pmpro_discount_codes = $wpdb->prefix . 'pmpro_discount_codes';
42
- $wpdb->pmpro_discount_codes_levels = $wpdb->prefix . 'pmpro_discount_codes_levels';
43
- $wpdb->pmpro_discount_codes_uses = $wpdb->prefix . 'pmpro_discount_codes_uses';
44
- }
45
- pmpro_setDBTables();
46
-
47
- //from: http://stackoverflow.com/questions/5266945/wordpress-how-detect-if-current-page-is-the-login-page/5892694#5892694
48
- function pmpro_is_login_page() {
49
- return (in_array($GLOBALS['pagenow'], array('wp-login.php', 'wp-register.php')) || is_page("login"));
50
- }
51
-
52
- //thanks: http://wordpress.org/support/topic/is_plugin_active
53
- function pmpro_is_plugin_active( $plugin ) {
54
- return in_array( $plugin, (array) get_option( 'active_plugins', array() ) );
55
- }
56
-
57
- //scraping - override n if you have more than 1 group of matches and don't want the first group
58
- function pmpro_getMatches($p, $s, $firstvalue = FALSE, $n = 1)
59
- {
60
- $ok = preg_match_all($p, $s, $matches);
61
-
62
- if(!$ok)
63
- return false;
64
- else
65
- {
66
- if($firstvalue)
67
- return $matches[$n][0];
68
- else
69
- return $matches[$n];
70
- }
71
- }
72
-
73
- function pmpro_br2nl($text, $tags = "br")
74
- {
75
- if(!is_array($tags))
76
- $tags = explode(" ", $tags);
77
-
78
- foreach($tags as $tag)
79
- {
80
- $text = eregi_replace("<" . $tag . "[^>]*>", "\n", $text);
81
- $text = eregi_replace("</" . $tag . "[^>]*>", "\n", $text);
82
- }
83
-
84
- return($text);
85
- }
86
-
87
- function pmpro_getOption($s, $force = false)
88
- {
89
- if(get_option("pmpro_" . $s))
90
- return get_option("pmpro_" . $s);
91
- else
92
- return "";
93
- }
94
-
95
- function pmpro_setOption($s, $v = NULL)
96
- {
97
- //no value is given, set v to the p var
98
- if($v === NULL && isset($_POST[$s]))
99
- $v = $_POST[$s];
100
-
101
- if(is_array($v))
102
- $v = implode(",", $v);
103
- else
104
- $v = trim($v);
105
-
106
- return update_option("pmpro_" . $s, $v);
107
- }
108
-
109
- function pmpro_get_slug($post_id)
110
- {
111
- global $pmpro_slugs, $wpdb;
112
-
113
- //make sure post id is int for security
114
- $post_id = intval($post_id);
115
-
116
- if(!$pmpro_slugs[$post_id])
117
- $pmpro_slugs[$post_id] = $wpdb->get_var("SELECT post_name FROM $wpdb->posts WHERE ID = '" . $post_id . "' LIMIT 1");
118
-
119
- return $pmpro_slugs[$post_id];
120
- }
121
-
122
- function pmpro_url($page = NULL, $querystring = "", $scheme = NULL)
123
- {
124
- global $besecure;
125
- $besecure = apply_filters("besecure", $besecure);
126
-
127
- if(!$scheme && $besecure)
128
- $scheme = "https";
129
- elseif(!$scheme)
130
- $scheme = "http";
131
-
132
- if(!$page)
133
- $page = "levels";
134
-
135
- global $pmpro_pages;
136
-
137
- //start with the permalink
138
- $url = get_permalink($pmpro_pages[$page]);
139
-
140
- //WPML/etc support
141
- if(function_exists("icl_object_id") && defined("ICL_LANGUAGE_CODE"))
142
- {
143
- $trans_id = icl_object_id($pmpro_pages[$page], "page", false, ICL_LANGUAGE_CODE);
144
- if(!empty($trans_id))
145
- {
146
- $url = get_permalink($trans_id);
147
- }
148
- }
149
-
150
- //figure out querystring
151
- if(strpos($url, "?"))
152
- $querystring = str_replace("?", "&", $querystring);
153
- $url .= $querystring;
154
-
155
- //figure out scheme
156
- if(is_ssl())
157
- $url = str_replace("http:", "https:", $url);
158
-
159
- return $url;
160
- }
161
-
162
- function pmpro_isLevelFree(&$level)
163
- {
164
- if(!empty($level) && $level->initial_payment <= 0 && $level->billing_amount <= 0 && $level->trial_amount <= 0)
165
- return true;
166
- else
167
- return false;
168
- }
169
-
170
- function pmpro_isLevelRecurring(&$level)
171
- {
172
- if(!empty($level) && ($level->billing_amount > 0 || $level->trial_amount > 0))
173
- return true;
174
- else
175
- return false;
176
- }
177
-
178
- function pmpro_isLevelTrial(&$level)
179
- {
180
- if($level->trial_limit > 0)
181
- {
182
- return true;
183
- }
184
- else
185
- return false;
186
- }
187
-
188
- function pmpro_isLevelExpiring(&$level)
189
- {
190
- if($level->expiration_number > 0)
191
- return true;
192
- else
193
- return false;
194
- }
195
-
196
- function pmpro_getLevelCost(&$level, $tags = true, $short = false)
197
- {
198
- //initial payment
199
- if(!$short)
200
- $r = sprintf(__('The price for membership is <strong>%s</strong> now', 'pmpro'), pmpro_formatPrice($level->initial_payment));
201
- else
202
- $r = sprintf(__('<strong>%s</strong> now', 'pmpro'), pmpro_formatPrice($level->initial_payment));
203
-
204
- //recurring part
205
- if($level->billing_amount != '0.00')
206
- {
207
- if($level->billing_limit > 1)
208
- {
209
- if($level->cycle_number == '1')
210
- {
211
- $r .= sprintf(__(' and then <strong>%s per %s for %d more %s</strong>.', 'pmpro'), pmpro_formatPrice($level->billing_amount), pmpro_translate_billing_period($level->cycle_period), $level->billing_limit, pmpro_translate_billing_period($level->cycle_period, $level->billing_limit));
212
- }
213
- else
214
- {
215
- $r .= sprintf(__(' and then <strong>%s every %d %s for %d more %s</strong>.', 'pmpro'), pmpro_formatPrice($level->billing_amount), $level->cycle_number, pmpro_translate_billing_period($level->cycle_period, $level->cycle_number), $level->billing_limit, pmpro_translate_billing_period($level->cycle_period, $level->billing_limit));
216
- }
217
- }
218
- elseif($level->billing_limit == 1)
219
- {
220
- $r .= sprintf(__(' and then <strong>%s after %d %s</strong>.', 'pmpro'), pmpro_formatPrice($level->billing_amount), $level->cycle_number, pmpro_translate_billing_period($level->cycle_period, $level->cycle_number));
221
- }
222
- else
223
- {
224
- if( $level->billing_amount === $level->initial_payment ) {
225
- if($level->cycle_number == '1')
226
- {
227
- if(!$short)
228
- $r = sprintf(__('The price for membership is <strong>%s per %s</strong>.', 'pmpro'), pmpro_formatPrice($level->initial_payment), pmpro_translate_billing_period($level->cycle_period) );
229
- else
230
- $r = sprintf(__('<strong>%s per %s</strong>.', 'pmpro'), pmpro_formatPrice($level->initial_payment), pmpro_translate_billing_period($level->cycle_period) );
231
- }
232
- else
233
- {
234
- if(!$short)
235
- $r = sprintf(__('The price for membership is <strong>%s every %d %s</strong>.', 'pmpro'), pmpro_formatPrice($level->initial_payment), $level->cycle_number, pmpro_translate_billing_period($level->cycle_period, $level->cycle_number) );
236
- else
237
- $r = sprintf(__('<strong>%s every %d %s</strong>.', 'pmpro'), pmpro_formatPrice($level->initial_payment), $level->cycle_number, pmpro_translate_billing_period($level->cycle_period, $level->cycle_number) );
238
- }
239
- } else {
240
- if($level->cycle_number == '1')
241
- {
242
- $r .= sprintf(__(' and then <strong>%s per %s</strong>.', 'pmpro'), pmpro_formatPrice($level->billing_amount), pmpro_translate_billing_period($level->cycle_period));
243
- }
244
- else
245
- {
246
- $r .= sprintf(__(' and then <strong>%s every %d %s</strong>.', 'pmpro'), pmpro_formatPrice($level->billing_amount), $level->cycle_number, pmpro_translate_billing_period($level->cycle_period, $level->cycle_number));
247
- }
248
- }
249
- }
250
- }
251
- else
252
- $r .= '.';
253
-
254
- //add a space
255
- $r .= ' ';
256
-
257
- //trial part
258
- if($level->trial_limit)
259
- {
260
- if($level->trial_amount == '0.00')
261
- {
262
- if($level->trial_limit == '1')
263
- {
264
- $r .= ' ' . __('After your initial payment, your first payment is Free.', 'pmpro');
265
- }
266
- else
267
- {
268
- $r .= ' ' . sprintf(__('After your initial payment, your first %d payments are Free.', 'pmpro'), $level->trial_limit);
269
- }
270
- }
271
- else
272
- {
273
- if($level->trial_limit == '1')
274
- {
275
- $r .= ' ' . sprintf(__('After your initial payment, your first payment will cost %s.', 'pmpro'), pmpro_formatPrice($level->trial_amount));
276
- }
277
- else
278
- {
279
- $r .= ' ' . sprintf(__('After your initial payment, your first %d payments will cost %s.', 'pmpro'), $level->trial_limit, pmpro_formatPrice($level->trial_amount));
280
- }
281
- }
282
- }
283
-
284
- //taxes part
285
- $tax_state = pmpro_getOption("tax_state");
286
- $tax_rate = pmpro_getOption("tax_rate");
287
-
288
- if($tax_state && $tax_rate && !pmpro_isLevelFree($level))
289
- {
290
- $r .= sprintf(__('Customers in %s will be charged %s%% tax.', 'pmpro'), $tax_state, round($tax_rate * 100, 2));
291
- }
292
-
293
- if(!$tags)
294
- $r = strip_tags($r);
295
-
296
- $r = apply_filters("pmpro_level_cost_text", $r, $level, $tags, $short); //passing $tags and $short since v1.8
297
- return $r;
298
- }
299
-
300
- function pmpro_getLevelExpiration(&$level)
301
- {
302
- if($level->expiration_number)
303
- {
304
- $expiration_text = sprintf(__("Membership expires after %d %s.", "pmpro"), $level->expiration_number, pmpro_translate_billing_period($level->expiration_period, $level->expiration_number));
305
- }
306
- else
307
- $expiration_text = "";
308
-
309
- $expiration_text = apply_filters("pmpro_level_expiration_text", $expiration_text, $level);
310
- return $expiration_text;
311
- }
312
-
313
- function pmpro_hideAds()
314
- {
315
- global $pmpro_display_ads;
316
- return !$pmpro_display_ads;
317
- }
318
-
319
- function pmpro_displayAds()
320
- {
321
- global $pmpro_display_ads;
322
- return $pmpro_display_ads;
323
- }
324
-
325
- function pmpro_next_payment($user_id = NULL, $order_status = "success", $format = "timestamp")
326
- {
327
- global $wpdb, $current_user;
328
- if(!$user_id)
329
- $user_id = $current_user->ID;
330
-
331
- if(!$user_id)
332
- $r = false;
333
- else
334
- {
335
- //get last order
336
- $order = new MemberOrder();
337
- $order->getLastMemberOrder($user_id, $order_status);
338
-
339
- //get current membership level
340
- $level = pmpro_getMembershipLevelForUser($user_id);
341
-
342
- if(!empty($order) && !empty($order->id) && !empty($level) && !empty($level->id) && !empty($level->cycle_number))
343
- {
344
- //last payment date
345
- $lastdate = date("Y-m-d", $order->timestamp);
346
-
347
- //next payment date
348
- $nextdate = $wpdb->get_var("SELECT UNIX_TIMESTAMP('" . $lastdate . "' + INTERVAL " . $level->cycle_number . " " . $level->cycle_period . ")");
349
-
350
- $r = $nextdate;
351
- }
352
- else
353
- {
354
- //no order or level found, or level was not recurring
355
- $r = false;
356
- }
357
- }
358
-
359
- /**
360
- * Filter the next payment date.
361
- *
362
- * @since 1.8.5
363
- *
364
- * @param mixed $r false or the next payment date timestamp
365
- * @param int $user_id The user id to get the next payment date for
366
- * @param string $order_status Status or array of statuses to find the last order based on.
367
- */
368
- $r = apply_filters('pmpro_next_payment', $r, $user_id, $order_status);
369
-
370
- //return in desired format
371
- if($r === false)
372
- return false; //always return false when no date found
373
- elseif($format == "timestamp")
374
- return $r;
375
- elseif($format == "date_format")
376
- return date(get_option('date_format'), $r);
377
- else
378
- return date($format, $r); //assume a PHP date format
379
- }
380
-
381
- if(!function_exists("last4"))
382
- {
383
- function last4($t)
384
- {
385
- return substr($t, strlen($t) - 4, 4);
386
- }
387
- }
388
-
389
- if(!function_exists("hideCardNumber"))
390
- {
391
- function hideCardNumber($c, $dashes = true)
392
- {
393
- if($c)
394
- {
395
- if($dashes)
396
- return "XXXX-XXXX-XXXX-" . substr($c, strlen($c) - 4, 4);
397
- else
398
- return "XXXXXXXXXXXX" . substr($c, strlen($c) - 4, 4);
399
- }
400
- else
401
- {
402
- return "";
403
- }
404
- }
405
- }
406
-
407
- //check for existing functions since we didn't use a prefix for this function
408
- if(!function_exists("cleanPhone"))
409
- {
410
- /**
411
- * Function to remove special characters from a phone number.
412
- * NOTE: Could probably replace with preg_replace("[^0-9]", "", $phone)
413
- *
414
- * @since 1.0
415
- *
416
- * @param string $phone The phone number to clean.
417
- */
418
- function cleanPhone($phone)
419
- {
420
- //if a + is passed, just pass it along
421
- if(strpos($phone, "+") !== false)
422
- return $phone;
423
- //clean the phone
424
- $phone = str_replace("-", "", $phone);
425
- $phone = str_replace(".", "", $phone);
426
- $phone = str_replace("(", "", $phone);
427
- $phone = str_replace(")", "", $phone);
428
- $phone = str_replace(" ", "", $phone);
429
- return $phone;
430
- }
431
- }
432
-
433
- //check for existing functions since we didn't use a prefix for this function
434
- if(!function_exists("formatPhone"))
435
- {
436
- /**
437
- * Function to format a phone number.
438
- *
439
- * @since 1.0
440
- *
441
- * @param string $phone The phone number to format.
442
- */
443
- function formatPhone($phone)
444
- {
445
- $r = cleanPhone($phone);
446
-
447
- if(strlen($r) == 11)
448
- $r = substr($r, 0, 1) . " (" . substr($r, 1, 3) . ") " . substr($r, 4, 3) . "-" . substr($r, 7, 4);
449
- elseif(strlen($r) == 10)
450
- $r = "(" . substr($r, 0, 3) . ") " . substr($r, 3, 3) . "-" . substr($r, 6, 4);
451
- elseif(strlen($r) == 7)
452
- $r = substr($r, 0, 3) . "-" . substr($r, 3, 4);
453
-
454
- /**
455
- * Filter to do more or less cleaning of phone numbers.
456
- *
457
- * @since 1.8.4.4
458
- *
459
- * @param string $r The formatted phone number.
460
- * @param string $phone The original phone number.
461
- */
462
- return apply_filters('pmpro_format_phone', $r, $phone);
463
- }
464
- }
465
-
466
- function pmpro_showRequiresMembershipMessage()
467
- {
468
- //TODO $current_user $post_membership_levels_names are undefined variables
469
- //get the correct message
470
- if(is_feed())
471
- {
472
- $content = pmpro_getOption("rsstext");
473
- $content = str_replace("!!levels!!", implode(", ", $post_membership_levels_names), $content);
474
- }
475
- elseif($current_user->ID)
476
- {
477
- //not a member
478
- $content = pmpro_getOption("nonmembertext");
479
- $content = str_replace("!!levels!!", implode(", ", $post_membership_levels_names), $content);
480
- }
481
- else
482
- {
483
- //not logged in!
484
- $content = pmpro_getOption("notloggedintext");
485
- $content = str_replace("!!levels!!", implode(", ", $post_membership_levels_names), $content);
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
-
514
- if(empty($user_id)) //no user_id passed, check the current user
515
- {
516
- $user_id = $current_user->ID;
517
- $membership_levels = $current_user->membership_levels;
518
- }
519
- elseif(is_numeric($user_id)) //get membership levels for given user
520
- {
521
- $membership_levels = pmpro_getMembershipLevelsForUser($user_id);
522
- }
523
- else
524
- return false; //invalid user_id
525
-
526
- if($levels === "0" || $levels === 0) //if 0 was passed, return true if they have no level and false if they have any
527
- {
528
- $return = empty($membership_levels);
529
- }
530
- elseif(empty($levels)) //if no level var was passed, we're just checking if they have any level
531
- {
532
- $return = !empty($membership_levels);
533
- }
534
- else
535
- {
536
- if(!is_array($levels)) //make an array out of a single element so we can use the same code
537
- {
538
- $levels = array($levels);
539
- }
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
- {
558
- foreach($levels as $level)
559
- {
560
- if(strtoupper($level) == "L")
561
- {
562
- //checking if user is logged in
563
- if(!empty($user_id) && $user_id == $current_user->ID)
564
- $return = true;
565
- }
566
- elseif(strtoupper($level) == "-L")
567
- {
568
- //checking if user is logged out
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
- {
578
- //checking a level id
579
- $level_obj = pmpro_getLevel(is_numeric($level) ? abs(intval($level)) : $level); //make sure our level is in a proper format
580
- if(empty($level_obj)){continue;} //invalid level
581
- $found_level = false;
582
- foreach($membership_levels as $membership_level)
583
- {
584
- if($membership_level->id == $level_obj->id) //found a match
585
- {
586
- $found_level = true;
587
- }
588
- }
589
-
590
- if(is_numeric($level) && intval($level) < 0 && !$found_level) //checking for the absence of this level and they don't have it
591
- {
592
- $return = true;
593
- }
594
- elseif(is_numeric($level) && intval($level) > 0 && $found_level) //checking for the presence of this level and they have it
595
- {
596
- $return = true;
597
- }
598
- elseif(!is_numeric($level)) //if a level name was passed
599
- $return = $found_level;
600
- }
601
- }
602
- }
603
- }
604
-
605
- $return = apply_filters("pmpro_has_membership_level", $return, $user_id, $levels);
606
- return $return;
607
- }
608
-
609
- /* pmpro_changeMembershipLevel() creates or updates the membership level of the given user to the given level.
610
- *
611
- * $level may either be the ID or name of the desired membership_level.
612
- * If $user_id is omitted, the value will be retrieved from $current_user.
613
- *
614
- * Return values:
615
- * Success returns boolean true.
616
- * Failure returns boolean false.
617
- */
618
- function pmpro_changeMembershipLevel($level, $user_id = NULL, $old_level_status = 'inactive')
619
- {
620
- global $wpdb;
621
- global $current_user, $pmpro_error;
622
-
623
- if(empty($user_id))
624
- {
625
- $user_id = $current_user->ID;
626
- }
627
-
628
- if(empty($user_id))
629
- {
630
- $pmpro_error = __("User ID not found.", "pmpro");
631
- return false;
632
- }
633
-
634
- //make sure user id is int for security
635
- $user_id = intval($user_id);
636
-
637
- if(empty($level)) //cancelling membership
638
- {
639
- $level = 0;
640
- }
641
- else if(is_array($level))
642
- {
643
- //custom level
644
- }
645
- else
646
- {
647
- $level_obj = pmpro_getLevel($level);
648
- if(empty($level_obj))
649
- {
650
- $pmpro_error = __("Invalid level.", "pmpro");
651
- return false;
652
- }
653
- $level = $level_obj->id;
654
- }
655
-
656
- //if it's a custom level, they're changing
657
- if(!is_array($level))
658
- {
659
- //are they even changing?
660
- if(pmpro_hasMembershipLevel($level, $user_id)) {
661
- $pmpro_error = __("not changing?", "pmpro");
662
- return false; //not changing
663
- }
664
- }
665
-
666
- //get all active membershipships for this user
667
- $old_levels = pmpro_getMembershipLevelsForUser($user_id);
668
-
669
- //deactivate old memberships based on the old_level_status passed in (updates pmpro_memberships_users table)
670
- if($old_levels)
671
- {
672
- foreach($old_levels as $old_level) {
673
-
674
- $sql = "UPDATE $wpdb->pmpro_memberships_users SET `status`='$old_level_status', `enddate`='" . current_time('mysql') . "' WHERE `id`=".$old_level->subscription_id;
675
-
676
- if(!$wpdb->query($sql))
677
- {
678
- $pmpro_error = __("Error interacting with database", "pmpro") . ": ".(mysql_errno()?mysql_error():'unavailable');
679
-
680
- return false;
681
- }
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)
702
- $pmpro_cancel_previous_subscriptions = false;
703
- $pmpro_cancel_previous_subscriptions = apply_filters("pmpro_cancel_previous_subscriptions", $pmpro_cancel_previous_subscriptions);
704
-
705
- //cancel any other subscriptions they have (updates pmpro_membership_orders table)
706
- if($pmpro_cancel_previous_subscriptions)
707
- {
708
- $other_order_ids = $wpdb->get_col("SELECT id FROM $wpdb->pmpro_membership_orders WHERE user_id = '" . $user_id . "' AND status = 'success' ORDER BY id DESC");
709
-
710
- foreach($other_order_ids as $order_id)
711
- {
712
- $c_order = new MemberOrder($order_id);
713
- $c_order->cancel();
714
-
715
- if(!empty($c_order->error))
716
- $pmpro_error = $c_order->error;
717
- }
718
- }
719
-
720
- //insert current membership
721
- if(!empty($level)) //are we getting a new one or just cancelling the old ones
722
- {
723
- if(is_array($level))
724
- {
725
- //make sure the dates are in good formats
726
- if($level['startdate'] != current_time('mysql') && $level['startdate'] != "NULL" && substr($level['startdate'], 0, 1) != "'")
727
- $level['startdate'] = "'" . $level['startdate'] . "'";
728
-
729
- if($level['enddate'] != current_time('mysql') && $level['enddate'] != "NULL" && substr($level['enddate'], 0, 1) != "'")
730
- $level['enddate'] = "'" . $level['enddate'] . "'";
731
-
732
- //Better support mySQL Strict Mode by passing a proper enum value for cycle_period
733
- if ($level['cycle_period'] == '') $level['cycle_period'] = 0;
734
-
735
- $sql = "INSERT INTO $wpdb->pmpro_memberships_users (user_id, membership_id, code_id, initial_payment, billing_amount, cycle_number, cycle_period, billing_limit, trial_amount, trial_limit, startdate, enddate)
736
- VALUES('" . $level['user_id'] . "',
737
- '" . $level['membership_id'] . "',
738
- '" . intval($level['code_id']) . "',
739
- '" . $level['initial_payment'] . "',
740
- '" . $level['billing_amount'] . "',
741
- '" . $level['cycle_number'] . "',
742
- '" . $level['cycle_period'] . "',
743
- '" . $level['billing_limit'] . "',
744
- '" . $level['trial_amount'] . "',
745
- '" . $level['trial_limit'] . "',
746
- " . $level['startdate'] . ",
747
- " . $level['enddate'] . ")";
748
-
749
- if(!$wpdb->query($sql))
750
- {
751
- $pmpro_error = __("Error interacting with database", "pmpro") . ": ".(mysql_errno()?mysql_error():'unavailable');
752
- return false;
753
- }
754
- }
755
- else
756
- {
757
- $sql = "INSERT INTO $wpdb->pmpro_memberships_users (user_id, membership_id, code_id, initial_payment, billing_amount, cycle_number, cycle_period, billing_limit, trial_amount, trial_limit, startdate, enddate)
758
- VALUES (
759
- '" . $user_id . "',
760
- '" . $level . "',
761
- '0',
762
- '0',
763
- '0',
764
- '0',
765
- '0',
766
- '0',
767
- '0',
768
- '0',
769
- '" . current_time('mysql') . "',
770
- '0000-00-00 00:00:00'
771
- )";
772
-
773
- if(!$wpdb->query($sql))
774
- {
775
- $pmpro_error = __("Error interacting with database", "pmpro") . ": ".(mysql_errno()?mysql_error():'unavailable');
776
- return false;
777
- }
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
-
798
- /* pmpro_toggleMembershipCategory() creates or deletes a linking entry between the membership level and post category tables.
799
- *
800
- * $level may either be the ID or name of the desired membership_level.
801
- * $category must be a valid post category ID.
802
- *
803
- * Return values:
804
- * Success returns boolean true.
805
- * Failure returns a string containing the error message.
806
- */
807
- function pmpro_toggleMembershipCategory( $level, $category, $value )
808
- {
809
- global $wpdb;
810
- $category = intval($category);
811
-
812
- if ( ($level = intval($level)) <= 0 )
813
- {
814
- $safe = addslashes($level);
815
- if ( ($level = intval($wpdb->get_var("SELECT id FROM {$wpdb->pmpro_membership_levels} WHERE name = '$safe' LIMIT 1"))) <= 0 )
816
- {
817
- return __("Membership level not found.", "pmpro");
818
- }
819
- }
820
-
821
- if ( $value )
822
- {
823
- $sql = "REPLACE INTO {$wpdb->pmpro_memberships_categories} (`membership_id`,`category_id`) VALUES ('$level','$category')";
824
- $wpdb->query($sql);
825
- if(mysql_errno()) return mysql_error();
826
- }
827
- else
828
- {
829
- $sql = "DELETE FROM {$wpdb->pmpro_memberships_categories} WHERE `membership_id` = '$level' AND `category_id` = '$category' LIMIT 1";
830
- $wpdb->query($sql);
831
- if(mysql_errno()) return mysql_error();
832
- }
833
-
834
- return true;
835
- }
836
-
837
- /* pmpro_updateMembershipCategories() ensures that all those and only those categories given
838
- * are associated with the given membership level.
839
- *
840
- * $level is a valid membership level ID or name
841
- * $categories is an array of post category IDs
842
- *
843
- * Return values:
844
- * Success returns boolean true.
845
- * Failure returns a string containing the error message.
846
- */
847
- function pmpro_updateMembershipCategories($level, $categories)
848
- {
849
- global $wpdb;
850
-
851
- if(!is_numeric($level))
852
- {
853
- $level = $wpdb->get_var("SELECT id FROM $wpdb->pmpro_membership_levels WHERE name = '" . esc_sql($level) . "' LIMIT 1");
854
- if(empty($level))
855
- {
856
- return __("Membership level not found.", "pmpro");
857
- }
858
- }
859
-
860
- // remove all existing links...
861
- $sqlQuery = "DELETE FROM $wpdb->pmpro_memberships_categories WHERE `membership_id` = '" . esc_sql($level) . "'";
862
- $wpdb->query($sqlQuery);
863
- if(mysql_errno()) return mysql_error();
864
-
865
- // add the given links [back?] in...
866
- foreach($categories as $cat)
867
- {
868
- if(is_string($r = pmpro_toggleMembershipCategory( $level, $cat, true)))
869
- {
870
- //uh oh, error
871
- return $r;
872
- }
873
- }
874
-
875
- //all good
876
- return true;
877
- }
878
-
879
- /* pmpro_getMembershipCategories() returns the categories for a given level
880
- *
881
- * $level_id is a valid membership level ID
882
- *
883
- * Return values:
884
- * Success returns boolean true.
885
- * Failure returns boolean false.
886
- */
887
- function pmpro_getMembershipCategories($level_id)
888
- {
889
- $level_id = intval($level_id);
890
-
891
- global $wpdb;
892
- $categories = $wpdb->get_col("SELECT c.category_id
893
- FROM {$wpdb->pmpro_memberships_categories} AS c
894
- WHERE c.membership_id = '" . $level_id . "'");
895
-
896
- return $categories;
897
- }
898
-
899
-
900
- function pmpro_isAdmin($user_id = NULL)
901
- {
902
- global $current_user, $wpdb;
903
- if(!$user_id)
904
- $user_id = $current_user->ID;
905
-
906
- if(!$user_id)
907
- return false;
908
-
909
- $admincap = user_can($user_id, "manage_options");
910
- if($admincap)
911
- return true;
912
- else
913
- return false;
914
- }
915
-
916
- function pmpro_replaceUserMeta($user_id, $meta_keys, $meta_values, $prev_values = NULL)
917
- {
918
- //expects all arrays for last 3 params or all strings
919
- if(!is_array($meta_keys))
920
- {
921
- $meta_keys = array($meta_keys);
922
- $meta_values = array($meta_values);
923
- $prev_values = array($prev_values);
924
- }
925
-
926
- for($i = 0; $i < count($meta_values); $i++)
927
- {
928
- if($prev_values[$i])
929
- {
930
- update_user_meta($user_id, $meta_keys[$i], $meta_values[$i], $prev_values[$i]);
931
- }
932
- else
933
- {
934
- $old_value = get_user_meta($user_id, $meta_keys[$i], true);
935
- if($old_value)
936
- {
937
- update_user_meta($user_id, $meta_keys[$i], $meta_values[$i], $old_value);
938
- }
939
- else
940
- {
941
- update_user_meta($user_id, $meta_keys[$i], $meta_values[$i]);
942
- }
943
- }
944
- }
945
-
946
- return $i;
947
- }
948
-
949
- function pmpro_getMetavalues($query)
950
- {
951
- global $wpdb;
952
-
953
- $results = $wpdb->get_results($query);
954
- $r = new stdClass();
955
- foreach($results as $result)
956
- {
957
- if(!empty($r))
958
- $r->{$result->key} = $result->value;
959
- }
960
-
961
- return $r;
962
- }
963
-
964
- //function to return the pagination string
965
- function pmpro_getPaginationString($page = 1, $totalitems, $limit = 15, $adjacents = 1, $targetpage = "/", $pagestring = "&pn=")
966
- {
967
- //defaults
968
- if(!$adjacents) $adjacents = 1;
969
- if(!$limit) $limit = 15;
970
- if(!$page) $page = 1;
971
- if(!$targetpage) $targetpage = "/";
972
-
973
- //other vars
974
- $prev = $page - 1; //previous page is page - 1
975
- $next = $page + 1; //next page is page + 1
976
- $lastpage = ceil($totalitems / $limit); //lastpage is = total items / items per page, rounded up.
977
- $lpm1 = $lastpage - 1; //last page minus 1
978
-
979
- /*
980
- Now we apply our rules and draw the pagination object.
981
- We're actually saving the code to a variable in case we want to draw it more than once.
982
- */
983
- $pagination = "";
984
- if($lastpage > 1)
985
- {
986
- $pagination .= "<div class=\"pmpro_pagination\"";
987
- if(!empty($margin) || !empty($padding))
988
- {
989
- $pagination .= " style=\"";
990
- if($margin)
991
- $pagination .= "margin: $margin;";
992
- if($padding)
993
- $pagination .= "padding: $padding;";
994
- $pagination .= "\"";
995
- }
996
- $pagination .= ">";
997
-
998
- //previous button
999
- if ($page > 1)
1000
- $pagination .= "<a href=\"$targetpage$pagestring$prev\">&laquo; prev</a>";
1001
- else
1002
- $pagination .= "<span class=\"disabled\">&laquo; prev</span>";
1003
-
1004
- //pages
1005
- if ($lastpage < 7 + ($adjacents * 2)) //not enough pages to bother breaking it up
1006
- {
1007
- for ($counter = 1; $counter <= $lastpage; $counter++)
1008
- {
1009
- if ($counter == $page)
1010
- $pagination .= "<span class=\"current\">$counter</span>";
1011
- else
1012
- $pagination .= "<a href=\"" . $targetpage . $pagestring . $counter . "\">$counter</a>";
1013
- }
1014
- }
1015
- elseif($lastpage >= 7 + ($adjacents * 2)) //enough pages to hide some
1016
- {
1017
- //close to beginning; only hide later pages
1018
- if($page < 1 + ($adjacents * 3))
1019
- {
1020
- for ($counter = 1; $counter < 4 + ($adjacents * 2); $counter++)
1021
- {
1022
- if ($counter == $page)
1023
- $pagination .= "<span class=\"current\">$counter</span>";
1024
- else
1025
- $pagination .= "<a href=\"" . $targetpage . $pagestring . $counter . "\">$counter</a>";
1026
- }
1027
- $pagination .= "...";
1028
- $pagination .= "<a href=\"" . $targetpage . $pagestring . $lpm1 . "\">$lpm1</a>";
1029
- $pagination .= "<a href=\"" . $targetpage . $pagestring . $lastpage . "\">$lastpage</a>";
1030
- }
1031
- //in middle; hide some front and some back
1032
- elseif($lastpage - ($adjacents * 2) > $page && $page > ($adjacents * 2))
1033
- {
1034
- $pagination .= "<a href=\"" . $targetpage . $pagestring . "1\">1</a>";
1035
- $pagination .= "<a href=\"" . $targetpage . $pagestring . "2\">2</a>";
1036
- $pagination .= "...";
1037
- for ($counter = $page - $adjacents; $counter <= $page + $adjacents; $counter++)
1038
- {
1039
- if ($counter == $page)
1040
- $pagination .= "<span class=\"current\">$counter</span>";
1041
- else
1042
- $pagination .= "<a href=\"" . $targetpage . $pagestring . $counter . "\">$counter</a>";
1043
- }
1044
- $pagination .= "...";
1045
- $pagination .= "<a href=\"" . $targetpage . $pagestring . $lpm1 . "\">$lpm1</a>";
1046
- $pagination .= "<a href=\"" . $targetpage . $pagestring . $lastpage . "\">$lastpage</a>";
1047
- }
1048
- //close to end; only hide early pages
1049
- else
1050
- {
1051
- $pagination .= "<a href=\"" . $targetpage . $pagestring . "1\">1</a>";
1052
- $pagination .= "<a href=\"" . $targetpage . $pagestring . "2\">2</a>";
1053
- $pagination .= "...";
1054
- for ($counter = $lastpage - (1 + ($adjacents * 3)); $counter <= $lastpage; $counter++)
1055
- {
1056
- if ($counter == $page)
1057
- $pagination .= "<span class=\"current\">$counter</span>";
1058
- else
1059
- $pagination .= "<a href=\"" . $targetpage . $pagestring . $counter . "\">$counter</a>";
1060
- }
1061
- }
1062
- }
1063
-
1064
- //next button
1065
- if ($page < $counter - 1)
1066
- $pagination .= "<a href=\"" . $targetpage . $pagestring . $next . "\">next &raquo;</a>";
1067
- else
1068
- $pagination .= "<span class=\"disabled\">next &raquo;</span>";
1069
- $pagination .= "</div>\n";
1070
- }
1071
-
1072
- return $pagination;
1073
-
1074
- }
1075
-
1076
- function pmpro_calculateInitialPaymentRevenue($s = NULL, $l = NULL)
1077
- {
1078
- global $wpdb;
1079
-
1080
- //if we're limiting users by search
1081
- if($s || $l)
1082
- {
1083
- $user_ids_query = "SELECT u.ID FROM $wpdb->users u LEFT JOIN $wpdb->usermeta um ON u.ID = um.user_id LEFT JOIN $wpdb->pmpro_memberships_users mu ON u.ID = mu.user_id WHERE mu.status = 'active' ";
1084
- if($s)
1085
- $user_ids_query .= "AND (u.user_login LIKE '%" . esc_sql($s) . "%' OR u.user_email LIKE '%" . esc_sql($s) . "%' OR um.meta_value LIKE '%$" . esc_sql(s) . "%') ";
1086
- if($l)
1087
- $user_ids_query .= "AND mu.membership_id = '" . esc_sql($l) . "' ";
1088
- }
1089
-
1090
- //query to sum initial payments
1091
- $sqlQuery = "SELECT SUM(initial_payment) FROM $wpdb->pmpro_memberships_users WHERE `status` = 'active' ";
1092
- if(!empty($user_ids_query))
1093
- $sqlQuery .= "AND user_id IN(" . $user_ids_query . ") ";
1094
-
1095
- $total = $wpdb->get_var($sqlQuery);
1096
-
1097
- return (double)$total;
1098
- }
1099
-
1100
- function pmpro_calculateRecurringRevenue($s, $l)
1101
- {
1102
- global $wpdb;
1103
-
1104
- //if we're limiting users by search
1105
- if($s || $l)
1106
- {
1107
- $user_ids_query = "AND user_id IN(SELECT u.ID FROM $wpdb->users u LEFT JOIN $wpdb->usermeta um ON u.ID = um.user_id LEFT JOIN $wpdb->pmpro_memberships_users mu ON u.ID = mu.user_id WHERE mu.status = 'active' ";
1108
- if($s)
1109
- $user_ids_query .= "AND (u.user_login LIKE '%" . esc_sql($s) . "%' OR u.user_email LIKE '%" . esc_sql($s) . "%' OR um.meta_value LIKE '%" . esc_sql($s) . "%') ";
1110
- if($l)
1111
- $user_ids_query .= "AND mu.membership_id = '" . esc_sql($l) . "' ";
1112
- $user_ids_query .= ")";
1113
- }
1114
- else
1115
- $user_ids_query = "";
1116
-
1117
- //4 queries to get annual earnings for each cycle period. currently ignoring trial periods and billing limits.
1118
- $sqlQuery = "
1119
- SELECT SUM((12/cycle_number)*billing_amount) FROM $wpdb->pmpro_memberships_users WHERE status = 'active' AND cycle_period = 'Month' AND cycle_number <> 12 $user_ids_query
1120
- UNION
1121
- SELECT SUM((365/cycle_number)*billing_amount) FROM $wpdb->pmpro_memberships_users WHERE status = 'active' AND cycle_period = 'Day' AND cycle_number <> 365 $user_ids_query
1122
- UNION
1123
- SELECT SUM((52/cycle_number)*billing_amount) FROM $wpdb->pmpro_memberships_users WHERE status = 'active' AND cycle_period = 'Week' AND cycle_number <> 52 $user_ids_query
1124
- UNION
1125
- SELECT SUM(billing_amount) FROM $wpdb->pmpro_memberships_users WHERE status = 'active' AND cycle_period = 'Year' $user_ids_query
1126
- ";
1127
-
1128
- $annual_revenues = $wpdb->get_col($sqlQuery);
1129
-
1130
- $total = 0;
1131
- foreach($annual_revenues as $r)
1132
- {
1133
- $total += $r;
1134
- }
1135
-
1136
- return $total;
1137
- }
1138
-
1139
- function pmpro_generateUsername($firstname = "", $lastname = "", $email = "")
1140
- {
1141
- global $wpdb;
1142
-
1143
- //try first initial + last name, firstname, lastname
1144
- $firstname = preg_replace("/[^A-Za-z]/", "", $firstname);
1145
- $lastname = preg_replace("/[^A-Za-z]/", "", $lastname);
1146
- if($firstname && $lastname)
1147
- {
1148
- $username = substr($firstname, 0, 1) . $lastname;
1149
- }
1150
- elseif($firstname)
1151
- {
1152
- $username = $firstname;
1153
- }
1154
- elseif($lastname)
1155
- {
1156
- $username = $lastname;
1157
- }
1158
-
1159
- //is it taken?
1160
- $taken = $wpdb->get_var("SELECT user_login FROM $wpdb->users WHERE user_login = '" . esc_sql($username) . "' LIMIT 1");
1161
-
1162
- if(!$taken)
1163
- return $username;
1164
-
1165
- //try the beginning of the email address
1166
- $emailparts = explode("@", $email);
1167
- if(is_array($emailparts))
1168
- $email = preg_replace("/[^A-Za-z]/", "", $emailparts[0]);
1169
-
1170
- if(!empty($email))
1171
- {
1172
- $username = $email;
1173
- }
1174
-
1175
- //is this taken? if not, add numbers until it works
1176
- $taken = true;
1177
- $count = 0;
1178
- while($taken)
1179
- {
1180
- //add a # to the end
1181
- if($count)
1182
- {
1183
- $username = preg_replace("/[0-9]/", "", $username) . $count;
1184
- }
1185
-
1186
- //taken?
1187
- $taken = $wpdb->get_var("SELECT user_login FROM $wpdb->users WHERE user_login = '" . esc_sql($username) . "' LIMIT 1");
1188
-
1189
- //increment the number
1190
- $count++;
1191
- }
1192
-
1193
- //must have a good username now
1194
- return $username;
1195
- }
1196
-
1197
- //get a new random code for discount codes
1198
- function pmpro_getDiscountCode($seed = NULL)
1199
- {
1200
- global $wpdb;
1201
-
1202
- while(empty($code))
1203
- {
1204
- $scramble = md5(AUTH_KEY . current_time('timestamp') . $seed . SECURE_AUTH_KEY);
1205
- $code = substr($scramble, 0, 10);
1206
- $check = $wpdb->get_var("SELECT code FROM $wpdb->pmpro_discount_codes WHERE code = '" . esc_sql($code) . "' LIMIT 1");
1207
- if($check || is_numeric($code))
1208
- $code = NULL;
1209
- }
1210
-
1211
- return strtoupper($code);
1212
- }
1213
-
1214
- //is a discount code valid
1215
- function pmpro_checkDiscountCode($code, $level_id = NULL, $return_errors = false)
1216
- {
1217
- global $wpdb;
1218
-
1219
- $error = false;
1220
-
1221
- //make sure level id is int for security
1222
- $level_id = intval($level_id);
1223
-
1224
- //no code, no code
1225
- if(empty($code))
1226
- $error = __("No code was given to check.", "pmpro");
1227
-
1228
- //get code from db
1229
- if(!$error)
1230
- {
1231
- $dbcode = $wpdb->get_row("SELECT *, UNIX_TIMESTAMP(starts) as starts, UNIX_TIMESTAMP(expires) as expires FROM $wpdb->pmpro_discount_codes WHERE code ='" . esc_sql($code) . "' LIMIT 1");
1232
-
1233
- //did we find it?
1234
- if(empty($dbcode->id))
1235
- $error = __("The discount code could not be found.", "pmpro");
1236
- }
1237
-
1238
- //check if the code has started
1239
- if(!$error)
1240
- {
1241
- //fix the date timestamps
1242
- $dbcode->starts = strtotime(date("m/d/Y", $dbcode->starts));
1243
- $dbcode->expires = strtotime(date("m/d/Y", $dbcode->expires));
1244
-
1245
- //today
1246
- $today = strtotime(date("m/d/Y 00:00:00", current_time("timestamp")));
1247
-
1248
- //has this code started yet?
1249
- if(!empty($dbcode->starts) && $dbcode->starts > $today)
1250
- $error = sprintf(__("This discount code goes into effect on %s.", "pmpro"), date(get_option('date_format'), $dbcode->starts));
1251
- }
1252
-
1253
- //check if the code is expired
1254
- if(!$error)
1255
- {
1256
- if(!empty($dbcode->expires) && $dbcode->expires < $today)
1257
- $error = sprintf(__("This discount code expired on %s.", "pmpro"), date(get_option('date_format'), $dbcode->expires));
1258
- }
1259
-
1260
- //have we run out of uses?
1261
- if(!$error)
1262
- {
1263
- if($dbcode->uses > 0)
1264
- {
1265
- $used = $wpdb->get_var("SELECT COUNT(*) FROM $wpdb->pmpro_discount_codes_uses WHERE code_id = '" . $dbcode->id . "'");
1266
- if($used >= $dbcode->uses)
1267
- $error = __("This discount code is no longer valid.", "pmpro");
1268
- }
1269
- }
1270
-
1271
- //if a level was passed check if this code applies
1272
- if(!$error)
1273
- {
1274
- $pmpro_check_discount_code_levels = apply_filters("pmpro_check_discount_code_levels", true, $dbcode->id);
1275
- if(!empty($level_id) && $pmpro_check_discount_code_levels)
1276
- {
1277
- $code_level = $wpdb->get_row("SELECT l.id, cl.*, l.name, l.description, l.allow_signups FROM $wpdb->pmpro_discount_codes_levels cl LEFT JOIN $wpdb->pmpro_membership_levels l ON cl.level_id = l.id WHERE cl.code_id = '" . $dbcode->id . "' AND cl.level_id = '" . $level_id . "' LIMIT 1");
1278
-
1279
- if(empty($code_level))
1280
- $error = __("This discount code does not apply to this membership level.", "pmpro");
1281
- }
1282
- }
1283
-
1284
- //allow filter
1285
- $pmpro_check_discount_code = apply_filters("pmpro_check_discount_code", !$error, $dbcode, $level_id, $code);
1286
- if(is_string($pmpro_check_discount_code))
1287
- $error = $pmpro_check_discount_code; //string returned, this is an error
1288
- elseif(!$pmpro_check_discount_code && !$error)
1289
- $error = true; //no error before, but filter returned error
1290
- elseif($pmpro_check_discount_code)
1291
- $error = false; //filter is true, so error false
1292
-
1293
- //return
1294
- if($error)
1295
- {
1296
- //there was an error
1297
- if(!empty($return_errors))
1298
- return array(false, $error);
1299
- else
1300
- return false;
1301
- }
1302
- else
1303
- {
1304
- //guess we're all good
1305
- if(!empty($return_errors))
1306
- return array(true, __("This discount code is okay.", "pmpro"));
1307
- else
1308
- return true;
1309
- }
1310
- }
1311
-
1312
- function pmpro_no_quotes($s, $quotes = array("'", '"'))
1313
- {
1314
- return str_replace($quotes, "", $s);
1315
- }
1316
-
1317
- //from: http://www.php.net/manual/en/function.implode.php#86845
1318
- function pmpro_implodeToEnglish($array)
1319
- {
1320
- // sanity check
1321
- if (!$array || !count ($array))
1322
- return '';
1323
-
1324
- // get last element
1325
- $last = array_pop ($array);
1326
-
1327
- // if it was the only element - return it
1328
- if (!count ($array))
1329
- return $last;
1330
-
1331
- return implode (', ', $array).' ' . __('and', 'pmpro') . ' '.$last;
1332
- }
1333
-
1334
- //from yoast wordpress seo
1335
- function pmpro_text_limit( $text, $limit, $finish = '&hellip;')
1336
- {
1337
- if( strlen( $text ) > $limit ) {
1338
- $text = substr( $text, 0, $limit );
1339
- $text = substr( $text, 0, - ( strlen( strrchr( $text,' ') ) ) );
1340
- $text .= $finish;
1341
- }
1342
- return $text;
1343
- }
1344
-
1345
- /* pmpro_getMembershipLevelForUser() returns the first active membership level for a user
1346
- *
1347
- * If $user_id is omitted, the value will be retrieved from $current_user.
1348
- *
1349
- * Return values:
1350
- * Success returns the level object.
1351
- * Failure returns false.
1352
- */
1353
- function pmpro_getMembershipLevelForUser($user_id = NULL, $force = false)
1354
- {
1355
- if(empty($user_id))
1356
- {
1357
- global $current_user;
1358
- $user_id = $current_user->ID;
1359
- }
1360
-
1361
- if(empty($user_id))
1362
- {
1363
- return false;
1364
- }
1365
-
1366
- //make sure user id is int for security
1367
- $user_id = intval($user_id);
1368
-
1369
- global $all_membership_levels;
1370
-
1371
- if(isset($all_membership_levels[$user_id]) && !$force)
1372
- {
1373
- return $all_membership_levels[$user_id];
1374
- }
1375
- else
1376
- {
1377
- global $wpdb;
1378
- $all_membership_levels[$user_id] = $wpdb->get_row("SELECT
1379
- l.id AS ID,
1380
- l.id as id,
1381
- mu.id as subscription_id,
1382
- l.name AS name,
1383
- l.description,
1384
- l.expiration_number,
1385
- l.expiration_period,
1386
- mu.initial_payment,
1387
- mu.billing_amount,
1388
- mu.cycle_number,
1389
- mu.cycle_period,
1390
- mu.billing_limit,
1391
- mu.trial_amount,
1392
- mu.trial_limit,
1393
- mu.code_id as code_id,
1394
- UNIX_TIMESTAMP(startdate) as startdate,
1395
- UNIX_TIMESTAMP(enddate) as enddate
1396
- FROM {$wpdb->pmpro_membership_levels} AS l
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
- }
1415
-
1416
- /* pmpro_getMembershipLevelsForUser() returns the membership levels for a user
1417
- *
1418
- * If $user_id is omitted, the value will be retrieved from $current_user.
1419
- * By default it only includes actvie memberships.
1420
- *
1421
- * Return values:
1422
- * Success returns an array of level objects.
1423
- * Failure returns false.
1424
- */
1425
- function pmpro_getMembershipLevelsForUser($user_id = NULL, $include_inactive = false)
1426
- {
1427
- if(empty($user_id))
1428
- {
1429
- global $current_user;
1430
- $user_id = $current_user->ID;
1431
- }
1432
-
1433
- if(empty($user_id))
1434
- {
1435
- return false;
1436
- }
1437
-
1438
- //make sure user id is int for security
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,
1447
- l.name,
1448
- l.description,
1449
- l.expiration_number,
1450
- l.expiration_period,
1451
- mu.initial_payment,
1452
- mu.billing_amount,
1453
- mu.cycle_number,
1454
- mu.cycle_period,
1455
- mu.billing_limit,
1456
- mu.trial_amount,
1457
- mu.trial_limit,
1458
- mu.code_id as code_id,
1459
- UNIX_TIMESTAMP(startdate) as startdate,
1460
- UNIX_TIMESTAMP(enddate) as enddate
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
1479
- *
1480
- * $level may be the level id or name
1481
- *
1482
- * Return values:
1483
- * Success returns the level object.
1484
- * Failure returns false.
1485
- */
1486
- function pmpro_getLevel($level)
1487
- {
1488
- global $pmpro_levels;
1489
-
1490
- if(is_object($level) && !empty($level->id))
1491
- $level = $level->id;
1492
-
1493
- //was a name passed? (Todo: make sure level names have at least one non-numeric character.
1494
- if(is_numeric($level))
1495
- {
1496
- $level_id = intval($level);
1497
- if(isset($pmpro_levels[$level_id]))
1498
- {
1499
- return $pmpro_levels[$level_id];
1500
- }
1501
- else
1502
- {
1503
- global $wpdb;
1504
- $pmpro_levels[$level_id] = $wpdb->get_row("SELECT * FROM $wpdb->pmpro_membership_levels WHERE id = '" . $level_id . "' LIMIT 1");
1505
- return $pmpro_levels[$level_id];
1506
- }
1507
- }
1508
- else
1509
- {
1510
- global $wpdb;
1511
- $level_obj = $wpdb->get_row("SELECT * FROM $wpdb->pmpro_membership_levels WHERE name = '" . esc_sql($level) . "' LIMIT 1");
1512
-
1513
- if(!empty($level_obj))
1514
- $level_id = $level_obj->id;
1515
- else
1516
- return false;
1517
-
1518
- $pmpro_levels[$level_id] = $level_obj;
1519
- return $pmpro_levels[$level_id];
1520
- }
1521
- }
1522
-
1523
- /*
1524
- Function to populate pmpro_levels with all levels. We query the DB every time just to be sure we have the latest.
1525
- This should be called if you want to be sure you get all levels as $pmpro_levels may only have a subset of levels.
1526
- */
1527
- function pmpro_getAllLevels($include_hidden = false, $force = false)
1528
- {
1529
- global $pmpro_levels, $wpdb;
1530
-
1531
- //just use what's cached (doesn't take into account include_hidden setting)
1532
- if(!empty($pmpro_levels) && !$force)
1533
- return $pmpro_levels;
1534
-
1535
- //build query
1536
- $sqlQuery = "SELECT * FROM $wpdb->pmpro_membership_levels ";
1537
- if(!$include_hidden)
1538
- $sqlQuery .= " WHERE allow_signups = 1 ORDER BY id";
1539
-
1540
- //get levels from the DB
1541
- $raw_levels = $wpdb->get_results($sqlQuery);
1542
-
1543
- //lets put them into an array where the key is the id of the level
1544
- $pmpro_levels = array();
1545
- foreach($raw_levels as $raw_level)
1546
- {
1547
- $pmpro_levels[$raw_level->id] = $raw_level;
1548
- }
1549
-
1550
- return $pmpro_levels;
1551
- }
1552
-
1553
- function pmpro_getCheckoutButton($level_id, $button_text = NULL, $classes = NULL)
1554
- {
1555
- if(empty($button_text))
1556
- $button_text = __("Sign Up for !!name!! Now", "pmpro");
1557
-
1558
- if(empty($classes))
1559
- $classes = "pmpro_btn";
1560
-
1561
- if(empty($level_id))
1562
- $r = __("Please specify a level id.", "pmpro");
1563
- else
1564
- {
1565
- //get level
1566
- $level = pmpro_getLevel($level_id);
1567
-
1568
- //replace vars
1569
- $replacements = array(
1570
- "!!id!!" => $level->id,
1571
- "!!name!!" => $level->name,
1572
- "!!description!!" => $level->description,
1573
- "!!confirmation!!" => $level->confirmation,
1574
- "!!initial_payment!!" => $level->initial_payment,
1575
- "!!billing_amount!!" => $level->billing_amount,
1576
- "!!cycle_number!!" => $level->cycle_number,
1577
- "!!cycle_period!!" => $level->cycle_period,
1578
- "!!billing_limit!!" => $level->billing_limit,
1579
- "!!trial_amount!!" => $level->trial_amount,
1580
- "!!trial_limit!!" => $level->trial_limit,
1581
- "!!expiration_number!!" => $level->expiration_number,
1582
- "!!expiration_period!!" => $level->expiration_period
1583
- );
1584
- $button_text = str_replace(array_keys($replacements), $replacements, $button_text);
1585
-
1586
- //button text
1587
- $r = "<a href=\"" . pmpro_url("checkout", "?level=" . $level_id) . "\" class=\"" . $classes . "\">" . $button_text . "</a>";
1588
- }
1589
- return $r;
1590
- }
1591
-
1592
- /**
1593
- * Get the "domain" from a URL. By domain, we mean the host name, minus any subdomains. So just the domain and TLD.
1594
- *
1595
- * @param string $url The URL to parse. (generally pass site_url() in WP)
1596
- * @return string The domain.
1597
- */
1598
- function pmpro_getDomainFromURL($url = NULL)
1599
- {
1600
- $domainparts = parse_url($url);
1601
- $domainparts = explode(".", $domainparts['host']);
1602
- if(count($domainparts) > 1)
1603
- {
1604
- //check for ips
1605
- $isip = true;
1606
- foreach($domainparts as $part)
1607
- {
1608
- if(!is_numeric($part))
1609
- {
1610
- $isip = false;
1611
- break;
1612
- }
1613
- }
1614
-
1615
- if($isip)
1616
- {
1617
- //ip, e.g. 127.1.1.1
1618
- $domain = implode(".", $domainparts);
1619
- }
1620
- else
1621
- {
1622
- //www.something.com, etc.
1623
- $domain = $domainparts[count($domainparts)-2] . "." . $domainparts[count($domainparts)-1];
1624
- }
1625
- }
1626
- else
1627
- {
1628
- //localhost or another single word domain
1629
- $domain = $domainparts[0];
1630
- }
1631
-
1632
- return $domain;
1633
- }
1634
-
1635
- /*
1636
- Get a member's start date... either in general or for a specific level_id.
1637
- */
1638
- if(!function_exists("pmpro_getMemberStartdate"))
1639
- {
1640
- function pmpro_getMemberStartdate($user_id = NULL, $level_id = 0)
1641
- {
1642
- if(empty($user_id))
1643
- {
1644
- global $current_user;
1645
- $user_id = $current_user->ID;
1646
- }
1647
-
1648
- //make sure user and level id are int for security
1649
- $user_id = intval($user_id);
1650
- $level_id = intval($level_id);
1651
-
1652
- global $pmpro_startdates; //for cache
1653
- if(empty($pmpro_startdates[$user_id][$level_id]))
1654
- {
1655
- global $wpdb;
1656
-
1657
- if(!empty($level_id))
1658
- $sqlQuery = "SELECT UNIX_TIMESTAMP(startdate) FROM $wpdb->pmpro_memberships_users WHERE status = 'active' AND membership_id IN(" . esc_sql($level_id) . ") AND user_id = '" . $user_id . "' ORDER BY id LIMIT 1";
1659
- else
1660
- $sqlQuery = "SELECT UNIX_TIMESTAMP(startdate) FROM $wpdb->pmpro_memberships_users WHERE status = 'active' AND user_id = '" . $user_id . "' ORDER BY id LIMIT 1";
1661
-
1662
- $startdate = apply_filters("pmpro_member_startdate", $wpdb->get_var($sqlQuery), $user_id, $level_id);
1663
-
1664
- $pmpro_startdates[$user_id][$level_id] = $startdate;
1665
- }
1666
-
1667
- return $pmpro_startdates[$user_id][$level_id];
1668
- }
1669
- }
1670
-
1671
- /*
1672
- How long has this member been a member
1673
- */
1674
- if(!function_exists("pmpro_getMemberDays"))
1675
- {
1676
- function pmpro_getMemberDays($user_id = NULL, $level_id = 0)
1677
- {
1678
- if(empty($user_id))
1679
- {
1680
- global $current_user;
1681
- $user_id = $current_user->ID;
1682
- }
1683
-
1684
- global $pmpro_member_days;
1685
- if(empty($pmpro_member_days[$user_id][$level_id]))
1686
- {
1687
- $startdate = pmpro_getMemberStartdate($user_id, $level_id);
1688
-
1689
- //check that there was a startdate at all
1690
- if(empty($startdate))
1691
- $pmpro_member_days[$user_id][$level_id] = 0;
1692
- else
1693
- {
1694
- $now = current_time('timestamp');
1695
- $days = ($now - $startdate)/3600/24;
1696
-
1697
- $pmpro_member_days[$user_id][$level_id] = $days;
1698
- }
1699
- }
1700
-
1701
- return $pmpro_member_days[$user_id][$level_id];
1702
- }
1703
- }
1704
-
1705
- //the start of a message handling script
1706
- function pmpro_setMessage($message, $type, $force = false)
1707
- {
1708
- global $pmpro_msg, $pmpro_msgt;
1709
-
1710
- //for now, we only show the first message generated
1711
- if($force || empty($pmpro_msg))
1712
- {
1713
- $pmpro_msg = $message;
1714
- $pmpro_msgt = $type;
1715
- }
1716
- }
1717
-
1718
- /**
1719
- * Show a a PMPro message set via pmpro_setMessage
1720
- *
1721
- * @since 1.8.5
1722
- */
1723
- function pmpro_showMessage()
1724
- {
1725
- global $pmpro_msg, $pmpro_msgt;
1726
-
1727
- if(!empty($pmpro_msg))
1728
- {
1729
- ?>
1730
- <div class="<?php echo $pmpro_msgt;?>">
1731
- <p><?php echo $pmpro_msg;?></p>
1732
- </div>
1733
- <?php
1734
- }
1735
- }
1736
-
1737
- //used in class definitions for input fields to see if there was an error
1738
- function pmpro_getClassForField($field)
1739
- {
1740
- global $pmpro_error_fields, $pmpro_required_billing_fields, $pmpro_required_user_fields;
1741
- $classes = array();
1742
-
1743
- //error on this field?
1744
- if(!empty($pmpro_error_fields) && in_array($field, $pmpro_error_fields))
1745
- {
1746
- $classes[] = "pmpro_error";
1747
- }
1748
-
1749
- if(is_array($pmpro_required_billing_fields) && is_array($pmpro_required_user_fields))
1750
- $required_fields = array_merge(array_keys($pmpro_required_billing_fields), array_keys($pmpro_required_user_fields));
1751
- elseif(is_array($pmpro_required_billing_fields))
1752
- $required_fields = array_keys($pmpro_required_billing_fields);
1753
- elseif(is_array($pmpro_required_user_fields))
1754
- $required_fields = array_keys($pmpro_required_user_fields);
1755
- else
1756
- $required_fields = array();
1757
-
1758
- //required?
1759
- if(in_array($field, $required_fields))
1760
- {
1761
- $classes[] = "pmpro_required";
1762
- }
1763
-
1764
- $classes = apply_filters("pmpro_field_classes", $classes, $field);
1765
-
1766
- if(!empty($classes))
1767
- return implode(" ", $classes);
1768
- else
1769
- return "";
1770
- }
1771
-
1772
- //get a var from $_GET or $_POST
1773
- function pmpro_getParam($index, $method = "REQUEST", $default = "")
1774
- {
1775
- if($method == "REQUEST")
1776
- {
1777
- if(!empty($_REQUEST[$index]))
1778
- return $_REQUEST[$index];
1779
- }
1780
- elseif($method == "POST")
1781
- {
1782
- if(!empty($_POST[$index]))
1783
- return $_POST[$index];
1784
- }
1785
- elseif($method == "GET")
1786
- {
1787
- if(!empty($_GET[$index]))
1788
- return $_GET[$index];
1789
- }
1790
-
1791
- return $default;
1792
- }
1793
-
1794
- /*
1795
- Format an address from address, city, state, zip, country, and phone
1796
- */
1797
- function pmpro_formatAddress($name, $address1, $address2, $city, $state, $zip, $country, $phone, $nl2br = true)
1798
- {
1799
- $address = "";
1800
-
1801
- if(!empty($name))
1802
- $address .= $name . "\n";
1803
-
1804
- if(!empty($address1))
1805
- $address .= $address1 . "\n";
1806
-
1807
- if(!empty($address2))
1808
- $address .= $address2 . "\n";
1809
-
1810
- if(!empty($city) && !empty($state))
1811
- {
1812
- $address .= $city . ", " . $state;
1813
-
1814
- if(!empty($zip))
1815
- $address .= " " . $zip;
1816
-
1817
- $address .= "\n";
1818
- }
1819
-
1820
- if(!empty($country))
1821
- $address .= $country . "\n";
1822
-
1823
- if(!empty($phone))
1824
- $address .= formatPhone($phone);
1825
-
1826
- if($nl2br)
1827
- $address = nl2br($address);
1828
-
1829
- return $address;
1830
- }
1831
-
1832
- /*
1833
- Checks if all required settings are set.
1834
- */
1835
- function pmpro_is_ready()
1836
- {
1837
- global $wpdb, $pmpro_pages, $pmpro_level_ready, $pmpro_gateway_ready, $pmpro_pages_ready;
1838
-
1839
- //check if there is at least one level
1840
- $pmpro_level_ready = (bool)$wpdb->get_var("SELECT id FROM $wpdb->pmpro_membership_levels LIMIT 1");
1841
-
1842
- //check if the gateway settings are good. first check if it's needed (is there paid membership level)
1843
- $paid_membership_level = $wpdb->get_var("SELECT id FROM $wpdb->pmpro_membership_levels WHERE allow_signups = 1 AND (initial_payment > 0 OR billing_amount > 0 OR trial_amount > 0) LIMIT 1");
1844
- $paid_user_subscription = $wpdb->get_var("SELECT user_id FROM $wpdb->pmpro_memberships_users WHERE initial_payment > 0 OR billing_amount > 0 OR trial_amount > 0 LIMIT 1");
1845
-
1846
- if(empty($paid_membership_level) && empty($paid_user_subscription))
1847
- {
1848
- //no paid membership level now or attached to a user. we don't need the gateway setup
1849
- $pmpro_gateway_ready = true;
1850
- }
1851
- else
1852
- {
1853
- $gateway = pmpro_getOption("gateway");
1854
- if($gateway == "authorizenet")
1855
- {
1856
- if(pmpro_getOption("gateway_environment") && pmpro_getOption("loginname") && pmpro_getOption("transactionkey"))
1857
- $pmpro_gateway_ready = true;
1858
- else
1859
- $pmpro_gateway_ready = false;
1860
- }
1861
- elseif($gateway == "paypal" || $gateway == "paypalexpress")
1862
- {
1863
- if(pmpro_getOption("gateway_environment") && pmpro_getOption("gateway_email") && pmpro_getOption("apiusername") && pmpro_getOption("apipassword") && pmpro_getOption("apisignature"))
1864
- $pmpro_gateway_ready = true;
1865
- else
1866
- $pmpro_gateway_ready = false;
1867
- }
1868
- elseif($gateway == "paypalstandard")
1869
- {
1870
- if(pmpro_getOption("gateway_environment") && pmpro_getOption("gateway_email"))
1871
- $pmpro_gateway_ready = true;
1872
- else
1873
- $pmpro_gateway_ready = false;
1874
- }
1875
- elseif($gateway == "payflowpro")
1876
- {
1877
- if(pmpro_getOption("payflow_partner") && pmpro_getOption("payflow_vendor") && pmpro_getOption("payflow_user") && pmpro_getOption("payflow_pwd"))
1878
- $pmpro_gateway_ready = true;
1879
- else
1880
- $pmpro_gateway_ready = false;
1881
- }
1882
- elseif($gateway == "stripe")
1883
- {
1884
- if(pmpro_getOption("gateway_environment") && pmpro_getOption("stripe_secretkey") && pmpro_getOption("stripe_publishablekey"))
1885
- $pmpro_gateway_ready = true;
1886
- else
1887
- $pmpro_gateway_ready = false;
1888
- }
1889
- elseif($gateway == "braintree")
1890
- {
1891
- if(pmpro_getOption("gateway_environment") && pmpro_getOption("braintree_merchantid") && pmpro_getOption("braintree_publickey") && pmpro_getOption("braintree_privatekey"))
1892
- $pmpro_gateway_ready = true;
1893
- else
1894
- $pmpro_gateway_ready = false;
1895
- }
1896
- elseif($gateway == "twocheckout")
1897
- {
1898
- if(pmpro_getOption("gateway_environment") && pmpro_getOption("twocheckout_apiusername") && pmpro_getOption("twocheckout_apipassword"))
1899
- $pmpro_gateway_ready = true;
1900
- else
1901
- $pmpro_gateway_ready = false;
1902
- }
1903
- elseif($gateway == "cybersource")
1904
- {
1905
- if(pmpro_getOption("gateway_environment") && pmpro_getOption("cybersource_merchantid") && pmpro_getOption("cybersource_securitykey"))
1906
- $pmpro_gateway_ready = true;
1907
- else
1908
- $pmpro_gateway_ready = false;
1909
- }
1910
- elseif($gateway == "check")
1911
- {
1912
- $pmpro_gateway_ready = true;
1913
- }
1914
- else
1915
- {
1916
- $pmpro_gateway_ready = false;
1917
- }
1918
- }
1919
-
1920
- //check if we have all pages
1921
- if($pmpro_pages["account"] &&
1922
- $pmpro_pages["billing"] &&
1923
- $pmpro_pages["cancel"] &&
1924
- $pmpro_pages["checkout"] &&
1925
- $pmpro_pages["confirmation"] &&
1926
- $pmpro_pages["invoice"] &&
1927
- $pmpro_pages["levels"])
1928
- $pmpro_pages_ready = true;
1929
- else
1930
- $pmpro_pages_ready = false;
1931
-
1932
- //now check both
1933
- if($pmpro_gateway_ready && $pmpro_pages_ready)
1934
- $r = true;
1935
- else
1936
- $r = false;
1937
-
1938
- /**
1939
- * Filter to determine if PMPro setup is complete or
1940
- * if notices or warnings need to be shown in the PMPro settings.
1941
- *
1942
- * Note: The filter should return true or false and also set
1943
- * the $pmpro_level_ready, $pmpro_gateway_ready, $pmpro_pages_ready global variabls.
1944
- *
1945
- * @since 1.8.4.5
1946
- *
1947
- * @param bool $r ready?
1948
- */
1949
- $r = apply_filters('pmpro_is_ready', $r);
1950
-
1951
- return $r;
1952
- }
1953
-
1954
- /**
1955
- * Format a price per the currency settings.
1956
- *
1957
- * @since 1.7.15
1958
- */
1959
- function pmpro_formatPrice($price)
1960
- {
1961
- global $pmpro_currency, $pmpro_currency_symbol, $pmpro_currencies;
1962
-
1963
- //start with the price formatted with two decimals
1964
- $formatted = number_format($price, 2);
1965
-
1966
- //settings stored in array?
1967
- if(!empty($pmpro_currencies[$pmpro_currency]) && is_array($pmpro_currencies[$pmpro_currency]))
1968
- {
1969
- //format number do decimals, with decimal_separator and thousands_separator
1970
- $formatted = number_format($price,
1971
- (isset($pmpro_currencies[$pmpro_currency]['decimals']) ? (int)$pmpro_currencies[$pmpro_currency]['decimals'] : 2),
1972
- (isset($pmpro_currencies[$pmpro_currency]['decimal_separator']) ? $pmpro_currencies[$pmpro_currency]['decimal_separator'] : '.'),
1973
- (isset($pmpro_currencies[$pmpro_currency]['thousands_separator']) ? $pmpro_currencies[$pmpro_currency]['thousands_separator'] : ',')
1974
- );
1975
-
1976
- //which side is the symbol on?
1977
- if(!empty($pmpro_currencies[$pmpro_currency]['position']) && $pmpro_currencies[$pmpro_currency]['position']== 'left')
1978
- $formatted = $pmpro_currency_symbol . $formatted;
1979
- else
1980
- $formatted = $formatted . $pmpro_currency_symbol;
1981
- }
1982
- else
1983
- $formatted = $pmpro_currency_symbol . $formatted; //default to symbol on the left
1984
-
1985
- //filter
1986
- return apply_filters('pmpro_format_price', $formatted, $price, $pmpro_currency, $pmpro_currency_symbol);
1987
- }
1988
-
1989
- /**
1990
- * Which side does the currency symbol go on?
1991
- *
1992
- * @since 1.7.15
1993
- */
1994
- function pmpro_getCurrencyPosition()
1995
- {
1996
- global $pmpro_currency, $pmpro_currencies;
1997
-
1998
- if(!empty($pmpro_currencies[$pmpro_currency]) && is_array($pmpro_currencies[$pmpro_currency]) && !empty($pmpro_currencies[$pmpro_currency]['position']))
1999
- return $pmpro_currencies[$pmpro_currency]['position'];
2000
- else
2001
- return "left";
2002
- }
2003
-
2004
- /*
2005
- * What gateway should we be using?
2006
- *
2007
- * @since 1.8
2008
- */
2009
- function pmpro_getGateway()
2010
- {
2011
- //grab from param or options
2012
- if (!empty($_REQUEST['gateway']))
2013
- $gateway = $_REQUEST['gateway']; //gateway passed as param
2014
- elseif (!empty($_REQUEST['review']))
2015
- $gateway = "paypalexpress"; //if review param assume paypalexpress
2016
- else
2017
- $gateway = pmpro_getOption("gateway"); //get from options
2018
-
2019
- //set valid gateways - the active gateway in the settings and any gateway added through the filter will be allowed
2020
- if(pmpro_getOption("gateway", true) == "paypal")
2021
- $valid_gateways = apply_filters("pmpro_valid_gateways", array("paypal", "paypalexpress"));
2022
- else
2023
- $valid_gateways = apply_filters("pmpro_valid_gateways", array(pmpro_getOption("gateway", true)));
2024
-
2025
- //make sure it's valid
2026
- if(!in_array($gateway, $valid_gateways))
2027
- $gateway = false;
2028
-
2029
- //filter for good measure
2030
- $gateway = apply_filters('pmpro_get_gateway', $gateway, $valid_gateways);
2031
-
2032
- return $gateway;
2033
- }
2034
-
2035
- /*
2036
- * Does the date provided fall in this month.
2037
- * Used in logins/visits/views report.
2038
- *
2039
- * @since 1.8.3
2040
- */
2041
- function pmpro_isDateThisMonth($str)
2042
- {
2043
- $now = current_time('timestamp');
2044
- $this_month = intval(date("n", $now));
2045
- $this_year = intval(date("Y", $now));
2046
-
2047
- $date = strtotime($str, $now);
2048
- $date_month = intval(date("n", $date));
2049
- $date_year = intval(date("Y", $date));
2050
-
2051
- if($date_month === $this_month && $date_year === $this_year)
2052
- return true;
2053
- else
2054
- return false;
2055
- }
2056
-
2057
- /**
2058
- * Function to generate PMPro front end pages.
2059
- *
2060
- * @param array $pages {
2061
- * Formatted as array($name => $title) or array(array('title'=>'The Title', 'content'=>'The Content'))
2062
- *
2063
- * @type string $name Page name. (Letters, numbers, and underscores only.)
2064
- * @type string $title Page title.
2065
- * }
2066
- * @return array $created_pages Created page IDs.
2067
- * @since 1.8.5
2068
- */
2069
- function pmpro_generatePages($pages) {
2070
-
2071
- global $pmpro_pages;
2072
-
2073
- $pages_created = array();
2074
-
2075
- if(!empty($pages)) {
2076
- foreach($pages as $name => $page) {
2077
-
2078
- //does it already exist?
2079
- if(!empty($pmpro_pages[$name]))
2080
- continue;
2081
-
2082
- //no id set. create an array to store the page info
2083
- if(is_array($page)) {
2084
- $title = $page['title'];
2085
- $content = $page['content'];
2086
- } else {
2087
- $title = $page;
2088
- $content = '[pmpro_' . $name . ']';
2089
- }
2090
-
2091
- $insert = array(
2092
- 'post_title' => $title,
2093
- 'post_status' => 'publish',
2094
- 'post_type' => 'page',
2095
- 'post_content' => $content,
2096
- 'comment_status' => 'closed',
2097
- 'ping_status' => 'closed'
2098
- );
2099
-
2100
- //make non-account pages a subpage of account
2101
- if ($name != "account") {
2102
- $insert['post_parent'] = $pmpro_pages['account'];
2103
- }
2104
-
2105
- //create the page
2106
- $pmpro_pages[$name] = wp_insert_post($insert);
2107
-
2108
- //update the option too
2109
- pmpro_setOption($name . "_page_id", $pmpro_pages[$name]);
2110
- $pages_created[] = $pmpro_pages[$name];
2111
- }
2112
- }
2113
-
2114
- return $pages_created;
2115
- }
2116
-
1
+ <?php
2
+ /****************************************************************
3
+
4
+ IMPORTANT. PLEASE READ.
5
+
6
+ DO NOT EDIT THIS FILE or any other file in the /wp-content/plugins/paid-memberships-pro/ directory.
7
+ Doing so could break the PMPro plugin and/or keep you from upgrading this plugin in the future.
8
+ We regularly release updates to the plugin, including important security fixes and new features.
9
+ You want to be able to upgrade.
10
+
11
+ If you were asked to insert code into "your functions.php file", it was meant that you edit the functions.php
12
+ in the root folder of your active theme. e.g. /wp-content/themes/twentytwelve/functions.php
13
+ You can also create a custom plugin to place customization code into. Instructions are here:
14
+ http://www.paidmembershipspro.com/2012/08/create-a-plugin-for-pmpro-customizations/
15
+
16
+ Further documentation for customizing Paid Memberships Pro can be found here:
17
+ http://www.paidmembershipspro.com/documentation/
18
+
19
+ ****************************************************************/
20
+ if(!function_exists("sornot"))
21
+ {
22
+ function sornot($t, $n)
23
+ {
24
+ if($n == 1)
25
+ return $t;
26
+ else
27
+ return $t . "s";
28
+ }
29
+ }
30
+
31
+ //set up wpdb for the tables we need
32
+ function pmpro_setDBTables()
33
+ {
34
+ global $wpdb;
35
+ $wpdb->hide_errors();
36
+ $wpdb->pmpro_membership_levels = $wpdb->prefix . 'pmpro_membership_levels';
37
+ $wpdb->pmpro_memberships_users = $wpdb->prefix . 'pmpro_memberships_users';
38
+ $wpdb->pmpro_memberships_categories = $wpdb->prefix . 'pmpro_memberships_categories';
39
+ $wpdb->pmpro_memberships_pages = $wpdb->prefix . 'pmpro_memberships_pages';
40
+ $wpdb->pmpro_membership_orders = $wpdb->prefix . 'pmpro_membership_orders';
41
+ $wpdb->pmpro_discount_codes = $wpdb->prefix . 'pmpro_discount_codes';
42
+ $wpdb->pmpro_discount_codes_levels = $wpdb->prefix . 'pmpro_discount_codes_levels';
43
+ $wpdb->pmpro_discount_codes_uses = $wpdb->prefix . 'pmpro_discount_codes_uses';
44
+ }
45
+ pmpro_setDBTables();
46
+
47
+ //from: http://stackoverflow.com/questions/5266945/wordpress-how-detect-if-current-page-is-the-login-page/5892694#5892694
48
+ function pmpro_is_login_page() {
49
+ return (in_array($GLOBALS['pagenow'], array('wp-login.php', 'wp-register.php')) || is_page("login"));
50
+ }
51
+
52
+ //thanks: http://wordpress.org/support/topic/is_plugin_active
53
+ function pmpro_is_plugin_active( $plugin ) {
54
+ return in_array( $plugin, (array) get_option( 'active_plugins', array() ) );
55
+ }
56
+
57
+ //scraping - override n if you have more than 1 group of matches and don't want the first group
58
+ function pmpro_getMatches($p, $s, $firstvalue = FALSE, $n = 1)
59
+ {
60
+ $ok = preg_match_all($p, $s, $matches);
61
+
62
+ if(!$ok)
63
+ return false;
64
+ else
65
+ {
66
+ if($firstvalue)
67
+ return $matches[$n][0];
68
+ else
69
+ return $matches[$n];
70
+ }
71
+ }
72
+
73
+ function pmpro_br2nl($text, $tags = "br")
74
+ {
75
+ if(!is_array($tags))
76
+ $tags = explode(" ", $tags);
77
+
78
+ foreach($tags as $tag)
79
+ {
80
+ $text = eregi_replace("<" . $tag . "[^>]*>", "\n", $text);
81
+ $text = eregi_replace("</" . $tag . "[^>]*>", "\n", $text);
82
+ }
83
+
84
+ return($text);
85
+ }
86
+
87
+ function pmpro_getOption($s, $force = false)
88
+ {
89
+ if(get_option("pmpro_" . $s))
90
+ return get_option("pmpro_" . $s);
91
+ else
92
+ return "";
93
+ }
94
+
95
+ function pmpro_setOption($s, $v = NULL)
96
+ {
97
+ //no value is given, set v to the p var
98
+ if($v === NULL && isset($_POST[$s]))
99
+ $v = $_POST[$s];
100
+
101
+ if(is_array($v))
102
+ $v = implode(",", $v);
103
+ else
104
+ $v = trim($v);
105
+
106
+ return update_option("pmpro_" . $s, $v);
107
+ }
108
+
109
+ function pmpro_get_slug($post_id)
110
+ {
111
+ global $pmpro_slugs, $wpdb;
112
+
113
+ //make sure post id is int for security
114
+ $post_id = intval($post_id);
115
+
116
+ if(!$pmpro_slugs[$post_id])
117
+ $pmpro_slugs[$post_id] = $wpdb->get_var("SELECT post_name FROM $wpdb->posts WHERE ID = '" . $post_id . "' LIMIT 1");
118
+
119
+ return $pmpro_slugs[$post_id];
120
+ }
121
+
122
+ function pmpro_url($page = NULL, $querystring = "", $scheme = NULL)
123
+ {
124
+ global $besecure;
125
+ $besecure = apply_filters("besecure", $besecure);
126
+
127
+ if(!$scheme && $besecure)
128
+ $scheme = "https";
129
+ elseif(!$scheme)
130
+ $scheme = "http";
131
+
132
+ if(!$page)
133
+ $page = "levels";
134
+
135
+ global $pmpro_pages;
136
+
137
+ //start with the permalink
138
+ $url = get_permalink($pmpro_pages[$page]);
139
+
140
+ //WPML/etc support
141
+ if(function_exists("icl_object_id") && defined("ICL_LANGUAGE_CODE"))
142
+ {
143
+ $trans_id = icl_object_id($pmpro_pages[$page], "page", false, ICL_LANGUAGE_CODE);
144
+ if(!empty($trans_id))
145
+ {
146
+ $url = get_permalink($trans_id);
147
+ }
148
+ }
149
+
150
+ //figure out querystring
151
+ if(strpos($url, "?"))
152
+ $querystring = str_replace("?", "&", $querystring);
153
+ $url .= $querystring;
154
+
155
+ //figure out scheme
156
+ if(is_ssl())
157
+ $url = str_replace("http:", "https:", $url);
158
+
159
+ return $url;
160
+ }
161
+
162
+ function pmpro_isLevelFree(&$level)
163
+ {
164
+ if(!empty($level) && $level->initial_payment <= 0 && $level->billing_amount <= 0 && $level->trial_amount <= 0)
165
+ return true;
166
+ else
167
+ return false;
168
+ }
169
+
170
+ function pmpro_isLevelRecurring(&$level)
171
+ {
172
+ if(!empty($level) && ($level->billing_amount > 0 || $level->trial_amount > 0))
173
+ return true;
174
+ else
175
+ return false;
176
+ }
177
+
178
+ function pmpro_isLevelTrial(&$level)
179
+ {
180
+ if($level->trial_limit > 0)
181
+ {
182
+ return true;
183
+ }
184
+ else
185
+ return false;
186
+ }
187
+
188
+ function pmpro_isLevelExpiring(&$level)
189
+ {
190
+ if($level->expiration_number > 0)
191
+ return true;
192
+ else
193
+ return false;
194
+ }
195
+
196
+ function pmpro_getLevelCost(&$level, $tags = true, $short = false)
197
+ {
198
+ //initial payment
199
+ if(!$short)
200
+ $r = sprintf(__('The price for membership is <strong>%s</strong> now', 'pmpro'), pmpro_formatPrice($level->initial_payment));
201
+ else
202
+ $r = sprintf(__('<strong>%s</strong> now', 'pmpro'), pmpro_formatPrice($level->initial_payment));
203
+
204
+ //recurring part
205
+ if($level->billing_amount != '0.00')
206
+ {
207
+ if($level->billing_limit > 1)
208
+ {
209
+ if($level->cycle_number == '1')
210
+ {
211
+ $r .= sprintf(__(' and then <strong>%s per %s for %d more %s</strong>.', 'pmpro'), pmpro_formatPrice($level->billing_amount), pmpro_translate_billing_period($level->cycle_period), $level->billing_limit, pmpro_translate_billing_period($level->cycle_period, $level->billing_limit));
212
+ }
213
+ else
214
+ {
215
+ $r .= sprintf(__(' and then <strong>%s every %d %s for %d more %s</strong>.', 'pmpro'), pmpro_formatPrice($level->billing_amount), $level->cycle_number, pmpro_translate_billing_period($level->cycle_period, $level->cycle_number), $level->billing_limit, pmpro_translate_billing_period($level->cycle_period, $level->billing_limit));
216
+ }
217
+ }
218
+ elseif($level->billing_limit == 1)
219
+ {
220
+ $r .= sprintf(__(' and then <strong>%s after %d %s</strong>.', 'pmpro'), pmpro_formatPrice($level->billing_amount), $level->cycle_number, pmpro_translate_billing_period($level->cycle_period, $level->cycle_number));
221
+ }
222
+ else
223
+ {
224
+ if( $level->billing_amount === $level->initial_payment ) {
225
+ if($level->cycle_number == '1')
226
+ {
227
+ if(!$short)
228
+ $r = sprintf(__('The price for membership is <strong>%s per %s</strong>.', 'pmpro'), pmpro_formatPrice($level->initial_payment), pmpro_translate_billing_period($level->cycle_period) );
229
+ else
230
+ $r = sprintf(__('<strong>%s per %s</strong>.', 'pmpro'), pmpro_formatPrice($level->initial_payment), pmpro_translate_billing_period($level->cycle_period) );
231
+ }
232
+ else
233
+ {
234
+ if(!$short)
235
+ $r = sprintf(__('The price for membership is <strong>%s every %d %s</strong>.', 'pmpro'), pmpro_formatPrice($level->initial_payment), $level->cycle_number, pmpro_translate_billing_period($level->cycle_period, $level->cycle_number) );
236
+ else
237
+ $r = sprintf(__('<strong>%s every %d %s</strong>.', 'pmpro'), pmpro_formatPrice($level->initial_payment), $level->cycle_number, pmpro_translate_billing_period($level->cycle_period, $level->cycle_number) );
238
+ }
239
+ } else {
240
+ if($level->cycle_number == '1')
241
+ {
242
+ $r .= sprintf(__(' and then <strong>%s per %s</strong>.', 'pmpro'), pmpro_formatPrice($level->billing_amount), pmpro_translate_billing_period($level->cycle_period));
243
+ }
244
+ else
245
+ {
246
+ $r .= sprintf(__(' and then <strong>%s every %d %s</strong>.', 'pmpro'), pmpro_formatPrice($level->billing_amount), $level->cycle_number, pmpro_translate_billing_period($level->cycle_period, $level->cycle_number));
247
+ }
248
+ }
249
+ }
250
+ }
251
+ else
252
+ $r .= '.';
253
+
254
+ //add a space
255
+ $r .= ' ';
256
+
257
+ //trial part
258
+ if($level->trial_limit)
259
+ {
260
+ if($level->trial_amount == '0.00')
261
+ {
262
+ if($level->trial_limit == '1')
263
+ {
264
+ $r .= ' ' . __('After your initial payment, your first payment is Free.', 'pmpro');
265
+ }
266
+ else
267
+ {
268
+ $r .= ' ' . sprintf(__('After your initial payment, your first %d payments are Free.', 'pmpro'), $level->trial_limit);
269
+ }
270
+ }
271
+ else
272
+ {
273
+ if($level->trial_limit == '1')
274
+ {
275
+ $r .= ' ' . sprintf(__('After your initial payment, your first payment will cost %s.', 'pmpro'), pmpro_formatPrice($level->trial_amount));
276
+ }
277
+ else
278
+ {
279
+ $r .= ' ' . sprintf(__('After your initial payment, your first %d payments will cost %s.', 'pmpro'), $level->trial_limit, pmpro_formatPrice($level->trial_amount));
280
+ }
281
+ }
282
+ }
283
+
284
+ //taxes part
285
+ $tax_state = pmpro_getOption("tax_state");
286
+ $tax_rate = pmpro_getOption("tax_rate");
287
+
288
+ if($tax_state && $tax_rate && !pmpro_isLevelFree($level))
289
+ {
290
+ $r .= sprintf(__('Customers in %s will be charged %s%% tax.', 'pmpro'), $tax_state, round($tax_rate * 100, 2));
291
+ }
292
+
293
+ if(!$tags)
294
+ $r = strip_tags($r);
295
+
296
+ $r = apply_filters("pmpro_level_cost_text", $r, $level, $tags, $short); //passing $tags and $short since v2.0
297
+ return $r;
298
+ }
299
+
300
+ function pmpro_getLevelExpiration(&$level)
301
+ {
302
+ if($level->expiration_number)
303
+ {
304
+ $expiration_text = sprintf(__("Membership expires after %d %s.", "pmpro"), $level->expiration_number, pmpro_translate_billing_period($level->expiration_period, $level->expiration_number));
305
+ }
306
+ else
307
+ $expiration_text = "";
308
+
309
+ $expiration_text = apply_filters("pmpro_level_expiration_text", $expiration_text, $level);
310
+ return $expiration_text;
311
+ }
312
+
313
+ function pmpro_hideAds()
314
+ {
315
+ global $pmpro_display_ads;
316
+ return !$pmpro_display_ads;
317
+ }
318
+
319
+ function pmpro_displayAds()
320
+ {
321
+ global $pmpro_display_ads;
322
+ return $pmpro_display_ads;
323
+ }
324
+
325
+ function pmpro_next_payment($user_id = NULL, $order_status = "success", $format = "timestamp")
326
+ {
327
+ global $wpdb, $current_user;
328
+ if(!$user_id)
329
+ $user_id = $current_user->ID;
330
+
331
+ if(!$user_id)
332
+ $r = false;
333
+ else
334
+ {
335
+ //get last order
336
+ $order = new MemberOrder();
337
+ $order->getLastMemberOrder($user_id, $order_status);
338
+
339
+ //get current membership level
340
+ $level = pmpro_getMembershipLevelForUser($user_id);
341
+
342
+ if(!empty($order) && !empty($order->id) && !empty($level) && !empty($level->id) && !empty($level->cycle_number))
343
+ {
344
+ //last payment date
345
+ $lastdate = date("Y-m-d", $order->timestamp);
346
+
347
+ //next payment date
348
+ $nextdate = $wpdb->get_var("SELECT UNIX_TIMESTAMP('" . $lastdate . "' + INTERVAL " . $level->cycle_number . " " . $level->cycle_period . ")");
349
+
350
+ $r = $nextdate;
351
+ }
352
+ else
353
+ {
354
+ //no order or level found, or level was not recurring
355
+ $r = false;
356
+ }
357
+ }
358
+
359
+ /**
360
+ * Filter the next payment date.
361
+ *
362
+ * @since 1.8.5
363
+ *
364
+ * @param mixed $r false or the next payment date timestamp
365
+ * @param int $user_id The user id to get the next payment date for
366
+ * @param string $order_status Status or array of statuses to find the last order based on.
367
+ */
368
+ $r = apply_filters('pmpro_next_payment', $r, $user_id, $order_status);
369
+
370
+ //return in desired format
371
+ if($r === false)
372
+ return false; //always return false when no date found
373
+ elseif($format == "timestamp")
374
+ return $r;
375
+ elseif($format == "date_format")
376
+ return date(get_option('date_format'), $r);
377
+ else
378
+ return date($format, $r); //assume a PHP date format
379
+ }
380
+
381
+ if(!function_exists("last4"))
382
+ {
383
+ function last4($t)
384
+ {
385
+ return substr($t, strlen($t) - 4, 4);
386
+ }
387
+ }
388
+
389
+ if(!function_exists("hideCardNumber"))
390
+ {
391
+ function hideCardNumber($c, $dashes = true)
392
+ {
393
+ if($c)
394
+ {
395
+ if($dashes)
396
+ return "XXXX-XXXX-XXXX-" . substr($c, strlen($c) - 4, 4);
397
+ else
398
+ return "XXXXXXXXXXXX" . substr($c, strlen($c) - 4, 4);
399
+ }
400
+ else
401
+ {
402
+ return "";
403
+ }
404
+ }
405
+ }
406
+
407
+ //check for existing functions since we didn't use a prefix for this function
408
+ if(!function_exists("cleanPhone"))
409
+ {
410
+ /**
411
+ * Function to remove special characters from a phone number.
412
+ * NOTE: Could probably replace with preg_replace("[^0-9]", "", $phone)
413
+ *
414
+ * @since 1.0
415
+ *
416
+ * @param string $phone The phone number to clean.
417
+ */
418
+ function cleanPhone($phone)
419
+ {
420
+ //if a + is passed, just pass it along
421
+ if(strpos($phone, "+") !== false)
422
+ return $phone;
423
+ //clean the phone
424
+ $phone = str_replace("-", "", $phone);
425
+ $phone = str_replace(".", "", $phone);
426
+ $phone = str_replace("(", "", $phone);
427
+ $phone = str_replace(")", "", $phone);
428
+ $phone = str_replace(" ", "", $phone);
429
+ return $phone;
430
+ }
431
+ }
432
+
433
+ //check for existing functions since we didn't use a prefix for this function
434
+ if(!function_exists("formatPhone"))
435
+ {
436
+ /**
437
+ * Function to format a phone number.
438
+ *
439
+ * @since 1.0
440
+ *
441
+ * @param string $phone The phone number to format.
442
+ */
443
+ function formatPhone($phone)
444
+ {
445
+ $r = cleanPhone($phone);
446
+
447
+ if(strlen($r) == 11)
448
+ $r = substr($r, 0, 1) . " (" . substr($r, 1, 3) . ") " . substr($r, 4, 3) . "-" . substr($r, 7, 4);
449
+ elseif(strlen($r) == 10)
450
+ $r = "(" . substr($r, 0, 3) . ") " . substr($r, 3, 3) . "-" . substr($r, 6, 4);
451
+ elseif(strlen($r) == 7)
452
+ $r = substr($r, 0, 3) . "-" . substr($r, 3, 4);
453
+
454
+ /**
455
+ * Filter to do more or less cleaning of phone numbers.
456
+ *
457
+ * @since 1.8.4.4
458
+ *
459
+ * @param string $r The formatted phone number.
460
+ * @param string $phone The original phone number.
461
+ */
462
+ return apply_filters('pmpro_format_phone', $r, $phone);
463
+ }
464
+ }
465
+
466
+ function pmpro_showRequiresMembershipMessage()
467
+ {
468
+ //TODO $current_user $post_membership_levels_names are undefined variables
469
+ //get the correct message
470
+ if(is_feed())
471
+ {
472
+ $content = pmpro_getOption("rsstext");
473
+ $content = str_replace("!!levels!!", implode(", ", $post_membership_levels_names), $content);
474
+ }
475
+ elseif($current_user->ID)
476
+ {
477
+ //not a member
478
+ $content = pmpro_getOption("nonmembertext");
479
+ $content = str_replace("!!levels!!", implode(", ", $post_membership_levels_names), $content);
480
+ }
481
+ else
482
+ {
483
+ //not logged in!
484
+ $content = pmpro_getOption("notloggedintext");
485
+ $content = str_replace("!!levels!!", implode(", ", $post_membership_levels_names), $content);
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
+
514
+ if(empty($user_id)) //no user_id passed, check the current user
515
+ {
516
+ $user_id = $current_user->ID;
517
+ $membership_levels = $current_user->membership_levels;
518
+ }
519
+ elseif(is_numeric($user_id)) //get membership levels for given user
520
+ {
521
+ $membership_levels = pmpro_getMembershipLevelsForUser($user_id);
522
+ }
523
+ else
524
+ return false; //invalid user_id
525
+
526
+ if($levels === "0" || $levels === 0) //if 0 was passed, return true if they have no level and false if they have any
527
+ {
528
+ $return = empty($membership_levels);
529
+ }
530
+ elseif(empty($levels)) //if no level var was passed, we're just checking if they have any level
531
+ {
532
+ $return = !empty($membership_levels);
533
+ }
534
+ else
535
+ {
536
+ if(!is_array($levels)) //make an array out of a single element so we can use the same code
537
+ {
538
+ $levels = array($levels);
539
+ }
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
+ {
558
+ foreach($levels as $level)
559
+ {
560
+ if(strtoupper($level) == "L")
561
+ {
562
+ //checking if user is logged in
563
+ if(!empty($user_id) && $user_id == $current_user->ID)
564
+ $return = true;
565
+ }
566
+ elseif(strtoupper($level) == "-L")
567
+ {
568
+ //checking if user is logged out
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
+ {
578
+ //checking a level id
579
+ $level_obj = pmpro_getLevel(is_numeric($level) ? abs(intval($level)) : $level); //make sure our level is in a proper format
580
+ if(empty($level_obj)){continue;} //invalid level
581
+ $found_level = false;
582
+ foreach($membership_levels as $membership_level)
583
+ {
584
+ if($membership_level->id == $level_obj->id) //found a match
585
+ {
586
+ $found_level = true;
587
+ }
588
+ }
589
+
590
+ if(is_numeric($level) && intval($level) < 0 && !$found_level) //checking for the absence of this level and they don't have it
591
+ {
592
+ $return = true;
593
+ }
594
+ elseif(is_numeric($level) && intval($level) > 0 && $found_level) //checking for the presence of this level and they have it
595
+ {
596
+ $return = true;
597
+ }
598
+ elseif(!is_numeric($level)) //if a level name was passed
599
+ $return = $found_level;
600
+ }
601
+ }
602
+ }
603
+ }
604
+
605
+ $return = apply_filters("pmpro_has_membership_level", $return, $user_id, $levels);
606
+ return $return;
607
+ }
608
+
609
+ /* pmpro_changeMembershipLevel() creates or updates the membership level of the given user to the given level.
610
+ *
611
+ * $level may either be the ID or name of the desired membership_level.
612
+ * If $user_id is omitted, the value will be retrieved from $current_user.
613
+ *
614
+ * Return values:
615
+ * Success returns boolean true.
616
+ * Failure returns boolean false.
617
+ */
618
+ function pmpro_changeMembershipLevel($level, $user_id = NULL, $old_level_status = 'inactive')
619
+ {
620
+ global $wpdb;
621
+ global $current_user, $pmpro_error;
622
+
623
+ if(empty($user_id))
624
+ {
625
+ $user_id = $current_user->ID;
626
+ }
627
+
628
+ if(empty($user_id))
629
+ {
630
+ $pmpro_error = __("User ID not found.", "pmpro");
631
+ return false;
632
+ }
633
+
634
+ //make sure user id is int for security
635
+ $user_id = intval($user_id);
636
+
637
+ if(empty($level)) //cancelling membership
638
+ {
639
+ $level = 0;
640
+ }
641
+ else if(is_array($level))
642
+ {
643
+ //custom level
644
+ }
645
+ else
646
+ {
647
+ $level_obj = pmpro_getLevel($level);
648
+ if(empty($level_obj))
649
+ {
650
+ $pmpro_error = __("Invalid level.", "pmpro");
651
+ return false;
652
+ }
653
+ $level = $level_obj->id;
654
+ }
655
+
656
+ //if it's a custom level, they're changing
657
+ if(!is_array($level))
658
+ {
659
+ //are they even changing?
660
+ if(pmpro_hasMembershipLevel($level, $user_id)) {
661
+ $pmpro_error = __("not changing?", "pmpro");
662
+ return false; //not changing
663
+ }
664
+ }
665
+
666
+ //get all active membershipships for this user
667
+ $old_levels = pmpro_getMembershipLevelsForUser($user_id);
668
+
669
+ //deactivate old memberships based on the old_level_status passed in (updates pmpro_memberships_users table)
670
+ if($old_levels)
671
+ {
672
+ foreach($old_levels as $old_level) {
673
+
674
+ $sql = "UPDATE $wpdb->pmpro_memberships_users SET `status`='$old_level_status', `enddate`='" . current_time('mysql') . "' WHERE `id`=".$old_level->subscription_id;
675
+
676
+ if(!$wpdb->query($sql))
677
+ {
678
+ $pmpro_error = __("Error interacting with database", "pmpro") . ": ".(mysql_errno()?mysql_error():'unavailable');
679
+
680
+ return false;
681
+ }
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)
702
+ $pmpro_cancel_previous_subscriptions = false;
703
+ $pmpro_cancel_previous_subscriptions = apply_filters("pmpro_cancel_previous_subscriptions", $pmpro_cancel_previous_subscriptions);
704
+
705
+ //cancel any other subscriptions they have (updates pmpro_membership_orders table)
706
+ if($pmpro_cancel_previous_subscriptions)
707
+ {
708
+ $other_order_ids = $wpdb->get_col("SELECT id FROM $wpdb->pmpro_membership_orders WHERE user_id = '" . $user_id . "' AND status = 'success' ORDER BY id DESC");
709
+
710
+ foreach($other_order_ids as $order_id)
711
+ {
712
+ $c_order = new MemberOrder($order_id);
713
+ $c_order->cancel();
714
+
715
+ if(!empty($c_order->error))
716
+ $pmpro_error = $c_order->error;
717
+ }
718
+ }
719
+
720
+ //insert current membership
721
+ if(!empty($level)) //are we getting a new one or just cancelling the old ones
722
+ {
723
+ if(is_array($level))
724
+ {
725
+ //make sure the dates are in good formats
726
+ if($level['startdate'] != current_time('mysql') && $level['startdate'] != "NULL" && substr($level['startdate'], 0, 1) != "'")
727
+ $level['startdate'] = "'" . $level['startdate'] . "'";
728
+
729
+ if($level['enddate'] != current_time('mysql') && $level['enddate'] != "NULL" && substr($level['enddate'], 0, 1) != "'")
730
+ $level['enddate'] = "'" . $level['enddate'] . "'";
731
+
732
+ //Better support mySQL Strict Mode by passing a proper enum value for cycle_period
733
+ if ($level['cycle_period'] == '') $level['cycle_period'] = 0;
734
+
735
+ $sql = "INSERT INTO $wpdb->pmpro_memberships_users (user_id, membership_id, code_id, initial_payment, billing_amount, cycle_number, cycle_period, billing_limit, trial_amount, trial_limit, startdate, enddate)
736
+ VALUES('" . $level['user_id'] . "',
737
+ '" . $level['membership_id'] . "',
738
+ '" . intval($level['code_id']) . "',
739
+ '" . $level['initial_payment'] . "',
740
+ '" . $level['billing_amount'] . "',
741
+ '" . $level['cycle_number'] . "',
742
+ '" . $level['cycle_period'] . "',
743
+ '" . $level['billing_limit'] . "',
744
+ '" . $level['trial_amount'] . "',
745
+ '" . $level['trial_limit'] . "',
746
+ " . $level['startdate'] . ",
747
+ " . $level['enddate'] . ")";
748
+
749
+ if(!$wpdb->query($sql))
750
+ {
751
+ $pmpro_error = __("Error interacting with database", "pmpro") . ": ".(mysql_errno()?mysql_error():'unavailable');
752
+ return false;
753
+ }
754
+ }
755
+ else
756
+ {
757
+ $sql = "INSERT INTO $wpdb->pmpro_memberships_users (user_id, membership_id, code_id, initial_payment, billing_amount, cycle_number, cycle_period, billing_limit, trial_amount, trial_limit, startdate, enddate)
758
+ VALUES (
759
+ '" . $user_id . "',
760
+ '" . $level . "',
761
+ '0',
762
+ '0',
763
+ '0',
764
+ '0',
765
+ '0',
766
+ '0',
767
+ '0',
768
+ '0',
769
+ '" . current_time('mysql') . "',
770
+ '0000-00-00 00:00:00'
771
+ )";
772
+
773
+ if(!$wpdb->query($sql))
774
+ {
775
+ $pmpro_error = __("Error interacting with database", "pmpro") . ": ".(mysql_errno()?mysql_error():'unavailable');
776
+ return false;
777
+ }
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
+
798
+ /* pmpro_toggleMembershipCategory() creates or deletes a linking entry between the membership level and post category tables.
799
+ *
800
+ * $level may either be the ID or name of the desired membership_level.
801
+ * $category must be a valid post category ID.
802
+ *
803
+ * Return values:
804
+ * Success returns boolean true.
805
+ * Failure returns a string containing the error message.
806
+ */
807
+ function pmpro_toggleMembershipCategory( $level, $category, $value )
808
+ {
809
+ global $wpdb;
810
+ $category = intval($category);
811
+
812
+ if ( ($level = intval($level)) <= 0 )
813
+ {
814
+ $safe = addslashes($level);
815
+ if ( ($level = intval($wpdb->get_var("SELECT id FROM {$wpdb->pmpro_membership_levels} WHERE name = '$safe' LIMIT 1"))) <= 0 )
816
+ {
817
+ return __("Membership level not found.", "pmpro");
818
+ }
819
+ }
820
+
821
+ if ( $value )
822
+ {
823
+ $sql = "REPLACE INTO {$wpdb->pmpro_memberships_categories} (`membership_id`,`category_id`) VALUES ('$level','$category')";
824
+ $wpdb->query($sql);
825
+ if(mysql_errno()) return mysql_error();
826
+ }
827
+ else
828
+ {
829
+ $sql = "DELETE FROM {$wpdb->pmpro_memberships_categories} WHERE `membership_id` = '$level' AND `category_id` = '$category' LIMIT 1";
830
+ $wpdb->query($sql);
831
+ if(mysql_errno()) return mysql_error();
832
+ }
833
+
834
+ return true;
835
+ }
836
+
837
+ /* pmpro_updateMembershipCategories() ensures that all those and only those categories given
838
+ * are associated with the given membership level.
839
+ *
840
+ * $level is a valid membership level ID or name
841
+ * $categories is an array of post category IDs
842
+ *
843
+ * Return values:
844
+ * Success returns boolean true.
845
+ * Failure returns a string containing the error message.
846
+ */
847
+ function pmpro_updateMembershipCategories($level, $categories)
848
+ {
849
+ global $wpdb;
850
+
851
+ if(!is_numeric($level))
852
+ {
853
+ $level = $wpdb->get_var("SELECT id FROM $wpdb->pmpro_membership_levels WHERE name = '" . esc_sql($level) . "' LIMIT 1");
854
+ if(empty($level))
855
+ {
856
+ return __("Membership level not found.", "pmpro");
857
+ }
858
+ }
859
+
860
+ // remove all existing links...
861
+ $sqlQuery = "DELETE FROM $wpdb->pmpro_memberships_categories WHERE `membership_id` = '" . esc_sql($level) . "'";
862
+ $wpdb->query($sqlQuery);
863
+ if(mysql_errno()) return mysql_error();
864
+
865
+ // add the given links [back?] in...
866
+ foreach($categories as $cat)
867
+ {
868
+ if(is_string($r = pmpro_toggleMembershipCategory( $level, $cat, true)))
869
+ {
870
+ //uh oh, error
871
+ return $r;
872
+ }
873
+ }
874
+
875
+ //all good
876
+ return true;
877
+ }
878
+
879
+ /* pmpro_getMembershipCategories() returns the categories for a given level
880
+ *
881
+ * $level_id is a valid membership level ID
882
+ *
883
+ * Return values:
884
+ * Success returns boolean true.
885
+ * Failure returns boolean false.
886
+ */
887
+ function pmpro_getMembershipCategories($level_id)
888
+ {
889
+ $level_id = intval($level_id);
890
+
891
+ global $wpdb;
892
+ $categories = $wpdb->get_col("SELECT c.category_id
893
+ FROM {$wpdb->pmpro_memberships_categories} AS c
894
+ WHERE c.membership_id = '" . $level_id . "'");
895
+
896
+ return $categories;
897
+ }
898
+
899
+
900
+ function pmpro_isAdmin($user_id = NULL)
901
+ {
902
+ global $current_user, $wpdb;
903
+ if(!$user_id)
904
+ $user_id = $current_user->ID;
905
+
906
+ if(!$user_id)
907
+ return false;
908
+
909
+ $admincap = user_can($user_id, "manage_options");
910
+ if($admincap)
911
+ return true;
912
+ else
913
+ return false;
914
+ }
915
+
916
+ function pmpro_replaceUserMeta($user_id, $meta_keys, $meta_values, $prev_values = NULL)
917
+ {
918
+ //expects all arrays for last 3 params or all strings
919
+ if(!is_array($meta_keys))
920
+ {
921
+ $meta_keys = array($meta_keys);
922
+ $meta_values = array($meta_values);
923
+ $prev_values = array($prev_values);
924
+ }
925
+
926
+ for($i = 0; $i < count($meta_values); $i++)
927
+ {
928
+ if($prev_values[$i])
929
+ {
930
+ update_user_meta($user_id, $meta_keys[$i], $meta_values[$i], $prev_values[$i]);
931
+ }
932
+ else
933
+ {
934
+ $old_value = get_user_meta($user_id, $meta_keys[$i], true);
935
+ if($old_value)
936
+ {
937
+ update_user_meta($user_id, $meta_keys[$i], $meta_values[$i], $old_value);
938
+ }
939
+ else
940
+ {
941
+ update_user_meta($user_id, $meta_keys[$i], $meta_values[$i]);
942
+ }
943
+ }
944
+ }
945
+
946
+ return $i;
947
+ }
948
+
949
+ function pmpro_getMetavalues($query)
950
+ {
951
+ global $wpdb;
952
+
953
+ $results = $wpdb->get_results($query);
954
+ $r = new stdClass();
955
+ foreach($results as $result)
956
+ {
957
+ if(!empty($r))
958
+ $r->{$result->key} = $result->value;
959
+ }
960
+
961
+ return $r;
962
+ }
963
+
964
+ //function to return the pagination string
965
+ function pmpro_getPaginationString($page = 1, $totalitems, $limit = 15, $adjacents = 1, $targetpage = "/", $pagestring = "&pn=")
966
+ {
967
+ //defaults
968
+ if(!$adjacents) $adjacents = 1;
969
+ if(!$limit) $limit = 15;
970
+ if(!$page) $page = 1;
971
+ if(!$targetpage) $targetpage = "/";
972
+
973
+ //other vars
974
+ $prev = $page - 1; //previous page is page - 1
975
+ $next = $page + 1; //next page is page + 1
976
+ $lastpage = ceil($totalitems / $limit); //lastpage is = total items / items per page, rounded up.
977
+ $lpm1 = $lastpage - 1; //last page minus 1
978
+
979
+ /*
980
+ Now we apply our rules and draw the pagination object.
981
+ We're actually saving the code to a variable in case we want to draw it more than once.
982
+ */
983
+ $pagination = "";
984
+ if($lastpage > 1)
985
+ {
986
+ $pagination .= "<div class=\"pmpro_pagination\"";
987
+ if(!empty($margin) || !empty($padding))
988
+ {
989
+ $pagination .= " style=\"";
990
+ if($margin)
991
+ $pagination .= "margin: $margin;";
992
+ if($padding)
993
+ $pagination .= "padding: $padding;";
994
+ $pagination .= "\"";
995
+ }
996
+ $pagination .= ">";
997
+
998
+ //previous button
999
+ if ($page > 1)
1000
+ $pagination .= "<a href=\"$targetpage$pagestring$prev\">&laquo; prev</a>";
1001
+ else
1002
+ $pagination .= "<span class=\"disabled\">&laquo; prev</span>";
1003
+
1004
+ //pages
1005
+ if ($lastpage < 7 + ($adjacents * 2)) //not enough pages to bother breaking it up
1006
+ {
1007
+ for ($counter = 1; $counter <= $lastpage; $counter++)
1008
+ {
1009
+ if ($counter == $page)
1010
+ $pagination .= "<span class=\"current\">$counter</span>";
1011
+ else
1012
+ $pagination .= "<a href=\"" . $targetpage . $pagestring . $counter . "\">$counter</a>";
1013
+ }
1014
+ }
1015
+ elseif($lastpage >= 7 + ($adjacents * 2)) //enough pages to hide some
1016
+ {
1017
+ //close to beginning; only hide later pages
1018
+ if($page < 1 + ($adjacents * 3))
1019
+ {
1020
+ for ($counter = 1; $counter < 4 + ($adjacents * 2); $counter++)
1021
+ {
1022
+ if ($counter == $page)
1023
+ $pagination .= "<span class=\"current\">$counter</span>";
1024
+ else
1025
+ $pagination .= "<a href=\"" . $targetpage . $pagestring . $counter . "\">$counter</a>";
1026
+ }
1027
+ $pagination .= "...";
1028
+ $pagination .= "<a href=\"" . $targetpage . $pagestring . $lpm1 . "\">$lpm1</a>";
1029
+ $pagination .= "<a href=\"" . $targetpage . $pagestring . $lastpage . "\">$lastpage</a>";
1030
+ }
1031
+ //in middle; hide some front and some back
1032
+ elseif($lastpage - ($adjacents * 2) > $page && $page > ($adjacents * 2))
1033
+ {
1034
+ $pagination .= "<a href=\"" . $targetpage . $pagestring . "1\">1</a>";
1035
+ $pagination .= "<a href=\"" . $targetpage . $pagestring . "2\">2</a>";
1036
+ $pagination .= "...";
1037
+ for ($counter = $page - $adjacents; $counter <= $page + $adjacents; $counter++)
1038
+ {
1039
+ if ($counter == $page)
1040
+ $pagination .= "<span class=\"current\">$counter</span>";
1041
+ else
1042
+ $pagination .= "<a href=\"" . $targetpage . $pagestring . $counter . "\">$counter</a>";
1043
+ }
1044
+ $pagination .= "...";
1045
+ $pagination .= "<a href=\"" . $targetpage . $pagestring . $lpm1 . "\">$lpm1</a>";
1046
+ $pagination .= "<a href=\"" . $targetpage . $pagestring . $lastpage . "\">$lastpage</a>";
1047
+ }
1048
+ //close to end; only hide early pages
1049
+ else
1050
+ {
1051
+ $pagination .= "<a href=\"" . $targetpage . $pagestring . "1\">1</a>";
1052
+ $pagination .= "<a href=\"" . $targetpage . $pagestring . "2\">2</a>";
1053
+ $pagination .= "...";
1054
+ for ($counter = $lastpage - (1 + ($adjacents * 3)); $counter <= $lastpage; $counter++)
1055
+ {
1056
+ if ($counter == $page)
1057
+ $pagination .= "<span class=\"current\">$counter</span>";
1058
+ else
1059
+ $pagination .= "<a href=\"" . $targetpage . $pagestring . $counter . "\">$counter</a>";
1060
+ }
1061
+ }
1062
+ }
1063
+
1064
+ //next button
1065
+ if ($page < $counter - 1)
1066
+ $pagination .= "<a href=\"" . $targetpage . $pagestring . $next . "\">next &raquo;</a>";
1067
+ else
1068
+ $pagination .= "<span class=\"disabled\">next &raquo;</span>";
1069
+ $pagination .= "</div>\n";
1070
+ }
1071
+
1072
+ return $pagination;
1073
+
1074
+ }
1075
+
1076
+ function pmpro_calculateInitialPaymentRevenue($s = NULL, $l = NULL)
1077
+ {
1078
+ global $wpdb;
1079
+
1080
+ //if we're limiting users by search
1081
+ if($s || $l)
1082
+ {
1083
+ $user_ids_query = "SELECT u.ID FROM $wpdb->users u LEFT JOIN $wpdb->usermeta um ON u.ID = um.user_id LEFT JOIN $wpdb->pmpro_memberships_users mu ON u.ID = mu.user_id WHERE mu.status = 'active' ";
1084
+ if($s)
1085
+ $user_ids_query .= "AND (u.user_login LIKE '%" . esc_sql($s) . "%' OR u.user_email LIKE '%" . esc_sql($s) . "%' OR um.meta_value LIKE '%$" . esc_sql(s) . "%') ";
1086
+ if($l)
1087
+ $user_ids_query .= "AND mu.membership_id = '" . esc_sql($l) . "' ";
1088
+ }
1089
+
1090
+ //query to sum initial payments
1091
+ $sqlQuery = "SELECT SUM(initial_payment) FROM $wpdb->pmpro_memberships_users WHERE `status` = 'active' ";
1092
+ if(!empty($user_ids_query))
1093
+ $sqlQuery .= "AND user_id IN(" . $user_ids_query . ") ";
1094
+
1095
+ $total = $wpdb->get_var($sqlQuery);
1096
+
1097
+ return (double)$total;
1098
+ }
1099
+
1100
+ function pmpro_calculateRecurringRevenue($s, $l)
1101
+ {
1102
+ global $wpdb;
1103
+
1104
+ //if we're limiting users by search
1105
+ if($s || $l)
1106
+ {
1107
+ $user_ids_query = "AND user_id IN(SELECT u.ID FROM $wpdb->users u LEFT JOIN $wpdb->usermeta um ON u.ID = um.user_id LEFT JOIN $wpdb->pmpro_memberships_users mu ON u.ID = mu.user_id WHERE mu.status = 'active' ";
1108
+ if($s)
1109
+ $user_ids_query .= "AND (u.user_login LIKE '%" . esc_sql($s) . "%' OR u.user_email LIKE '%" . esc_sql($s) . "%' OR um.meta_value LIKE '%" . esc_sql($s) . "%') ";
1110
+ if($l)
1111
+ $user_ids_query .= "AND mu.membership_id = '" . esc_sql($l) . "' ";
1112
+ $user_ids_query .= ")";
1113
+ }
1114
+ else
1115
+ $user_ids_query = "";
1116
+
1117
+ //4 queries to get annual earnings for each cycle period. currently ignoring trial periods and billing limits.
1118
+ $sqlQuery = "
1119
+ SELECT SUM((12/cycle_number)*billing_amount) FROM $wpdb->pmpro_memberships_users WHERE status = 'active' AND cycle_period = 'Month' AND cycle_number <> 12 $user_ids_query
1120
+ UNION
1121
+ SELECT SUM((365/cycle_number)*billing_amount) FROM $wpdb->pmpro_memberships_users WHERE status = 'active' AND cycle_period = 'Day' AND cycle_number <> 365 $user_ids_query
1122
+ UNION
1123
+ SELECT SUM((52/cycle_number)*billing_amount) FROM $wpdb->pmpro_memberships_users WHERE status = 'active' AND cycle_period = 'Week' AND cycle_number <> 52 $user_ids_query
1124
+ UNION
1125
+ SELECT SUM(billing_amount) FROM $wpdb->pmpro_memberships_users WHERE status = 'active' AND cycle_period = 'Year' $user_ids_query
1126
+ ";
1127
+
1128
+ $annual_revenues = $wpdb->get_col($sqlQuery);
1129
+
1130
+ $total = 0;
1131
+ foreach($annual_revenues as $r)
1132
+ {
1133
+ $total += $r;
1134
+ }
1135
+
1136
+ return $total;
1137
+ }
1138
+
1139
+ function pmpro_generateUsername($firstname = "", $lastname = "", $email = "")
1140
+ {
1141
+ global $wpdb;
1142
+
1143
+ //try first initial + last name, firstname, lastname
1144
+ $firstname = preg_replace("/[^A-Za-z]/", "", $firstname);
1145
+ $lastname = preg_replace("/[^A-Za-z]/", "", $lastname);
1146
+ if($firstname && $lastname)
1147
+ {
1148
+ $username = substr($firstname, 0, 1) . $lastname;
1149
+ }
1150
+ elseif($firstname)
1151
+ {
1152
+ $username = $firstname;
1153
+ }
1154
+ elseif($lastname)
1155
+ {
1156
+ $username = $lastname;
1157
+ }
1158
+
1159
+ //is it taken?
1160
+ $taken = $wpdb->get_var("SELECT user_login FROM $wpdb->users WHERE user_login = '" . esc_sql($username) . "' LIMIT 1");
1161
+
1162
+ if(!$taken)
1163
+ return $username;
1164
+
1165
+ //try the beginning of the email address
1166
+ $emailparts = explode("@", $email);
1167
+ if(is_array($emailparts))
1168
+ $email = preg_replace("/[^A-Za-z]/", "", $emailparts[0]);
1169
+
1170
+ if(!empty($email))
1171
+ {
1172
+ $username = $email;
1173
+ }
1174
+
1175
+ //is this taken? if not, add numbers until it works
1176
+ $taken = true;
1177
+ $count = 0;
1178
+ while($taken)
1179
+ {
1180
+ //add a # to the end
1181
+ if($count)
1182
+ {
1183
+ $username = preg_replace("/[0-9]/", "", $username) . $count;
1184
+ }
1185
+
1186
+ //taken?
1187
+ $taken = $wpdb->get_var("SELECT user_login FROM $wpdb->users WHERE user_login = '" . esc_sql($username) . "' LIMIT 1");
1188
+
1189
+ //increment the number
1190
+ $count++;
1191
+ }
1192
+
1193
+ //must have a good username now
1194
+ return $username;
1195
+ }
1196
+
1197
+ //get a new random code for discount codes
1198
+ function pmpro_getDiscountCode($seed = NULL)
1199
+ {
1200
+ global $wpdb;
1201
+
1202
+ while(empty($code))
1203
+ {
1204
+ $scramble = md5(AUTH_KEY . current_time('timestamp') . $seed . SECURE_AUTH_KEY);
1205
+ $code = substr($scramble, 0, 10);
1206
+ $check = $wpdb->get_var("SELECT code FROM $wpdb->pmpro_discount_codes WHERE code = '" . esc_sql($code) . "' LIMIT 1");
1207
+ if($check || is_numeric($code))
1208
+ $code = NULL;
1209
+ }
1210
+
1211
+ return strtoupper($code);
1212
+ }
1213
+
1214
+ //is a discount code valid
1215
+ function pmpro_checkDiscountCode($code, $level_id = NULL, $return_errors = false)
1216
+ {
1217
+ global $wpdb;
1218
+
1219
+ $error = false;
1220
+
1221
+ //make sure level id is int for security
1222
+ $level_id = intval($level_id);
1223
+
1224
+ //no code, no code
1225
+ if(empty($code))
1226
+ $error = __("No code was given to check.", "pmpro");
1227
+
1228
+ //get code from db
1229
+ if(!$error)
1230
+ {
1231
+ $dbcode = $wpdb->get_row("SELECT *, UNIX_TIMESTAMP(starts) as starts, UNIX_TIMESTAMP(expires) as expires FROM $wpdb->pmpro_discount_codes WHERE code ='" . esc_sql($code) . "' LIMIT 1");
1232
+
1233
+ //did we find it?
1234
+ if(empty($dbcode->id))
1235
+ $error = __("The discount code could not be found.", "pmpro");
1236
+ }
1237
+
1238
+ //check if the code has started
1239
+ if(!$error)
1240
+ {
1241
+ //fix the date timestamps
1242
+ $dbcode->starts = strtotime(date("m/d/Y", $dbcode->starts));
1243
+ $dbcode->expires = strtotime(date("m/d/Y", $dbcode->expires));
1244
+
1245
+ //today
1246
+ $today = strtotime(date("m/d/Y 00:00:00", current_time("timestamp")));
1247
+
1248
+ //has this code started yet?
1249
+ if(!empty($dbcode->starts) && $dbcode->starts > $today)
1250
+ $error = sprintf(__("This discount code goes into effect on %s.", "pmpro"), date(get_option('date_format'), $dbcode->starts));
1251
+ }
1252
+
1253
+ //check if the code is expired
1254
+ if(!$error)
1255
+ {
1256
+ if(!empty($dbcode->expires) && $dbcode->expires < $today)
1257
+ $error = sprintf(__("This discount code expired on %s.", "pmpro"), date(get_option('date_format'), $dbcode->expires));
1258
+ }
1259
+
1260
+ //have we run out of uses?
1261
+ if(!$error)
1262
+ {
1263
+ if($dbcode->uses > 0)
1264
+ {
1265
+ $used = $wpdb->get_var("SELECT COUNT(*) FROM $wpdb->pmpro_discount_codes_uses WHERE code_id = '" . $dbcode->id . "'");
1266
+ if($used >= $dbcode->uses)
1267
+ $error = __("This discount code is no longer valid.", "pmpro");
1268
+ }
1269
+ }
1270
+
1271
+ //if a level was passed check if this code applies
1272
+ if(!$error)
1273
+ {
1274
+ $pmpro_check_discount_code_levels = apply_filters("pmpro_check_discount_code_levels", true, $dbcode->id);
1275
+ if(!empty($level_id) && $pmpro_check_discount_code_levels)
1276
+ {
1277
+ $code_level = $wpdb->get_row("SELECT l.id, cl.*, l.name, l.description, l.allow_signups FROM $wpdb->pmpro_discount_codes_levels cl LEFT JOIN $wpdb->pmpro_membership_levels l ON cl.level_id = l.id WHERE cl.code_id = '" . $dbcode->id . "' AND cl.level_id = '" . $level_id . "' LIMIT 1");
1278
+
1279
+ if(empty($code_level))
1280
+ $error = __("This discount code does not apply to this membership level.", "pmpro");
1281
+ }
1282
+ }
1283
+
1284
+ //allow filter
1285
+ $pmpro_check_discount_code = apply_filters("pmpro_check_discount_code", !$error, $dbcode, $level_id, $code);
1286
+ if(is_string($pmpro_check_discount_code))
1287
+ $error = $pmpro_check_discount_code; //string returned, this is an error
1288
+ elseif(!$pmpro_check_discount_code && !$error)
1289
+ $error = true; //no error before, but filter returned error
1290
+ elseif($pmpro_check_discount_code)
1291
+ $error = false; //filter is true, so error false
1292
+
1293
+ //return
1294
+ if($error)
1295
+ {
1296
+ //there was an error
1297
+ if(!empty($return_errors))
1298
+ return array(false, $error);
1299
+ else
1300
+ return false;
1301
+ }
1302
+ else
1303
+ {
1304
+ //guess we're all good
1305
+ if(!empty($return_errors))
1306
+ return array(true, __("This discount code is okay.", "pmpro"));
1307
+ else
1308
+ return true;
1309
+ }
1310
+ }
1311
+
1312
+ function pmpro_no_quotes($s, $quotes = array("'", '"'))
1313
+ {
1314
+ return str_replace($quotes, "", $s);
1315
+ }
1316
+
1317
+ //from: http://www.php.net/manual/en/function.implode.php#86845
1318
+ function pmpro_implodeToEnglish($array)
1319
+ {
1320
+ // sanity check
1321
+ if (!$array || !count ($array))
1322
+ return '';
1323
+
1324
+ // get last element
1325
+ $last = array_pop ($array);
1326
+
1327
+ // if it was the only element - return it
1328
+ if (!count ($array))
1329
+ return $last;
1330
+
1331
+ return implode (', ', $array).' ' . __('and', 'pmpro') . ' '.$last;
1332
+ }
1333
+
1334
+ //from yoast wordpress seo
1335
+ function pmpro_text_limit( $text, $limit, $finish = '&hellip;')
1336
+ {
1337
+ if( strlen( $text ) > $limit ) {
1338
+ $text = substr( $text, 0, $limit );
1339
+ $text = substr( $text, 0, - ( strlen( strrchr( $text,' ') ) ) );
1340
+ $text .= $finish;
1341
+ }
1342
+ return $text;
1343
+ }
1344
+
1345
+ /* pmpro_getMembershipLevelForUser() returns the first active membership level for a user
1346
+ *
1347
+ * If $user_id is omitted, the value will be retrieved from $current_user.
1348
+ *
1349
+ * Return values:
1350
+ * Success returns the level object.
1351
+ * Failure returns false.
1352
+ */
1353
+ function pmpro_getMembershipLevelForUser($user_id = NULL, $force = false)
1354
+ {
1355
+ if(empty($user_id))
1356
+ {
1357
+ global $current_user;
1358
+ $user_id = $current_user->ID;
1359
+ }
1360
+
1361
+ if(empty($user_id))
1362
+ {
1363
+ return false;
1364
+ }
1365
+
1366
+ //make sure user id is int for security
1367
+ $user_id = intval($user_id);
1368
+
1369
+ global $all_membership_levels;
1370
+
1371
+ if(isset($all_membership_levels[$user_id]) && !$force)
1372
+ {
1373
+ return $all_membership_levels[$user_id];
1374
+ }
1375
+ else
1376
+ {
1377
+ global $wpdb;
1378
+ $all_membership_levels[$user_id] = $wpdb->get_row("SELECT
1379
+ l.id AS ID,
1380
+ l.id as id,
1381
+ mu.id as subscription_id,
1382
+ l.name AS name,
1383
+ l.description,
1384
+ l.expiration_number,
1385
+ l.expiration_period,
1386
+ mu.initial_payment,
1387
+ mu.billing_amount,
1388
+ mu.cycle_number,
1389
+ mu.cycle_period,
1390
+ mu.billing_limit,
1391
+ mu.trial_amount,
1392
+ mu.trial_limit,
1393
+ mu.code_id as code_id,
1394
+ UNIX_TIMESTAMP(startdate) as startdate,
1395
+ UNIX_TIMESTAMP(enddate) as enddate
1396
+ FROM {$wpdb->pmpro_membership_levels} AS l
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
+ }
1415
+
1416
+ /* pmpro_getMembershipLevelsForUser() returns the membership levels for a user
1417
+ *
1418
+ * If $user_id is omitted, the value will be retrieved from $current_user.
1419
+ * By default it only includes actvie memberships.
1420
+ *
1421
+ * Return values:
1422
+ * Success returns an array of level objects.
1423
+ * Failure returns false.
1424
+ */
1425
+ function pmpro_getMembershipLevelsForUser($user_id = NULL, $include_inactive = false)
1426
+ {
1427
+ if(empty($user_id))
1428
+ {
1429
+ global $current_user;
1430
+ $user_id = $current_user->ID;
1431
+ }
1432
+
1433
+ if(empty($user_id))
1434
+ {
1435
+ return false;
1436
+ }
1437
+
1438
+ //make sure user id is int for security
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,
1447
+ l.name,
1448
+ l.description,
1449
+ l.expiration_number,
1450
+ l.expiration_period,
1451
+ mu.initial_payment,
1452
+ mu.billing_amount,
1453
+ mu.cycle_number,
1454
+ mu.cycle_period,
1455
+ mu.billing_limit,
1456
+ mu.trial_amount,
1457
+ mu.trial_limit,
1458
+ mu.code_id as code_id,
1459
+ UNIX_TIMESTAMP(startdate) as startdate,
1460
+ UNIX_TIMESTAMP(enddate) as enddate
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
1479
+ *
1480
+ * $level may be the level id or name
1481
+ *
1482
+ * Return values:
1483
+ * Success returns the level object.
1484
+ * Failure returns false.
1485
+ */
1486
+ function pmpro_getLevel($level)
1487
+ {
1488
+ global $pmpro_levels;
1489
+
1490
+ if(is_object($level) && !empty($level->id))
1491
+ $level = $level->id;
1492
+
1493
+ //was a name passed? (Todo: make sure level names have at least one non-numeric character.
1494
+ if(is_numeric($level))
1495
+ {
1496
+ $level_id = intval($level);
1497
+ if(isset($pmpro_levels[$level_id]))
1498
+ {
1499
+ return $pmpro_levels[$level_id];
1500
+ }
1501
+ else
1502
+ {
1503
+ global $wpdb;
1504
+ $pmpro_levels[$level_id] = $wpdb->get_row("SELECT * FROM $wpdb->pmpro_membership_levels WHERE id = '" . $level_id . "' LIMIT 1");
1505
+ return $pmpro_levels[$level_id];
1506
+ }
1507
+ }
1508
+ else
1509
+ {
1510
+ global $wpdb;
1511
+ $level_obj = $wpdb->get_row("SELECT * FROM $wpdb->pmpro_membership_levels WHERE name = '" . esc_sql($level) . "' LIMIT 1");
1512
+
1513
+ if(!empty($level_obj))
1514
+ $level_id = $level_obj->id;
1515
+ else
1516
+ return false;
1517
+
1518
+ $pmpro_levels[$level_id] = $level_obj;
1519
+ return $pmpro_levels[$level_id];
1520
+ }
1521
+ }
1522
+
1523
+ /*
1524
+ Function to populate pmpro_levels with all levels. We query the DB every time just to be sure we have the latest.
1525
+ This should be called if you want to be sure you get all levels as $pmpro_levels may only have a subset of levels.
1526
+ */
1527
+ function pmpro_getAllLevels($include_hidden = false, $force = false)
1528
+ {
1529
+ global $pmpro_levels, $wpdb;
1530
+
1531
+ //just use what's cached (doesn't take into account include_hidden setting)
1532
+ if(!empty($pmpro_levels) && !$force)
1533
+ return $pmpro_levels;
1534
+
1535
+ //build query
1536
+ $sqlQuery = "SELECT * FROM $wpdb->pmpro_membership_levels ";
1537
+ if(!$include_hidden)
1538
+ $sqlQuery .= " WHERE allow_signups = 1 ORDER BY id";
1539
+
1540
+ //get levels from the DB
1541
+ $raw_levels = $wpdb->get_results($sqlQuery);
1542
+
1543
+ //lets put them into an array where the key is the id of the level
1544
+ $pmpro_levels = array();
1545
+ foreach($raw_levels as $raw_level)
1546
+ {
1547
+ $pmpro_levels[$raw_level->id] = $raw_level;
1548
+ }
1549
+
1550
+ return $pmpro_levels;
1551
+ }
1552
+
1553
+ function pmpro_getCheckoutButton($level_id, $button_text = NULL, $classes = NULL)
1554
+ {
1555
+ if(empty($button_text))
1556
+ $button_text = __("Sign Up for !!name!! Now", "pmpro");
1557
+
1558
+ if(empty($classes))
1559
+ $classes = "pmpro_btn";
1560
+
1561
+ if(empty($level_id))
1562
+ $r = __("Please specify a level id.", "pmpro");
1563
+ else
1564
+ {
1565
+ //get level
1566
+ $level = pmpro_getLevel($level_id);
1567
+
1568
+ //replace vars
1569
+ $replacements = array(
1570
+ "!!id!!" => $level->id,
1571
+ "!!name!!" => $level->name,
1572
+ "!!description!!" => $level->description,
1573
+ "!!confirmation!!" => $level->confirmation,
1574
+ "!!initial_payment!!" => $level->initial_payment,
1575
+ "!!billing_amount!!" => $level->billing_amount,
1576
+ "!!cycle_number!!" => $level->cycle_number,
1577
+ "!!cycle_period!!" => $level->cycle_period,
1578
+ "!!billing_limit!!" => $level->billing_limit,
1579
+ "!!trial_amount!!" => $level->trial_amount,
1580
+ "!!trial_limit!!" => $level->trial_limit,
1581
+ "!!expiration_number!!" => $level->expiration_number,
1582
+ "!!expiration_period!!" => $level->expiration_period
1583
+ );
1584
+ $button_text = str_replace(array_keys($replacements), $replacements, $button_text);
1585
+
1586
+ //button text
1587
+ $r = "<a href=\"" . pmpro_url("checkout", "?level=" . $level_id) . "\" class=\"" . $classes . "\">" . $button_text . "</a>";
1588
+ }
1589
+ return $r;
1590
+ }
1591
+
1592
+ /**
1593
+ * Get the "domain" from a URL. By domain, we mean the host name, minus any subdomains. So just the domain and TLD.
1594
+ *
1595
+ * @param string $url The URL to parse. (generally pass site_url() in WP)
1596
+ * @return string The domain.
1597
+ */
1598
+ function pmpro_getDomainFromURL($url = NULL)
1599
+ {
1600
+ $domainparts = parse_url($url);
1601
+ $domainparts = explode(".", $domainparts['host']);
1602
+ if(count($domainparts) > 1)
1603
+ {
1604
+ //check for ips
1605
+ $isip = true;
1606
+ foreach($domainparts as $part)
1607
+ {
1608
+ if(!is_numeric($part))
1609
+ {
1610
+ $isip = false;
1611
+ break;
1612
+ }
1613
+ }
1614
+
1615
+ if($isip)
1616
+ {
1617
+ //ip, e.g. 127.1.1.1
1618
+ $domain = implode(".", $domainparts);
1619
+ }
1620
+ else
1621
+ {
1622
+ //www.something.com, etc.
1623
+ $domain = $domainparts[count($domainparts)-2] . "." . $domainparts[count($domainparts)-1];
1624
+ }
1625
+ }
1626
+ else
1627
+ {
1628
+ //localhost or another single word domain
1629
+ $domain = $domainparts[0];
1630
+ }
1631
+
1632
+ return $domain;
1633
+ }
1634
+
1635
+ /*
1636
+ Get a member's start date... either in general or for a specific level_id.
1637
+ */
1638
+ if(!function_exists("pmpro_getMemberStartdate"))
1639
+ {
1640
+ function pmpro_getMemberStartdate($user_id = NULL, $level_id = 0)
1641
+ {
1642
+ if(empty($user_id))
1643
+ {
1644
+ global $current_user;
1645
+ $user_id = $current_user->ID;
1646
+ }
1647
+
1648
+ //make sure user and level id are int for security
1649
+ $user_id = intval($user_id);
1650
+ $level_id = intval($level_id);
1651
+
1652
+ global $pmpro_startdates; //for cache
1653
+ if(empty($pmpro_startdates[$user_id][$level_id]))
1654
+ {
1655
+ global $wpdb;
1656
+
1657
+ if(!empty($level_id))
1658
+ $sqlQuery = "SELECT UNIX_TIMESTAMP(startdate) FROM $wpdb->pmpro_memberships_users WHERE status = 'active' AND membership_id IN(" . esc_sql($level_id) . ") AND user_id = '" . $user_id . "' ORDER BY id LIMIT 1";
1659
+ else
1660
+ $sqlQuery = "SELECT UNIX_TIMESTAMP(startdate) FROM $wpdb->pmpro_memberships_users WHERE status = 'active' AND user_id = '" . $user_id . "' ORDER BY id LIMIT 1";
1661
+
1662
+ $startdate = apply_filters("pmpro_member_startdate", $wpdb->get_var($sqlQuery), $user_id, $level_id);
1663
+
1664
+ $pmpro_startdates[$user_id][$level_id] = $startdate;
1665
+ }
1666
+
1667
+ return $pmpro_startdates[$user_id][$level_id];
1668
+ }
1669
+ }
1670
+
1671
+ /*
1672
+ How long has this member been a member
1673
+ */
1674
+ if(!function_exists("pmpro_getMemberDays"))
1675
+ {
1676
+ function pmpro_getMemberDays($user_id = NULL, $level_id = 0)
1677
+ {
1678
+ if(empty($user_id))
1679
+ {
1680
+ global $current_user;
1681
+ $user_id = $current_user->ID;
1682
+ }
1683
+
1684
+ global $pmpro_member_days;
1685
+ if(empty($pmpro_member_days[$user_id][$level_id]))
1686
+ {
1687
+ $startdate = pmpro_getMemberStartdate($user_id, $level_id);
1688
+
1689
+ //check that there was a startdate at all
1690
+ if(empty($startdate))
1691
+ $pmpro_member_days[$user_id][$level_id] = 0;
1692
+ else
1693
+ {
1694
+ $now = current_time('timestamp');
1695
+ $days = ($now - $startdate)/3600/24;
1696
+
1697
+ $pmpro_member_days[$user_id][$level_id] = $days;
1698
+ }
1699
+ }
1700
+
1701
+ return $pmpro_member_days[$user_id][$level_id];
1702
+ }
1703
+ }
1704
+
1705
+ //the start of a message handling script
1706
+ function pmpro_setMessage($message, $type, $force = false)
1707
+ {
1708
+ global $pmpro_msg, $pmpro_msgt;
1709
+
1710
+ //for now, we only show the first message generated
1711
+ if($force || empty($pmpro_msg))
1712
+ {
1713
+ $pmpro_msg = $message;
1714
+ $pmpro_msgt = $type;
1715
+ }
1716
+ }
1717
+
1718
+ /**
1719
+ * Show a a PMPro message set via pmpro_setMessage
1720
+ *
1721
+ * @since 1.8.5
1722
+ */
1723
+ function pmpro_showMessage()
1724
+ {
1725
+ global $pmpro_msg, $pmpro_msgt;
1726
+
1727
+ if(!empty($pmpro_msg))
1728
+ {
1729
+ ?>
1730
+ <div class="<?php echo $pmpro_msgt;?>">
1731
+ <p><?php echo $pmpro_msg;?></p>
1732
+ </div>
1733
+ <?php
1734
+ }
1735
+ }
1736
+
1737
+ //used in class definitions for input fields to see if there was an error
1738
+ function pmpro_getClassForField($field)
1739
+ {
1740
+ global $pmpro_error_fields, $pmpro_required_billing_fields, $pmpro_required_user_fields;
1741
+ $classes = array();
1742
+
1743
+ //error on this field?
1744
+ if(!empty($pmpro_error_fields) && in_array($field, $pmpro_error_fields))
1745
+ {
1746
+ $classes[] = "pmpro_error";
1747
+ }
1748
+
1749
+ if(is_array($pmpro_required_billing_fields) && is_array($pmpro_required_user_fields))
1750
+ $required_fields = array_merge(array_keys($pmpro_required_billing_fields), array_keys($pmpro_required_user_fields));
1751
+ elseif(is_array($pmpro_required_billing_fields))
1752
+ $required_fields = array_keys($pmpro_required_billing_fields);
1753
+ elseif(is_array($pmpro_required_user_fields))
1754
+ $required_fields = array_keys($pmpro_required_user_fields);
1755
+ else
1756
+ $required_fields = array();
1757
+
1758
+ //required?
1759
+ if(in_array($field, $required_fields))
1760
+ {
1761
+ $classes[] = "pmpro_required";
1762
+ }
1763
+
1764
+ $classes = apply_filters("pmpro_field_classes", $classes, $field);
1765
+
1766
+ if(!empty($classes))
1767
+ return implode(" ", $classes);
1768
+ else
1769
+ return "";
1770
+ }
1771
+
1772
+ //get a var from $_GET or $_POST
1773
+ function pmpro_getParam($index, $method = "REQUEST", $default = "")
1774
+ {
1775
+ if($method == "REQUEST")
1776
+ {
1777
+ if(!empty($_REQUEST[$index]))
1778
+ return $_REQUEST[$index];
1779
+ }
1780
+ elseif($method == "POST")
1781
+ {
1782
+ if(!empty($_POST[$index]))
1783
+ return $_POST[$index];
1784
+ }
1785
+ elseif($method == "GET")
1786
+ {
1787
+ if(!empty($_GET[$index]))
1788
+ return $_GET[$index];
1789
+ }
1790
+
1791
+ return $default;
1792
+ }
1793
+
1794
+ /*
1795
+ Format an address from address, city, state, zip, country, and phone
1796
+ */
1797
+ function pmpro_formatAddress($name, $address1, $address2, $city, $state, $zip, $country, $phone, $nl2br = true)
1798
+ {
1799
+ $address = "";
1800
+
1801
+ if(!empty($name))
1802
+ $address .= $name . "\n";
1803
+
1804
+ if(!empty($address1))
1805
+ $address .= $address1 . "\n";
1806
+
1807
+ if(!empty($address2))
1808
+ $address .= $address2 . "\n";
1809
+
1810
+ if(!empty($city) && !empty($state))
1811
+ {
1812
+ $address .= $city . ", " . $state;
1813
+
1814
+ if(!empty($zip))
1815
+ $address .= " " . $zip;
1816
+
1817
+ $address .= "\n";
1818
+ }
1819
+
1820
+ if(!empty($country))
1821
+ $address .= $country . "\n";
1822
+
1823
+ if(!empty($phone))
1824
+ $address .= formatPhone($phone);
1825
+
1826
+ if($nl2br)
1827
+ $address = nl2br($address);
1828
+
1829
+ return $address;
1830
+ }
1831
+
1832
+ /*
1833
+ Checks if all required settings are set.
1834
+ */
1835
+ function pmpro_is_ready()
1836
+ {
1837
+ global $wpdb, $pmpro_pages, $pmpro_level_ready, $pmpro_gateway_ready, $pmpro_pages_ready;
1838
+
1839
+ //check if there is at least one level
1840
+ $pmpro_level_ready = (bool)$wpdb->get_var("SELECT id FROM $wpdb->pmpro_membership_levels LIMIT 1");
1841
+
1842
+ //check if the gateway settings are good. first check if it's needed (is there paid membership level)
1843
+ $paid_membership_level = $wpdb->get_var("SELECT id FROM $wpdb->pmpro_membership_levels WHERE allow_signups = 1 AND (initial_payment > 0 OR billing_amount > 0 OR trial_amount > 0) LIMIT 1");
1844
+ $paid_user_subscription = $wpdb->get_var("SELECT user_id FROM $wpdb->pmpro_memberships_users WHERE initial_payment > 0 OR billing_amount > 0 OR trial_amount > 0 LIMIT 1");
1845
+
1846
+ if(empty($paid_membership_level) && empty($paid_user_subscription))
1847
+ {
1848
+ //no paid membership level now or attached to a user. we don't need the gateway setup
1849
+ $pmpro_gateway_ready = true;
1850
+ }
1851
+ else
1852
+ {
1853
+ $gateway = pmpro_getOption("gateway");
1854
+ if($gateway == "authorizenet")
1855
+ {
1856
+ if(pmpro_getOption("gateway_environment") && pmpro_getOption("loginname") && pmpro_getOption("transactionkey"))
1857
+ $pmpro_gateway_ready = true;
1858
+ else
1859
+ $pmpro_gateway_ready = false;
1860
+ }
1861
+ elseif($gateway == "paypal" || $gateway == "paypalexpress")
1862
+ {
1863
+ if(pmpro_getOption("gateway_environment") && pmpro_getOption("gateway_email") && pmpro_getOption("apiusername") && pmpro_getOption("apipassword") && pmpro_getOption("apisignature"))
1864
+ $pmpro_gateway_ready = true;
1865
+ else
1866
+ $pmpro_gateway_ready = false;
1867
+ }
1868
+ elseif($gateway == "paypalstandard")
1869
+ {
1870
+ if(pmpro_getOption("gateway_environment") && pmpro_getOption("gateway_email"))
1871
+ $pmpro_gateway_ready = true;
1872
+ else
1873
+ $pmpro_gateway_ready = false;
1874
+ }
1875
+ elseif($gateway == "payflowpro")
1876
+ {
1877
+ if(pmpro_getOption("payflow_partner") && pmpro_getOption("payflow_vendor") && pmpro_getOption("payflow_user") && pmpro_getOption("payflow_pwd"))
1878
+ $pmpro_gateway_ready = true;
1879
+ else
1880
+ $pmpro_gateway_ready = false;
1881
+ }
1882
+ elseif($gateway == "stripe")
1883
+ {
1884
+ if(pmpro_getOption("gateway_environment") && pmpro_getOption("stripe_secretkey") && pmpro_getOption("stripe_publishablekey"))
1885
+ $pmpro_gateway_ready = true;
1886
+ else
1887
+ $pmpro_gateway_ready = false;
1888
+ }
1889
+ elseif($gateway == "braintree")
1890
+ {
1891
+ if(pmpro_getOption("gateway_environment") && pmpro_getOption("braintree_merchantid") && pmpro_getOption("braintree_publickey") && pmpro_getOption("braintree_privatekey"))
1892
+ $pmpro_gateway_ready = true;
1893
+ else
1894
+ $pmpro_gateway_ready = false;
1895
+ }
1896
+ elseif($gateway == "twocheckout")
1897
+ {
1898
+ if(pmpro_getOption("gateway_environment") && pmpro_getOption("twocheckout_apiusername") && pmpro_getOption("twocheckout_apipassword"))
1899
+ $pmpro_gateway_ready = true;
1900
+ else
1901
+ $pmpro_gateway_ready = false;
1902
+ }
1903
+ elseif($gateway == "cybersource")
1904
+ {
1905
+ if(pmpro_getOption("gateway_environment") && pmpro_getOption("cybersource_merchantid") && pmpro_getOption("cybersource_securitykey"))
1906
+ $pmpro_gateway_ready = true;
1907
+ else
1908
+ $pmpro_gateway_ready = false;
1909
+ }
1910
+ elseif($gateway == "check")
1911
+ {
1912
+ $pmpro_gateway_ready = true;
1913
+ }
1914
+ else
1915
+ {
1916
+ $pmpro_gateway_ready = false;
1917
+ }
1918
+ }
1919
+
1920
+ //check if we have all pages
1921
+ if($pmpro_pages["account"] &&
1922
+ $pmpro_pages["billing"] &&
1923
+ $pmpro_pages["cancel"] &&
1924
+ $pmpro_pages["checkout"] &&
1925
+ $pmpro_pages["confirmation"] &&
1926
+ $pmpro_pages["invoice"] &&
1927
+ $pmpro_pages["levels"])
1928
+ $pmpro_pages_ready = true;
1929
+ else
1930
+ $pmpro_pages_ready = false;
1931
+
1932
+ //now check both
1933
+ if($pmpro_gateway_ready && $pmpro_pages_ready)
1934
+ $r = true;
1935
+ else
1936
+ $r = false;
1937
+
1938
+ /**
1939
+ * Filter to determine if PMPro setup is complete or
1940
+ * if notices or warnings need to be shown in the PMPro settings.
1941
+ *
1942
+ * Note: The filter should return true or false and also set
1943
+ * the $pmpro_level_ready, $pmpro_gateway_ready, $pmpro_pages_ready global variabls.
1944
+ *
1945
+ * @since 1.8.4.5
1946
+ *
1947
+ * @param bool $r ready?
1948
+ */
1949
+ $r = apply_filters('pmpro_is_ready', $r);
1950
+
1951
+ return $r;
1952
+ }
1953
+
1954
+ /**
1955
+ * Format a price per the currency settings.
1956
+ *
1957
+ * @since 1.7.15
1958
+ */
1959
+ function pmpro_formatPrice($price)
1960
+ {
1961
+ global $pmpro_currency, $pmpro_currency_symbol, $pmpro_currencies;
1962
+
1963
+ //start with the price formatted with two decimals
1964
+ $formatted = number_format($price, 2);
1965
+
1966
+ //settings stored in array?
1967
+ if(!empty($pmpro_currencies[$pmpro_currency]) && is_array($pmpro_currencies[$pmpro_currency]))
1968
+ {
1969
+ //format number do decimals, with decimal_separator and thousands_separator
1970
+ $formatted = number_format($price,
1971
+ (isset($pmpro_currencies[$pmpro_currency]['decimals']) ? (int)$pmpro_currencies[$pmpro_currency]['decimals'] : 2),
1972
+ (isset($pmpro_currencies[$pmpro_currency]['decimal_separator']) ? $pmpro_currencies[$pmpro_currency]['decimal_separator'] : '.'),
1973
+ (isset($pmpro_currencies[$pmpro_currency]['thousands_separator']) ? $pmpro_currencies[$pmpro_currency]['thousands_separator'] : ',')
1974
+ );
1975
+
1976
+ //which side is the symbol on?
1977
+ if(!empty($pmpro_currencies[$pmpro_currency]['position']) && $pmpro_currencies[$pmpro_currency]['position']== 'left')
1978
+ $formatted = $pmpro_currency_symbol . $formatted;
1979
+ else
1980
+ $formatted = $formatted . $pmpro_currency_symbol;
1981
+ }
1982
+ else
1983
+ $formatted = $pmpro_currency_symbol . $formatted; //default to symbol on the left
1984
+
1985
+ //filter
1986
+ return apply_filters('pmpro_format_price', $formatted, $price, $pmpro_currency, $pmpro_currency_symbol);
1987
+ }
1988
+
1989
+ /**
1990
+ * Which side does the currency symbol go on?
1991
+ *
1992
+ * @since 1.7.15
1993
+ */
1994
+ function pmpro_getCurrencyPosition()
1995
+ {
1996
+ global $pmpro_currency, $pmpro_currencies;
1997
+
1998
+ if(!empty($pmpro_currencies[$pmpro_currency]) && is_array($pmpro_currencies[$pmpro_currency]) && !empty($pmpro_currencies[$pmpro_currency]['position']))
1999
+ return $pmpro_currencies[$pmpro_currency]['position'];
2000
+ else
2001
+ return "left";
2002
+ }
2003
+
2004
+ /*
2005
+ * What gateway should we be using?
2006
+ *
2007
+ * @since 1.8
2008
+ */
2009
+ function pmpro_getGateway()
2010
+ {
2011
+ //grab from param or options
2012
+ if (!empty($_REQUEST['gateway']))
2013
+ $gateway = $_REQUEST['gateway']; //gateway passed as param
2014
+ elseif (!empty($_REQUEST['review']))
2015
+ $gateway = "paypalexpress"; //if review param assume paypalexpress
2016
+ else
2017
+ $gateway = pmpro_getOption("gateway"); //get from options
2018
+
2019
+ //set valid gateways - the active gateway in the settings and any gateway added through the filter will be allowed
2020
+ if(pmpro_getOption("gateway", true) == "paypal")
2021
+ $valid_gateways = apply_filters("pmpro_valid_gateways", array("paypal", "paypalexpress"));
2022
+ else
2023
+ $valid_gateways = apply_filters("pmpro_valid_gateways", array(pmpro_getOption("gateway", true)));
2024
+
2025
+ //make sure it's valid
2026
+ if(!in_array($gateway, $valid_gateways))
2027
+ $gateway = false;
2028
+
2029
+ //filter for good measure
2030
+ $gateway = apply_filters('pmpro_get_gateway', $gateway, $valid_gateways);
2031
+
2032
+ return $gateway;
2033
+ }
2034
+
2035
+ /*
2036
+ * Does the date provided fall in this month.
2037
+ * Used in logins/visits/views report.
2038
+ *
2039
+ * @since 1.8.3
2040
+ */
2041
+ function pmpro_isDateThisMonth($str)
2042
+ {
2043
+ $now = current_time('timestamp');
2044
+ $this_month = intval(date("n", $now));
2045
+ $this_year = intval(date("Y", $now));
2046
+
2047
+ $date = strtotime($str, $now);
2048
+ $date_month = intval(date("n", $date));
2049
+ $date_year = intval(date("Y", $date));
2050
+
2051
+ if($date_month === $this_month && $date_year === $this_year)
2052
+ return true;
2053
+ else
2054
+ return false;
2055
+ }
2056
+
2057
+ /**
2058
+ * Function to generate PMPro front end pages.
2059
+ *
2060
+ * @param array $pages {
2061
+ * Formatted as array($name => $title) or array(array('title'=>'The Title', 'content'=>'The Content'))
2062
+ *
2063
+ * @type string $name Page name. (Letters, numbers, and underscores only.)
2064
+ * @type string $title Page title.
2065
+ * }
2066
+ * @return array $created_pages Created page IDs.
2067
+ * @since 1.8.5
2068
+ */
2069
+ function pmpro_generatePages($pages) {
2070
+
2071
+ global $pmpro_pages;
2072
+
2073
+ $pages_created = array();
2074
+
2075
+ if(!empty($pages)) {
2076
+ foreach($pages as $name => $page) {
2077
+
2078
+ //does it already exist?
2079
+ if(!empty($pmpro_pages[$name]))
2080
+ continue;
2081
+
2082
+ //no id set. create an array to store the page info
2083
+ if(is_array($page)) {
2084
+ $title = $page['title'];
2085
+ $content = $page['content'];
2086
+ } else {
2087
+ $title = $page;
2088
+ $content = '[pmpro_' . $name . ']';
2089
+ }
2090
+
2091
+ $insert = array(
2092
+ 'post_title' => $title,
2093
+ 'post_status' => 'publish',
2094
+ 'post_type' => 'page',
2095
+ 'post_content' => $content,
2096
+ 'comment_status' => 'closed',
2097
+ 'ping_status' => 'closed'
2098
+ );
2099
+
2100
+ //make non-account pages a subpage of account
2101
+ if ($name != "account") {
2102
+ $insert['post_parent'] = $pmpro_pages['account'];
2103
+ }
2104
+
2105
+ //create the page
2106
+ $pmpro_pages[$name] = wp_insert_post($insert);
2107
+
2108
+ //update the option too
2109
+ pmpro_setOption($name . "_page_id", $pmpro_pages[$name]);
2110
+ $pages_created[] = $pmpro_pages[$name];
2111
+ }
2112
+ }
2113
+
2114
+ return $pages_created;
2115
+ }
2116
+
paid-memberships-pro.php CHANGED
@@ -3,7 +3,7 @@
3
  Plugin Name: Paid Memberships Pro
4
  Plugin URI: http://www.paidmembershipspro.com
5
  Description: Plugin to Handle Memberships
6
- Version: 1.8.6.2
7
  Author: Stranger Studios
8
  Author URI: http://www.strangerstudios.com
9
  */
@@ -13,7 +13,7 @@ Author URI: http://www.strangerstudios.com
13
  */
14
 
15
  //version constant
16
- define("PMPRO_VERSION", "1.8.6.2");
17
 
18
  //if the session has been started yet, start it (ignore if running from command line)
19
  if(defined('STDIN') )
3
  Plugin Name: Paid Memberships Pro
4
  Plugin URI: http://www.paidmembershipspro.com
5
  Description: Plugin to Handle Memberships
6
+ Version: 1.8.6.1
7
  Author: Stranger Studios
8
  Author URI: http://www.strangerstudios.com
9
  */
13
  */
14
 
15
  //version constant
16
+ define("PMPRO_VERSION", "1.8.6.1");
17
 
18
  //if the session has been started yet, start it (ignore if running from command line)
19
  if(defined('STDIN') )
preheaders/checkout.php CHANGED
@@ -1,756 +1,754 @@
1
- <?php
2
- global $post, $gateway, $wpdb, $besecure, $discount_code, $discount_code_id, $pmpro_level, $pmpro_levels, $pmpro_msg, $pmpro_msgt, $pmpro_review, $skip_account_fields, $pmpro_paypal_token, $pmpro_show_discount_code, $pmpro_error_fields, $pmpro_required_billing_fields, $pmpro_required_user_fields, $wp_version, $current_user;
3
-
4
- //make sure we know current user's membership level
5
- if ($current_user->ID)
6
- $current_user->membership_level = pmpro_getMembershipLevelForUser($current_user->ID);
7
-
8
- //this var stores fields with errors so we can make them red on the frontend
9
- $pmpro_error_fields = array();
10
-
11
- //blank array for required fields, set below
12
- $pmpro_required_billing_fields = array();
13
- $pmpro_required_user_fields = array();
14
-
15
- //was a gateway passed?
16
- if (!empty($_REQUEST['gateway']))
17
- $gateway = $_REQUEST['gateway'];
18
- elseif (!empty($_REQUEST['review']))
19
- $gateway = "paypalexpress";
20
- else
21
- $gateway = pmpro_getOption("gateway");
22
-
23
- //set valid gateways - the active gateway in the settings and any gateway added through the filter will be allowed
24
- if (pmpro_getOption("gateway", true) == "paypal")
25
- $valid_gateways = apply_filters("pmpro_valid_gateways", array("paypal", "paypalexpress"));
26
- else
27
- $valid_gateways = apply_filters("pmpro_valid_gateways", array(pmpro_getOption("gateway", true)));
28
-
29
- //let's add an error now, if an invalid gateway is set
30
- if (!in_array($gateway, $valid_gateways))
31
- {
32
- $pmpro_msg = __("Invalid gateway.", 'pmpro');
33
- $pmpro_msgt = "pmpro_error";
34
- }
35
-
36
- //what level are they purchasing? (discount code passed)
37
- if (!empty($_REQUEST['level']) && !empty($_REQUEST['discount_code'])) {
38
- $discount_code = preg_replace("/[^A-Za-z0-9\-]/", "", $_REQUEST['discount_code']);
39
- $discount_code_id = $wpdb->get_var("SELECT id FROM $wpdb->pmpro_discount_codes WHERE code = '" . $discount_code . "' LIMIT 1");
40
-
41
- //check code
42
- $code_check = pmpro_checkDiscountCode($discount_code, (int)$_REQUEST['level'], true);
43
- if ($code_check[0] == false) {
44
- //error
45
- $pmpro_msg = $code_check[1];
46
- $pmpro_msgt = "pmpro_error";
47
-
48
- //don't use this code
49
- $use_discount_code = false;
50
- } else {
51
- $sqlQuery = "SELECT l.id, cl.*, l.name, l.description, l.allow_signups FROM $wpdb->pmpro_discount_codes_levels cl LEFT JOIN $wpdb->pmpro_membership_levels l ON cl.level_id = l.id LEFT JOIN $wpdb->pmpro_discount_codes dc ON dc.id = cl.code_id WHERE dc.code = '" . $discount_code . "' AND cl.level_id = '" . (int)$_REQUEST['level'] . "' LIMIT 1";
52
- $pmpro_level = $wpdb->get_row($sqlQuery);
53
-
54
- //if the discount code doesn't adjust the level, let's just get the straight level
55
- if (empty($pmpro_level))
56
- $pmpro_level = $wpdb->get_row("SELECT * FROM $wpdb->pmpro_membership_levels WHERE id = '" . (int)$_REQUEST['level'] . "' LIMIT 1");
57
-
58
- //filter adjustments to the level
59
- $pmpro_level->code_id = $discount_code_id;
60
- $pmpro_level = apply_filters("pmpro_discount_code_level", $pmpro_level, $discount_code_id);
61
-
62
- $use_discount_code = true;
63
- }
64
- }
65
-
66
- //what level are they purchasing? (no discount code)
67
- if (empty($pmpro_level) && !empty($_REQUEST['level'])) {
68
- $pmpro_level = $wpdb->get_row("SELECT * FROM $wpdb->pmpro_membership_levels WHERE id = '" . esc_sql($_REQUEST['level']) . "' AND allow_signups = 1 LIMIT 1");
69
- } elseif (empty($pmpro_level)) {
70
- //check if a level is defined in custom fields
71
- $default_level = get_post_meta($post->ID, "pmpro_default_level", true);
72
- if (!empty($default_level))
73
- {
74
- $pmpro_level = $wpdb->get_row("SELECT * FROM $wpdb->pmpro_membership_levels WHERE id = '" . esc_sql($default_level) . "' AND allow_signups = 1 LIMIT 1");
75
- }
76
- }
77
-
78
- //filter the level (for upgrades, etc)
79
- $pmpro_level = apply_filters("pmpro_checkout_level", $pmpro_level);
80
-
81
- if (empty($pmpro_level->id))
82
- {
83
- wp_redirect(pmpro_url("levels"));
84
- exit(0);
85
- }
86
-
87
- //enqueue some scripts
88
- wp_enqueue_script('jquery.creditCardValidator', plugins_url('/js/jquery.creditCardValidator.js' , dirname(__FILE__ )), array( 'jquery' ));
89
-
90
- global $wpdb, $current_user, $pmpro_requirebilling;
91
- //unless we're submitting a form, let's try to figure out if https should be used
92
-
93
- if (!pmpro_isLevelFree($pmpro_level)) {
94
- //require billing and ssl
95
- $pagetitle = __("Checkout: Payment Information", 'pmpro');
96
- $pmpro_requirebilling = true;
97
- $besecure = pmpro_getOption("use_ssl");
98
- } else {
99
- //no payment so we don't need ssl
100
- $pagetitle = __("Set Up Your Account", 'pmpro');
101
- $pmpro_requirebilling = false;
102
- $besecure = false;
103
- }
104
-
105
- //in case a discount code was used or something else made the level free, but we're already over ssl
106
- if (!$besecure && !empty($_REQUEST['submit-checkout']) && is_ssl())
107
- $besecure = true; //be secure anyway since we're already checking out
108
-
109
- //action to run extra code for gateways/etc
110
- do_action('pmpro_checkout_preheader');
111
-
112
- //get all levels in case we need them
113
- global $pmpro_levels;
114
- $pmpro_levels = pmpro_getAllLevels();
115
-
116
- //should we show the discount code field?
117
- if ($wpdb->get_var("SELECT id FROM $wpdb->pmpro_discount_codes LIMIT 1"))
118
- $pmpro_show_discount_code = true;
119
- else
120
- $pmpro_show_discount_code = false;
121
- $pmpro_show_discount_code = apply_filters("pmpro_show_discount_code", $pmpro_show_discount_code);
122
-
123
- //by default we show the account fields if the user isn't logged in
124
- if ($current_user->ID) {
125
- $skip_account_fields = true;
126
- } else {
127
- $skip_account_fields = false;
128
- }
129
- //in case people want to have an account created automatically
130
- $skip_account_fields = apply_filters("pmpro_skip_account_fields", $skip_account_fields, $current_user);
131
-
132
- //some options
133
- global $tospage;
134
- $tospage = pmpro_getOption("tospage");
135
- if ($tospage)
136
- $tospage = get_post($tospage);
137
-
138
- //load em up (other fields)
139
- global $username, $password, $password2, $bfirstname, $blastname, $baddress1, $baddress2, $bcity, $bstate, $bzipcode, $bcountry, $bphone, $bemail, $bconfirmemail, $CardType, $AccountNumber, $ExpirationMonth, $ExpirationYear;
140
-
141
- if (isset($_REQUEST['order_id']))
142
- $order_id = intval($_REQUEST['order_id']);
143
- else
144
- $order_id = "";
145
- if (isset($_REQUEST['bfirstname']))
146
- $bfirstname = sanitize_text_field(stripslashes($_REQUEST['bfirstname']));
147
- else
148
- $bfirstname = "";
149
- if (isset($_REQUEST['blastname']))
150
- $blastname = sanitize_text_field(stripslashes($_REQUEST['blastname']));
151
- else
152
- $blastname = "";
153
- if (isset($_REQUEST['fullname']))
154
- $fullname = $_REQUEST['fullname']; //honeypot for spammers
155
- if (isset($_REQUEST['baddress1']))
156
- $baddress1 = sanitize_text_field(stripslashes($_REQUEST['baddress1']));
157
- else
158
- $baddress1 = "";
159
- if (isset($_REQUEST['baddress2']))
160
- $baddress2 = sanitize_text_field(stripslashes($_REQUEST['baddress2']));
161
- else
162
- $baddress2 = "";
163
- if (isset($_REQUEST['bcity']))
164
- $bcity = sanitize_text_field(stripslashes($_REQUEST['bcity']));
165
- else
166
- $bcity = "";
167
-
168
- if (isset($_REQUEST['bstate']))
169
- $bstate = sanitize_text_field(stripslashes($_REQUEST['bstate']));
170
- else
171
- $bstate = "";
172
-
173
- //convert long state names to abbreviations
174
- if (!empty($bstate))
175
- {
176
- global $pmpro_states;
177
- foreach($pmpro_states as $abbr => $state)
178
- {
179
- if ($bstate == $state)
180
- {
181
- $bstate = $abbr;
182
- break;
183
- }
184
- }
185
- }
186
-
187
- if (isset($_REQUEST['bzipcode']))
188
- $bzipcode = sanitize_text_field(stripslashes($_REQUEST['bzipcode']));
189
- else
190
- $bzipcode = "";
191
- if (isset($_REQUEST['bcountry']))
192
- $bcountry = sanitize_text_field(stripslashes($_REQUEST['bcountry']));
193
- else
194
- $bcountry = "";
195
- if (isset($_REQUEST['bphone']))
196
- $bphone = sanitize_text_field(stripslashes($_REQUEST['bphone']));
197
- else
198
- $bphone = "";
199
- if (isset($_REQUEST['bemail']))
200
- $bemail = sanitize_email(stripslashes($_REQUEST['bemail']));
201
- else
202
- $bemail = "";
203
- if (isset($_REQUEST['bconfirmemail_copy']))
204
- $bconfirmemail = $bemail;
205
- elseif (isset($_REQUEST['bconfirmemail']))
206
- $bconfirmemail = sanitize_email(stripslashes($_REQUEST['bconfirmemail']));
207
- else
208
- $bconfirmemail = "";
209
-
210
- if (isset($_REQUEST['CardType']) && !empty($_REQUEST['AccountNumber']))
211
- $CardType = sanitize_text_field($_REQUEST['CardType']);
212
- else
213
- $CardType = "";
214
- if (isset($_REQUEST['AccountNumber']))
215
- $AccountNumber = sanitize_text_field($_REQUEST['AccountNumber']);
216
- else
217
- $AccountNumber = "";
218
-
219
- if (isset($_REQUEST['ExpirationMonth']))
220
- $ExpirationMonth = sanitize_text_field($_REQUEST['ExpirationMonth']);
221
- else
222
- $ExpirationMonth = "";
223
- if (isset($_REQUEST['ExpirationYear']))
224
- $ExpirationYear = sanitize_text_field($_REQUEST['ExpirationYear']);
225
- else
226
- $ExpirationYear = "";
227
- if (isset($_REQUEST['CVV']))
228
- $CVV = sanitize_text_field($_REQUEST['CVV']);
229
- else
230
- $CVV = "";
231
-
232
- if (isset($_REQUEST['discount_code']))
233
- $discount_code = sanitize_text_field($_REQUEST['discount_code']);
234
- else
235
- $discount_code = "";
236
- if (isset($_REQUEST['username']))
237
- $username = sanitize_user($_REQUEST['username']);
238
- else
239
- $username = "";
240
- if (isset($_REQUEST['password']))
241
- $password = $_REQUEST['password'];
242
- else
243
- $password = "";
244
- if (isset($_REQUEST['password2_copy']))
245
- $password2 = $password;
246
- elseif (isset($_REQUEST['password2']))
247
- $password2 = $_REQUEST['password2'];
248
- else
249
- $password2 = "";
250
- if (isset($_REQUEST['tos']))
251
- $tos = intval($_REQUEST['tos']);
252
- else
253
- $tos = "";
254
-
255
- //_x stuff in case they clicked on the image button with their mouse
256
- if (isset($_REQUEST['submit-checkout']))
257
- $submit = $_REQUEST['submit-checkout'];
258
- if (empty($submit) && isset($_REQUEST['submit-checkout_x']) )
259
- $submit = $_REQUEST['submit-checkout_x'];
260
- if (isset($submit) && $submit === "0")
261
- $submit = true;
262
- elseif (!isset($submit))
263
- $submit = false;
264
-
265
- //require fields
266
- $pmpro_required_billing_fields = array(
267
- "bfirstname" => $bfirstname,
268
- "blastname" => $blastname,
269
- "baddress1" => $baddress1,
270
- "bcity" => $bcity,
271
- "bstate" => $bstate,
272
- "bzipcode" => $bzipcode,
273
- "bphone" => $bphone,
274
- "bemail" => $bemail,
275
- "bcountry" => $bcountry,
276
- "CardType" => $CardType,
277
- "AccountNumber" => $AccountNumber,
278
- "ExpirationMonth" => $ExpirationMonth,
279
- "ExpirationYear" => $ExpirationYear,
280
- "CVV" => $CVV
281
- );
282
- $pmpro_required_billing_fields = apply_filters("pmpro_required_billing_fields", $pmpro_required_billing_fields);
283
- $pmpro_required_user_fields = array(
284
- "username" => $username,
285
- "password" => $password,
286
- "password2" => $password2,
287
- "bemail" => $bemail,
288
- "bconfirmemail" => $bconfirmemail
289
- );
290
- $pmpro_required_user_fields = apply_filters("pmpro_required_user_fields", $pmpro_required_user_fields);
291
-
292
- //pmpro_confirmed is set to true later if payment goes through
293
- $pmpro_confirmed = false;
294
-
295
- //check their fields if they clicked continue
296
- if ($submit && $pmpro_msgt != "pmpro_error") {
297
-
298
- //make sure javascript is ok
299
- if (apply_filters("pmpro_require_javascript_for_checkout", true) && !empty($_REQUEST['checkjavascript']) && empty($_REQUEST['javascriptok'])) {
300
- pmpro_setMessage(__("There are JavaScript errors on the page. Please contact the webmaster.", "pmpro"), "pmpro_error");
301
- }
302
-
303
- //if we're skipping the account fields and there is no user, we need to create a username and password
304
- if ($skip_account_fields && !$current_user->ID) {
305
- $username = pmpro_generateUsername($bfirstname, $blastname, $bemail);
306
- if (empty($username))
307
- $username = pmpro_getDiscountCode();
308
- $password = pmpro_getDiscountCode() . pmpro_getDiscountCode(); //using two random discount codes
309
- $password2 = $password;
310
- }
311
-
312
- //check billing fields
313
- if ($pmpro_requirebilling) {
314
- //filter
315
- foreach($pmpro_required_billing_fields as $key => $field) {
316
- if (!$field) {
317
- $pmpro_error_fields[] = $key;
318
- }
319
- }
320
- }
321
-
322
- //check user fields
323
- if (empty($current_user->ID)) {
324
- foreach($pmpro_required_user_fields as $key => $field) {
325
- if (!$field) {
326
- $pmpro_error_fields[] = $key;
327
- }
328
- }
329
- }
330
-
331
- if (!empty($pmpro_error_fields)) {
332
- pmpro_setMessage(__("Please complete all required fields.", "pmpro"), "pmpro_error");
333
- }
334
- if (!empty($password) && $password != $password2) {
335
- pmpro_setMessage(__("Your passwords do not match. Please try again.", "pmpro"), "pmpro_error");
336
- $pmpro_error_fields[] = "password";
337
- $pmpro_error_fields[] = "password2";
338
- }
339
- if (!empty($bemail) && $bemail != $bconfirmemail) {
340
- pmpro_setMessage(__("Your email addresses do not match. Please try again.", "pmpro"), "pmpro_error");
341
- $pmpro_error_fields[] = "bemail";
342
- $pmpro_error_fields[] = "bconfirmemail";
343
- }
344
- if (!empty($bemail) && !is_email($bemail)) {
345
- pmpro_setMessage(__("The email address entered is in an invalid format. Please try again.", "pmpro"), "pmpro_error");
346
- $pmpro_error_fields[] = "bemail";
347
- $pmpro_error_fields[] = "bconfirmemail";
348
- }
349
- if (!empty($tospage) && empty($tos)) {
350
- pmpro_setMessage(sprintf(__("Please check the box to agree to the %s.", "pmpro"), $tospage->post_title), "pmpro_error");
351
- $pmpro_error_fields[] = "tospage";
352
- }
353
- if (!in_array($gateway, $valid_gateways)) {
354
- pmpro_setMessage(__("Invalid gateway.", "pmpro"), "pmpro_error");
355
- }
356
- if (!empty($fullname)) {
357
- pmpro_setMessage(__("Are you a spammer?", "pmpro"), "pmpro_error");
358
- }
359
-
360
- if ($pmpro_msgt == "pmpro_error")
361
- $pmpro_continue_registration = false;
362
- else
363
- $pmpro_continue_registration = true;
364
- $pmpro_continue_registration = apply_filters("pmpro_registration_checks", $pmpro_continue_registration);
365
-
366
- if ($pmpro_continue_registration) {
367
- //if creating a new user, check that the email and username are available
368
- if (empty($current_user->ID)) {
369
- $oldusername = $wpdb->get_var("SELECT user_login FROM $wpdb->users WHERE user_login = '" . esc_sql($username) . "' LIMIT 1");
370
- $oldemail = $wpdb->get_var("SELECT user_email FROM $wpdb->users WHERE user_email = '" . esc_sql($bemail) . "' LIMIT 1");
371
-
372
- //this hook can be used to allow multiple accounts with the same email address
373
- $oldemail = apply_filters("pmpro_checkout_oldemail", $oldemail);
374
- }
375
-
376
- if (!empty($oldusername)) {
377
- pmpro_setMessage(__("That username is already taken. Please try another.", "pmpro"), "pmpro_error");
378
- $pmpro_error_fields[] = "username";
379
- }
380
-
381
- if (!empty($oldemail)) {
382
- pmpro_setMessage(__("That email address is already taken. Please try another.", "pmpro"), "pmpro_error");
383
- $pmpro_error_fields[] = "bemail";
384
- $pmpro_error_fields[] = "bconfirmemail";
385
- }
386
-
387
- //only continue if there are no other errors yet
388
- if ($pmpro_msgt != "pmpro_error") {
389
- //check recaptcha first
390
- global $recaptcha;
391
- if (!$skip_account_fields && ($recaptcha == 2 || ($recaptcha == 1 && pmpro_isLevelFree($pmpro_level)))) {
392
- global $recaptcha_privatekey;
393
-
394
- if(isset($_POST["recaptcha_challenge_field"]))
395
- {
396
- //using older recaptcha lib
397
- $resp = recaptcha_check_answer($recaptcha_privatekey,
398
- $_SERVER["REMOTE_ADDR"],
399
- $_POST["recaptcha_challenge_field"],
400
- $_POST["recaptcha_response_field"]);
401
-
402
- $recaptcha_valid = $resp->is_valid;
403
- $recaptcha_errors = $resp->error;
404
- }
405
- else
406
- {
407
- //using newer recaptcha lib
408
- $reCaptcha = new pmpro_ReCaptcha($recaptcha_privatekey);
409
- $resp = $reCaptcha->verifyResponse($_SERVER["REMOTE_ADDR"], $_POST["g-recaptcha-response"]);
410
-
411
- $recaptcha_valid = $resp->success;
412
- $recaptcha_errors = $resp->errorCodes;
413
- }
414
-
415
- if (!$recaptcha_valid) {
416
- $pmpro_msg = sprintf(__("reCAPTCHA failed. (%s) Please try again.", "pmpro"), $recaptcha_errors);
417
- $pmpro_msgt = "pmpro_error";
418
- } else {
419
- // Your code here to handle a successful verification
420
- if ($pmpro_msgt != "pmpro_error")
421
- $pmpro_msg = "All good!";
422
- }
423
- } else {
424
- if ($pmpro_msgt != "pmpro_error")
425
- $pmpro_msg = "All good!";
426
- }
427
-
428
- //no errors yet
429
- if ($pmpro_msgt != "pmpro_error") {
430
- do_action('pmpro_checkout_before_processing');
431
-
432
- //process checkout if required
433
- if ($pmpro_requirebilling) {
434
- $morder = new MemberOrder();
435
- $morder->membership_id = $pmpro_level->id;
436
- $morder->membership_name = $pmpro_level->name;
437
- $morder->discount_code = $discount_code;
438
- $morder->InitialPayment = $pmpro_level->initial_payment;
439
- $morder->PaymentAmount = $pmpro_level->billing_amount;
440
- $morder->ProfileStartDate = date("Y-m-d", current_time("timestamp")) . "T0:0:0";
441
- $morder->BillingPeriod = $pmpro_level->cycle_period;
442
- $morder->BillingFrequency = $pmpro_level->cycle_number;
443
-
444
- if ($pmpro_level->billing_limit)
445
- $morder->TotalBillingCycles = $pmpro_level->billing_limit;
446
-
447
- if (pmpro_isLevelTrial($pmpro_level)) {
448
- $morder->TrialBillingPeriod = $pmpro_level->cycle_period;
449
- $morder->TrialBillingFrequency = $pmpro_level->cycle_number;
450
- $morder->TrialBillingCycles = $pmpro_level->trial_limit;
451
- $morder->TrialAmount = $pmpro_level->trial_amount;
452
- }
453
-
454
- //credit card values
455
- $morder->cardtype = $CardType;
456
- $morder->accountnumber = $AccountNumber;
457
- $morder->expirationmonth = $ExpirationMonth;
458
- $morder->expirationyear = $ExpirationYear;
459
- $morder->ExpirationDate = $ExpirationMonth . $ExpirationYear;
460
- $morder->ExpirationDate_YdashM = $ExpirationYear . "-" . $ExpirationMonth;
461
- $morder->CVV2 = $CVV;
462
-
463
- //not saving email in order table, but the sites need it
464
- $morder->Email = $bemail;
465
-
466
- //sometimes we need these split up
467
- $morder->FirstName = $bfirstname;
468
- $morder->LastName = $blastname;
469
- $morder->Address1 = $baddress1;
470
- $morder->Address2 = $baddress2;
471
-
472
- //other values
473
- $morder->billing = new stdClass();
474
- $morder->billing->name = $bfirstname . " " . $blastname;
475
- $morder->billing->street = trim($baddress1 . " " . $baddress2);
476
- $morder->billing->city = $bcity;
477
- $morder->billing->state = $bstate;
478
- $morder->billing->country = $bcountry;
479
- $morder->billing->zip = $bzipcode;
480
- $morder->billing->phone = $bphone;
481
-
482
- //$gateway = pmpro_getOption("gateway");
483
- $morder->gateway = $gateway;
484
- $morder->setGateway();
485
-
486
- //setup level var
487
- $morder->getMembershipLevel();
488
- $morder->membership_level = apply_filters("pmpro_checkout_level", $morder->membership_level);
489
-
490
- //tax
491
- $morder->subtotal = $morder->InitialPayment;
492
- $morder->getTax();
493
-
494
- //filter for order, since v1.8
495
- $morder = apply_filters("pmpro_checkout_order", $morder);
496
-
497
- $pmpro_processed = $morder->process();
498
-
499
- if (!empty($pmpro_processed))
500
- {
501
- $pmpro_msg = __("Payment accepted.", "pmpro");
502
- $pmpro_msgt = "pmpro_success";
503
- $pmpro_confirmed = true;
504
- }
505
- else
506
- {
507
- $pmpro_msg = $morder->error;
508
- if (empty($pmpro_msg))
509
- $pmpro_msg = __("Unknown error generating account. Please contact us to set up your membership.", "pmpro");
510
- $pmpro_msgt = "pmpro_error";
511
- }
512
-
513
- }
514
- else // !$pmpro_requirebilling
515
- {
516
- //must have been a free membership, continue
517
- $pmpro_confirmed = true;
518
- }
519
- }
520
- }
521
- } //endif ($pmpro_continue_registration)
522
- }
523
-
524
- //make sure we have at least an empty morder here to avoid a warning
525
- if (empty($morder))
526
- $morder = false;
527
-
528
- //Hook to check payment confirmation or replace it. If we get an array back, pull the values (morder) out
529
- $pmpro_confirmed = apply_filters('pmpro_checkout_confirmed', $pmpro_confirmed, $morder);
530
- if (is_array($pmpro_confirmed))
531
- extract($pmpro_confirmed);
532
-
533
- //if payment was confirmed create/update the user.
534
- if (!empty($pmpro_confirmed)) {
535
- //just in case this hasn't been set yet
536
- $submit = true;
537
-
538
- //do we need to create a user account?
539
- if (!$current_user->ID) {
540
- /*
541
- create user
542
- */
543
- if (version_compare($wp_version, "3.1") < 0)
544
- require_once( ABSPATH . WPINC . '/registration.php'); //need this for WP versions before 3.1
545
-
546
- //first name
547
- if (!empty($_REQUEST['first_name']))
548
- $first_name = $_REQUEST['first_name'];
549
- else
550
- $first_name = $bfirstname;
551
- //last name
552
- if (!empty($_REQUEST['last_name']))
553
- $last_name = $_REQUEST['last_name'];
554
- else
555
- $last_name = $blastname;
556
-
557
- //insert user
558
- $new_user_array = apply_filters('pmpro_checkout_new_user_array', array(
559
- "user_login" => $username,
560
- "user_pass" => $password,
561
- "user_email" => $bemail,
562
- "first_name" => $first_name,
563
- "last_name" => $last_name)
564
- );
565
-
566
- $user_id = apply_filters('pmpro_new_user', '', $new_user_array);
567
- if (!$user_id)
568
- $user_id = wp_insert_user($new_user_array);
569
-
570
- if (!$user_id || is_wp_error($user_id)) {
571
- $pmpro_msg = __("Your payment was accepted, but there was an error setting up your account. Please contact us.", "pmpro");
572
- $pmpro_msgt = "pmpro_error";
573
- } elseif (apply_filters('pmpro_setup_new_user', true, $user_id, $new_user_array, $pmpro_level)) {
574
-
575
- //check pmpro_wp_new_user_notification filter before sending the default WP email
576
- if (apply_filters("pmpro_wp_new_user_notification", true, $user_id, $pmpro_level->id))
577
- wp_new_user_notification($user_id, $new_user_array['user_pass']);
578
-
579
- $wpuser = get_userdata($user_id);
580
-
581
- //make the user a subscriber
582
- $wpuser->set_role(get_option('default_role', 'subscriber'));
583
-
584
- //okay, log them in to WP
585
- $creds = array();
586
- $creds['user_login'] = $new_user_array['user_login'];
587
- $creds['user_password'] = $new_user_array['user_pass'];
588
- $creds['remember'] = true;
589
- $user = wp_signon( $creds, false );
590
-
591
- //setting some cookies
592
- wp_set_current_user($user_id, $username);
593
- wp_set_auth_cookie($user_id, true, apply_filters('pmpro_checkout_signon_secure', (force_ssl_login() || force_ssl_admin())));
594
- }
595
- }
596
- else
597
- $user_id = $current_user->ID;
598
-
599
- if ($user_id && !is_wp_error($user_id))
600
- {
601
- do_action('pmpro_checkout_before_change_membership_level', $user_id, $morder);
602
-
603
- //calculate the end date
604
- if (!empty($pmpro_level->expiration_number)) {
605
- $enddate = "'" . date("Y-m-d", strtotime("+ " . $pmpro_level->expiration_number . " " . $pmpro_level->expiration_period, current_time("timestamp"))) . "'";
606
- } else {
607
- $enddate = "NULL";
608
- }
609
-
610
- //update membership_user table.
611
- if (!empty($discount_code) && !empty($use_discount_code))
612
- $discount_code_id = $wpdb->get_var("SELECT id FROM $wpdb->pmpro_discount_codes WHERE code = '" . $discount_code . "' LIMIT 1");
613
- else
614
- $discount_code_id = "";
615
-
616
- //set the start date to NOW() but allow filters
617
- $startdate = apply_filters("pmpro_checkout_start_date", "'" . current_time("mysql") . "'", $user_id, $pmpro_level);
618
-
619
- $custom_level = array(
620
- 'user_id' => $user_id,
621
- 'membership_id' => $pmpro_level->id,
622
- 'code_id' => $discount_code_id,
623
- 'initial_payment' => $pmpro_level->initial_payment,
624
- 'billing_amount' => $pmpro_level->billing_amount,
625
- 'cycle_number' => $pmpro_level->cycle_number,
626
- 'cycle_period' => $pmpro_level->cycle_period,
627
- 'billing_limit' => $pmpro_level->billing_limit,
628
- 'trial_amount' => $pmpro_level->trial_amount,
629
- 'trial_limit' => $pmpro_level->trial_limit,
630
- 'startdate' => $startdate,
631
- 'enddate' => $enddate);
632
-
633
- if (pmpro_changeMembershipLevel($custom_level, $user_id, 'changed')) {
634
- //we're good
635
- //blank order for free levels
636
- if (empty($morder)) {
637
- $morder = new MemberOrder();
638
- $morder->InitialPayment = 0;
639
- $morder->Email = $bemail;
640
- $morder->gateway = "free";
641
-
642
- $morder = apply_filters("pmpro_checkout_order_free", $morder);
643
- }
644
-
645
- //add an item to the history table, cancel old subscriptions
646
- if (!empty($morder)) {
647
- $morder->user_id = $user_id;
648
- $morder->membership_id = $pmpro_level->id;
649
- $morder->saveOrder();
650
- }
651
-
652
- //update the current user
653
- global $current_user;
654
- if (!$current_user->ID && $user->ID)
655
- $current_user = $user; //in case the user just signed up
656
- pmpro_set_current_user();
657
-
658
- //add discount code use
659
- if ($discount_code && $use_discount_code) {
660
- if (!empty($morder->id))
661
- $code_order_id = $morder->id;
662
- else
663
- $code_order_id = "";
664
-
665
- $wpdb->query("INSERT INTO $wpdb->pmpro_discount_codes_uses (code_id, user_id, order_id, timestamp) VALUES('" . $discount_code_id . "', '" . $user_id . "', '" . intval($code_order_id) . "', '" . current_time("mysql") . "')");
666
- }
667
-
668
- //save billing info ect, as user meta
669
- $meta_keys = array("pmpro_bfirstname", "pmpro_blastname", "pmpro_baddress1", "pmpro_baddress2", "pmpro_bcity", "pmpro_bstate", "pmpro_bzipcode", "pmpro_bcountry", "pmpro_bphone", "pmpro_bemail", "pmpro_CardType", "pmpro_AccountNumber", "pmpro_ExpirationMonth", "pmpro_ExpirationYear");
670
- $meta_values = array($bfirstname, $blastname, $baddress1, $baddress2, $bcity, $bstate, $bzipcode, $bcountry, $bphone, $bemail, $CardType, hideCardNumber($AccountNumber), $ExpirationMonth, $ExpirationYear);
671
- pmpro_replaceUserMeta($user_id, $meta_keys, $meta_values);
672
-
673
- //save first and last name fields
674
- if (!empty($bfirstname)) {
675
- $old_firstname = get_user_meta($user_id, "first_name", true);
676
- if (empty($old_firstname))
677
- update_user_meta($user_id, "first_name", $bfirstname);
678
- }
679
- if (!empty($blastname)) {
680
- $old_lastname = get_user_meta($user_id, "last_name", true);
681
- if (empty($old_lastname))
682
- update_user_meta($user_id, "last_name", $blastname);
683
- }
684
-
685
- //show the confirmation
686
- $ordersaved = true;
687
-
688
- //hook
689
- do_action("pmpro_after_checkout", $user_id, $morder); //added $morder param in v2.0
690
-
691
- //setup some values for the emails
692
- if (!empty($morder))
693
- $invoice = new MemberOrder($morder->id);
694
- else
695
- $invoice = NULL;
696
- $current_user->membership_level = $pmpro_level; //make sure they have the right level info
697
-
698
- //send email to member
699
- $pmproemail = new PMProEmail();
700
- $pmproemail->sendCheckoutEmail($current_user, $invoice);
701
-
702
- //send email to admin
703
- $pmproemail = new PMProEmail();
704
- $pmproemail->sendCheckoutAdminEmail($current_user, $invoice);
705
-
706
- //redirect to confirmation
707
- $rurl = pmpro_url("confirmation", "?level=" . $pmpro_level->id);
708
- $rurl = apply_filters("pmpro_confirmation_url", $rurl, $user_id, $pmpro_level);
709
- wp_redirect($rurl);
710
- exit;
711
- } else {
712
- //uh oh. we charged them then the membership creation failed
713
- if (isset($morder) && $morder->cancel()) {
714
- $pmpro_msg = __("IMPORTANT: Something went wrong during membership creation. Your credit card authorized, but we cancelled the order immediately. You should not try to submit this form again. Please contact the site owner to fix this issue.", "pmpro");
715
- $morder = NULL;
716
- } else {
717
- $pmpro_msg = __("IMPORTANT: Something went wrong during membership creation. Your credit card was charged, but we couldn't assign your membership. You should not submit this form again. Please contact the site owner to fix this issue.", "pmpro");
718
- }
719
- }
720
- }
721
- }
722
-
723
- //default values
724
- if (empty($submit)) {
725
- //show message if the payment gateway is not setup yet
726
- if ($pmpro_requirebilling && !pmpro_getOption("gateway", true)) {
727
- if (pmpro_isAdmin())
728
- $pmpro_msg = sprintf(__('You must <a href="%s">set up a Payment Gateway</a> before any payments will be processed.', 'pmpro'), get_admin_url(NULL, '/admin.php?page=pmpro-membershiplevels&view=payment'));
729
- else
730
- $pmpro_msg = __("A Payment Gateway must be set up before any payments will be processed.", "pmpro");
731
- $pmpro_msgt = "";
732
- }
733
-
734
- //default values from DB
735
- if (!empty($current_user->ID)) {
736
- $bfirstname = get_user_meta($current_user->ID, "pmpro_bfirstname", true);
737
- $blastname = get_user_meta($current_user->ID, "pmpro_blastname", true);
738
- $baddress1 = get_user_meta($current_user->ID, "pmpro_baddress1", true);
739
- $baddress2 = get_user_meta($current_user->ID, "pmpro_baddress2", true);
740
- $bcity = get_user_meta($current_user->ID, "pmpro_bcity", true);
741
- $bstate = get_user_meta($current_user->ID, "pmpro_bstate", true);
742
- $bzipcode = get_user_meta($current_user->ID, "pmpro_bzipcode", true);
743
- $bcountry = get_user_meta($current_user->ID, "pmpro_bcountry", true);
744
- $bphone = get_user_meta($current_user->ID, "pmpro_bphone", true);
745
- $bemail = get_user_meta($current_user->ID, "pmpro_bemail", true);
746
- $bconfirmemail = $bemail; //as of 1.7.5, just setting to bemail
747
- $CardType = get_user_meta($current_user->ID, "pmpro_CardType", true);
748
- //$AccountNumber = hideCardNumber(get_user_meta($current_user->ID, "pmpro_AccountNumber", true), false);
749
- $ExpirationMonth = get_user_meta($current_user->ID, "pmpro_ExpirationMonth", true);
750
- $ExpirationYear = get_user_meta($current_user->ID, "pmpro_ExpirationYear", true);
751
- }
752
- }
753
-
754
- //clear out XXXX numbers (e.g. with Stripe)
755
- if (!empty($AccountNumber) && strpos($AccountNumber, "XXXX") === 0)
756
- $AccountNumber = "";
1
+ <?php
2
+ global $post, $gateway, $wpdb, $besecure, $discount_code, $discount_code_id, $pmpro_level, $pmpro_levels, $pmpro_msg, $pmpro_msgt, $pmpro_review, $skip_account_fields, $pmpro_paypal_token, $pmpro_show_discount_code, $pmpro_error_fields, $pmpro_required_billing_fields, $pmpro_required_user_fields, $wp_version, $current_user;
3
+
4
+ //make sure we know current user's membership level
5
+ if ($current_user->ID)
6
+ $current_user->membership_level = pmpro_getMembershipLevelForUser($current_user->ID);
7
+
8
+ //this var stores fields with errors so we can make them red on the frontend
9
+ $pmpro_error_fields = array();
10
+
11
+ //blank array for required fields, set below
12
+ $pmpro_required_billing_fields = array();
13
+ $pmpro_required_user_fields = array();
14
+
15
+ //was a gateway passed?
16
+ if (!empty($_REQUEST['gateway']))
17
+ $gateway = $_REQUEST['gateway'];
18
+ elseif (!empty($_REQUEST['review']))
19
+ $gateway = "paypalexpress";
20
+ else
21
+ $gateway = pmpro_getOption("gateway");
22
+
23
+ //set valid gateways - the active gateway in the settings and any gateway added through the filter will be allowed
24
+ if (pmpro_getOption("gateway", true) == "paypal")
25
+ $valid_gateways = apply_filters("pmpro_valid_gateways", array("paypal", "paypalexpress"));
26
+ else
27
+ $valid_gateways = apply_filters("pmpro_valid_gateways", array(pmpro_getOption("gateway", true)));
28
+
29
+ //let's add an error now, if an invalid gateway is set
30
+ if (!in_array($gateway, $valid_gateways))
31
+ {
32
+ $pmpro_msg = __("Invalid gateway.", 'pmpro');
33
+ $pmpro_msgt = "pmpro_error";
34
+ }
35
+
36
+ //what level are they purchasing? (discount code passed)
37
+ if (!empty($_REQUEST['level']) && !empty($_REQUEST['discount_code'])) {
38
+ $discount_code = preg_replace("/[^A-Za-z0-9\-]/", "", $_REQUEST['discount_code']);
39
+ $discount_code_id = $wpdb->get_var("SELECT id FROM $wpdb->pmpro_discount_codes WHERE code = '" . $discount_code . "' LIMIT 1");
40
+
41
+ //check code
42
+ $code_check = pmpro_checkDiscountCode($discount_code, (int)$_REQUEST['level'], true);
43
+ if ($code_check[0] == false) {
44
+ //error
45
+ $pmpro_msg = $code_check[1];
46
+ $pmpro_msgt = "pmpro_error";
47
+
48
+ //don't use this code
49
+ $use_discount_code = false;
50
+ } else {
51
+ $sqlQuery = "SELECT l.id, cl.*, l.name, l.description, l.allow_signups FROM $wpdb->pmpro_discount_codes_levels cl LEFT JOIN $wpdb->pmpro_membership_levels l ON cl.level_id = l.id LEFT JOIN $wpdb->pmpro_discount_codes dc ON dc.id = cl.code_id WHERE dc.code = '" . $discount_code . "' AND cl.level_id = '" . (int)$_REQUEST['level'] . "' LIMIT 1";
52
+ $pmpro_level = $wpdb->get_row($sqlQuery);
53
+
54
+ //if the discount code doesn't adjust the level, let's just get the straight level
55
+ if (empty($pmpro_level))
56
+ $pmpro_level = $wpdb->get_row("SELECT * FROM $wpdb->pmpro_membership_levels WHERE id = '" . (int)$_REQUEST['level'] . "' LIMIT 1");
57
+
58
+ //filter adjustments to the level
59
+ $pmpro_level->code_id = $discount_code_id;
60
+ $pmpro_level = apply_filters("pmpro_discount_code_level", $pmpro_level, $discount_code_id);
61
+
62
+ $use_discount_code = true;
63
+ }
64
+ }
65
+
66
+ //what level are they purchasing? (no discount code)
67
+ if (empty($pmpro_level) && !empty($_REQUEST['level'])) {
68
+ $pmpro_level = $wpdb->get_row("SELECT * FROM $wpdb->pmpro_membership_levels WHERE id = '" . esc_sql($_REQUEST['level']) . "' AND allow_signups = 1 LIMIT 1");
69
+ } elseif (empty($pmpro_level)) {
70
+ //check if a level is defined in custom fields
71
+ $default_level = get_post_meta($post->ID, "pmpro_default_level", true);
72
+ if (!empty($default_level))
73
+ {
74
+ $pmpro_level = $wpdb->get_row("SELECT * FROM $wpdb->pmpro_membership_levels WHERE id = '" . esc_sql($default_level) . "' AND allow_signups = 1 LIMIT 1");
75
+ }
76
+ }
77
+
78
+ //filter the level (for upgrades, etc)
79
+ $pmpro_level = apply_filters("pmpro_checkout_level", $pmpro_level);
80
+
81
+ if (empty($pmpro_level->id))
82
+ {
83
+ wp_redirect(pmpro_url("levels"));
84
+ exit(0);
85
+ }
86
+
87
+ //enqueue some scripts
88
+ wp_enqueue_script('jquery.creditCardValidator', plugins_url('/js/jquery.creditCardValidator.js' , dirname(__FILE__ )), array( 'jquery' ));
89
+
90
+ global $wpdb, $current_user, $pmpro_requirebilling;
91
+ //unless we're submitting a form, let's try to figure out if https should be used
92
+
93
+ if (!pmpro_isLevelFree($pmpro_level)) {
94
+ //require billing and ssl
95
+ $pagetitle = __("Checkout: Payment Information", 'pmpro');
96
+ $pmpro_requirebilling = true;
97
+ $besecure = pmpro_getOption("use_ssl");
98
+ } else {
99
+ //no payment so we don't need ssl
100
+ $pagetitle = __("Set Up Your Account", 'pmpro');
101
+ $pmpro_requirebilling = false;
102
+ $besecure = false;
103
+ }
104
+
105
+ //in case a discount code was used or something else made the level free, but we're already over ssl
106
+ if (!$besecure && !empty($_REQUEST['submit-checkout']) && is_ssl())
107
+ $besecure = true; //be secure anyway since we're already checking out
108
+
109
+ //action to run extra code for gateways/etc
110
+ do_action('pmpro_checkout_preheader');
111
+
112
+ //get all levels in case we need them
113
+ global $pmpro_levels;
114
+ $pmpro_levels = pmpro_getAllLevels();
115
+
116
+ //should we show the discount code field?
117
+ if ($wpdb->get_var("SELECT id FROM $wpdb->pmpro_discount_codes LIMIT 1"))
118
+ $pmpro_show_discount_code = true;
119
+ else
120
+ $pmpro_show_discount_code = false;
121
+ $pmpro_show_discount_code = apply_filters("pmpro_show_discount_code", $pmpro_show_discount_code);
122
+
123
+ //by default we show the account fields if the user isn't logged in
124
+ if ($current_user->ID) {
125
+ $skip_account_fields = true;
126
+ } else {
127
+ $skip_account_fields = false;
128
+ }
129
+ //in case people want to have an account created automatically
130
+ $skip_account_fields = apply_filters("pmpro_skip_account_fields", $skip_account_fields, $current_user);
131
+
132
+ //some options
133
+ global $tospage;
134
+ $tospage = pmpro_getOption("tospage");
135
+ if ($tospage)
136
+ $tospage = get_post($tospage);
137
+
138
+ //load em up (other fields)
139
+ global $username, $password, $password2, $bfirstname, $blastname, $baddress1, $baddress2, $bcity, $bstate, $bzipcode, $bcountry, $bphone, $bemail, $bconfirmemail, $CardType, $AccountNumber, $ExpirationMonth, $ExpirationYear;
140
+
141
+ if (isset($_REQUEST['order_id']))
142
+ $order_id = intval($_REQUEST['order_id']);
143
+ else
144
+ $order_id = "";
145
+ if (isset($_REQUEST['bfirstname']))
146
+ $bfirstname = sanitize_text_field(stripslashes($_REQUEST['bfirstname']));
147
+ else
148
+ $bfirstname = "";
149
+ if (isset($_REQUEST['blastname']))
150
+ $blastname = sanitize_text_field(stripslashes($_REQUEST['blastname']));
151
+ else
152
+ $blastname = "";
153
+ if (isset($_REQUEST['fullname']))
154
+ $fullname = $_REQUEST['fullname']; //honeypot for spammers
155
+ if (isset($_REQUEST['baddress1']))
156
+ $baddress1 = sanitize_text_field(stripslashes($_REQUEST['baddress1']));
157
+ else
158
+ $baddress1 = "";
159
+ if (isset($_REQUEST['baddress2']))
160
+ $baddress2 = sanitize_text_field(stripslashes($_REQUEST['baddress2']));
161
+ else
162
+ $baddress2 = "";
163
+ if (isset($_REQUEST['bcity']))
164
+ $bcity = sanitize_text_field(stripslashes($_REQUEST['bcity']));
165
+ else
166
+ $bcity = "";
167
+
168
+ if (isset($_REQUEST['bstate']))
169
+ $bstate = sanitize_text_field(stripslashes($_REQUEST['bstate']));
170
+ else
171
+ $bstate = "";
172
+
173
+ //convert long state names to abbreviations
174
+ if (!empty($bstate))
175
+ {
176
+ global $pmpro_states;
177
+ foreach($pmpro_states as $abbr => $state)
178
+ {
179
+ if ($bstate == $state)
180
+ {
181
+ $bstate = $abbr;
182
+ break;
183
+ }
184
+ }
185
+ }
186
+
187
+ if (isset($_REQUEST['bzipcode']))
188
+ $bzipcode = sanitize_text_field(stripslashes($_REQUEST['bzipcode']));
189
+ else
190
+ $bzipcode = "";
191
+ if (isset($_REQUEST['bcountry']))
192
+ $bcountry = sanitize_text_field(stripslashes($_REQUEST['bcountry']));
193
+ else
194
+ $bcountry = "";
195
+ if (isset($_REQUEST['bphone']))
196
+ $bphone = sanitize_text_field(stripslashes($_REQUEST['bphone']));
197
+ else
198
+ $bphone = "";
199
+ if (isset($_REQUEST['bemail']))
200
+ $bemail = sanitize_email(stripslashes($_REQUEST['bemail']));
201
+ else
202
+ $bemail = "";
203
+ if (isset($_REQUEST['bconfirmemail_copy']))
204
+ $bconfirmemail = $bemail;
205
+ elseif (isset($_REQUEST['bconfirmemail']))
206
+ $bconfirmemail = sanitize_email(stripslashes($_REQUEST['bconfirmemail']));
207
+ else
208
+ $bconfirmemail = "";
209
+
210
+ if (isset($_REQUEST['CardType']) && !empty($_REQUEST['AccountNumber']))
211
+ $CardType = sanitize_text_field($_REQUEST['CardType']);
212
+ else
213
+ $CardType = "";
214
+ if (isset($_REQUEST['AccountNumber']))
215
+ $AccountNumber = sanitize_text_field($_REQUEST['AccountNumber']);
216
+ else
217
+ $AccountNumber = "";
218
+
219
+ if (isset($_REQUEST['ExpirationMonth']))
220
+ $ExpirationMonth = sanitize_text_field($_REQUEST['ExpirationMonth']);
221
+ else
222
+ $ExpirationMonth = "";
223
+ if (isset($_REQUEST['ExpirationYear']))
224
+ $ExpirationYear = sanitize_text_field($_REQUEST['ExpirationYear']);
225
+ else
226
+ $ExpirationYear = "";
227
+ if (isset($_REQUEST['CVV']))
228
+ $CVV = sanitize_text_field($_REQUEST['CVV']);
229
+ else
230
+ $CVV = "";
231
+
232
+ if (isset($_REQUEST['discount_code']))
233
+ $discount_code = sanitize_text_field($_REQUEST['discount_code']);
234
+ else
235
+ $discount_code = "";
236
+ if (isset($_REQUEST['username']))
237
+ $username = sanitize_user($_REQUEST['username']);
238
+ else
239
+ $username = "";
240
+ if (isset($_REQUEST['password']))
241
+ $password = $_REQUEST['password'];
242
+ else
243
+ $password = "";
244
+ if (isset($_REQUEST['password2_copy']))
245
+ $password2 = $password;
246
+ elseif (isset($_REQUEST['password2']))
247
+ $password2 = $_REQUEST['password2'];
248
+ else
249
+ $password2 = "";
250
+ if (isset($_REQUEST['tos']))
251
+ $tos = intval($_REQUEST['tos']);
252
+ else
253
+ $tos = "";
254
+
255
+ //_x stuff in case they clicked on the image button with their mouse
256
+ if (isset($_REQUEST['submit-checkout']))
257
+ $submit = $_REQUEST['submit-checkout'];
258
+ if (empty($submit) && isset($_REQUEST['submit-checkout_x']) )
259
+ $submit = $_REQUEST['submit-checkout_x'];
260
+ if (isset($submit) && $submit === "0")
261
+ $submit = true;
262
+ elseif (!isset($submit))
263
+ $submit = false;
264
+
265
+ //require fields
266
+ $pmpro_required_billing_fields = array(
267
+ "bfirstname" => $bfirstname,
268
+ "blastname" => $blastname,
269
+ "baddress1" => $baddress1,
270
+ "bcity" => $bcity,
271
+ "bstate" => $bstate,
272
+ "bzipcode" => $bzipcode,
273
+ "bphone" => $bphone,
274
+ "bemail" => $bemail,
275
+ "bcountry" => $bcountry,
276
+ "CardType" => $CardType,
277
+ "AccountNumber" => $AccountNumber,
278
+ "ExpirationMonth" => $ExpirationMonth,
279
+ "ExpirationYear" => $ExpirationYear,
280
+ "CVV" => $CVV
281
+ );
282
+ $pmpro_required_billing_fields = apply_filters("pmpro_required_billing_fields", $pmpro_required_billing_fields);
283
+ $pmpro_required_user_fields = array(
284
+ "username" => $username,
285
+ "password" => $password,
286
+ "password2" => $password2,
287
+ "bemail" => $bemail,
288
+ "bconfirmemail" => $bconfirmemail
289
+ );
290
+ $pmpro_required_user_fields = apply_filters("pmpro_required_user_fields", $pmpro_required_user_fields);
291
+
292
+ //pmpro_confirmed is set to true later if payment goes through
293
+ $pmpro_confirmed = false;
294
+
295
+ //check their fields if they clicked continue
296
+ if ($submit && $pmpro_msgt != "pmpro_error") {
297
+
298
+ //make sure javascript is ok
299
+ if (apply_filters("pmpro_require_javascript_for_checkout", true) && !empty($_REQUEST['checkjavascript']) && empty($_REQUEST['javascriptok'])) {
300
+ pmpro_setMessage(__("There are JavaScript errors on the page. Please contact the webmaster.", "pmpro"), "pmpro_error");
301
+ }
302
+
303
+ //if we're skipping the account fields and there is no user, we need to create a username and password
304
+ if ($skip_account_fields && !$current_user->ID) {
305
+ $username = pmpro_generateUsername($bfirstname, $blastname, $bemail);
306
+ if (empty($username))
307
+ $username = pmpro_getDiscountCode();
308
+ $password = pmpro_getDiscountCode() . pmpro_getDiscountCode(); //using two random discount codes
309
+ $password2 = $password;
310
+ }
311
+
312
+ //check billing fields
313
+ if ($pmpro_requirebilling) {
314
+ //filter
315
+ foreach($pmpro_required_billing_fields as $key => $field) {
316
+ if (!$field) {
317
+ $pmpro_error_fields[] = $key;
318
+ }
319
+ }
320
+ }
321
+
322
+ //check user fields
323
+ if (empty($current_user->ID)) {
324
+ foreach($pmpro_required_user_fields as $key => $field) {
325
+ if (!$field) {
326
+ $pmpro_error_fields[] = $key;
327
+ }
328
+ }
329
+ }
330
+
331
+ if (!empty($pmpro_error_fields)) {
332
+ pmpro_setMessage(__("Please complete all required fields.", "pmpro"), "pmpro_error");
333
+ }
334
+ if (!empty($password) && $password != $password2) {
335
+ pmpro_setMessage(__("Your passwords do not match. Please try again.", "pmpro"), "pmpro_error");
336
+ $pmpro_error_fields[] = "password";
337
+ $pmpro_error_fields[] = "password2";
338
+ }
339
+ if (!empty($bemail) && $bemail != $bconfirmemail) {
340
+ pmpro_setMessage(__("Your email addresses do not match. Please try again.", "pmpro"), "pmpro_error");
341
+ $pmpro_error_fields[] = "bemail";
342
+ $pmpro_error_fields[] = "bconfirmemail";
343
+ }
344
+ if (!empty($bemail) && !is_email($bemail)) {
345
+ pmpro_setMessage(__("The email address entered is in an invalid format. Please try again.", "pmpro"), "pmpro_error");
346
+ $pmpro_error_fields[] = "bemail";
347
+ $pmpro_error_fields[] = "bconfirmemail";
348
+ }
349
+ if (!empty($tospage) && empty($tos)) {
350
+ pmpro_setMessage(sprintf(__("Please check the box to agree to the %s.", "pmpro"), $tospage->post_title), "pmpro_error");
351
+ $pmpro_error_fields[] = "tospage";
352
+ }
353
+ if (!in_array($gateway, $valid_gateways)) {
354
+ pmpro_setMessage(__("Invalid gateway.", "pmpro"), "pmpro_error");
355
+ }
356
+ if (!empty($fullname)) {
357
+ pmpro_setMessage(__("Are you a spammer?", "pmpro"), "pmpro_error");
358
+ }
359
+
360
+ if ($pmpro_msgt == "pmpro_error")
361
+ $pmpro_continue_registration = false;
362
+ else
363
+ $pmpro_continue_registration = true;
364
+ $pmpro_continue_registration = apply_filters("pmpro_registration_checks", $pmpro_continue_registration);
365
+
366
+ if ($pmpro_continue_registration) {
367
+ //if creating a new user, check that the email and username are available
368
+ if (empty($current_user->ID)) {
369
+ $oldusername = $wpdb->get_var("SELECT user_login FROM $wpdb->users WHERE user_login = '" . esc_sql($username) . "' LIMIT 1");
370
+ $oldemail = $wpdb->get_var("SELECT user_email FROM $wpdb->users WHERE user_email = '" . esc_sql($bemail) . "' LIMIT 1");
371
+
372
+ //this hook can be used to allow multiple accounts with the same email address
373
+ $oldemail = apply_filters("pmpro_checkout_oldemail", $oldemail);
374
+ }
375
+
376
+ if (!empty($oldusername)) {
377
+ pmpro_setMessage(__("That username is already taken. Please try another.", "pmpro"), "pmpro_error");
378
+ $pmpro_error_fields[] = "username";
379
+ }
380
+
381
+ if (!empty($oldemail)) {
382
+ pmpro_setMessage(__("That email address is already taken. Please try another.", "pmpro"), "pmpro_error");
383
+ $pmpro_error_fields[] = "bemail";
384
+ $pmpro_error_fields[] = "bconfirmemail";
385
+ }
386
+
387
+ //only continue if there are no other errors yet
388
+ if ($pmpro_msgt != "pmpro_error") {
389
+ //check recaptcha first
390
+ global $recaptcha;
391
+ if (!$skip_account_fields && ($recaptcha == 2 || ($recaptcha == 1 && pmpro_isLevelFree($pmpro_level)))) {
392
+ global $recaptcha_privatekey;
393
+
394
+ if(isset($_POST["recaptcha_challenge_field"]))
395
+ {
396
+ //using older recaptcha lib
397
+ $resp = recaptcha_check_answer($recaptcha_privatekey,
398
+ $_SERVER["REMOTE_ADDR"],
399
+ $_POST["recaptcha_challenge_field"],
400
+ $_POST["recaptcha_response_field"]);
401
+
402
+ $recaptcha_valid = $resp->is_valid;
403
+ $recaptcha_errors = $resp->error;
404
+ }
405
+ else
406
+ {
407
+ //using newer recaptcha lib
408
+ $reCaptcha = new pmpro_ReCaptcha($recaptcha_privatekey);
409
+ $resp = $reCaptcha->verifyResponse($_SERVER["REMOTE_ADDR"], $_POST["g-recaptcha-response"]);
410
+
411
+ $recaptcha_valid = $resp->success;
412
+ $recaptcha_errors = $resp->errorCodes;
413
+ }
414
+
415
+ if (!$recaptcha_valid) {
416
+ $pmpro_msg = sprintf(__("reCAPTCHA failed. (%s) Please try again.", "pmpro"), $recaptcha_errors);
417
+ $pmpro_msgt = "pmpro_error";
418
+ } else {
419
+ // Your code here to handle a successful verification
420
+ if ($pmpro_msgt != "pmpro_error")
421
+ $pmpro_msg = "All good!";
422
+ }
423
+ } else {
424
+ if ($pmpro_msgt != "pmpro_error")
425
+ $pmpro_msg = "All good!";
426
+ }
427
+
428
+ //no errors yet
429
+ if ($pmpro_msgt != "pmpro_error") {
430
+ do_action('pmpro_checkout_before_processing');
431
+
432
+ //process checkout if required
433
+ if ($pmpro_requirebilling) {
434
+ $morder = new MemberOrder();
435
+ $morder->membership_id = $pmpro_level->id;
436
+ $morder->membership_name = $pmpro_level->name;
437
+ $morder->discount_code = $discount_code;
438
+ $morder->InitialPayment = $pmpro_level->initial_payment;
439
+ $morder->PaymentAmount = $pmpro_level->billing_amount;
440
+ $morder->ProfileStartDate = date("Y-m-d", current_time("timestamp")) . "T0:0:0";
441
+ $morder->BillingPeriod = $pmpro_level->cycle_period;
442
+ $morder->BillingFrequency = $pmpro_level->cycle_number;
443
+
444
+ if ($pmpro_level->billing_limit)
445
+ $morder->TotalBillingCycles = $pmpro_level->billing_limit;
446
+
447
+ if (pmpro_isLevelTrial($pmpro_level)) {
448
+ $morder->TrialBillingPeriod = $pmpro_level->cycle_period;
449
+ $morder->TrialBillingFrequency = $pmpro_level->cycle_number;
450
+ $morder->TrialBillingCycles = $pmpro_level->trial_limit;
451
+ $morder->TrialAmount = $pmpro_level->trial_amount;
452
+ }
453
+
454
+ //credit card values
455
+ $morder->cardtype = $CardType;
456
+ $morder->accountnumber = $AccountNumber;
457
+ $morder->expirationmonth = $ExpirationMonth;
458
+ $morder->expirationyear = $ExpirationYear;
459
+ $morder->ExpirationDate = $ExpirationMonth . $ExpirationYear;
460
+ $morder->ExpirationDate_YdashM = $ExpirationYear . "-" . $ExpirationMonth;
461
+ $morder->CVV2 = $CVV;
462
+
463
+ //not saving email in order table, but the sites need it
464
+ $morder->Email = $bemail;
465
+
466
+ //sometimes we need these split up
467
+ $morder->FirstName = $bfirstname;
468
+ $morder->LastName = $blastname;
469
+ $morder->Address1 = $baddress1;
470
+ $morder->Address2 = $baddress2;
471
+
472
+ //other values
473
+ $morder->billing = new stdClass();
474
+ $morder->billing->name = $bfirstname . " " . $blastname;
475
+ $morder->billing->street = trim($baddress1 . " " . $baddress2);
476
+ $morder->billing->city = $bcity;
477
+ $morder->billing->state = $bstate;
478
+ $morder->billing->country = $bcountry;
479
+ $morder->billing->zip = $bzipcode;
480
+ $morder->billing->phone = $bphone;
481
+
482
+ //$gateway = pmpro_getOption("gateway");
483
+ $morder->gateway = $gateway;
484
+ $morder->setGateway();
485
+
486
+ //setup level var
487
+ $morder->getMembershipLevel();
488
+ $morder->membership_level = apply_filters("pmpro_checkout_level", $morder->membership_level);
489
+
490
+ //tax
491
+ $morder->subtotal = $morder->InitialPayment;
492
+ $morder->getTax();
493
+
494
+ //filter for order, since v2.0
495
+ $morder = apply_filters("pmpro_checkout_order", $morder);
496
+
497
+ $pmpro_processed = $morder->process();
498
+
499
+ if (!empty($pmpro_processed))
500
+ {
501
+ $pmpro_msg = __("Payment accepted.", "pmpro");
502
+ $pmpro_msgt = "pmpro_success";
503
+ $pmpro_confirmed = true;
504
+ }
505
+ else
506
+ {
507
+ $pmpro_msg = $morder->error;
508
+ if (empty($pmpro_msg))
509
+ $pmpro_msg = __("Unknown error generating account. Please contact us to set up your membership.", "pmpro");
510
+ $pmpro_msgt = "pmpro_error";
511
+ }
512
+
513
+ }
514
+ else // !$pmpro_requirebilling
515
+ {
516
+ //must have been a free membership, continue
517
+ $pmpro_confirmed = true;
518
+ }
519
+ }
520
+ }
521
+ } //endif ($pmpro_continue_registration)
522
+ }
523
+
524
+ //make sure we have at least an empty morder here to avoid a warning
525
+ if (empty($morder))
526
+ $morder = false;
527
+
528
+ //Hook to check payment confirmation or replace it. If we get an array back, pull the values (morder) out
529
+ $pmpro_confirmed = apply_filters('pmpro_checkout_confirmed', $pmpro_confirmed, $morder);
530
+ if (is_array($pmpro_confirmed))
531
+ extract($pmpro_confirmed);
532
+
533
+ //if payment was confirmed create/update the user.
534
+ if (!empty($pmpro_confirmed)) {
535
+ //just in case this hasn't been set yet
536
+ $submit = true;
537
+
538
+ //do we need to create a user account?
539
+ if (!$current_user->ID) {
540
+ /*
541
+ create user
542
+ */
543
+ if (version_compare($wp_version, "3.1") < 0)
544
+ require_once( ABSPATH . WPINC . '/registration.php'); //need this for WP versions before 3.1
545
+
546
+ //first name
547
+ if (!empty($_REQUEST['first_name']))
548
+ $first_name = $_REQUEST['first_name'];
549
+ else
550
+ $first_name = $bfirstname;
551
+ //last name
552
+ if (!empty($_REQUEST['last_name']))
553
+ $last_name = $_REQUEST['last_name'];
554
+ else
555
+ $last_name = $blastname;
556
+
557
+ //insert user
558
+ $new_user_array = apply_filters('pmpro_checkout_new_user_array', array(
559
+ "user_login" => $username,
560
+ "user_pass" => $password,
561
+ "user_email" => $bemail,
562
+ "first_name" => $first_name,
563
+ "last_name" => $last_name)
564
+ );
565
+
566
+ $user_id = apply_filters('pmpro_new_user', '', $new_user_array);
567
+ if (!$user_id)
568
+ $user_id = wp_insert_user($new_user_array);
569
+
570
+ if (!$user_id || is_wp_error($user_id)) {
571
+ $pmpro_msg = __("Your payment was accepted, but there was an error setting up your account. Please contact us.", "pmpro");
572
+ $pmpro_msgt = "pmpro_error";
573
+ } elseif (apply_filters('pmpro_setup_new_user', true, $user_id, $new_user_array, $pmpro_level)) {
574
+
575
+ //check pmpro_wp_new_user_notification filter before sending the default WP email
576
+ if (apply_filters("pmpro_wp_new_user_notification", true, $user_id, $pmpro_level->id))
577
+ wp_new_user_notification($user_id, $new_user_array['user_pass']);
578
+
579
+ $wpuser = get_userdata($user_id);
580
+
581
+ //make the user a subscriber
582
+ $wpuser->set_role(get_option('default_role', 'subscriber'));
583
+
584
+ //okay, log them in to WP
585
+ $creds = array();
586
+ $creds['user_login'] = $new_user_array['user_login'];
587
+ $creds['user_password'] = $new_user_array['user_pass'];
588
+ $creds['remember'] = true;
589
+ $user = wp_signon( $creds, false );
590
+
591
+ //setting some cookies
592
+ wp_set_current_user($user_id, $username);
593
+ wp_set_auth_cookie($user_id, true, apply_filters('pmpro_checkout_signon_secure', (force_ssl_login() || force_ssl_admin())));
594
+ }
595
+ }
596
+ else
597
+ $user_id = $current_user->ID;
598
+
599
+ if ($user_id && !is_wp_error($user_id))
600
+ {
601
+ do_action('pmpro_checkout_before_change_membership_level', $user_id, $morder);
602
+
603
+ //calculate the end date
604
+ if (!empty($pmpro_level->expiration_number)) {
605
+ $enddate = "'" . date("Y-m-d", strtotime("+ " . $pmpro_level->expiration_number . " " . $pmpro_level->expiration_period, current_time("timestamp"))) . "'";
606
+ } else {
607
+ $enddate = "NULL";
608
+ }
609
+
610
+ //update membership_user table.
611
+ if (!empty($discount_code) && !empty($use_discount_code))
612
+ $discount_code_id = $wpdb->get_var("SELECT id FROM $wpdb->pmpro_discount_codes WHERE code = '" . $discount_code . "' LIMIT 1");
613
+ else
614
+ $discount_code_id = "";
615
+
616
+ //set the start date to NOW() but allow filters
617
+ $startdate = apply_filters("pmpro_checkout_start_date", "'" . current_time("mysql") . "'", $user_id, $pmpro_level);
618
+
619
+ $custom_level = array(
620
+ 'user_id' => $user_id,
621
+ 'membership_id' => $pmpro_level->id,
622
+ 'code_id' => $discount_code_id,
623
+ 'initial_payment' => $pmpro_level->initial_payment,
624
+ 'billing_amount' => $pmpro_level->billing_amount,
625
+ 'cycle_number' => $pmpro_level->cycle_number,
626
+ 'cycle_period' => $pmpro_level->cycle_period,
627
+ 'billing_limit' => $pmpro_level->billing_limit,
628
+ 'trial_amount' => $pmpro_level->trial_amount,
629
+ 'trial_limit' => $pmpro_level->trial_limit,
630
+ 'startdate' => $startdate,
631
+ 'enddate' => $enddate);
632
+
633
+ if (pmpro_changeMembershipLevel($custom_level, $user_id, 'changed')) {
634
+ //we're good
635
+ //blank order for free levels
636
+ if (empty($morder)) {
637
+ $morder = new MemberOrder();
638
+ $morder->InitialPayment = 0;
639
+ $morder->Email = $bemail;
640
+ $morder->gateway = "free";
641
+ }
642
+
643
+ //add an item to the history table, cancel old subscriptions
644
+ if (!empty($morder)) {
645
+ $morder->user_id = $user_id;
646
+ $morder->membership_id = $pmpro_level->id;
647
+ $morder->saveOrder();
648
+ }
649
+
650
+ //update the current user
651
+ global $current_user;
652
+ if (!$current_user->ID && $user->ID)
653
+ $current_user = $user; //in case the user just signed up
654
+ pmpro_set_current_user();
655
+
656
+ //add discount code use
657
+ if ($discount_code && $use_discount_code) {
658
+ if (!empty($morder->id))
659
+ $code_order_id = $morder->id;
660
+ else
661
+ $code_order_id = "";
662
+
663
+ $wpdb->query("INSERT INTO $wpdb->pmpro_discount_codes_uses (code_id, user_id, order_id, timestamp) VALUES('" . $discount_code_id . "', '" . $user_id . "', '" . intval($code_order_id) . "', '" . current_time("mysql") . "')");
664
+ }
665
+
666
+ //save billing info ect, as user meta
667
+ $meta_keys = array("pmpro_bfirstname", "pmpro_blastname", "pmpro_baddress1", "pmpro_baddress2", "pmpro_bcity", "pmpro_bstate", "pmpro_bzipcode", "pmpro_bcountry", "pmpro_bphone", "pmpro_bemail", "pmpro_CardType", "pmpro_AccountNumber", "pmpro_ExpirationMonth", "pmpro_ExpirationYear");
668
+ $meta_values = array($bfirstname, $blastname, $baddress1, $baddress2, $bcity, $bstate, $bzipcode, $bcountry, $bphone, $bemail, $CardType, hideCardNumber($AccountNumber), $ExpirationMonth, $ExpirationYear);
669
+ pmpro_replaceUserMeta($user_id, $meta_keys, $meta_values);
670
+
671
+ //save first and last name fields
672
+ if (!empty($bfirstname)) {
673
+ $old_firstname = get_user_meta($user_id, "first_name", true);
674
+ if (empty($old_firstname))
675
+ update_user_meta($user_id, "first_name", $bfirstname);
676
+ }
677
+ if (!empty($blastname)) {
678
+ $old_lastname = get_user_meta($user_id, "last_name", true);
679
+ if (empty($old_lastname))
680
+ update_user_meta($user_id, "last_name", $blastname);
681
+ }
682
+
683
+ //show the confirmation
684
+ $ordersaved = true;
685
+
686
+ //hook
687
+ do_action("pmpro_after_checkout", $user_id, $morder); //added $morder param in v2.0
688
+
689
+ //setup some values for the emails
690
+ if (!empty($morder))
691
+ $invoice = new MemberOrder($morder->id);
692
+ else
693
+ $invoice = NULL;
694
+ $current_user->membership_level = $pmpro_level; //make sure they have the right level info
695
+
696
+ //send email to member
697
+ $pmproemail = new PMProEmail();
698
+ $pmproemail->sendCheckoutEmail($current_user, $invoice);
699
+
700
+ //send email to admin
701
+ $pmproemail = new PMProEmail();
702
+ $pmproemail->sendCheckoutAdminEmail($current_user, $invoice);
703
+
704
+ //redirect to confirmation
705
+ $rurl = pmpro_url("confirmation", "?level=" . $pmpro_level->id);
706
+ $rurl = apply_filters("pmpro_confirmation_url", $rurl, $user_id, $pmpro_level);
707
+ wp_redirect($rurl);
708
+ exit;
709
+ } else {
710
+ //uh oh. we charged them then the membership creation failed
711
+ if (isset($morder) && $morder->cancel()) {
712
+ $pmpro_msg = __("IMPORTANT: Something went wrong during membership creation. Your credit card authorized, but we cancelled the order immediately. You should not try to submit this form again. Please contact the site owner to fix this issue.", "pmpro");
713
+ $morder = NULL;
714
+ } else {
715
+ $pmpro_msg = __("IMPORTANT: Something went wrong during membership creation. Your credit card was charged, but we couldn't assign your membership. You should not submit this form again. Please contact the site owner to fix this issue.", "pmpro");
716
+ }
717
+ }
718
+ }
719
+ }
720
+
721
+ //default values
722
+ if (empty($submit)) {
723
+ //show message if the payment gateway is not setup yet
724
+ if ($pmpro_requirebilling && !pmpro_getOption("gateway", true)) {
725
+ if (pmpro_isAdmin())
726
+ $pmpro_msg = sprintf(__('You must <a href="%s">set up a Payment Gateway</a> before any payments will be processed.', 'pmpro'), get_admin_url(NULL, '/admin.php?page=pmpro-membershiplevels&view=payment'));
727
+ else
728
+ $pmpro_msg = __("A Payment Gateway must be set up before any payments will be processed.", "pmpro");
729
+ $pmpro_msgt = "";
730
+ }
731
+
732
+ //default values from DB
733
+ if (!empty($current_user->ID)) {
734
+ $bfirstname = get_user_meta($current_user->ID, "pmpro_bfirstname", true);
735
+ $blastname = get_user_meta($current_user->ID, "pmpro_blastname", true);
736
+ $baddress1 = get_user_meta($current_user->ID, "pmpro_baddress1", true);
737
+ $baddress2 = get_user_meta($current_user->ID, "pmpro_baddress2", true);
738
+ $bcity = get_user_meta($current_user->ID, "pmpro_bcity", true);
739
+ $bstate = get_user_meta($current_user->ID, "pmpro_bstate", true);
740
+ $bzipcode = get_user_meta($current_user->ID, "pmpro_bzipcode", true);
741
+ $bcountry = get_user_meta($current_user->ID, "pmpro_bcountry", true);
742
+ $bphone = get_user_meta($current_user->ID, "pmpro_bphone", true);
743
+ $bemail = get_user_meta($current_user->ID, "pmpro_bemail", true);
744
+ $bconfirmemail = $bemail; //as of 1.7.5, just setting to bemail
745
+ $CardType = get_user_meta($current_user->ID, "pmpro_CardType", true);
746
+ //$AccountNumber = hideCardNumber(get_user_meta($current_user->ID, "pmpro_AccountNumber", true), false);
747
+ $ExpirationMonth = get_user_meta($current_user->ID, "pmpro_ExpirationMonth", true);
748
+ $ExpirationYear = get_user_meta($current_user->ID, "pmpro_ExpirationYear", true);
749
+ }
750
+ }
751
+
752
+ //clear out XXXX numbers (e.g. with Stripe)
753
+ if (!empty($AccountNumber) && strpos($AccountNumber, "XXXX") === 0)
754
+ $AccountNumber = "";
 
 
readme.txt CHANGED
@@ -3,7 +3,7 @@ Contributors: strangerstudios
3
  Tags: memberships, membership, authorize.net, ecommerce, paypal, stripe, braintree, restrict access, restrict content, directory site, payflow
4
  Requires at least: 3.5
5
  Tested up to: 4.3.1
6
- Stable tag: 1.8.6.2
7
 
8
  A revenue-generating machine for membership sites. Unlimited levels with recurring payment, protected content and member management.
9
 
@@ -115,17 +115,6 @@ Not sure? You can find out by doing a bit a research.
115
  [View All Screenshots](http://www.paidmembershipspro.com/features/screenshots/)
116
 
117
  == Changelog ==
118
- = 1.8.6.2 =
119
- * BUG: Stripe supports daily recurring subscriptions. Fixed issue where PMPro was still throwing a warning in some cases.
120
- * BUG: Removed warnings in the membership stats report.
121
- * CODE: Changed a couple instances of "since v2.0" to "since v1.8".
122
- * ENHANCEMENT: Added a hook for pmpro_checkout_order_free that functions like pmpro_checkout_order but fires for the blank order created for free checkouts.
123
- * ENHANCEMENT: Using site_url to get full URL to for the redirect_to value generated by !!referrer!! in the non-member/logged-out text strings in the advanced settings page. This fixes issues with using !!referrer!! on multisite setups. (Thanks, Keith Hall)
124
- * ENHANCEMENT: Updated pmpro_post_classes to work on the $post_id passed by the filter instead of the global $post variable.
125
- * ENHANCEMENT: Added pmpro_body_classes to add classes like pmpro-body-level-required, pmpro-body-level-1, and pmpro-body-has-access to the body tag based on the queried object.
126
- * ENHANCEMENT: Reformatted the PMPro Reports dashboard.
127
- * ENHANCEMENT: Now using separate invoice templates for email and print.
128
-
129
  = 1.8.6.1 =
130
  * SECURITY: Removed debug code from the PayPal IPN Handler script that was causing invalid IPN requests to process as if they were valid. (Thanks, Francois Harvey)
131
 
@@ -855,4 +844,340 @@ useful for determining if/what discount code was applied to the level when proce
855
  * Added hooks for changing the discount code page: pmpro_save_discount_code_level, pmpro_save_discount_code, pmpro_discount_code_after_settings, pmpro_discount_code_after_level_settings. Look them up in discountcodes.php to see how they work.
856
  * Updated pmpro_send_html(), which filters emails, to use wpautop instead of nl2br. This will fix any extra double spacing you may have noticed in your emails.
857
  * Added a stripslashes around the membership level confirmation text on the confirmation page. Extra slashes were breaking links, etc.
858
- * Added membership level to subj
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3
  Tags: memberships, membership, authorize.net, ecommerce, paypal, stripe, braintree, restrict access, restrict content, directory site, payflow
4
  Requires at least: 3.5
5
  Tested up to: 4.3.1
6
+ Stable tag: 1.8.6.1
7
 
8
  A revenue-generating machine for membership sites. Unlimited levels with recurring payment, protected content and member management.
9
 
115
  [View All Screenshots](http://www.paidmembershipspro.com/features/screenshots/)
116
 
117
  == Changelog ==
 
 
 
 
 
 
 
 
 
 
 
118
  = 1.8.6.1 =
119
  * SECURITY: Removed debug code from the PayPal IPN Handler script that was causing invalid IPN requests to process as if they were valid. (Thanks, Francois Harvey)
120
 
844
  * Added hooks for changing the discount code page: pmpro_save_discount_code_level, pmpro_save_discount_code, pmpro_discount_code_after_settings, pmpro_discount_code_after_level_settings. Look them up in discountcodes.php to see how they work.
845
  * Updated pmpro_send_html(), which filters emails, to use wpautop instead of nl2br. This will fix any extra double spacing you may have noticed in your emails.
846
  * Added a stripslashes around the membership level confirmation text on the confirmation page. Extra slashes were breaking links, etc.
847
+ * Added membership level to subject of checkout confirmation email sent to admins.
848
+
849
+ = 1.4.2 =
850
+ * Fixed bug that was added slashes into a level's description and confirmation when saving.
851
+ * Removed wp_editor use is the blog is running a version of WordPress < 3.3. (Note: We will only officially support the latest version of WordPress with each release.)
852
+ * Added the pmpro_pages_shortcode_{membership page} filter. This can be used to filter the content output by the pmpro_checkout and other page shortcodes. e.g. use pmpro_pages_shortcode_checkout to tweak the HTML output of the pmpro_checkout shortcode. The pages are "account", "billing", "cancel", "checkout", "confirmation", and "levels".
853
+ * Added a "use_ssl" option. For the PayPal Website Payments Pro and Authorize.net gateways, this must be on. For Stripe, this will default to on, but can be switched off. For PayPal Express and the test gateway, it will default to off but can be switched on. When on, the checkout and update billing pages will be forced to be served over SSL. If off, those pages will redirect to non-ssl versions. The previous hooks/filters for overriding this will still work.
854
+ * Added pmpro_save_membership_level and pmpro_membership_level_after_other_settings hooks to be able to add fields to the new/edit membership level page.
855
+ * Fixed some more warnings and notices.
856
+ * Updated checkout page to use pmpro_isLevelFree() in logic to display recaptcha or not.
857
+
858
+ = 1.4.1 =
859
+ * Fixed critical bugs with PayPal Express.
860
+ * When a PayPal cancellation returns error "11556" (The subscription must have status "active" or "suspended".) I am cancelling the membership without an error. Most likely the PayPal subscription was already cancelled on the PayPal side.
861
+ * No longer trying to cancel a subscription with the gateway if a membership/order doesn't have a subscription_transaction_id. (It was a initial payment only membership probably.)
862
+
863
+ = 1.4 =
864
+ * Rewrote how gateways are handled to make it easier to add and manage new gateway options.
865
+ * Added Stripe as a gateway option. (http://www.stripe.com)
866
+ * Added a "confirmation message" field to the level editor that is shown on the confirmation page after checking out. The message is added to the text that can be filtered using the pmpro_confirmation_message hook.
867
+ * Now applying "the_content" filters on the confirmation message on the confirmation page. e.g. wpautop will be run on the text to automatically add paragraphs.
868
+ * Now showing the level description on the checkout page. You can use this code to remove the description from the checkout page: https://gist.github.com/2323424
869
+ * The description and confirmation fields of the membership level editor now use WP Editor fields with full WYSIWYG support.
870
+ * Fixed the logic around setting the $pmpro_display_ads global, used by the pmpro_displayAds() and pmpro_hideAds() functions.
871
+ * Fixed bug with recaptcha logic.
872
+ * Updated /pages/checkout.php to use wp_login_url function for login link.
873
+ * Small changes to pmpro_changeMembershipLevel function to support deleting users when they cancel their account.
874
+ * Added the pmpro_member_links_top and pmpro_member_links_bottom hooks so you can add links to the "Member Links" list of the account page. Just output an a tag wrapped in a li tag. (May tweak this to build an array of links that can be filters, but this is good for now.)
875
+ * Fixed some more notices.
876
+
877
+ = 1.3.19 =
878
+ * Rewrote the pmpro_login_redirect function. It's cleaner now. Important: there was a pmpro_login_redirect hook in there that was fairly redundant with the core login_redirect hook. I've renamed the pmpro hook to pmpro_login_redirect_to because I had a hook with the same name (pmpro_login_redirect) used in a different place to control whether or not PMPro redirects the register page to the levels page. Having one hook for two things is a bad idea. It seems like more people were using the hook for controlling the registration redirect, so I left that one alone and renamed these.
879
+ * Changed PMPro page creation to set all membership pages as subpages of the membership account page. This results in nicer menus for themes that add all top level pages to the menu.
880
+ * Updated the checkout page to submit to "" (itself) instead of echoing the checkout page URL here. (Since we can have multiple checkout pages.) This also fixes from SSL conflicts that may crop up on the checkout page.
881
+ * Updated the priority of a few actions/hooks so the "besecure" https stuff gets run as soon as possible. Before it was possible that some URLs could be written out with http: on an HTTPS page before PMPro had a chance to fix things. You should have fewer SSL errors on the checkout page to deal with now.
882
+ * Added an option on the payment settings page to "nuke" http: links on all secure pages. This option can add time to your page loads, but will ensure that all http: links for your domain are replaced with https: links.
883
+ * Allowing multiple pages to use the [pmpro_checkout] shortcode so you can create multiple checkout pages. This is good if you want a separate templated checkout page for each membership level or product you have.
884
+ * You can now add a pmpro_default_level custom field, set to the id # of the level you want, that will be used if you navigate directly to a checkout page without setting a level.
885
+ * Added some stuff to support adding shipping fields via hooks. Add this plugin to your site, edit, and activate to add shipping to your checkout: https://gist.github.com/1894897
886
+ * Removed the price from the description sent to PayPal. The DESC field is limited to 127 characters and must match up across API calls. So there is a good chance the price would get truncated which could be confusing. This was a kind of hack anyway. PayPal should show the price data it has. Not sure why it won't. The price is still reviewed on the review page of your site though.
887
+ * The recaptcha code now checks for a previous error before changing pmpro_msg to "All Good".
888
+ * Fixed warning in pmpro_has_membership_access(). Fixed a bunch of other warnings here and there.
889
+ * Rewrote pmpro_updateMembershipCategories() just to be cleaner
890
+ * Added pmpro_state_dropdowns filter. If you return true, the state field will become a dropdown with US states. Use the pmpro_states and pmpro_states_abbreviations filters to change the array of states used.
891
+
892
+ = 1.3.18.1 =
893
+ * Added the new email .html templates to svn.
894
+
895
+ = 1.3.18 =
896
+ * Fixed some warnings: admin bar warning that showed up on admin pages; warning issued by pmpro_setOption(); warning in pmpro_hasMembershipLevel(); warning in billing update; warnings on the user edit page; warnings in the getTax method of the order class; warnings in save method of order class.
897
+ * Added a pmpro_checkout_confirm_email filter that can return false to hide and not require the "Confirm E-mail" field at checkout.
898
+ * Added a pmpro_checkout_confirm_password filter that can return false to hide and not require the "Confirm Password" field at checkout.
899
+ * If the PMPRO_DEFAULT_LEVEL constant is set, traffic on the levels page is redirected to the checkout page. This redirect no longer forces HTTPS.
900
+ * Moved the pmpro_paypalexpress_session_vars hook call so it will run even if existing users are checking out (upgrades, etc).
901
+ * Added some confirmation emails for admins: (1) for new user signups, (2) when an admin changes a member's level, (3) when a user cancel's their membership, and (4) when a user update's their billing information. New email templates (ending with "_admin.html") have been added to the /email/ folder of the plugin.
902
+ * Added new email settings to enable/disable the new admin emails. They will be enabled by default on install and upgrade to 1.3.18. The settings are on the email tab of the PMPro settings.
903
+ * Added a couple hooks to the checkout page to have more control over where you add fields, etc. pmpro_checkout_before_submit_button and pmpro_checkout_after_billing_fields.
904
+
905
+ = 1.3.17.1 =
906
+ * Fixing activation bug from 1.3.17.
907
+
908
+ = 1.3.17 =
909
+ * Updated pmpro_hasMembershipLevel() and [membership] shortcode to allow passing a level like "-5" which will return true if the user does NOT have level #5.
910
+ * Updated how PMPro notifications are retrieved and shown on the PMPro admin pages. We're using admin-ajax to call the pmpro_notifications function which uses WP's HTTP API to call the www.paidmembershipspro.com server. Only the PMPro version number is passed to check if a notification should be shown. This method shouldn't slow page load since the javascript is called using jQuery's ready function. If the PMPro server is unavailable, you'll get a JS error instead of a PHP one.
911
+ * Fixed warning on discount codes page. Fixed some other warnings.
912
+ * Updated expiration/trial crons to avoid blank ('') and zero ('0000-00-00 00:00:00') DB values in addition to NULLs. (Some backup programs will incorrectly export NULL dates as '' which could be interpretted as 1/1/1970... meaning the membership has expired.)
913
+ * Fixed bug where "Billing Information" was shown on the account page for some free levels.
914
+
915
+ = 1.3.16 =
916
+ * Moved the SSL Seal box lower on the payment settings page.
917
+ * Made dashboard menu and admin bar menus consistent.
918
+ * Fixed bug with selecting categories when adding a new level.
919
+ * Fixed bug where the user was sometimes redirected to the add level page after adding a level.
920
+
921
+ = 1.3.15 =
922
+ * Fixed SSL handling on the billing page for members without an order.
923
+ * Removed single quotes from shortcode examples on page settings page. Doh! (Thanks, Caps)
924
+ * Added Multisite/Network FAQ item.
925
+ * Updated the payments settings page to convert tax rates like 7 into 0.07. (Tax rates > 1 are divided by 100.)
926
+
927
+ = 1.3.14 =
928
+ * Added pmpro_show_cvv filter to hide the CVV from the checkout and billing information pages.
929
+ * Updated the billing page to use the pmpro_required_billing_fields like the checkout page does.
930
+ * Updated the Authorize.net integration to not pass an empty CVV if the value is empty. Authorize.net will still throw an error if you require CVV via your gateway settings. If you update your settings and PMpro to not require a CVV, you won't get an error.
931
+ * Passing the level cost to PayPal Express through the description.
932
+ * The billing page doesn't require SSL now if the gateway for the order was PayPal Express. A link to PayPal is shown instead of the form. (Be sure to remove the "becesure" custom field from your billing page if it has one and you don't want this page served over SSL.)
933
+ * Fixed bug where the membership level name wasn't being passed to Authorize.net in the description field for the order.
934
+ * Added a second paramter ($tags = true) to the pmpro_getLevelCost function. If this is false, strip_tags is run on the cost before returning it. (By default we wrap the prices in <strong> tags which is not good for passing to PayPal for example.)
935
+ * Some bug fixes for updating billing against Authorize.net.
936
+
937
+ = 1.3.13 =
938
+ * Fixed warning on checkout page. (Thanks Caps!)
939
+ * Fixed bug in PayPal Express checkout that resulted in trying to load the confirmation page over SSL (which would break on some servers). (Thanks Caps!)
940
+ * Updated getTaxFromPrice method of order class to allow for better filtering, by level, etc. The pmpro_tax filter now passes the $tax amount, $values (array with price passed and other values), and $this (the order object). It's a little clunky, but must be for backwards compatibility. Custom tax example here: http://www.paidmembershipspro.com/2012/02/custom-tax-structure-using-the-pmpro_tax-hook/
941
+ * Removed all TAXAMT NVP parameters in PayPal Express calls. Including these would sometimes introduce errors during checkout. The tax amount is still included in the total amounts passed. Not sure what impact dropping the TAXAMT property will have on reporting in PayPal. I don't believe their tax reporting is the best anyway. Maybe we can build a tax report into PMPro.
942
+
943
+ = 1.3.12 =
944
+ * Fixed bug in members list pagination on sites installed in a subdirectory.
945
+ * Now swapping out the PayPal Express checkout button if the level is free or becomes free with a discount code. (Thanks, Caps!)
946
+
947
+ = 1.3.11 =
948
+ * Fixed bug with cancelling a user's membership through the admin.
949
+
950
+ = 1.3.10 =
951
+ * Fixed the links in the discount code table.
952
+ * pmpro_hasMembershipLevel(0) and [membership level="0"] will once again return true for non-members. (This broke whent he pmpro_has_membership_level filter was added.)
953
+ * WP 3.3.1 testing. (Looks good!)
954
+
955
+ = 1.3.9 =
956
+ * Added a "pmpro_has_membership_level" filter ($r = apply_filters("pmpro_has_membership_level", $r, $user_id, $levels);) which can be used to override the default behavior here.
957
+ * Fixed the pmpro shortcodes to allow content above and below the shortcodes on the membership pages. (Thanks, Bluewind!)
958
+ * Now setting the user's first and last name to the billing first and last name after checkout.
959
+ * Added billing first/last name, billing address, and phone number to the members list screen and CSV export.
960
+ * Removed email header/footer code from email class because sometimes it was added twice. Now it is added by the pmpro_send_html function in paid-memberships-pro.php for all emails (WP or PMPro) if a header or footer file are found in your theme folder.
961
+ * Added a pmpro_after_phpmailer_init. (The old hook pmpro_after_pmpmailer_init had a typo -- pmpmailer instead of phpmailer.) I left the old hook in for backwards compatibility.
962
+
963
+ = 1.3.8 =
964
+ * Fixed a bug with canceling memberships. Important Note: User requested cancellations were not being forwarded to PayPal and Authorize.net in the past couple updates. Please double check your members lists with your payment gateway subscriptions. Sorry for the inconvenience.
965
+ * Fixed a bug in the billing update form.
966
+ * Wrapped some output on the billing update form in esc_attr.
967
+ * Now sorting countries alphabetically if international orders are turned on.
968
+ * Updated the membership-billing page to show country and long form fields if enabled via the hooks pmpro_international_addresses and pmpro_longform_address. (These were only showing up on the checkout form before.)
969
+
970
+ = 1.3.7 =
971
+ * Added "expiration" field to user profile page. Updated the email class to include information on expiration dates in the admin change emails.
972
+ * Added "pmpro_profile_show_membership_level" and "pmpro_profile_show_expiration" filters which will hide those fields from the edit profile screen if false is returned.
973
+ * Added a pmpro_getMembershipLevelForUser($user_id) function and replaced some redundant code in a few places where we query the DB for this. Maybe we'll have a membership level class as some point. Makes sense now.
974
+ * Fixed bug where the wrong price for levels was showing up on the edit profile page in the admin. (It would show the current user's level info instead of the edited user's info.)
975
+ * Cleaned up a few more warnings, etc.
976
+
977
+ = 1.3.6 =
978
+ * Changed a few split() calls to explode() to avoid warnings.
979
+ * Fixed a couple other warnings/notifices.
980
+ * Updated account page to hide the change billing info link if the user doesn't have an active subscription or signed up with PayPal Express.
981
+ * Added a filter pmpro_paypal_express_return_url_parameters which can be used to add parameters to the ReturnURL when checking out with PayPal Express. Return an array of key, value pairs. { return array("option" => "1"); }
982
+
983
+ = 1.3.5 =
984
+ * Important update to Authorize.net processing code to account for the "credit card expires before the start of this subscription" error that comes up. For levels/discount codes with no trials or only free trials/initial payments, the subscription setup with Authorize.net starts the day of checkout and a free trial is tacked on for 1 period vs. setting up the subscription one period out. One period is added to the billing limit as well, if applicable. Check the blog for more information.
985
+ * Important update for PayPal Website Payments Pro users. When using PayPal WPP, the user will have an option to checkout via PayPal Express as well. PayPal requires this and now we support it.
986
+
987
+ = 1.3.4 =
988
+ * Swapped the $ in the levels page code for $pmpro_currency_symbol.
989
+ * Changed the membership shortcode to apply the_content filters to the return value instead of just wpautop. This allows shortcodes within that shortcode and other filters to be run on the content. (Let me know if issues arrise from this.)
990
+ * Wrapped some post variables in checkout and billing preheaders with trim()
991
+ * Now voiding authorizations with Authorize.net. (The plugin will authorize $1 before setting up a subscription without an initial payment.)
992
+ * Now voiding an initial payment with Authorize.net if the subscription setup fails.
993
+ * Now refunding an intial payment with PayPal if the subscription setup fails.
994
+ * Added a "pmpro_checkout_after_level_cost" to add fields or arbitrary code after the level cost description on the checkout page.
995
+ * Added Diner's Club, EnRoute, and JCB as credit card options. Make sure you congiture your Gateway/Merchant account to accept these card types as well.
996
+
997
+ = 1.3.3 =
998
+ * Fixed bug where country field was resetting to default when there were errors with the checkout form submission. (If you templatized your checkout page and have international addresses enabled, you will need to add $bcountry to the globals setup at the top of your checkout template .php)
999
+
1000
+ = 1.3.2 =
1001
+ * Fixed issue introduced in 1.3.1 where checkout page would not redirect to HTTPS when it should have.
1002
+ * Fixing issues with slashes in addresses/etc in the checkout form.
1003
+ * Updated the PMProEmail class to use the wp_mail function instead of use PHPMailer directly. (Thanks VadaPrime: http://wordpress.org/support/topic/plugin-paid-memberships-pro-wp_mail?replies=6#post-2449672)
1004
+ * Fixed some more notices and warnings.
1005
+
1006
+ = 1.3.1 =
1007
+ * Fixed automatic page creation, which broke in the last update.
1008
+ * Added hook pmpro_checkout_level which allows you to tweak the $level object before checkout, e.g. to change pricing for upgrades.
1009
+ * Added hook pmpro_checkout_start_date which allows you to change the start date of a membership before checkout. (preheaders/checkout.php)
1010
+ * Added hook pmpro_profile_start_date which allows you to change the start date of a membership that is sent to the gateway. (classes/class.memberorder.php)
1011
+ * Cleaned up some notices and warnings. Will hopefully finish the remaining ones next update.
1012
+ * Removed some old tinyMCE code that wasn't in use anymore. FYI, WP 3.3 will have a way to include visual editors on other pages, so we may add it to the description field of the membership levels.
1013
+ * Updated order class to send phone and email to Authorize.net when creating subscriptions. The charge/authorize API support international phone numbers, but the ARB API does not. So if a customer enters an international phone number (or other phone number over 10 characters), the number will be sent for any initial payment/charge, but not for the subscription setup.
1014
+ * Fixed where !!discount_code!! was not being parsed out in emails.
1015
+
1016
+ = 1.3 =
1017
+ * Added a filter pmpro_login_redirect. You can return false to allow users to signup via the default WP login page.
1018
+ * Member CSV export no longer limiting to 15 members.
1019
+ * Correctly adding code_id to the pmpro_memberships_users table on signup. View here for retroactively updating your users tables in case you intend to use that value for advanced functionality.
1020
+ * Changed URL to send IPN checks for live PayPal instances from www.live.paypal.com to www.paypal.com.
1021
+ * Updated getfile.php to work when WP is installed in a subdomain.
1022
+ * Added links to individual settings tabs in the WP menu.
1023
+ * Changed the architecture of the settings pages which used to all be coded in the membershiplevels.php page. Each settings page has its own script now. I removed the pmpro-data.php service and have the pages submit to themselves now. This won't impact how things work, but will make it easier for me to develop going forward.
1024
+
1025
+ = 1.2.10 =
1026
+ * Added pmpro_confirmation_message hook to change the output on the confirmation page without having to templatize it. The filter passes the constructed html string with the confirmation message and a second parameter containing the order/invoice object if it is a paid membership.
1027
+ * Added a pmpro_checkout_boxes hook that can be used to output extra fields and other content in the middle of the checkout page.
1028
+ * Now showing 2 decimals places for the tax rate when showing a membership level's cost.
1029
+
1030
+ = 1.2.9 =
1031
+ * IMPORTANT fix so new user email addresses are properly captured when using PayPal Express.
1032
+ * rewrote the IPN handler to use the WordPress HTTP API for better compatibility.
1033
+ * added extra id to tables and fields for easier styling. (let me know if you have suggestions for small changes like these that can save you from having to templatize a page)
1034
+ * fixed query in readiness check function.
1035
+ * Authorize.net doesn't support international phone numbers, so we're not sending them to Authorize.net anymore.
1036
+
1037
+ = 1.2.8 =
1038
+ * Ordering levels by id (ascending) on the levels page now. Added a "pmpro_levels_array" filter that can be used to reorder the levels or alter the levels before displaying them on the levels page. The array of levels is the only parameter.
1039
+ * Added expiration date to the member list and export.
1040
+ * Showing a member count on the member list page.
1041
+ * Added filter to change subject lines for PMPro emails. (pmpro_email_subject) The filter's first paramter is the subject, the second parameter is an object containg all of the email information. There are also filters for pmpro_email_recipient, pmpro_email_sender, pmpro_email_sender_name, pmpro_email_template, amd pmpro_email_body.
1042
+ * Added an RSS feed from the PMPro blog to the dashboard.
1043
+ * Now only showing the discount code field at checkout if there are discount codes in the database. Can be overriden by the pmpro_show_discount_code filter.
1044
+ * Cancelling with PayPal now properly updates status to "cancelled".
1045
+ * No longer trying to unsubscribe from PayPal or Authorize.net if there is no subscription ID to check against (e.g. when the user was manually added to a membership level) or if the last order does not have "success" status (e.g. they already cancelled).
1046
+ * Removed PHP short tags (e.g., <?=$variable?>) for wider compatibility.
1047
+
1048
+ = 1.2.7 =
1049
+ * Fixed bug with non-USD currencies.
1050
+ * Fixed bug with phone number formatting.
1051
+
1052
+ = 1.2.6 =
1053
+ * Fixed bug with discount codes showing up in emails, confirmation pages, and invoices.
1054
+ * Added currency option to gateway settings.
1055
+
1056
+ = 1.2.5 =
1057
+ * PayPal Express support! PayPal Express requires just a verified PayPal Business account (no monthly fees from PayPal).
1058
+ * Fixed a bug when plans with a "cycle number"/billing frequency that was greater than 1 (e.g. every 4 months). Before the first payment was being scheduled 1 day/month ext out instead of e.g. 4 months out... resulting in an extra payment.
1059
+ * Added some hooks to support international orders: pmpro_international_addresses, pmpro_default_country, pmpro_countries, pmpro_required_billing_fields. Example code to support international credit cards: https://gist.github.com/1212479
1060
+
1061
+ = 1.2.4 =
1062
+ * VERY IMPORTANT BUG FIX: The getMembershipLevel function of the MemberOrder class had an error where the membership level object was not being created properly during signup and so * recurring subscriptions were not being created *. This update fixes the bug. Thanks to mvp29 for catching this.
1063
+ * Fixed another bug that was causing warnings on some setups, e.g. WAMP server for Windows.
1064
+ * Fixed a bug that would show warnings when visiting a login page over HTTPS.
1065
+ * Fixed membership pricing wording for certain cases, e.g. every 4 months for 4 more payments.
1066
+ * Fixed a bug in the email generation for order confirmations when discount codes were used. This will no longer freeze the screen.
1067
+
1068
+ = 1.2.3 =
1069
+ * Fixed an error in the DB upgrade code that was keeping the "enddate" from being added to new members' records.
1070
+
1071
+ = 1.2.2 =
1072
+ * Added pmpro_skip_account_fields hook. This value is used to determine if the username/password accounts fields should show up at checkout. By default, it is shown when the user is logged out and not shown when logged in. The hook allows you to return true or false to override this behavior. If the fields are skipped while no user is logged in a username and password will be automatically generated for the new user after checkout.
1073
+ * You can delete discount codes now from the admin.
1074
+ * Added a hook pmpro_level_cost_text to allow you to override how the cost is shown on the checkout page. Obviously don't abuse this by showing a different price than what will be charged. Be careful if you change your membership levels pricing to update your filter if needed. The hook passes the text generated by the pmpro_getLevelCost(&$level) function and also a level object which is prepopulated with levels pricing and expiration settings already adjusted for any discount codes that may be in affect.
1075
+ * Added expiration settings for levels. You can set an "expiration number" and "expiration period" for any level now. e.g. "6 months" or "180 days". You can also alter expiration settings via discount codes. Expirations will be useful for offering free trials which don't require a credit card... and other scenarios you guys have come up with. A script is run once a day using WP Cron that checks for any membership that has ended and then cancels that membership. The user will lose access and the subscription setup in your payment gateway will be canceled.
1076
+ * Users can "extend" a membership that is set to expire via the Membership Account page.
1077
+ * Added a hook pmpro_level_expiration_text to allow you to override how the expiration information is shown on the levels and checkout pages. Again don't abuse this by showing a different expiration than is real. Be careful if you change your expiration settings to update your filter if needed. The hook passes the text generated by the pmpro_getLevelExpiration(&$level) function and also a level object which is prepopulated with levels pricing and expiration settings already adjusted for any discount codes that may be in affect.
1078
+ * Added an error check if the MySQL insertion of the membership level fails. This happens after the user's credit card/etc has already been charged. The plugin tries to cancel the order just made, but might fail. The user is adviced to contact the site owner instead of trying again. I don't want to scare you. We test the checkout process a lot. So assuming that the code hasn't been tampered with and there isn't an internet outage in the microseconds between the order going through and the database being updates, you should never run into this. Still it's nice to have, just in case.
1079
+ * Fixed a bug that may have caused the billing amount to show up incorrectly on the Membership Account page.
1080
+ * Added the discount code used to the confirmation page, invoices, and invoice emails.
1081
+ * Now sending notification emails to members 1 week before their trial period ends (if applicable). A WP cron job is setup on plugin activation. You can disable the email via the pmpro_send_trial_ending_email hook.
1082
+ * Now sending notification emails to members 1 week before their membership expires (if applicable). A WP cron job is setup on plugin activation. You can disable the email via the pmpro_send_expiration_warning_email hook.
1083
+ * An email is sent when a membership expires. A WP cron job is setup on plugin activation. You can disable the email via the pmpro_send_expiration_email hook.
1084
+ * Note: Right now users cannot "extend" a membership that is about to expire without first canceling their current membership. I plan to add "membership extensions" for these cases, but it's a little complicated and I didn't want to hold up this release for them. So Real Soon Now.
1085
+
1086
+ = 1.2.1 =
1087
+ * Fixed bug where non-member admins would be redirected away from the "All Pages" page in the admin.
1088
+
1089
+ = 1.2 =
1090
+ * Fixing some wonkiness with the 1.1.15 update.
1091
+ * Fixed "warning" showing up on discount code pages.
1092
+ * Tweaked the admin pages a bit for consistency.
1093
+ * Added screenshots and FAQ to the readme.
1094
+ * Figured we were due for a bigger version step.
1095
+
1096
+ = 1.1.15 =
1097
+ * Discount Codes Added!
1098
+ * Removed some redundant files that slipped into the services folder.
1099
+ * Fixed the !!levels!! variable for message settings of the advanced tab.
1100
+ * Changing some ids columns in tables to unsigned.
1101
+
1102
+ = 1.1.14 =
1103
+ * Now encoding #'s when sending info via Authorize.net's API. This may prevent some address conflicts.
1104
+
1105
+ = 1.1.13 =
1106
+ * No longer adding "besecure" custom field to the billing and checkout pages. You can still add this manually to selectively require SSL on a page. If you are trying to do a free membership without SSL, you will have to make sure the besecure custom field is deleted from the Membership-Checkout page, especially if you are upgrading from an older version of PMPro.
1107
+ * Added a filter before sending the default WP welcome notification email. Return false for the "pmpro_wp_new_user_notification" hook/filter to skip sending the WP default welcome email (because in many cases they are already getting an email from PMPro as well).
1108
+
1109
+ = 1.1.12 =
1110
+ * Revenue report on members list page. (Rought estimate only that doesn't take into account trial periods and billing limits.)
1111
+ * Enabling weekly recurring payments for Authorize.net by converting week period to 7 days * # months.
1112
+ * Improved error handling on checkout page.
1113
+ * Now running "pmpro_after_change_membership_level" actions after the "pmpro_after_checkout" action. Previously this hook was only called when a membership level was changed via the WP admin.
1114
+ * Won't complain about setting up a Payment Gateway if you only have free membership levels.
1115
+ * The "besecure" custom field is not added to the billing or checkout by default anymore when you run the "create the pages for me" option in the settings. Whether or not to use HTTPS on a page is now handled in the preheader files for each page (see below).
1116
+ * The plugin won't force SSL on the checkout page anymore unless the membership level requires payment. If your checkout page is still running over HTTPS/SSL for free membership checkouts, make sure the "besecure" custom field has been deleted on your checkout page. You can use the "besecure" custom field or the "pmpro_besecure" filter to override the plugin's decision.
1117
+ * The plugin won't force SSL on the cancel page anymore. Again, you can override this using the "besecure" custom field or the "pmpro_besecure" filter.
1118
+
1119
+ = 1.1.11 =
1120
+ * Removed some debug code from the invoice page that might have shown on error.
1121
+ * Added check to recaptcha library code incase it is already installed. (Let's hope other plugin developers are doing the same.)
1122
+ * Removed the TinyMCE editor from the description field on the edit membership level page. It was a little buggy. Might bring it back later.
1123
+
1124
+ = 1.1.10 =
1125
+ * added a hook/filter "pmpro_rss_text_filter"
1126
+ * added a hook/filter "pmpro_non_member_text_filter"
1127
+ * added a hook/filter "pmpro_not_logged_in_text_filter"
1128
+ * adjusted the pmpro_has_membership_access() function
1129
+ * added a hook/filter "pmpro_has_membership_access_filter"
1130
+ * updated the hook/filter "pmpro_has_membership_access_filter_{post-type}"
1131
+ * removed the "pmpro_has_membership_access_action_{post-type}" hook/action
1132
+ * update invoice page to handle case where no invoice is found
1133
+
1134
+ = 1.1.9 =
1135
+ * You can now set individual posts to require membership without assigning them to a category.
1136
+ * Fixed bug with the confirmation email during signup.
1137
+ * Fixed a CSS bug on the cancel membership page.
1138
+
1139
+ = 1.1.8 =
1140
+ * Fix for login/registration URL rerouting.
1141
+ * Added members list to admin bar menu.
1142
+ * Added warning/error when trying to checkout before the payment gateway is setup.
1143
+ * Fixed some error handling in the order class.
1144
+ * Fixed a bug that occurred when processing amounts less than $1.
1145
+
1146
+ = 1.1.7 =
1147
+ * Fixed bugs with http to https redirects and visa versa.
1148
+ * Fixed redirect bugs for sites installed in a subdomain.
1149
+
1150
+ = 1.1.6 =
1151
+ * Fixed MySQL bug showing up on some users add membership level page.
1152
+
1153
+ = 1.1.5 =
1154
+ * Required fix for PayPal Website Payments Pro processing. Please update.
1155
+ * Fixed bug with pagination on members list.
1156
+ * Fixed bugs with errors thrown by MemberOrder class.
1157
+ * Updated login/registration URL rerouting.
1158
+
1159
+ = 1.1.4 =
1160
+ * Custom Post Types default to allowing access
1161
+ * Fixed login_redirect code.
1162
+ * Added pmpro_login_redirect filter for when members login.
1163
+
1164
+ = 1.1.3 =
1165
+ * Getting ready for the WP plugin repository
1166
+ * License text update.
1167
+
1168
+ = 1.1.2 =
1169
+ * Added hooks to checkout page for customizing registration fields.
1170
+ * Fixed bug in pmpro_getLevelCost();
1171
+ * Another CCV/CVV fix for Authorize.net.
1172
+ * License text update.
1173
+ * Admin notices are loaded via Ajax now.
1174
+
1175
+ = 1.1.1 =
1176
+ * Added honeypot to signup page.
1177
+ * Updated pmpro_add_pages to use capabilities instead of user levels
1178
+ * Fixed checkboxes in admin screens.
1179
+ * Now checking that passwords match on signup.
1180
+ * Properly sending CCV/CVV codes to Authorize.net.
1181
+
1182
+ = 1.0 =
1183
+ * This is the launch version. No changes yet.