PluginProbe
GamiPress – Gamification plugin to reward points, badges & ranks in WordPress, now with AI / trunk
GamiPress – Gamification plugin to reward points, badges & ranks in WordPress, now with AI vtrunk
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 7.8.8 7.8.7 7.8.6 7.8.5 All 44 releases
gamipress / includes / ajax-functions.php

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

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