PluginProbe
GamiPress – Gamification plugin to reward points, badges & ranks in WordPress, now with AI / 7.9.8
GamiPress – Gamification plugin to reward points, badges & ranks in WordPress, now with AI v7.9.8
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 7.8.7 All 46 releases
gamipress / includes / functions / ranks.php

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

1,459 lines 42.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Rank Functions
4 *
5 * @package GamiPress\Rank_Functions
6 * @author GamiPress <[email protected]>, Ruben Garcia <[email protected]>
7 * @since 1.3.1
8 */
9 // Exit if accessed directly
10 if( !defined( 'ABSPATH' ) ) exit;
11
12 /**
13 * Check if post is a registered GamiPress rank.
14 *
15 * @since 1.3.1
16 *
17 * @param object|int|string $post Post object or ID.
18 * @return bool True if post is an rank, otherwise false.
19 */
20 function gamipress_is_rank( $post = null ) {
21
22 $return = true;
23
24 if( gettype($post) === 'string' ) {
25 if( is_numeric($post) ) {
26 $post_type = gamipress_get_post_type( $post );
27 } else {
28 $post_type = $post;
29 }
30 } else {
31 $post_type = gamipress_get_post_type( $post );
32 }
33
34 // If post type is NOT a registered rank type, it cannot be an rank
35 if ( ! in_array( $post_type, gamipress_get_rank_types_slugs() ) ) {
36 $return = false;
37 }
38
39 // If we pass both previous tests, this is a valid rank (with filter to override)
40 return apply_filters( 'gamipress_is_rank', $return, $post, $post_type );
41
42 }
43
44 /**
45 * Return registered ranks
46 *
47 * @since 1.3.1
48 *
49 * @return array Array of ranks as WP_Post
50 */
51 function gamipress_get_ranks( $args = array() ) {
52
53 // If not properly upgrade to required version fallback to compatibility function
54 if( ! is_gamipress_upgraded_to( '1.5.1' ) ) {
55 return gamipress_get_ranks_old( $args );
56 }
57
58 // Setup our defaults
59 $defaults = array(
60 'post_type' => array_merge( gamipress_get_rank_types_slugs(), gamipress_get_requirement_types_slugs() ),
61 'numberposts' => -1,
62 'orderby' => 'menu_order',
63 'suppress_filters' => false,
64 'rank_relationship' => 'any',
65 );
66
67 $args = wp_parse_args( $args, $defaults );
68
69 // Since 1.5.1, requirements has their parent stored in the post_parent field, so it isn't required at all
70 if ( isset( $args['parent_of'] ) ) {
71
72 $post_parent = absint( gamipress_get_post_field( 'post_parent', $args['parent_of'] ) );
73
74 if( $post_parent === 0 ) {
75 return array();
76 }
77
78 $args['post__in'] = array( $post_parent );
79
80 }
81
82 // Since 1.5.1, requirements has their parent stored in the post_parent field, so it isn't required at all
83 if ( isset( $args['children_of'] ) ) {
84
85 $args['post_parent'] = $args['children_of'];
86
87 // When looking to get rank requirements, order is important to sequential requirements
88 $args['orderby'] = 'menu_order';
89 $args['order'] = 'ASC';
90
91 }
92
93 // Get our ranks posts
94 $ranks = get_posts( $args );
95
96 return $ranks;
97
98 }
99
100 /**
101 * Return user current rank id
102 *
103 * @since 1.3.1
104 *
105 * @param integer $user_id The given user's ID
106 * @param string $rank_type The rank type
107 *
108 * @return integer
109 */
110 function gamipress_get_user_rank_id( $user_id = null, $rank_type = '' ) {
111
112 global $wpdb;
113
114 if( $user_id === null ) {
115 $user_id = get_current_user_id();
116 }
117
118 $meta = '_gamipress_rank';
119
120 if( ! empty( $rank_type ) ) {
121 $meta = "_gamipress_{$rank_type}_rank";
122 }
123
124 $current_rank_id = absint( gamipress_get_user_meta( $user_id, $meta ) );
125 $is_rank = $current_rank_id > 0 ? gamipress_is_rank( $current_rank_id ) : false;
126
127 if( ! $is_rank ) {
128
129 $posts = GamiPress()->db->posts;
130
131 // Get lowest priority rank as default rank to all users
132 $current_rank_id = $wpdb->get_var( $wpdb->prepare(
133 "SELECT p.ID
134 FROM {$posts} AS p
135 WHERE p.post_type = %s
136 AND p.post_status = %s
137 ORDER BY menu_order ASC
138 LIMIT 1",
139 $rank_type,
140 'publish'
141 ) );
142
143 }
144
145 return apply_filters( 'gamipress_get_user_rank_id', $current_rank_id, $user_id );
146
147 }
148
149 /**
150 * Return user current rank
151 *
152 * @since 1.3.1
153 *
154 * @param integer $user_id The given user's ID
155 * @param string $rank_type The rank type
156 *
157 * @return bool|WP_Post
158 */
159 function gamipress_get_user_rank( $user_id = null, $rank_type = '' ) {
160
161 global $wpdb;
162
163 if( $user_id === null ) {
164 $user_id = get_current_user_id();
165 }
166
167 $current_rank_id = gamipress_get_user_rank_id( $user_id, $rank_type );
168
169 $rank = gamipress_get_post( $current_rank_id );
170
171 if( $rank ) {
172 return apply_filters( 'gamipress_get_user_rank', $rank, $user_id, $current_rank_id );
173 }
174
175 return false;
176
177 }
178
179 /**
180 * Return user next rank id
181 *
182 * @since 1.3.1
183 *
184 * @param integer $user_id The given user's ID
185 * @param string $rank_type The rank type
186 *
187 * @return integer
188 */
189 function gamipress_get_next_user_rank_id( $user_id = null, $rank_type = '' ) {
190
191 global $wpdb;
192
193 if( $user_id === null ) {
194 $user_id = get_current_user_id();
195 }
196
197 $current_rank_id = gamipress_get_user_rank_id( $user_id, $rank_type );
198
199 $next_rank_id = gamipress_get_next_rank_id( $current_rank_id );
200
201 return apply_filters( 'gamipress_get_next_user_rank_id', absint( $next_rank_id ), $user_id, $rank_type, $current_rank_id );
202
203 }
204
205 /**
206 * Return user next rank
207 *
208 * @since 1.3.1
209 *
210 * @param integer $user_id The given user's ID
211 * @param string $rank_type The rank type
212 *
213 * @return bool|WP_Post
214 */
215 function gamipress_get_next_user_rank( $user_id = null, $rank_type = '' ) {
216
217 global $wpdb;
218
219 if( $user_id === null ) {
220 $user_id = get_current_user_id();
221 }
222
223 $current_rank_id = gamipress_get_user_rank_id( $user_id, $rank_type );
224
225 $next_rank = gamipress_get_next_rank( $current_rank_id );
226
227 if( $next_rank ) {
228 return apply_filters( 'gamipress_get_next_user_rank', $next_rank, $user_id, $rank_type, $current_rank_id );
229 }
230
231 return false;
232
233 }
234
235 /**
236 * Return next rank id based of given rank priority
237 *
238 * @since 1.3.1
239 *
240 * @param integer $rank_id The given rank's ID
241 *
242 * @return integer
243 */
244 function gamipress_get_next_rank_id( $rank_id = null ) {
245
246 global $wpdb;
247
248 if( $rank_id === null ) {
249 $rank_id = get_the_ID();
250 }
251
252 $cache = gamipress_get_cache( 'next_rank_id', array(), false );
253
254 // If result already cached, return it
255 if( isset( $cache[$rank_id] ) ) {
256 return $cache[$rank_id];
257 }
258
259 $rank_type = gamipress_get_post_type( $rank_id );
260
261 $posts = GamiPress()->db->posts;
262
263 // Get lowest priority rank but bigger than current one
264 $next_rank_id = $wpdb->get_var( $wpdb->prepare(
265 "SELECT p.ID
266 FROM {$posts} AS p
267 WHERE p.post_type = %s
268 AND p.post_status = %s
269 AND p.menu_order > %s
270 ORDER BY menu_order ASC
271 LIMIT 1",
272 $rank_type,
273 'publish',
274 gamipress_get_post_field( 'menu_order', $rank_id )
275 ) );
276
277 $next_rank_id = absint( $next_rank_id );
278
279 if( $next_rank_id === $rank_id ) {
280 $next_rank_id = 0;
281 }
282
283 $next_rank_id = apply_filters( 'gamipress_get_next_rank_id', $next_rank_id, $rank_id );
284
285 // Cache function result
286 $cache[$rank_id] = $next_rank_id;
287
288 gamipress_set_cache( 'next_rank_id', $cache );
289
290 return $next_rank_id;
291
292 }
293
294 /**
295 * Return next rank based of given rank priority
296 *
297 * @since 1.3.1
298 *
299 * @param integer $rank_id The given rank's ID
300 *
301 * @return bool|WP_Post
302 */
303 function gamipress_get_next_rank( $rank_id = null ) {
304
305 if( $rank_id === null ) {
306 $rank_id = get_the_ID();
307 }
308
309 $next_rank_id = gamipress_get_next_rank_id( $rank_id );
310
311 if( $next_rank_id === 0 ) {
312 return false;
313 }
314
315 $rank = gamipress_get_post( $next_rank_id );
316
317 if( $rank ) {
318 return apply_filters( 'gamipress_get_next_rank', $rank, $next_rank_id, $rank_id );
319 }
320
321 return false;
322
323 }
324
325 /**
326 * Return previous rank id based of given rank priority
327 *
328 * @since 1.3.1
329 *
330 * @param integer $rank_id The given rank's ID
331 *
332 * @return integer
333 */
334 function gamipress_get_prev_rank_id( $rank_id = null ) {
335
336 global $wpdb;
337
338 if( $rank_id === null ) {
339 $rank_id = get_the_ID();
340 }
341
342 $cache = gamipress_get_cache( 'prev_rank_id', array(), false );
343
344 // If result already cached, return it
345 if( isset( $cache[$rank_id] ) ) {
346 return $cache[$rank_id];
347 }
348
349 $rank_type = gamipress_get_post_type( $rank_id );
350
351 $posts = GamiPress()->db->posts;
352
353 // Get highest priority rank but less than current one
354 $prev_rank_id = $wpdb->get_var( $wpdb->prepare(
355 "SELECT p.ID
356 FROM {$posts} AS p
357 WHERE p.post_type = %s
358 AND p.post_status = %s
359 AND p.menu_order < %s
360 ORDER BY menu_order DESC
361 LIMIT 1",
362 $rank_type,
363 'publish',
364 gamipress_get_post_field( 'menu_order', $rank_id )
365 ) );
366
367 $prev_rank_id = absint( $prev_rank_id );
368
369 if( $prev_rank_id === $rank_id ) {
370 $prev_rank_id = 0;
371 }
372
373 $prev_rank_id = apply_filters( 'gamipress_get_prev_rank_id', $prev_rank_id, $rank_id );
374
375 // Cache function result
376 $cache[$rank_id] = $prev_rank_id;
377
378 gamipress_set_cache( 'prev_rank_id', $cache );
379
380 return $prev_rank_id;
381
382 }
383
384 /**
385 * Return previous rank based of given rank priority
386 *
387 * @since 1.3.1
388 *
389 * @param integer $rank_id The given rank's ID
390 *
391 * @return bool|WP_Post
392 */
393 function gamipress_get_prev_rank( $rank_id = null ) {
394
395 if( $rank_id === null ) {
396 $rank_id = get_the_ID();
397 }
398
399 $prev_rank_id = gamipress_get_prev_rank_id( $rank_id );
400
401 if( $prev_rank_id === 0 ) {
402 return false;
403 }
404
405 $rank = gamipress_get_post( $prev_rank_id );
406
407 if( $rank ) {
408 return apply_filters( 'gamipress_get_prev_rank', $rank, $prev_rank_id, $rank_id );
409 }
410
411 return false;
412
413 }
414
415 /**
416 * Return previous user rank
417 *
418 * @since 1.4.3
419 *
420 * @param integer $user_id The given user's ID
421 * @param string $rank_type The given rank's type
422 *
423 * @return bool|int
424 */
425 function gamipress_get_prev_user_rank_id( $user_id = null, $rank_type = '' ) {
426
427 if( $user_id === null ) {
428 $user_id = get_current_user_id();
429 }
430
431 $old_meta = "_gamipress_{$rank_type}_previous_rank";
432 $prev_user_rank_id = absint( gamipress_get_user_meta( $user_id, $old_meta ) );
433
434 // Check if there is a previous rank stored, if not, try to get previous rank of current user rank
435 if( $prev_user_rank_id === 0 ) {
436
437 $user_rank_id = gamipress_get_user_rank_id( $user_id, $rank_type );
438
439 // If there is not previous rank and not current rank, return lowest priority rank ID
440 if( ! $user_rank_id ) {
441 return gamipress_get_lowest_priority_rank_id( $rank_type );
442 }
443
444 $prev_user_rank_id = gamipress_get_prev_rank_id( $user_rank_id );
445 }
446
447 if( $prev_user_rank_id ) {
448 return apply_filters( 'gamipress_get_prev_user_rank_id', $prev_user_rank_id, $user_id, $rank_type );
449 }
450
451 return false;
452
453 }
454
455 /**
456 * Helper function to check if a rank is the lowest priority rank (aka the default rank to anyone)
457 *
458 * @since 1.3.6
459 *
460 * @param integer $rank_id The given rank's ID
461 *
462 * @return bool
463 */
464 function gamipress_is_lowest_priority_rank( $rank_id = null ) {
465
466 if( $rank_id === null ) {
467 $rank_id = get_the_ID();
468 }
469
470 $prev_rank_id = gamipress_get_prev_rank_id( $rank_id );
471
472 // Return true if previous rank is 0 or the same that given one
473 return (bool) ( $rank_id === $prev_rank_id || $prev_rank_id === 0 );
474
475 }
476
477 /**
478 * Get the lowest priority rank ID of a rank type
479 *
480 * @since 1.4.3
481 *
482 * @param string $rank_type The given rank's type
483 *
484 * @return int
485 */
486 function gamipress_get_lowest_priority_rank_id( $rank_type = null ) {
487
488 global $wpdb;
489
490 $posts = GamiPress()->db->posts;
491
492 // Get the lowest priority rank
493 $rank_id = $wpdb->get_var( $wpdb->prepare(
494 "SELECT p.ID
495 FROM {$posts} AS p
496 WHERE p.post_type = %s
497 AND p.post_status = %s
498 ORDER BY menu_order ASC
499 LIMIT 1",
500 $rank_type,
501 'publish'
502 ) );
503
504 // Return true if previous rank is 0 or the same that given one
505 return apply_filters( 'gamipress_get_lowest_priority_rank_id', absint( $rank_id ), $rank_type );
506
507 }
508
509 /**
510 * Award rank to a user
511 *
512 * @since 1.4.3
513 *
514 * @param integer $rank_id The rank is being awarded
515 * @param integer $user_id The given user's ID
516 * @param array $args Array of extra arguments
517 *
518 * @return bool|WP_Post WP Post object of newly rank awarded, false if process fails
519 */
520 function gamipress_award_rank_to_user( $rank_id = 0, $user_id = 0, $args = array() ) {
521
522 // Bail if not is a valid rank
523 if( ! gamipress_is_rank( $rank_id ) ) {
524 return false;
525 }
526
527 // Initialize args
528 $args = wp_parse_args( $args, array(
529 'admin_id' => 0,
530 'achievement_id' => null,
531 ) );
532
533 // Available action for triggering other processes
534 do_action( 'gamipress_award_rank_to_user', $user_id, $rank_id, $args );
535
536 return gamipress_update_user_rank( $user_id, $rank_id, $args['admin_id'], $args['achievement_id'] );
537
538 }
539
540 /**
541 * Revoke rank to a user
542 *
543 * @since 1.4.3
544 *
545 * @param integer $user_id The given user's ID
546 * @param integer $rank_id The rank is being revoked
547 * @param integer $new_rank_id The new user rank, if not provided user will get the previous rank
548 * @param array $args Array of extra arguments
549 *
550 * @return bool|WP_Post WP Post object of newly rank revoked, false if process fails
551 */
552 function gamipress_revoke_rank_to_user( $user_id = 0, $rank_id = 0, $new_rank_id = 0, $args = array() ) {
553
554 // Bail if not is a valid rank
555 if( ! gamipress_is_rank( $rank_id ) ) {
556 return false;
557 }
558
559 // Initialize args
560 $args = wp_parse_args( $args, array(
561 'admin_id' => 0,
562 'achievement_id' => null,
563 ) );
564
565 // If not new rank provided, then get the previous user rank
566 if( $new_rank_id === 0 ) {
567 $rank_type = gamipress_get_post_type( $rank_id );
568
569 $new_rank_id = gamipress_get_prev_user_rank_id( $user_id, $rank_type );
570 }
571
572 // Available action for triggering other processes
573 do_action( 'gamipress_revoke_rank_to_user', $user_id, $rank_id, $new_rank_id, $args );
574
575 // Removes the rank from the user earnings table
576 gamipress_revoke_achievement_to_user( $rank_id, $user_id );
577
578 // Set the new rank to the user
579 return gamipress_update_user_rank( $user_id, $new_rank_id, $args['admin_id'], $args['achievement_id'] );
580
581 }
582
583 /**
584 * Upgrade the given user to the next rank of a rank type
585 *
586 * @since 1.4.3
587 *
588 * @param int $user_id The given user's ID
589 * @param string $rank_type The given rank's type
590 *
591 * @return bool|WP_Post WP Post object of newly rank upgraded, false if process fails
592 */
593 function gamipress_upgrade_user_to_next_rank( $user_id = 0, $rank_type = '' ) {
594
595 // Bail if not is a valid rank type
596 if( ! gamipress_is_rank( $rank_type ) ) {
597 return false;
598 }
599
600 $user_rank_id = gamipress_get_user_rank_id( $user_id, $rank_type );
601 $next_rank_id = gamipress_get_next_rank_id( $user_rank_id );
602
603 // User is already on higher rank, so bail here
604 if( $next_rank_id === $user_rank_id ) {
605 return false;
606 }
607
608 // Award the next rank to the user
609 return gamipress_award_rank_to_user( $next_rank_id, $user_id );
610
611 }
612
613 /**
614 * Downgrade the given user to the previous rank of a rank type
615 *
616 * @since 1.4.3
617 *
618 * @param int $user_id The given user's ID
619 * @param string $rank_type The given rank's type
620 *
621 * @return bool|WP_Post WP Post object of newly rank downgraded, false if process fails
622 */
623 function gamipress_downgrade_user_to_prev_rank( $user_id = 0, $rank_type = '' ) {
624
625 // Bail if not is a valid rank type
626 if( ! gamipress_is_rank( $rank_type ) ) {
627 return false;
628 }
629
630 $user_rank_id = gamipress_get_user_rank_id( $user_id, $rank_type );
631 $prev_rank_id = gamipress_get_prev_rank_id( $user_rank_id );
632
633 // User is already on lowest rank, so bail here
634 if( $prev_rank_id === $user_rank_id ) {
635 return false;
636 }
637
638 // Award the previous rank to the user
639 return gamipress_revoke_rank_to_user( $user_id, $user_rank_id, $prev_rank_id );
640
641 }
642
643 /**
644 * Update user rank to the given one
645 *
646 * @since 1.3.1
647 *
648 * @param integer $user_id The given user's ID
649 * @param integer $rank_id The new rank the user is being awarded
650 * @param integer $admin_id If being awarded by an admin, the admin's user ID
651 * @param integer $achievement_id The achievement that generated the rank upgrade, if applicable
652 *
653 * @return bool|WP_Post
654 */
655 function gamipress_update_user_rank( $user_id = 0, $rank_id = 0, $admin_id = 0, $achievement_id = null ) {
656
657 if( ! $rank_id )
658 return false;
659
660 if( ! $user_id )
661 $user_id = get_current_user_id();
662
663 $new_rank = gamipress_get_post( $rank_id );
664
665 // Get user old rank if has one
666 $old_rank = gamipress_get_user_rank( $user_id, $new_rank->post_type );
667
668 // If is the same rank, return
669 if( $old_rank && $new_rank && $new_rank->ID === $old_rank->ID )
670 return $new_rank;
671
672 // Check if is a valid rank and is not the same rank as current one
673 if( $new_rank && gamipress_is_rank( $new_rank ) ) {
674
675 $meta = "_gamipress_{$new_rank->post_type}_rank";
676
677 // Update the user rank and the time when this rank has been earned
678 gamipress_update_user_meta( $user_id, $meta, $new_rank->ID );
679 gamipress_update_user_meta( $user_id, $meta . '_earned_time', current_time( 'timestamp' ) );
680
681 if( $old_rank ) {
682
683 // Stores the user old rank to meet it for revokes
684 $old_meta = "_gamipress_{$new_rank->post_type}_previous_rank";
685 gamipress_update_user_meta( $user_id, $old_meta, $old_rank->ID );
686
687 }
688
689 // Available action for triggering other processes
690 do_action( 'gamipress_update_user_rank', $user_id, $new_rank, $old_rank, $admin_id, $achievement_id );
691
692 // Maybe award some points-based achievements
693 foreach ( gamipress_get_rank_based_achievements() as $achievement ) {
694 gamipress_maybe_award_achievement_to_user( $achievement->ID, $user_id );
695 }
696
697 return $new_rank;
698 }
699
700 return false;
701
702 }
703
704 /**
705 * Register a user's updated rank as user earning
706 *
707 * @since 1.3.1
708 *
709 * @param integer $user_id The user ID
710 * @param WP_Post $new_rank New rank post object
711 * @param WP_Post $old_rank Old rank post object
712 * @param integer $admin_id An admin ID (if admin-awarded)
713 * @param integer $achievement_id Achievement that has been triggered it
714 */
715 function gamipress_register_user_rank_earning( $user_id, $new_rank, $old_rank, $admin_id = 0, $achievement_id = null ) {
716
717 // Setup our achievement object
718 $achievement_object = gamipress_build_achievement_object( $new_rank->ID );
719
720 // Update user's earned achievements
721 gamipress_update_user_achievements( array( 'user_id' => $user_id, 'new_achievements' => array( $achievement_object ) ) );
722
723 }
724 add_action( 'gamipress_update_user_rank', 'gamipress_register_user_rank_earning', 10, 5 );
725
726 /**
727 * Log a user's updated rank
728 *
729 * @since 1.3.1
730 *
731 * @param integer $user_id The user ID
732 * @param WP_Post $new_rank New rank post object
733 * @param WP_Post $old_rank Old rank post object
734 * @param integer $admin_id An admin ID (if admin-awarded)
735 * @param integer $achievement_id Achievement that has been triggered it
736 */
737 function gamipress_log_user_rank( $user_id, $new_rank, $old_rank, $admin_id = 0, $achievement_id = null ) {
738
739 $log_meta = array(
740 'rank_id' => ( $new_rank ? $new_rank->ID : 0 ),
741 'old_rank_id' => ( $old_rank ? $old_rank->ID : 0 ),
742 );
743
744 $access = 'public';
745 $trigger = '';
746
747 // Alter our log pattern if this was an admin action
748 if ( $admin_id ) {
749 $type = 'rank_award';
750 $access = 'private';
751
752 $log_meta['pattern'] = apply_filters( 'gamipress_rank_awarded_log_pattern', __( '{admin} ranked {user} to {rank_type} {rank}', 'gamipress' ) );
753 $log_meta['admin_id'] = $admin_id;
754 $trigger = 'gamipress_award_rank';
755 } else {
756 $type = 'rank_earn';
757 $log_meta['pattern'] = apply_filters( 'gamipress_rank_earned_log_pattern', __( '{user} ranked to {rank_type} {rank}', 'gamipress' ) );
758
759 if( $achievement_id ) {
760 $log_meta['achievement_id'] = $achievement_id;
761 $trigger = gamipress_get_post_meta( $achievement_id, '_gamipress_trigger_type' );
762 }
763
764 if( empty( $trigger ) ) {
765 $trigger = 'gamipress_unlock_rank';
766 }
767 }
768
769 // Create the log entry
770 gamipress_insert_log( $type, $user_id, $access, $trigger, $log_meta );
771
772 }
773 add_action( 'gamipress_update_user_rank', 'gamipress_log_user_rank', 10, 5 );
774
775 /**
776 * Get rank requirement's rank
777 *
778 * @since 1.3.1
779 *
780 * @param integer $rank_requirement_id The given rank requirement's post ID
781 *
782 * @return object|bool The post object of the rank, or false if none
783 */
784 function gamipress_get_rank_requirement_rank( $rank_requirement_id = 0 ) {
785
786 // If not properly upgrade to required version fallback to compatibility function
787 if( ! is_gamipress_upgraded_to( '1.5.1' ) ) {
788 return gamipress_get_rank_requirement_rank_old( $rank_requirement_id );
789 }
790
791 // Grab the current post ID if no rank requirement ID was specified
792 if ( ! $rank_requirement_id ) {
793 global $post;
794 $rank_requirement_id = $post->ID;
795 }
796
797 // The rank requirement's rank is the post parent
798 $rank_id = absint( gamipress_get_post_field( 'post_parent', $rank_requirement_id ) );
799
800 // If has parent, return his post object
801 if( $rank_id !== 0 )
802 return gamipress_get_post( $rank_id );
803 else
804 return false;
805 }
806
807 /**
808 * Get Rank Earned Time
809 *
810 * @since 1.3.1
811 *
812 * @param integer $user_id The given rank requirement's post ID
813 * @param string $rank_type The given rank requirement's post ID
814 *
815 * @return integer Timestamp of user has earned the last rank of this type, if not, fall back to the rank created date
816 */
817 function gamipress_get_rank_earned_time( $user_id = 0, $rank_type = '' ) {
818
819 $earned_time = absint( gamipress_get_user_meta( $user_id, "_gamipress_{$rank_type}_rank_earned_time" ) );
820
821 // If user has not earned a rank of this type, try to get the lowest priority rank and get its publish date
822 if( $earned_time === 0 ) {
823 $rank = gamipress_get_user_rank( $user_id, $rank_type );
824
825 if( $rank )
826 $earned_time = strtotime( $rank->post_date );
827 }
828
829 return $earned_time;
830
831 }
832
833 /**
834 * Get rank requirements
835 *
836 * @since 1.3.1
837 * @updated 1.5.1 Added $post_status parameter
838 *
839 * @param integer $rank_id The given rank requirement's post ID
840 * @param string $post_status The rank requirements status (publish by default)
841 *
842 * @return array An array of post objects with the rank requirements
843 */
844 function gamipress_get_rank_requirements( $rank_id = 0, $post_status = 'publish' ) {
845
846 // If not properly upgrade to required version fallback to compatibility function
847 if( ! is_gamipress_upgraded_to( '1.5.1' ) ) {
848 // gamipress_get_assigned_requirements() has backward compatibility and merge new and old results
849 return gamipress_get_assigned_requirements( $rank_id, 'rank-requirement', $post_status );
850 }
851
852 // Grab the current post ID if no rank_id was specified
853 if ( ! $rank_id ) {
854 global $post;
855 $rank_id = $post->ID;
856 }
857
858 $cache = gamipress_get_cache( 'rank_requirements', array(), false );
859
860 // If result already cached, return it
861 if( isset( $cache[$rank_id] ) && isset( $cache[$rank_id][$post_status] ) ) {
862 return $cache[$rank_id][$post_status];
863 }
864
865 $requirements = get_posts( array(
866 'post_type' => 'rank-requirement',
867 'post_parent' => $rank_id,
868 'post_status' => $post_status,
869 'orderby' => 'menu_order',
870 'order' => 'ASC',
871 'posts_per_page' => -1,
872 'suppress_filters' => false,
873 ) );
874
875 // Cache function result
876 if( ! isset( $cache[$rank_id] ) ){
877 $cache[$rank_id] = array();
878 }
879
880 $cache[$rank_id][$post_status] = $requirements;
881
882 gamipress_set_cache( 'rank_requirements', $cache );
883
884 // Return rank requirements array
885 return $requirements;
886
887 }
888
889 /**
890 * Helper function to retrieve an rank post thumbnail
891 *
892 * @since 1.3.1
893 *
894 * @param integer $post_id The rank's post ID
895 * @param string|array $image_size Image size to use. Accepts any valid image size, or
896 * an array of width and height values in pixels (in that order).
897 * Default 'gamipress-rank'.
898 * @param string $class A custom class to use for the image tag
899 *
900 * @return string Our formatted image tag
901 */
902 function gamipress_get_rank_post_thumbnail( $post_id = 0, $image_size = 'gamipress-rank', $class = 'gamipress-rank-thumbnail' ) {
903
904 // Get our rank thumbnail
905 $image = gamipress_get_the_post_thumbnail( $post_id, $image_size, array( 'class' => $class ) );
906
907 // If we don't have an image...
908 if ( ! $image ) {
909
910 // Grab our rank type's post thumbnail
911 $rank = get_page_by_path( gamipress_get_post_type( $post_id ), OBJECT, 'rank-type' );
912 $image = is_object( $rank ) ? gamipress_get_the_post_thumbnail( $rank->ID, $image_size, array( 'class' => $class ) ) : false;
913
914 // If we still have no image
915 if ( ! $image ) {
916
917 // If we already have an array for image size
918 if ( is_array( $image_size ) ) {
919 // Write our sizes to an associative array
920 $image_sizes['width'] = $image_size[0];
921 $image_sizes['height'] = $image_size[1];
922
923 // Otherwise, attempt to grab the width/height from our specified image size
924 } else {
925 global $_wp_additional_image_sizes;
926 if ( isset( $_wp_additional_image_sizes[$image_size] ) )
927 $image_sizes = $_wp_additional_image_sizes[$image_size];
928 }
929
930 // If we can't get the defined width/height, set our own
931 if ( empty( $image_sizes ) ) {
932 $image_sizes = array(
933 'width' => 100,
934 'height' => 100
935 );
936 }
937
938 // Available filter: 'gamipress_default_rank_post_thumbnail'
939 $default_thumbnail = apply_filters( 'gamipress_default_rank_post_thumbnail', '', $rank, $image_sizes );
940
941 if( ! empty( $default_thumbnail ) ) {
942 $image = '<img src="' . $default_thumbnail . '" width="' . $image_sizes['width'] . '" height="' . $image_sizes['height'] . '" class="' . $class . '">';
943 }
944
945 }
946 }
947
948 // Return our image tag
949 return $image;
950 }
951
952 /**
953 * Get an array of all users who have currently on a given rank
954 *
955 * @since 1.3.1
956 * @updated 1.6.7 Added $args parameter
957 * @updated 1.6.9 Make earner list work on default ranks
958 *
959 * @param int $rank_id The given rank's post ID
960 * @param array $args Array of arguments that modifies the earners list result
961 *
962 * @return array Array of user objects
963 */
964 function gamipress_get_rank_earners( $rank_id = 0, $args = array() ) {
965
966 global $wpdb;
967
968 // Setup the meta_key
969 $rank_type = gamipress_get_post_type( $rank_id );
970
971 $meta = '_gamipress_rank';
972
973 if( ! empty( $rank_type ) ) {
974 $meta = "_gamipress_{$rank_type}_rank";
975 }
976
977 if ( is_multisite() && ! gamipress_is_network_wide_active() ) {
978 $meta = $wpdb->get_blog_prefix() . $meta;
979 }
980
981 // Setup vars
982 $from = "{$wpdb->usermeta} AS u RIGHT JOIN {$wpdb->users} AS usr ON (usr.ID = u.user_id)";
983 $where = "u.meta_key = '{$meta}' AND u.meta_value = '{$rank_id}' ";
984
985 // On multisite, get earners only of the current site
986 if( is_multisite() && ! gamipress_is_network_wide_active() ) {
987 $from .= "RIGHT JOIN {$wpdb->usermeta} AS umcap ON (
988 umcap.user_id = u.user_id
989 AND umcap.meta_key = '" . $wpdb->get_blog_prefix( gamipress_get_original_site_id() ) . "capabilities'
990 ) ";
991 }
992
993
994 // If is lowest priority rank then requires an extra check to get users that doesn't have the rank meta
995 if( gamipress_is_lowest_priority_rank( $rank_id ) ) {
996
997 $where .= "OR NOT EXISTS (
998 SELECT meta.*
999 FROM {$wpdb->usermeta} as meta
1000 WHERE meta.meta_key = '{$meta}'
1001 AND meta.user_id = u.user_id
1002 ) ";
1003
1004 }
1005
1006 $group_by = "u.user_id";
1007 $order_by = '';
1008 $limit = '';
1009
1010 // Setup default args
1011 $defaults = array(
1012 'limit' => -1,
1013 'orderby' => 'u.user_id',
1014 'order' => 'DESC',
1015 );
1016
1017 /**
1018 * Filters the earners args
1019 *
1020 * @since 1.6.7
1021 *
1022 * @param array $args Array of given arguments
1023 * @param int $rank_id The given rank's post ID
1024 *
1025 * @return array
1026 */
1027 $args = apply_filters( 'gamipress_get_rank_earners_args', $args, $rank_id );
1028
1029 $args = wp_parse_args( $args, $defaults );
1030
1031 // Setup FROM
1032 if( ! empty( $args['from'] ) ) {
1033 $from .= $args['from'];
1034 }
1035
1036 // Setup WHERE
1037 if( ! empty( $args['where'] ) ) {
1038 $where .= $args['where'];
1039 }
1040
1041 // Setup LIMIT
1042 if( $args['limit'] > 0 ) {
1043 $limit = '0, ' . $args['limit'];
1044 }
1045
1046 // Setup ORDER BY
1047 if( ! empty( $args['orderby'] ) ) {
1048 $order_by = $args['orderby'] . ' ' . $args['order'];
1049 }
1050
1051 $earners = $wpdb->get_col(
1052 "SELECT u.user_id
1053 FROM {$from}
1054 WHERE {$where}
1055 GROUP BY {$group_by} "
1056 . ( ! empty( $order_by ) ? "ORDER BY {$order_by} " : '' )
1057 . ( ! empty( $limit ) ? "LIMIT {$limit} " : '' )
1058 );
1059
1060 // Build an array of wp users based of IDs found
1061 $earned_users = array();
1062
1063 foreach( $earners as $earner_id ) {
1064
1065 if( absint( $earner_id ) === 0 ) {
1066 continue;
1067 }
1068
1069 $earned_users[] = new WP_User( $earner_id );
1070 }
1071
1072 return $earned_users;
1073
1074 }
1075
1076 /**
1077 * Build an unordered list of users who have earned a given rank
1078 *
1079 * @since 1.3.1
1080 * @updated 1.6.7 Added $args parameter
1081 *
1082 * @param int $rank_id The given rank's post ID
1083 * @param array $args Array of arguments that modifies the earners list result
1084 *
1085 * @return string Concatenated markup
1086 */
1087 function gamipress_get_rank_earners_list( $rank_id = 0, $args = array() ) {
1088
1089 /**
1090 * Filters the earners list args
1091 *
1092 * @since 1.6.7
1093 *
1094 * @param array $args Array of given arguments
1095 * @param int $rank_id The given rank's post ID
1096 *
1097 * @return array
1098 */
1099 $args = apply_filters( 'gamipress_get_rank_earners_list_args', $args, $rank_id );
1100
1101 // Grab our users
1102 $earners = gamipress_get_rank_earners( $rank_id, $args );
1103 $output = '';
1104
1105 // Only generate output if we have earners
1106 if ( ! empty( $earners ) ) {
1107
1108 /**
1109 * Filters the rank earners heading text
1110 *
1111 * @since 1.8.6
1112 *
1113 * @param string $heading_text The heading text
1114 * @param int $rank_id The given rank's post ID
1115 * @param array $args Array of given arguments
1116 *
1117 * @return string
1118 */
1119 $heading_text = apply_filters( 'gamipress_rank_earners_heading', __( 'People who have reached this:', 'gamipress' ), $rank_id, $args );
1120
1121 // Loop through each user and build our output
1122 $output .= '<h4>' . $heading_text . '</h4>';
1123
1124 $output .= '<ul class="gamipress-rank-earners-list rank-' . $rank_id . '-earners-list">';
1125
1126 foreach ( $earners as $user ) {
1127
1128 $user_url = get_author_posts_url( $user->ID );
1129
1130 /**
1131 * Filters the rank earner url
1132 *
1133 * @since 1.8.6
1134 *
1135 * @param string $user_url The user URl, by default the get_author_posts_url()
1136 * @param int $user_id The rendered user ID
1137 * @param int $rank_id The given rank's post ID
1138 * @param array $args Array of given arguments
1139 *
1140 * @return string
1141 */
1142 $user_url = apply_filters( 'gamipress_rank_earner_user_url', $user_url, $user->ID, $rank_id, $args );
1143
1144 /**
1145 * Filters the rank earner display
1146 *
1147 * @since 1.8.6
1148 *
1149 * @param string $user_display The user display, by default the user display name
1150 * @param int $user_id The rendered user ID
1151 * @param int $rank_id The given rank's post ID
1152 * @param array $args Array of given arguments
1153 *
1154 * @return string
1155 */
1156 $user_display = apply_filters( 'gamipress_rank_earner_user_display', $user->display_name, $user->ID, $rank_id, $args );
1157
1158 $user_content = '<li>'
1159 . '<a href="' . $user_url . '">'
1160 . get_avatar( $user->ID )
1161 . '<span class="earner-display-name">' . $user_display . '</span>'
1162 . '</a>'
1163 . '</li>';
1164
1165 /**
1166 * Filters the earners list user output
1167 *
1168 * @since 1.0.0
1169 * @updated 1.6.7 Added $rank_id and $args arguments
1170 *
1171 * @param string $user_content User output
1172 * @param int $user_id The rendered user ID
1173 * @param int $rank_id The given rank's post ID
1174 * @param array $args Array of given arguments
1175 *
1176 * @return string
1177 */
1178 $output .= apply_filters( 'gamipress_get_rank_earners_list_user', $user_content, $user->ID, $rank_id, $args );
1179 }
1180
1181 $output .= '</ul>';
1182
1183 }
1184
1185 /**
1186 * Filters the rank earners list output
1187 *
1188 * @since 1.0.0
1189 *
1190 * @param string $output Achievement earners list output
1191 * @param int $rank_id The given rank's post ID
1192 * @param array $args Array of given arguments
1193 * @param array $earners Array of rank earners
1194 *
1195 * @return string
1196 */
1197 return apply_filters( 'gamipress_get_rank_earners_list', $output, $rank_id, $args, $earners );
1198 }
1199
1200 /**
1201 * Return the given rank's priority
1202 *
1203 * @since 1.3.7
1204 *
1205 * @param integer|WP_Post $rank_id The given rank's post ID or rank's post object
1206 *
1207 * @return integer The given rank's priority
1208 */
1209 function gamipress_get_rank_priority( $rank_id = 0 ) {
1210
1211 global $wpdb;
1212
1213 if( gamipress_get_post_field( 'post_status', $rank_id ) === 'auto-draft' ) {
1214
1215 $rank_type = gamipress_get_post_type( $rank_id );
1216
1217 $posts = GamiPress()->db->posts;
1218
1219 // Get higher menu order
1220 $last = $wpdb->get_var( $wpdb->prepare(
1221 "SELECT p.menu_order
1222 FROM {$posts} AS p
1223 WHERE p.post_type = %s
1224 AND p.post_status = %s
1225 ORDER BY menu_order DESC
1226 LIMIT 1",
1227 $rank_type,
1228 'publish'
1229 ) );
1230
1231 return absint( $last ) + 1;
1232 }
1233
1234 return absint( gamipress_get_post_field( 'menu_order', $rank_id ) );
1235
1236 }
1237
1238 /**
1239 * Flush rewrite rules whenever an rank type is published.
1240 *
1241 * @since 1.3.1
1242 *
1243 * @param string $new_status New status.
1244 * @param string $old_status Old status.
1245 * @param object $post Post object.
1246 */
1247 function gamipress_flush_rewrite_on_published_rank( $new_status, $old_status, $post ) {
1248 if ( 'rank-type' === $post->post_type && 'publish' === $new_status && 'publish' !== $old_status ) {
1249 gamipress_flush_rewrite_rules();
1250 }
1251 }
1252 add_action( 'transition_post_status', 'gamipress_flush_rewrite_on_published_rank', 10, 3 );
1253
1254 /**
1255 * Update all dependent data if rank type name has changed.
1256 *
1257 * @since 1.3.1
1258 *
1259 * @param array $data Post data.
1260 * @param array $post_args Post args.
1261 * @return array Updated post data.
1262 */
1263 function gamipress_maybe_update_rank_type( $data = array(), $post_args = array() ) {
1264
1265 // Bail if not is main site, on network wide installs ranks are just available on main site
1266 if( gamipress_is_network_wide_active() && ! is_main_site() ) {
1267 return $data;
1268 }
1269
1270 // Bail if not is a points type
1271 if( $post_args['post_type'] !== 'rank-type') {
1272 return $data;
1273 }
1274
1275 // If user set an empty slug, then generate it
1276 if( empty( $post_args['post_name'] ) ) {
1277 $post_args['post_name'] = wp_unique_post_slug(
1278 sanitize_title( $post_args['post_title'] ),
1279 $post_args['ID'],
1280 $post_args['post_status'],
1281 $post_args['post_type'],
1282 $post_args['post_parent']
1283 );
1284 }
1285
1286 // Sanitize post_name
1287 $post_args['post_name'] = gamipress_sanitize_slug( $post_args['post_name'] );
1288
1289 if ( gamipress_rank_type_changed( $post_args ) ) {
1290
1291 $original_type = gamipress_get_post( $post_args['ID'] )->post_name;
1292 $new_type = $post_args['post_name'];
1293
1294 $data['post_name'] = gamipress_update_rank_types( $original_type, $new_type );
1295
1296 add_filter( 'redirect_post_location', 'gamipress_rank_type_rename_redirect', 99 );
1297
1298 }
1299
1300 return $data;
1301 }
1302 add_filter( 'wp_insert_post_data' , 'gamipress_maybe_update_rank_type' , 99, 2 );
1303
1304 /**
1305 * Check if an rank type name has changed.
1306 *
1307 * @since 1.3.1
1308 *
1309 * @param array $post_args Post args.
1310 * @return bool True if name has changed, otherwise false.
1311 */
1312 function gamipress_rank_type_changed( $post_args = array() ) {
1313
1314 if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) {
1315 return false;
1316 }
1317
1318 $original_post = ( !empty( $post_args['ID'] ) && isset( $post_args['ID'] ) ) ? get_post( $post_args['ID'] ) : null;
1319 $status = false;
1320
1321 if ( is_object( $original_post ) ) {
1322 if (
1323 'rank-type' === $post_args['post_type']
1324 && $original_post->post_status !== 'auto-draft'
1325 && ! empty( $original_post->post_name )
1326 && $original_post->post_name !== $post_args['post_name']
1327 ) {
1328 $status = true;
1329 }
1330 }
1331
1332 return $status;
1333 }
1334
1335 /**
1336 * Replace all instances of one rank type with another.
1337 *
1338 * @since 1.3.1
1339 *
1340 * @param string $original_type Original rank type.
1341 * @param string $new_type New rank type.
1342 * @return string New rank type.
1343 */
1344 function gamipress_update_rank_types( $original_type = '', $new_type = '' ) {
1345
1346 // Sanity check to prevent alterating core posts
1347 if ( empty( $original_type ) || in_array( $original_type, array( 'post', 'page', 'attachment', 'revision', 'nav_menu_item' ) ) ) {
1348 return $new_type;
1349 }
1350
1351 gamipress_update_ranks_rank_types( $original_type, $new_type );
1352 gamipress_update_earned_meta_rank_types( $original_type, $new_type );
1353
1354 /**
1355 * Action triggered when a rank type gets updated (called before flush rewrite rules)
1356 *
1357 * @since 1.5.1
1358 *
1359 * @param string $original_type The original type slug.
1360 * @param string $new_type The new type slug.
1361 */
1362 do_action( 'gamipress_update_rank_type', $original_type, $new_type );
1363
1364 gamipress_flush_rewrite_rules();
1365
1366 return $new_type;
1367
1368 }
1369
1370 /**
1371 * Change all ranks of one type to a new type.
1372 *
1373 * @since 1.3.1
1374 *
1375 * @param string $original_type Original rank type.
1376 * @param string $new_type New rank type.
1377 */
1378 function gamipress_update_ranks_rank_types( $original_type = '', $new_type = '' ) {
1379
1380 $items = get_posts( array(
1381 'posts_per_page' => -1,
1382 'post_status' => 'any',
1383 'post_type' => $original_type,
1384 'fields' => 'id',
1385 'suppress_filters' => false,
1386 ) );
1387
1388 foreach ( $items as $item ) {
1389 set_post_type( $item->ID, $new_type );
1390 }
1391
1392 }
1393
1394 /**
1395 * Change all earned meta from one rank type to another.
1396 *
1397 * @since 1.3.1
1398 *
1399 * @param string $original_type Original rank type.
1400 * @param string $new_type New rank type.
1401 */
1402 function gamipress_update_earned_meta_rank_types( $original_type = '', $new_type = '' ) {
1403
1404 // Setup CT object
1405 $ct_table = ct_setup_table( 'gamipress_user_earnings' );
1406
1407 $ct_table->db->update(
1408 array( 'post_type' => $new_type ),
1409 array( 'post_type' => $original_type )
1410 );
1411
1412 ct_reset_setup_table();
1413
1414 global $wpdb;
1415
1416 $wpdb->get_results( $wpdb->prepare(
1417 "UPDATE {$wpdb->usermeta}
1418 SET meta_key = %s
1419 WHERE meta_key = %s",
1420 "_gamipress_{$new_type}_rank",
1421 "_gamipress_{$original_type}_rank"
1422 ) );
1423
1424 }
1425
1426 /**
1427 * Redirect to include custom rename message.
1428 *
1429 * @since 1.3.1
1430 *
1431 * @param string $location Original URI.
1432 * @return string Updated URI.
1433 */
1434 function gamipress_rank_type_rename_redirect( $location = '' ) {
1435
1436 remove_filter( 'redirect_post_location', __FUNCTION__, 99 );
1437
1438 return add_query_arg( 'message', 99, $location );
1439
1440 }
1441
1442 /**
1443 * Filter the "post updated" messages to include support for rank types.
1444 *
1445 * @since 1.3.1
1446 *
1447 * @param array $messages Array of messages to display.
1448 *
1449 * @return array $messages Compiled list of messages.
1450 */
1451 function gamipress_rank_type_update_messages( $messages ) {
1452
1453 $messages['rank-type'] = array_fill( 1, 10, __( 'Rank Type saved successfully.', 'gamipress' ) );
1454 $messages['rank-type']['99'] = sprintf( __('Rank Type renamed successfully. <p>All ranks of this type, and all active and earned user ranks, have been updated <strong>automatically</strong>.</p> All shortcodes, %s, and URIs that reference the old rank type slug must be updated <strong>manually</strong>.', 'gamipress'), '<a href="' . esc_url( admin_url( 'widgets.php' ) ) . '">' . __( 'widgets', 'gamipress' ) . '</a>' );
1455
1456 return $messages;
1457
1458 }
1459 add_filter( 'post_updated_messages', 'gamipress_rank_type_update_messages' );