PluginProbe
GamiPress – Gamification plugin to reward points, badges & ranks in WordPress, now with AI / 8.0.5
GamiPress – Gamification plugin to reward points, badges & ranks in WordPress, now with AI v8.0.5
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 7.8.8 All 47 releases
← All changes | includes/ajax-functions.php +1142 -0 7.9.1 → 8.0.5 View file →
@@ -1,0 +1,1142 @@
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 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' );