PluginProbe
GamiPress – Gamification plugin to reward points, badges & ranks in WordPress, now with AI / 8.0.6
GamiPress – Gamification plugin to reward points, badges & ranks in WordPress, now with AI v8.0.6
8.0.6 8.0.5 8.0.4 8.0.3 8.0.1 8.0.2 8.0.0 7.9.9.7 7.9.9.6 7.9.9.5 7.9.9.4 7.9.9.3 7.9.9.2 7.9.9.1 7.9.9 7.9.8 7.9.7 7.9.6 7.9.5 7.9.4 7.9.3 7.9.2 7.9.1 7.9.0 7.8.9 All 48 releases
gamipress / includes / ajax-functions.php

ajax-functions.php in GamiPress – Gamification plugin to reward points, badges & ranks in WordPress, now with AI 8.0.6, at includes/ajax-functions.php

1,147 lines 37.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Ajax Functions
4 *
5 * @package GamiPress\Ajax_Functions
6 * @author GamiPress <[email protected]>, Ruben Garcia <[email protected]>
7 * @since 1.0.0
8 */
9 // Exit if accessed directly
10 if( !defined( 'ABSPATH' ) ) exit;
11
12 /**
13 * Ajax Helper for returning achievements
14 *
15 * @since 1.0.0
16 * @updated 1.7.9 Added nonce usage
17 *
18 * @return void
19 */
20 function gamipress_ajax_get_achievements() {
21 // Security check, forces to die if not security passed
22 check_ajax_referer( 'gamipress', 'nonce' );
23
24 // Send back our successful response
25 wp_send_json_success( gamipress_achievements_shortcode_query( $_REQUEST ) );
26
27 }
28 add_action( 'wp_ajax_gamipress_get_achievements', 'gamipress_ajax_get_achievements' );
29 add_action( 'wp_ajax_nopriv_gamipress_get_achievements', 'gamipress_ajax_get_achievements' );
30
31 /**
32 * Ajax Helper for returning logs
33 *
34 * @since 1.4.9
35 * @updated 1.7.9 Added nonce usage
36 *
37 * @return void
38 */
39 function gamipress_ajax_get_logs() {
40 // Security check, forces to die if not security passed
41 check_ajax_referer( 'gamipress', 'nonce' );
42
43 // Set current page var
44 if( isset( $_REQUEST['page'] ) && absint( $_REQUEST['page'] ) > 1 ) {
45 set_query_var( 'paged', absint( $_REQUEST['page'] ) );
46 }
47
48 $atts = $_REQUEST;
49
50 // Change the attribute to display only public logs if current user is different
51 if( $atts['access'] !== 'public' ) {
52 $current_user_id = get_current_user_id();
53
54 // If user is not logged in, force to public
55 if ( $current_user_id === 0 ) $atts['access'] = 'public';
56 // If user ID is different from logged in, force to public
57 if ( $current_user_id !== absint( $atts['user_id'] ) ) $atts['access'] = 'public';
58 }
59
60 // Unset non required shortcode atts
61 unset( $atts['action'] );
62 unset( $atts['page'] );
63
64 // Sanitize
65 foreach( $atts as $attr => $value ) {
66 $value = sanitize_text_field( $value );
67 $atts[$attr] = str_replace( array( '[', ']' ), '', $value);
68 }
69
70 $atts = shortcode_atts( gamipress_logs_shortcode_defaults(), $atts, 'gamipress_logs' );
71
72 // Send back our successful response
73 wp_send_json_success( gamipress_do_shortcode( 'gamipress_logs', $atts ) );
74
75 }
76 add_action( 'wp_ajax_gamipress_get_logs', 'gamipress_ajax_get_logs' );
77 add_action( 'wp_ajax_nopriv_gamipress_get_logs', 'gamipress_ajax_get_logs' );
78
79 /**
80 * Ajax Helper for returning user earnings
81 *
82 * @since 1.4.9
83 * @updated 1.7.9 Added nonce usage
84 *
85 * @return void
86 */
87 function gamipress_ajax_get_user_earnings() {
88 // Security check, forces to die if not security passed
89 check_ajax_referer( 'gamipress', 'nonce' );
90
91 // Set current page var
92 if( isset( $_REQUEST['page'] ) && absint( $_REQUEST['page'] ) > 1 ) {
93 set_query_var( 'paged', absint( $_REQUEST['page'] ) );
94 }
95
96 $atts = $_REQUEST;
97
98 // Unset non required shortcode atts
99 unset( $atts['action'] );
100 unset( $atts['page'] );
101
102 // Sanitize
103 foreach( $atts as $attr => $value ) {
104 $atts[$attr] = sanitize_text_field( $value );
105 $atts[$attr] = str_replace( array( '[', ']' ), '', $value);
106 }
107
108 $atts = shortcode_atts( gamipress_earnings_shortcode_defaults(), $atts, 'gamipress_earnings' );
109
110 // Send back our successful response
111 wp_send_json_success( gamipress_do_shortcode( 'gamipress_earnings', $atts ) );
112
113 }
114 add_action( 'wp_ajax_gamipress_get_user_earnings', 'gamipress_ajax_get_user_earnings' );
115 add_action( 'wp_ajax_nopriv_gamipress_get_user_earnings', 'gamipress_ajax_get_user_earnings' );
116
117 /**
118 * Ajax Helper for save email settings
119 *
120 * @since 2.2.1
121 *
122 * @return void
123 */
124 function gamipress_ajax_save_email_settings() {
125 // Security check, forces to die if not security passed
126 check_ajax_referer( 'gamipress', 'nonce' );
127
128 if( ! isset( $_REQUEST['setting'] ) ) {
129 wp_send_json_error( __( 'Invalid setting key.', 'gamipress' ) );
130 }
131
132 if( ! isset( $_REQUEST['value'] ) ) {
133 wp_send_json_error( __( 'Invalid setting value.', 'gamipress' ) );
134 }
135
136 // Sanitize the setting key
137 $setting = sanitize_text_field( $_REQUEST['setting'] );
138 $setting = sanitize_key( $setting );
139
140 // Sanitize the setting value
141 $value = sanitize_text_field( $_REQUEST['value'] );
142
143 // The unique accepted values are "yes" or "no"
144 if( $value !== 'yes' && $value !== 'no' ) {
145 $value = 'yes';
146 }
147
148 $user_id = get_current_user_id();
149
150 // Bail if user is not logged in
151 if( $user_id === 0 ) {
152 wp_send_json_error( __( 'Please, log in to update your email preferences.', 'gamipress' ) );
153 }
154
155 $user_settings = gamipress_get_user_email_settings( $user_id );
156
157 $user_settings[$setting] = $value;
158
159 switch ( $setting ) {
160 case 'all':
161 // Update all user settings to the same value
162 foreach( $user_settings as $user_setting => $setting_value ) {
163 $user_settings[$user_setting] = $value;
164 }
165 break;
166 case 'points_types':
167 case 'achievement_types':
168 case 'rank_types':
169 // Update the group settings to the same value
170 foreach( $user_settings as $user_setting => $setting_value ) {
171 if( gamipress_starts_with( $user_setting, $setting . '_' ) ) {
172 $user_settings[$user_setting] = $value;
173 }
174 }
175 break;
176 }
177
178 update_user_meta( $user_id, 'gamipress_email_settings', $user_settings );
179
180 // Send back our successful response
181 wp_send_json_success( __( 'Email preferences saved successfully.', 'gamipress' ) );
182
183 }
184 add_action( 'wp_ajax_gamipress_save_email_settings', 'gamipress_ajax_save_email_settings' );
185
186 /**
187 * AJAX Helper for selecting users in Shortcode Embedder
188 *
189 * @since 1.0.0
190 */
191 function gamipress_ajax_get_users() {
192 // Security check, forces to die if not security passed
193 check_ajax_referer( 'gamipress_admin', 'nonce' );
194
195 if( ! current_user_can( gamipress_get_manager_capability() ) ) {
196 return;
197 }
198
199 // If no word query sent, initialize it
200 if ( ! isset( $_REQUEST['q'] ) ) {
201 $_REQUEST['q'] = '';
202 }
203
204 global $wpdb;
205
206 // Pull back the search string
207 $search = esc_sql( $wpdb->esc_like( $_REQUEST['q'] ) );
208 $where = '';
209 $from = "FROM {$wpdb->users} as u ";
210
211 if ( ! empty( $search ) ) {
212 $where = "WHERE ( u.user_login LIKE '%{$search}%' ";
213 $where .= "OR u.user_email LIKE '%{$search}%' ";
214 $where .= "OR u.display_name LIKE '%{$search}%' ";
215 $where .= "OR u.ID LIKE '%{$search}%' ) ";
216 }
217
218 // Get users from the current site
219 if( is_multisite() ) {
220 $from .= "LEFT JOIN {$wpdb->usermeta} AS umcap ON ( umcap.user_id = u.ID ) ";
221
222 // Check if where have been initialized or not
223 if( empty( $where ) ) {
224 $where = "WHERE ";
225 } else {
226 $where .= "AND ";
227 }
228
229 $where .= "umcap.meta_key = '" . $wpdb->get_blog_prefix() . "capabilities' ";
230 }
231
232 // Pagination args
233 $page = isset( $_REQUEST['page'] ) ? absint( $_REQUEST['page'] ) : 1;
234 $limit = 20;
235 $offset = $limit * ( $page - 1 );
236
237 // Fetch our results (store as associative array)
238 $results = $wpdb->get_results(
239 "SELECT ID, user_login, user_email, display_name
240 {$from}
241 {$where}
242 LIMIT {$offset}, {$limit}",
243 'ARRAY_A'
244 );
245
246 $count = absint( $wpdb->get_var( "SELECT COUNT(*) {$from} {$where}" ) );
247
248 /**
249 * Ajax users results (used on almost every post selector)
250 * Note: Use $_REQUEST for all given parameters
251 *
252 * @since 1.0.0
253 *
254 * @param array $results
255 *
256 * @return array
257 */
258 $results = apply_filters( 'gamipress_ajax_get_user_results', $results );
259
260 $response = array(
261 'results' => $results,
262 'more_results' => $count > $offset,
263 );
264
265 // Return our results
266 wp_send_json_success( $response );
267 }
268 add_action( 'wp_ajax_gamipress_get_users', 'gamipress_ajax_get_users' );
269
270 /**
271 * AJAX Helper for selecting posts
272 *
273 * @since 1.0.0
274 * @updated 1.4.8 Added multisite support
275 */
276 function gamipress_ajax_get_posts() {
277 // Security check, forces to die if not security passed
278 check_ajax_referer( 'gamipress_admin', 'nonce' );
279
280 if( ! current_user_can( gamipress_get_manager_capability() ) ) {
281 return;
282 }
283
284 global $wpdb;
285
286 // Pull back the search string
287 $search = isset( $_REQUEST['q'] ) ? esc_sql( $wpdb->esc_like( $_REQUEST['q'] ) ) : '';
288
289 // Setup where conditions (initialized with 1=1)
290 $where = '1=1';
291
292 // Post type conditional
293 $post_type = ( isset( $_REQUEST['post_type'] ) && ! empty( $_REQUEST['post_type'] ) ? $_REQUEST['post_type'] : array( 'post', 'page' ) );
294
295 if ( is_array( $post_type ) ) {
296
297 // Sanitize all post types given
298 foreach( $post_type as $i => $value ) {
299 $post_type[$i] = sanitize_text_field( $value );
300 }
301
302 $post_type = sprintf( ' AND p.post_type IN(\'%s\')', implode( "','", $post_type ) );
303 } else {
304
305 // Sanitize the post type
306 $post_type = sanitize_text_field( $post_type );
307
308 $post_type = sprintf( ' AND p.post_type = \'%s\'', $post_type );
309 }
310
311 $where .= $post_type;
312
313 // Post title and ID conditional
314 $where .= " AND ( p.post_title LIKE '%{$search}%' OR p.ID LIKE '%{$search}%' )";
315
316 // Post status conditional
317 $where .= " AND p.post_status IN( 'publish', 'private', 'inherit' )";
318
319 // Check for trigger type extra conditionals
320 if( isset( $_REQUEST['trigger_type'] ) ) {
321
322 $query_args = array();
323 $trigger_type = sanitize_text_field( $_REQUEST['trigger_type'] );
324
325 // Get trigger type query args (This function is filtered!)
326 $query_args = gamipress_get_specific_activity_triggers_query_args( $query_args, $trigger_type );
327
328 if( ! empty( $query_args ) ) {
329
330 if( is_array( $query_args ) ) {
331 // If is an array of conditionals, then build the new conditionals
332 foreach( $query_args as $field => $value ) {
333 $where .= " AND p.{$field} = '$value'";
334 }
335 } else {
336 // Leave an extra space if query args doesn't have one
337 $where .= ' ' . $query_args;
338 }
339
340 }
341 }
342
343 /**
344 * Ajax posts query args (used on almost every post selector)
345 *
346 * Note: Use $_REQUEST for all given parameters
347 *
348 * @since 1.6.6
349 *
350 * @param string $query_args
351 *
352 * @return array|string
353 */
354 $extra_query_args = apply_filters( 'gamipress_ajax_get_posts_query_args', '' );
355
356 // Check for extra conditionals
357 if( ! empty( $extra_query_args ) ) {
358
359 if( is_array( $extra_query_args ) ) {
360 // If is an array of conditionals, then build the new conditionals
361 foreach( $extra_query_args as $field => $value ) {
362 $where .= " AND p.{$field} = '$value'";
363 }
364 } else {
365 // Leave an extra space if extra query args doesn't have one
366 $where .= ' ' . $extra_query_args;
367 }
368
369 }
370
371 // Setup from (from is filtered to allow joins)
372 $from = "{$wpdb->posts} AS p";
373
374 /**
375 * Ajax posts from (used on almost every post selector)
376 * Note: Use $_REQUEST for all given parameters
377 *
378 * @since 1.6.6
379 *
380 * @param string $from By default '{$wpdb->posts} AS p'
381 *
382 * @return string
383 */
384 $from = apply_filters( 'gamipress_ajax_get_posts_from', $from );
385
386 /**
387 * Ajax posts where (used on almost every post selector)
388 * Note: Use $_REQUEST for all given parameters
389 *
390 * @since 1.6.6
391 *
392 * @param string $where Contains all wheres
393 *
394 * @return string
395 */
396 $where = apply_filters( 'gamipress_ajax_get_posts_where', $where );
397
398 // Setup order by
399 $order_by = "p.post_type ASC, p.menu_order DESC";
400
401 /**
402 * Ajax posts order by (used on almost every post selector)
403 * Note: Use $_REQUEST for all given parameters
404 *
405 * @since 1.6.6
406 *
407 * @param string $order_by By default 'p.post_type ASC, p.menu_order DESC'
408 *
409 * @return string
410 */
411 $order_by = apply_filters( 'gamipress_ajax_get_posts_order_by', $order_by );
412
413 // Pagination args
414 $page = isset( $_REQUEST['page'] ) ? absint( $_REQUEST['page'] ) : 1;
415 $limit = 20;
416 $offset = $limit * ( $page - 1 );
417
418 if( gamipress_is_network_wide_active() ) {
419
420 $results = array();
421 $count = 0;
422
423 foreach( gamipress_get_network_site_ids() as $site_id ) {
424
425 // Switch to site
426 switch_to_blog( $site_id );
427
428 // Get the current site name to append it to results
429 $site_name = get_bloginfo( 'name' );
430
431 // Setup from after switch site to get the site's posts table correctly
432 $from = "{$wpdb->posts} AS p";
433
434 /**
435 * Ajax posts from (for current switched site)
436 *
437 * @since 1.7.4
438 *
439 * @param string $from By default '{$wpdb->posts} AS p'
440 * @param int $site_id Site ID
441 *
442 * @return string
443 */
444 $from = apply_filters( 'gamipress_ajax_get_posts_site_from', $from, $site_id );
445
446 // On this query, keep $wpdb->posts to get sub site posts
447 $site_results = $wpdb->get_results(
448 "SELECT p.ID, p.post_title, p.post_type
449 FROM {$from}
450 WHERE {$where}
451 ORDER BY {$order_by}
452 LIMIT {$offset}, {$limit}"
453 );
454
455 // Loop all site results to add the site ID and name
456 foreach( $site_results as $index => $site_result ) {
457
458 $site_result->site_id = absint( $site_id );
459 $site_result->site_name = $site_name;
460
461 $site_results[$index] = $site_result;
462 }
463
464 // Merge it to all results
465 $results = array_merge( $results, $site_results );
466
467 $count += absint( $wpdb->get_var( "SELECT COUNT(*) FROM {$from} WHERE {$where}" ) );
468
469 // Restore current site
470 restore_current_blog();
471
472 }
473
474 } else {
475
476 // On this query, keep $wpdb->posts to get current site posts
477 $results = $wpdb->get_results(
478 "SELECT p.ID, p.post_title, p.post_type
479 FROM {$from}
480 WHERE {$where}
481 ORDER BY {$order_by}
482 LIMIT {$offset}, {$limit}"
483 );
484
485 $count = absint( $wpdb->get_var( "SELECT COUNT(*) FROM {$from} WHERE {$where}" ) );
486
487 }
488
489 $response = array(
490 'results' => $results,
491 'more_results' => $count > $limit && $count > $offset,
492 );
493
494 // Return our results
495 wp_send_json_success( $response );
496
497 }
498 add_action( 'wp_ajax_gamipress_get_posts', 'gamipress_ajax_get_posts' );
499
500 /**
501 * AJAX Helper for selecting requirements
502 *
503 * @since 1.0.0
504 * @updated 1.4.8 Added multisite support
505 */
506 function gamipress_ajax_get_requirements() {
507 // Security check, forces to die if not security passed
508 check_ajax_referer( 'gamipress_admin', 'nonce' );
509
510 if( ! current_user_can( gamipress_get_manager_capability() ) ) {
511 return;
512 }
513
514 global $wpdb;
515
516 // Pull back the search string
517 $search = isset( $_REQUEST['q'] ) ? esc_sql( $wpdb->esc_like( $_REQUEST['q'] ) ) : '';
518
519 // Setup where conditions (initialized with 1=1)
520 $where = '1=1';
521
522 // Post type conditional
523 $post_type = ( isset( $_REQUEST['post_type'] ) && ! empty( $_REQUEST['post_type'] ) ? $_REQUEST['post_type'] : gamipress_get_requirement_types_slugs() );
524
525 if ( is_array( $post_type ) ) {
526
527 // Sanitize all post types given
528 foreach( $post_type as $i => $value ) {
529 $post_type[$i] = sanitize_text_field( $value );
530 }
531
532 $post_type = sprintf( ' AND p.post_type IN(\'%s\')', implode( "','", $post_type ) );
533 } else {
534
535 // Sanitize the post type
536 $post_type = sanitize_text_field( $post_type );
537
538 $post_type = sprintf( ' AND p.post_type = \'%s\'', $post_type );
539 }
540
541 $where .= $post_type;
542
543 // Post title and ID conditional
544 $where .= " AND ( p.post_title LIKE '%{$search}%' OR p.ID LIKE '%{$search}%' )";
545
546 // Post status conditional
547 $where .= " AND p.post_status = 'publish'";
548
549 // Setup from (from is filtered to allow joins)
550 $posts = GamiPress()->db->posts;
551 $from = "{$posts} AS p";
552
553 /**
554 * Ajax requirements FROM (used on almost every post selector)
555 * Note: Use $_REQUEST for all given parameters
556 *
557 * @since 1.6.6
558 *
559 * @param string $from By default '{GamiPress()->db->posts} AS p'
560 *
561 * @return string
562 */
563 $from = apply_filters( 'gamipress_ajax_get_requirements_from', $from );
564
565 /**
566 * Ajax requirements WHERE (used on almost every post selector)
567 * Note: Use $_REQUEST for all given parameters
568 *
569 * @since 1.6.6
570 *
571 * @param string $where Contains all wheres
572 *
573 * @return string
574 */
575 $where = apply_filters( 'gamipress_ajax_get_requirements_where', $where );
576
577 // Setup order by
578 $order_by = "p.post_type ASC, p.post_parent ASC, p.menu_order DESC";
579
580 /**
581 * Ajax requirements ORDER BY (used on almost every post selector)
582 * Note: Use $_REQUEST for all given parameters
583 *
584 * @since 1.6.6
585 *
586 * @param string $order_by By default 'p.post_type ASC, p.post_parent ASC, p.menu_order DESC'
587 *
588 * @return string
589 */
590 $order_by = apply_filters( 'gamipress_ajax_get_requirements_order_by', $order_by );
591
592 // Pagination args
593 $page = isset( $_REQUEST['page'] ) ? absint( $_REQUEST['page'] ) : 1;
594 $limit = 20;
595 $offset = $limit * ( $page - 1 );
596
597 // On this query, keep $wpdb->posts to get current site posts
598 $results = $wpdb->get_results(
599 "SELECT p.ID, p.post_title, p.post_type, p.post_parent
600 FROM {$from}
601 WHERE {$where}
602 ORDER BY {$order_by}
603 LIMIT {$offset}, {$limit}", ARRAY_A
604 );
605
606 $count = absint( $wpdb->get_var( "SELECT COUNT(*) FROM {$from} WHERE {$where}" ) );
607
608 $parents = array();
609
610 foreach( $results as $i => $result ) {
611 $parent_id = absint( $result['post_parent'] );
612
613 // Check if parent have been found previously
614 if( ! isset( $parents[$parent_id] ) ) {
615 $parent = $wpdb->get_row( $wpdb->prepare(
616 "SELECT p.post_title, p.post_type
617 FROM {$posts} AS p
618 WHERE p.ID = %d
619 LIMIT 1",
620 $parent_id
621 ), ARRAY_A );
622
623 if( $parent ) {
624 $parents[$parent_id] = $parent;
625 }
626 }
627
628 // Add the parent post type & title
629 $parent = ( isset( $parents[$parent_id] ) ? $parents[$parent_id] : null );
630
631 if( $parent !== null ) {
632 $results[$i]['post_parent_title'] = $parent['post_title'];
633 $results[$i]['post_parent_type'] = $parent['post_type'];
634 }
635 }
636
637 $response = array(
638 'results' => $results,
639 'more_results' => $count > $limit && $count > $offset,
640 );
641
642 // Return our results
643 wp_send_json_success( $response );
644
645 }
646 add_action( 'wp_ajax_gamipress_get_requirements', 'gamipress_ajax_get_requirements' );
647
648 /**
649 * AJAX Helper for selecting posts in Shortcode Embedder
650 *
651 * @since 1.0.0
652 */
653 function gamipress_ajax_get_achievements_options() {
654
655 global $wpdb;
656
657 // Pull back the search string
658 $search = isset( $_REQUEST['q'] ) ? $wpdb->esc_like( $_REQUEST['q'] ) : '';
659 $achievement_types = isset( $_REQUEST['post_type'] ) && 'all' !== $_REQUEST['post_type']
660 ? array( esc_sql( $_REQUEST['post_type'] ) )
661 : gamipress_get_achievement_types_slugs();
662 $post_type = sprintf( 'AND p.post_type IN(\'%s\')', implode( "','", $achievement_types ) );
663
664 // For single type, is not needed to add the post type, but for multiples types is a better option to distinguish them easily
665 $select = 'p.ID, p.post_title';
666
667 if( count( $achievement_types ) > 1 ) {
668 $select = 'p.ID, p.post_title, p.post_type';
669 }
670
671 $posts = GamiPress()->db->posts;
672 $postmeta = GamiPress()->db->postmeta;
673
674 $results = $wpdb->get_results( $wpdb->prepare(
675 "SELECT {$select}
676 FROM {$posts} AS p
677 JOIN {$postmeta} AS pm
678 ON p.ID = pm.post_id
679 WHERE p.post_title LIKE %s
680 {$post_type}
681 AND p.post_status IN( 'publish', 'private', 'inherit' )
682 AND pm.meta_key = %s
683 AND pm.meta_value = %s",
684 "%%{$search}%%",
685 "_gamipress_hidden",
686 "show"
687 ) );
688
689 // Return our results
690 wp_send_json_success( $results );
691
692 }
693 add_action( 'wp_ajax_gamipress_get_achievements_options', 'gamipress_ajax_get_achievements_options' );
694
695 /**
696 * AJAX helper for getting our posts and returning select options
697 *
698 * @since 1.0.0
699 * @updated 1.0.5
700 * @updated 1.3.0
701 * @updated 1.3.5 Make function accessible through gamipress_get_achievements_options_html action
702 */
703 function gamipress_achievement_post_ajax_handler() {
704 // Security check, forces to die if not security passed
705 check_ajax_referer( 'gamipress_admin', 'nonce' );
706
707 $selected = '';
708
709 // If requirement_id requested, then retrieve the selected option from this requirement
710 if( isset( $_REQUEST['requirement_id'] ) && ! empty( $_REQUEST['requirement_id'] ) ) {
711
712 $requirements = gamipress_get_requirement_object( absint( $_REQUEST['requirement_id'] ) );
713
714 $selected = isset( $requirements['achievement_post'] ) ? $requirements['achievement_post'] : '';
715 } else if( isset( $_REQUEST['selected'] ) && ! empty( $_REQUEST['selected'] ) ) {
716 $selected = $_REQUEST['selected'];
717 }
718
719 // Sanitize
720 $selected = absint( $selected );
721
722 $achievement_type = sanitize_text_field( $_REQUEST['achievement_type'] );
723 $exclude_posts = isset( $_REQUEST['excluded_posts'] ) ? (array) $_REQUEST['excluded_posts'] : array();
724
725 // If we don't have an achievement type, bail now
726 if ( empty( $achievement_type ) ) {
727 die();
728 }
729
730 $achievement_types = gamipress_get_achievement_types();
731
732 if( ! isset( $achievement_types[$achievement_type] ) ) {
733 return;
734 }
735
736 // Sanitize excluded posts
737 foreach( $exclude_posts as $i => $exclude_post ) {
738 $exclude_posts[$i] = absint( $exclude_post );
739 }
740
741 $singular_name = ! empty( $achievement_types[$achievement_type]['singular_name'] ) ? $achievement_types[$achievement_type]['singular_name'] : __( 'Achievement', 'gamipress' );
742
743 // Grab all our posts for this achievement type
744 $achievements = get_posts( array(
745 'post_type' => $achievement_type,
746 'post__not_in' => $exclude_posts,
747 'posts_per_page' => -1,
748 'orderby' => 'title',
749 'order' => 'ASC',
750 'suppress_filters' => false,
751 ));
752
753 // Setup our output
754 $output = '<option value="">' . sprintf( __( 'Choose the %s', 'gamipress' ), $singular_name ) . '</option>';
755 foreach ( $achievements as $achievement ) {
756 $achievement_id = absint( $achievement->ID );
757 $output .= '<option value="' . $achievement_id . '" ' . selected( $selected, $achievement_id, false ) . '>' . $achievement->post_title . '</option>';
758 }
759
760 // Send back our results and die like a man
761 echo $output;
762 die();
763
764 }
765 add_action( 'wp_ajax_gamipress_requirement_achievement_post', 'gamipress_achievement_post_ajax_handler' );
766 add_action( 'wp_ajax_gamipress_get_achievements_options_html', 'gamipress_achievement_post_ajax_handler' );
767
768 /**
769 * AJAX Helper for selecting ranks in achievement earned by
770 *
771 * @since 1.3.1
772 */
773 function gamipress_ajax_get_ranks_options_html() {
774 // Security check, forces to die if not security passed
775 check_ajax_referer( 'gamipress_admin', 'nonce' );
776
777 global $wpdb;
778
779 // Post type conditional
780 $post_type = ( isset( $_REQUEST['post_type'] ) && ! empty( $_REQUEST['post_type'] ) ? $_REQUEST['post_type'] : gamipress_get_rank_types_slugs() );
781
782 if ( is_array( $post_type ) ) {
783
784 // Sanitize all post types given
785 foreach( $post_type as $i => $value ) {
786 $post_type[$i] = sanitize_text_field( $value );
787 }
788
789 $post_type = sprintf( 'AND p.post_type IN(\'%s\')', implode( "','", $post_type ) );
790 $singular_name = __( 'Rank', 'gamipress' );
791 } else {
792
793 // Sanitize the post type
794 $post_type = sanitize_text_field( $post_type );
795
796 $singular_name = gamipress_get_rank_type_singular( $post_type, true );
797 $post_type = sprintf( 'AND p.post_type = \'%s\'', $post_type );
798 }
799
800 $selected = '';
801
802 // If requirement_id requested, then retrieve the selected option from this requirement
803 if( isset( $_REQUEST['requirement_id'] ) && ! empty( $_REQUEST['requirement_id'] ) ) {
804
805 $requirements = gamipress_get_requirement_object( absint( $_REQUEST['requirement_id'] ) );
806
807 $selected = isset( $requirements['rank_required'] ) ? $requirements['rank_required'] : '';
808 } else if( isset( $_REQUEST['selected'] ) && ! empty( $_REQUEST['selected'] ) ) {
809 $selected = $_REQUEST['selected'];
810 }
811
812 // Sanitize
813 $selected = absint( $selected );
814
815 $posts = GamiPress()->db->posts;
816
817 $ranks = $wpdb->get_results(
818 "SELECT p.ID, p.post_title
819 FROM {$posts} AS p
820 WHERE p.post_status IN( 'publish', 'private', 'inherit' )
821 {$post_type}
822 ORDER BY p.post_type ASC, p.menu_order DESC"
823 );
824
825 // Setup our output
826 $output = '<option value="">' . sprintf( __( 'Choose the %s', 'gamipress' ), $singular_name ) . '</option>';
827 foreach ( $ranks as $rank ) {
828 $rank_id = absint( $rank->ID );
829 $output .= '<option value="' . $rank_id . '" ' . selected( $selected, $rank_id, false ) . '>' . $rank->post_title . '</option>';
830 }
831
832 // Send back our results and die like a man
833 echo $output;
834 die();
835
836 }
837 add_action( 'wp_ajax_gamipress_get_ranks_options_html', 'gamipress_ajax_get_ranks_options_html' );
838
839 /**
840 * AJAX Helper for selecting ranks in Shortcode Embedder
841 *
842 * @since 1.3.1
843 */
844 function gamipress_ajax_get_ranks_options() {
845 global $wpdb;
846
847 // Pull back the search string
848 $search = isset( $_REQUEST['q'] ) ? $wpdb->esc_like( $_REQUEST['q'] ) : '';
849
850 // For single type, is not needed to add the post type, but for multiples types is a better option to distinguish them easily
851 $select = 'p.ID, p.post_title';
852
853 // Post type conditional
854 $post_type = ( isset( $_REQUEST['post_type'] ) && ! empty( $_REQUEST['post_type'] ) ? $_REQUEST['post_type'] : gamipress_get_rank_types_slugs() );
855
856 if ( is_array( $post_type ) ) {
857
858 // Sanitize all post types given
859 foreach( $post_type as $i => $value ) {
860 $post_type[$i] = sanitize_text_field( $value );
861 }
862
863 $post_type = sprintf( 'AND p.post_type IN(\'%s\')', implode( "','", $post_type ) );
864
865 $select = 'p.ID, p.post_title, p.post_type';
866 } else {
867
868 // Sanitize the post type
869 $post_type = sanitize_text_field( $post_type );
870
871 $post_type = sprintf( 'AND p.post_type = \'%s\'', $post_type );
872 }
873
874 $posts = GamiPress()->db->posts;
875
876 $ranks = $wpdb->get_results( $wpdb->prepare(
877 "SELECT {$select}
878 FROM {$posts} AS p
879 WHERE p.post_status IN( 'publish', 'private', 'inherit' )
880 {$post_type}
881 AND p.post_title LIKE %s
882 ORDER BY p.post_type ASC, p.menu_order DESC",
883 "%%{$search}%%"
884 ) );
885
886 // Return our results
887 wp_send_json_success( $ranks );
888 }
889 add_action( 'wp_ajax_gamipress_get_ranks_options', 'gamipress_ajax_get_ranks_options' );
890
891 /**
892 * Ajax function to check and unlock an achievement by expend an amount of points
893 *
894 * @since 1.3.7
895 * @updated 1.7.9 Added nonce usage
896 *
897 * @return void
898 */
899 function gamipress_ajax_unlock_achievement_with_points() {
900 // Security check, forces to die if not security passed
901 check_ajax_referer( 'gamipress', 'nonce' );
902
903 $achievement_id = isset( $_POST['achievement_id'] ) ? absint( $_POST['achievement_id'] ) : 0;
904
905 $achievement = gamipress_get_post( $achievement_id );
906
907 // Return if achievement not exists
908 if( ! $achievement )
909 wp_send_json_error( __( 'Achievement not found.', 'gamipress' ) );
910
911 $achievement_types = gamipress_get_achievement_types();
912
913 // Return if not a valid achievement
914 if( ! isset( $achievement_types[$achievement->post_type] ) )
915 wp_send_json_error( __( 'Invalid achievement.', 'gamipress' ) );
916
917 $achievement_type = $achievement_types[$achievement->post_type];
918
919 $user_id = get_current_user_id();
920
921 // Guest not supported yet (basically because they has not points)
922 if( $user_id === 0 )
923 wp_send_json_error( __( 'You are not allowed to perform this action.', 'gamipress' ) );
924
925 // Return if this option not was enabled
926 if( ! (bool) gamipress_get_post_meta( $achievement_id, '_gamipress_unlock_with_points' ) )
927 wp_send_json_error( sprintf( __( 'You are not allowed to unlock this %s.', 'gamipress' ), $achievement_type['singular_name'] ) );
928
929 $points = absint( gamipress_get_post_meta( $achievement_id, '_gamipress_points_to_unlock' ) );
930
931 // Return if no points configured
932 if( $points === 0 )
933 wp_send_json_error( sprintf( __( 'You are not allowed to unlock this %s.', 'gamipress' ), $achievement_type['singular_name'] ) );
934
935 $earned = gamipress_achievement_user_exceeded_max_earnings( $user_id, $achievement_id );
936
937 // Return if user has completely earned this achievement
938 if( $earned )
939 wp_send_json_error( sprintf( __( 'You already unlocked this %s.', 'gamipress' ), $achievement_type['singular_name'] ) );
940
941 // Setup points type
942 $points_types = gamipress_get_points_types();
943 $points_type = gamipress_get_post_meta( $achievement_id, '_gamipress_points_type_to_unlock' );
944
945 // Default points label
946 $points_label = __( 'Points', 'gamipress' );
947
948 // Points type label
949 if( isset( $points_types[$points_type] ) )
950 $points_label = $points_types[$points_type]['plural_name'];
951
952 // Setup user points
953 $user_points = gamipress_get_user_points( $user_id, $points_type );
954
955 // Return if insufficient points
956 if( $user_points < $points ) {
957
958 $message = sprintf( __( 'Insufficient %s.', 'gamipress' ), $points_label );
959
960 /**
961 * Available filter to override the insufficient points text when unlocking a rank using points
962 *
963 * @since 1.0.5
964 *
965 * @param string $message The insufficient points message
966 * @param int $achievement_id The achievement ID
967 * @param int $user_id The current logged in user ID
968 * @param int $points The required amount of points
969 * @param string $points_type The required amount points type
970 */
971 $message = apply_filters( 'gamipress_unlock_achievement_with_points_insufficient_points_message', $message, $achievement_id, $user_id, $points, $points_type );
972
973 wp_send_json_error( $message );
974 }
975
976 // Deduct points to user
977 gamipress_deduct_points_to_user( $user_id, $points, $points_type, array(
978 'log_type' => 'points_expend',
979 'reason' => apply_filters( 'gamipress_points_expended_log_pattern', __( '{user} expended {points} {points_type} for a new total of {total_points} {points_type}', 'gamipress' ) )
980 ) );
981
982 // Award the achievement to the user
983 gamipress_award_achievement_to_user( $achievement_id, $user_id );
984
985 $congratulations = gamipress_get_post_meta( $achievement_id, '_gamipress_congratulations_text' );
986
987 if( empty( $congratulations ) ) {
988 $congratulations = sprintf( __( 'Congratulations! You unlocked the %s %s.', 'gamipress' ), $achievement_type['singular_name'], $achievement->post_title );
989 }
990
991 $congratulations = wpautop( $congratulations );
992 $congratulations = do_shortcode( $congratulations );
993
994 // Filter to change congratulations message
995 $congratulations = apply_filters( 'gamipress_achievement_unlocked_with_points_congratulations', $congratulations, $achievement_id, $user_id, $points, $points_type );
996
997 /**
998 * Achievement unlocked with points action
999 *
1000 * @since 1.3.7
1001 *
1002 * @param integer $achievement_id The achievement unlocked ID
1003 * @param integer $user_id The user ID
1004 * @param integer $points The amount of points expended
1005 * @param string $points_type The points type of the amount of points expended
1006 */
1007 do_action( 'gamipress_achievement_unlocked_with_points', $achievement_id, $user_id, $points, $points_type );
1008
1009 wp_send_json_success( $congratulations );
1010
1011 }
1012 add_action( 'wp_ajax_gamipress_unlock_achievement_with_points', 'gamipress_ajax_unlock_achievement_with_points' );
1013
1014 /**
1015 * Ajax function to check and unlock a rank by expend an amount of points
1016 *
1017 * @since 1.3.7
1018 * @updated 1.7.9 Added nonce usage
1019 *
1020 * @return void
1021 */
1022 function gamipress_ajax_unlock_rank_with_points() {
1023 // Security check, forces to die if not security passed
1024 check_ajax_referer( 'gamipress', 'nonce' );
1025
1026 $rank_id = isset( $_POST['rank_id'] ) ? absint( $_POST['rank_id'] ) : 0;
1027
1028 $rank = gamipress_get_post( $rank_id );
1029
1030 // Return if rank not exists
1031 if( ! $rank ) {
1032 wp_send_json_error( __( 'Rank not found.', 'gamipress' ) );
1033 }
1034
1035 $rank_types = gamipress_get_rank_types();
1036
1037 // Return if not a valid rank
1038 if( ! isset( $rank_types[$rank->post_type] ) ) {
1039 wp_send_json_error( __( 'Invalid rank.', 'gamipress' ) );
1040 }
1041
1042 $rank_type = $rank_types[$rank->post_type];
1043
1044 $user_id = get_current_user_id();
1045
1046 // Guest not supported yet (basically because they has not points)
1047 if( $user_id === 0 ) {
1048 wp_send_json_error( __( 'You are not allowed to perform this action.', 'gamipress' ) );
1049 }
1050
1051 // Return if this option not was enabled
1052 if( ! (bool) gamipress_get_post_meta( $rank_id, '_gamipress_unlock_with_points' ) ) {
1053 wp_send_json_error( sprintf( __( 'You are not allowed to unlock this %s.', 'gamipress' ), $rank_type['singular_name'] ) );
1054 }
1055
1056 $points = absint( gamipress_get_post_meta( $rank_id, '_gamipress_points_to_unlock' ) );
1057
1058 // Return if no points configured
1059 if( $points === 0 ) {
1060 wp_send_json_error( sprintf( __( 'You are not allowed to unlock this %s.', 'gamipress' ), $rank_type['singular_name'] ) );
1061 }
1062
1063 // Bail if not is the next rank to unlock
1064 if( gamipress_get_next_user_rank_id( $user_id, $rank->post_type ) !== $rank_id ) {
1065 wp_send_json_error( sprintf( __( 'You are not allowed to unlock this %s.', 'gamipress' ), $rank_type['singular_name'] ) );
1066 }
1067
1068 $user_rank = gamipress_get_user_rank( $user_id, $rank_type );
1069
1070 // Return if user is in a higher rank
1071 if( gamipress_get_rank_priority( $rank_id ) <= gamipress_get_rank_priority( $user_rank ) ) {
1072 wp_send_json_error( sprintf( __( 'You are already in a higher %s.', 'gamipress' ), $rank_type['singular_name'] ) );
1073 }
1074
1075 // Setup points type
1076 $points_types = gamipress_get_points_types();
1077 $points_type = gamipress_get_post_meta( $rank_id, '_gamipress_points_type_to_unlock' );
1078
1079 // Default points label
1080 $points_label = __( 'Points', 'gamipress' );
1081
1082 // Points type label
1083 if( isset( $points_types[$points_type] ) )
1084 $points_label = $points_types[$points_type]['plural_name'];
1085
1086 // Setup user points
1087 $user_points = gamipress_get_user_points( $user_id, $points_type );
1088
1089 // Return if insufficient points
1090 if( $user_points < $points ) {
1091
1092 $message = sprintf( __( 'Insufficient %s.', 'gamipress' ), $points_label );
1093
1094 /**
1095 * Available filter to override the insufficient points text when unlocking a rank using points
1096 *
1097 * @since 1.0.5
1098 *
1099 * @param string $message The insufficient points message
1100 * @param int $rank_id The rank ID
1101 * @param int $user_id The current logged in user ID
1102 * @param int $points The required amount of points
1103 * @param string $points_type The required amount points type
1104 */
1105 $message = apply_filters( 'gamipress_unlock_rank_with_points_insufficient_points_message', $message, $rank_id, $user_id, $points, $points_type );
1106
1107 wp_send_json_error( $message );
1108 }
1109
1110 // Deduct points to user
1111 gamipress_deduct_points_to_user( $user_id, $points, $points_type, array(
1112 'log_type' => 'points_expend',
1113 'reason' => apply_filters( 'gamipress_points_expended_log_pattern', __( '{user} expended {points} {points_type} for a new total of {total_points} {points_type}', 'gamipress' ) )
1114 ) );
1115
1116 // Award the rank to the user
1117 gamipress_update_user_rank( $user_id, $rank_id );
1118
1119 $congratulations = gamipress_get_post_meta( $rank_id, '_gamipress_congratulations_text' );
1120
1121 if( empty( $congratulations ) ) {
1122 $congratulations = sprintf( __( 'Congratulations! You reached to the %s %s.', 'gamipress' ), $rank_type['singular_name'], $rank->post_title );
1123 }
1124
1125 $congratulations = wpautop( $congratulations );
1126 $congratulations = do_shortcode( $congratulations );
1127
1128 // Filter to change congratulations message
1129 $congratulations = apply_filters( 'gamipress_rank_unlocked_with_points_congratulations', $congratulations, $rank_id, $user_id, $points, $points_type );
1130
1131 /**
1132 * Achievement unlocked with points action
1133 *
1134 * @since 1.3.7
1135 *
1136 * @param integer $rank_id The rank unlocked ID
1137 * @param integer $user_id The user ID
1138 * @param integer $points The amount of points expended
1139 * @param string $points_type The points type of the amount of points expended
1140 */
1141 do_action( 'gamipress_rank_unlocked_with_points', $rank_id, $user_id, $points, $points_type );
1142
1143 wp_send_json_success( $congratulations );
1144
1145 }
1146 add_action( 'wp_ajax_gamipress_unlock_rank_with_points', 'gamipress_ajax_unlock_rank_with_points' );
1147