-
Notifications
You must be signed in to change notification settings - Fork 11
Expand file tree
/
Copy pathpmpro-group-discount-codes.php
More file actions
executable file
·472 lines (410 loc) · 16.6 KB
/
pmpro-group-discount-codes.php
File metadata and controls
executable file
·472 lines (410 loc) · 16.6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
<?php
/*
Plugin Name: Paid Memberships Pro - Group Discount Codes Add On
Plugin URI: https://www.paidmembershipspro.com/add-ons/group-discount-codes/
Description: Adds features to PMPro to better manage grouped discount codes or large numbers of discount codes.
Version: 0.5
Author: Paid Memberships Pro
Author URI: https://www.paidmembershipspro.com/
Text Domain: pmpro-group-discount-codes
Domain Path: /languages
*/
/**
* Load the textdomain.
*/
function pmpro_groupcodes_load_textdomain() {
load_plugin_textdomain( 'pmpro-group-discount-codes', false, basename( dirname( __FILE__ ) ) . '/languages' );
}
add_action( 'plugins_loaded', 'pmpro_groupcodes_load_textdomain' );
/*
* Setup DB Tables
*/
function pmpro_groupcodes_set_up_db() {
global $wpdb;
$wpdb->pmpro_group_discount_codes = $wpdb->prefix . "pmpro_group_discount_codes";
if ( is_admin() ) {
$db_version = get_option('pmpro_groupcodes_db_version', 0);
if ( empty( $db_version ) ) {
// Set up DB.
require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
// Create/update wp_pmpro_group_discount_codes table.
$sqlQuery = "
CREATE TABLE `" . $wpdb->pmpro_group_discount_codes . "` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`code` varchar(32) NOT NULL,
`code_parent` bigint(20) unsigned NOT NULL,
`order_id` bigint(20) unsigned NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `code` (`code`),
KEY `code_parent` (`code_parent`),
KEY `order_id` (`order_id`)
);
";
dbDelta($sqlQuery);
update_option('pmpro_groupcodes_db_version', ".1");
}
}
}
pmpro_groupcodes_set_up_db();
/**
* Add the group code field to the discount code form.
*/
function pmpro_groupcodes_pmpro_discount_code_after_settings() {
global $wpdb;
// Get the current group codes.
$code_id = intval($_REQUEST['edit']);
if ( $code_id > 0 ) {
$group_codes = $wpdb->get_col( "SELECT code FROM $wpdb->pmpro_group_discount_codes WHERE code_parent = '" . $code_id . "'" );
} else {
$group_codes = array();
}
// Show the field.
?>
<hr />
<h3><?php esc_html_e( 'Group Codes', 'pmpro-group-discount-codes' ); ?></h3>
<p>
<?php
echo wp_kses(
sprintf(
// translators: %s is a link to a random string generator.
__( 'Enter additional codes that will use the same settings, one code per line. Codes may only contain letters and numbers (use a <a href="%s" target="_blank">random string generator</a> or a spreadsheet program to create bulk codes). <strong>Keep the main code secret, and leave the uses of the main code blank.</strong> Each code below may only be used once.', 'pmpro-group-discount-codes' ),
'https://www.random.org/strings/'
),
array(
'strong' => array(),
'a' => array(
'href' => array(),
'target' => array()
)
)
);
?>
</p>
<textarea id="group_codes" name="group_codes" cols="70" rows="8"><?php echo esc_attr( implode( "\n", $group_codes ) );?></textarea>
<hr />
<?php
}
add_action( 'pmpro_discount_code_after_settings', 'pmpro_groupcodes_pmpro_discount_code_after_settings' );
/**
* Save the group codes.
*
* @param int $code_id The ID of the discount code.
*/
function pmpro_groupcodes_pmpro_save_discount_code( $code_id ) {
global $wpdb;
// Make sure we have a code ID.
if ( empty( $code_id ) ) {
return;
}
// Get old codes.
$old_group_codes = $wpdb->get_col("SELECT code FROM $wpdb->pmpro_group_discount_codes WHERE code_parent = '" . (int)$code_id . "'");
// Get new codes.
$group_codes = $_REQUEST['group_codes'];
$group_codes = str_replace( "\r", "", $group_codes );
$group_codes = explode( "\n", str_replace( array( ", ", ",", "; ", ";", " " ), "\n", $group_codes ) );
// Figure out which codes to add and delete.
$intersect = array_intersect( $old_group_codes, $group_codes );
$codes_to_add = array_diff( $group_codes, $intersect );
$codes_to_delete = array_diff( $old_group_codes, $intersect );
// Add new group codes.
foreach( $codes_to_add as $code ) {
$sqlQuery = "INSERT IGNORE INTO $wpdb->pmpro_group_discount_codes (id, code, code_parent) VALUES('', '" . esc_sql( trim( $code ) ) . "', '" . $code_id . "')";
$wpdb->query( $sqlQuery );
}
// Delete old group codes.
foreach( $codes_to_delete as $code ) {
$sqlQuery = "DELETE FROM $wpdb->pmpro_group_discount_codes WHERE code = '" . esc_sql( $code ) . "' LIMIT 1";
$wpdb->query( $sqlQuery );
}
}
add_action( 'pmpro_save_discount_code', 'pmpro_groupcodes_pmpro_save_discount_code' );
/**
* Delete group codes when the parent code is deleted.
*
* @param int $code_id The ID of the parent discount code.
*/
function pmpro_groupcodes_pmpro_delete_discount_code( $code_id ) {
global $wpdb;
$sqlQuery = "DELETE FROM $wpdb->pmpro_group_discount_codes WHERE code_parent = '" . intval( $code_id ) . "'";
$wpdb->query($sqlQuery);
}
add_action( 'pmpro_delete_discount_code', 'pmpro_groupcodes_pmpro_delete_discount_code' );
/**
* Get the database entry for a group code.
*
* @param string $group_code The group code.
* @return object|false The database entry for the group code, or false if not found.
*/
function pmpro_groupcodes_getGroupCode( $group_code ) {
global $wpdb;
return $wpdb->get_row( "SELECT * FROM $wpdb->pmpro_group_discount_codes WHERE code = '" . esc_sql( strtolower( trim( $group_code ) ) ) . "' LIMIT 1" );
}
/**
* Get the group code database entry for a particular order.
*
* @since 0.4
*
* @param int $order_id The order ID.
* @return object|false The database entry for the group code, or false if not found.
*/
function pmpro_groupcodes_get_group_code_for_order( $order_id ) {
global $wpdb;
return $wpdb->get_row( "SELECT * FROM $wpdb->pmpro_group_discount_codes WHERE order_id = '" . intval( $order_id ) . "' LIMIT 1" );
}
/*
* Make sure checkDiscountCode works for group codes.
*
* @param bool|string $okay True if okay, false or error message string if not okay
* @param string $dbcode The discount code.
* @param int $level_id The level ID.
* @param string $code The discount code.
* @return bool|string True if okay, false or error message string if not okay
*/
function pmpro_groupcodes_pmpro_check_discount_code( $okay, $dbcode, $level_id, $code ) {
// If okay, just return.
if ( $okay === true ) {
return $okay;
}
$group_code = pmpro_groupcodes_getGroupCode( $code );
if ( ! empty( $group_code ) ) {
global $wpdb;
// Check if this group code was used already.
if ( $group_code->order_id > 0 ) {
return esc_html__( 'This code has already been used.', 'pmpro-group-discount-codes' );
}
// Okay check parent.
$code_parent = $wpdb->get_var( "SELECT code FROM $wpdb->pmpro_discount_codes WHERE id = '" . (int)$group_code->code_parent . "' LIMIT 1" );
if ( ! empty( $code_parent) ) {
return pmpro_checkDiscountCode($code_parent, $level_id);
}
}
return $okay;
}
add_filter( 'pmpro_check_discount_code', 'pmpro_groupcodes_pmpro_check_discount_code', 10, 4 );
/**
* Fix code level when a group code is used.
*
* @param object $code_level The level object.
* @param int $discount_code_id The discount code ID used.
* @return object The level object.
*/
function pmpro_groupcodes_pmpro_discount_code_level( $code_level, $discount_code_id ) {
global $wpdb;
// If we don't have a level, bail.
if ( empty( $code_level ) || empty( $code_level->id ) ) {
return $code_level;
}
// If a real discount code was used, we don't want to make any futher changes.
if ( ! empty( $discount_code_id ) ) {
return $code_level;
}
// Check if a group code was used.
$group_code = false;
// Check prefixed parameter in PMPro v3.0+.
if ( ! empty( $_REQUEST['pmpro_discount_code'] ) ) {
$group_code = pmpro_groupcodes_getGroupCode( $_REQUEST['pmpro_discount_code'] );
}
// Check the non-prefixed paramter for PMPro < 3.0.
if ( empty( $group_code ) && ! empty( $_REQUEST['discount_code'] ) ) {
$group_code = pmpro_groupcodes_getGroupCode( $_REQUEST['discount_code'] );
}
// Check the code parameter for the applydiscountcode.php service.
if ( empty( $group_code ) && ! empty( $_REQUEST['code'] ) ) {
$group_code = pmpro_groupcodes_getGroupCode( $_REQUEST['code'] );
}
// If we don't have a group code, bail.
if ( empty( $group_code ) ) {
return $code_level;
}
// If this group code was used already, bail.
if ( $group_code->order_id > 0 ) {
return $code_level;
}
// Get the parent code.
$parent_code = $wpdb->get_row( "SELECT * FROM $wpdb->pmpro_discount_codes WHERE id = '" . esc_sql( $group_code->code_parent ) . "' LIMIT 1" );
if ( empty( $parent_code ) ) {
return $code_level;
}
// Unhook this function and get the checkout level with the parent code.
remove_filter( 'pmpro_discount_code_level', 'pmpro_groupcodes_pmpro_discount_code_level', 10, 2 );
$code_level = pmpro_getLevelAtCheckout( (int) $code_level->id, $parent_code->code );
add_filter( 'pmpro_discount_code_level', 'pmpro_groupcodes_pmpro_discount_code_level', 10, 2 );
// Update the discount_code property on the level to the group code to avoid leaking the parent code.
$code_level->discount_code = $group_code->code;
return $code_level;
}
add_filter( 'pmpro_discount_code_level', 'pmpro_groupcodes_pmpro_discount_code_level', 10, 2 );
/**
* When a group code is used, update the discount code uses, group discount code uses, and order notes.
* Note: Moved from pmpro_discount_code_used to pmpro_added_order in version 0.5.
*
* @since 0.4
*
* @param MemberOrder $order The Paid Memberships Pro order object.
*/
function pmpro_groupcodes_pmpro_discount_code_used( $order ) {
global $wpdb;
// Get the membership level object associated with the order.
$level = $order->getMembershipLevelAtCheckout( true );
// Bail if no discount code was used at checkout
if ( empty( $level->discount_code ) ) {
return;
}
// Get the discount code object for the discount code used at checkout
$discount_code = new PMPro_Discount_Code( $level->discount_code );
// Get the discount code id from the discount code object
$discount_code_id = $discount_code->id;
// If $discount_code_id is not empty, then a legitemate discount code was used. Bail.
if ( ! empty( $discount_code_id ) ) {
return;
}
// Clean up any discount code uses that were already created for this order.
$wpdb->query( "DELETE FROM $wpdb->pmpro_discount_codes_uses WHERE order_id = '" . intval( $order->id ) . "'" );
// If the discount code used does not have a parent code, bail.
$group_code = pmpro_groupcodes_getGroupCode( $level->discount_code );
if ( empty( $group_code ) || empty( $group_code->code_parent ) ) {
return;
}
// Update the discount code uses.
$wpdb->query( $wpdb->prepare(
"INSERT INTO $wpdb->pmpro_discount_codes_uses (code_id, user_id, order_id, timestamp) VALUES(%d, %d, %d, %s)",
$group_code->code_parent,
$order->user_id,
$order->id,
current_time( "mysql" )
) );
// Update the group discount code uses.
$sqlQuery = "UPDATE $wpdb->pmpro_group_discount_codes SET order_id = '" . intval( $order->id ) . "'WHERE code='" . esc_sql( $group_code->code ) . "' LIMIT 1";
$wpdb->query( $sqlQuery );
// Update the order notes (legacy functionality, the custom table is the "source of truth").
$order->notes .= "\n---\n{GROUPCODE:" . $group_code->code . "}\n---\n";
$order->saveOrder();
}
add_action( 'pmpro_added_order', 'pmpro_groupcodes_pmpro_discount_code_used' );
/**
* Filter discount code when showing invoice.
*
* @param object $code The discount code.
* @param MemberOrder $order The order object.
* @return object The discount code.
*/
function pmpro_groupcodes_pmpro_order_discount_code( $code, $order ) {
// Check if this order is part of an entry in the group discount codes table.
$group_code = pmpro_groupcodes_get_group_code_for_order( $order->id );
// If so, set the code to the group code.
if ( ! empty( $group_code->code ) ) {
$code->code = $group_code->code;
}
return $code;
}
add_filter( 'pmpro_order_discount_code', 'pmpro_groupcodes_pmpro_order_discount_code', 10, 2 );
/*
* Show group discount codes in emails.
*
* @param array $data The email data.
* @param PMProEmail $email The email object.
* @return array The email data.
*/
function pmpro_groupcodes_pmpro_email_data( $data, $email ) {
if ( ! empty( $data['invoice_id'] ) ) {
// Check if this invoice is part of an entry in the group discount codes table.
$group_code = pmpro_groupcodes_get_group_code_for_order( $data['invoice_id'] );
if ( ! empty( $group_code ) ) {
// Set the discount code variable to the group code.
$data['discount_code'] = $group_code->code;
}
}
return $data;
}
add_filter( 'pmpro_email_data', 'pmpro_groupcodes_pmpro_email_data', 10, 2 );
/**
* Add group codes column to the discount codes table view.
*/
function pmpro_groupcodes_pmpro_discountcodes_extra_cols_header() {
?>
<th><?php esc_html_e( 'Group Code Uses', 'pmpro-group-discount-codes' );?></th>
<?php
}
add_action( 'pmpro_discountcodes_extra_cols_header', 'pmpro_groupcodes_pmpro_discountcodes_extra_cols_header' );
/**
* Fill the group codes column in the discount codes table view.
*
* @param object $code The discount code.
*/
function pmpro_groupcodes_pmpro_discountcodes_extra_cols_body( $code ) {
global $wpdb;
// Get number of group codes and number of codes that have been used.
if ( $code->id > 0 ) {
$number_total_codes = $wpdb->get_var( "SELECT COUNT(code) FROM $wpdb->pmpro_group_discount_codes WHERE code_parent = '" . esc_sql( $code->id ) . "'" );
$number_used_codes = $wpdb->get_var( "SELECT COUNT(code) FROM $wpdb->pmpro_group_discount_codes WHERE code_parent = '" . esc_sql( $code->id ) . "' AND order_id > 0" );
}
?>
<td>
<?php if ( ! empty( $number_total_codes ) ) {
echo '<strong>' . esc_html( $number_used_codes ) . '</strong>/' . esc_html( $number_total_codes );
} else {
echo '--';
} ?>
</td>
<?php
}
add_action( 'pmpro_discountcodes_extra_cols_body', 'pmpro_groupcodes_pmpro_discountcodes_extra_cols_body' );
/**
* Adds an extra colum to your Memberships > Orders > Export to CSV file. Displays the group discount code used.
*
* @param array $columns The columns to export.
* @return array The columns to export.
*/
function pmpro_groupcodes_pmpro_orders_csv_extra_columns( $columns ) {
$columns['group_code'] = 'pmpro_groupcodes_pmpro_orders_csv_extra_columns_group_code';
return $columns;
}
add_filter( 'pmpro_orders_csv_extra_columns', 'pmpro_groupcodes_pmpro_orders_csv_extra_columns' );
/**
* Fill the extra colum for the Memberships > Orders > Export to CSV file. Displays the group discount code used.
*
* @param MemberOrder $order The order object.
* @return string The group code.
*/
function pmpro_groupcodes_pmpro_orders_csv_extra_columns_group_code( $order ) {
// Check if this order is part of an entry in the group discount codes table.
$group_code = pmpro_groupcodes_get_group_code_for_order( $order->id );
if ( ! empty( $group_code ) ) {
return $group_code->code;
} else {
return '';
}
}
// Load deprecated functions.
require_once( dirname( __FILE__ ) . '/includes/deprecated.php' );
/*
* Function to add links to the plugin action links.
*
* @param array $links The links array.
* @return array The links array.
*/
function pmpro_groupcodes_add_action_links($links) {
$new_links = array(
'<a href="' . get_admin_url(NULL, '/admin.php?page=pmpro-discountcodes') . '">' . esc_html__( 'Manage Discount Codes', 'pmpro-group-discount-codes' ) . '</a>',
);
return array_merge( $new_links, $links );
}
add_filter( 'plugin_action_links_' . plugin_basename(__FILE__), 'pmpro_groupcodes_add_action_links' );
/*
* Function to add links to the plugin row meta.
*
* @param array $links The links array.
* @param string $file The plugin file.
* @return array The links array.
*/
function pmpro_groupcodes_plugin_row_meta( $links, $file ) {
if ( strpos( $file, 'pmpro-group-discount-codes.php') !== false ) {
$new_links = array(
'<a href="' . esc_url('https://www.paidmembershipspro.com/add-ons/group-discount-codes/' ) . '" title="' . esc_attr( __( 'View Documentation', 'pmpro-group-discount-codes' ) ) . '">' . __( 'Docs', 'pmpro-group-discount-codes' ) . '</a>',
'<a href="' . esc_url('https://www.paidmembershipspro.com/support/') . '" title="' . esc_attr( __( 'Visit Customer Support Forum', 'pmpro-group-discount-codes' ) ) . '">' . __( 'Support', 'pmpro-group-discount-codes' ) . '</a>',
);
$links = array_merge( $links, $new_links );
}
return $links;
}
add_filter( 'plugin_row_meta', 'pmpro_groupcodes_plugin_row_meta', 10, 2 );