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/functions/achievements.php +1367 -0 7.9.1 → 8.0.5 View file →
@@ -1,0 +1,1367 @@
1 +<?php
2 +/**
3 + * Achievements Functions
4 + *
5 + * @package GamiPress\Achievements_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 + * Check if post is a registered GamiPress achievement.
14 + *
15 + * @since 1.0.0
16 + *
17 + * @param object|int $post Post object or ID.
18 + * @return bool True if post is an achievement, otherwise false.
19 + */
20 +function gamipress_is_achievement( $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 achievement type, it cannot be an achievement
35 + if ( ! in_array( $post_type, gamipress_get_achievement_types_slugs() ) ) {
36 + $return = false;
37 + }
38 +
39 + // If we pass both previous tests, this is a valid achievement (with filter to override)
40 + return apply_filters( 'gamipress_is_achievement', $return, $post );
41 +
42 +}
43 +
44 +/**
45 + * Get an array of achievements
46 + *
47 + * @since 1.0.0
48 + * @param array $args An array of our relevant arguments
49 + * @return array An array of the queried achievements
50 + */
51 +function gamipress_get_achievements( $args = array() ) {
52 +
53 + // Setup our defaults
54 + $defaults = array(
55 + 'post_type' => array_merge( gamipress_get_achievement_types_slugs(), gamipress_get_requirement_types_slugs() ),
56 + 'numberposts' => -1,
57 + 'suppress_filters' => false,
58 + 'achievement_relationship' => 'any',
59 + );
60 +
61 + $args = wp_parse_args( $args, $defaults );
62 +
63 + // Since 1.5.1, requirements has their parent stored in the post_parent field, so it isn't required at all
64 + if ( isset( $args['parent_of'] ) ) {
65 +
66 + $post_parent = absint( gamipress_get_post_field( 'post_parent', $args['parent_of'] ) );
67 +
68 + // Bail if achievement hasn't any parent
69 + if( $post_parent === 0 )
70 + return array();
71 +
72 + $args['post__in'] = array( $post_parent );
73 +
74 + }
75 +
76 + // Since 1.5.1, requirements has their parent stored in the post_parent field, so it isn't required at all
77 + if ( isset( $args['children_of'] ) ) {
78 +
79 + $args['post_parent'] = $args['children_of'];
80 +
81 + // When looking to get achievement steps, order is important to sequential steps
82 + $args['orderby'] = 'menu_order';
83 + $args['order'] = 'ASC';
84 +
85 + }
86 +
87 + // Get our achievement posts
88 + $achievements = get_posts( $args );
89 +
90 + return $achievements;
91 +
92 +}
93 +
94 +/**
95 + * Get an achievement's parent posts
96 + *
97 + * @since 1.0.0
98 + *
99 + * @param integer $achievement_id The given achievement's post ID
100 + *
101 + * @return object|bool The post object of the achievement's parent, or false if none
102 + */
103 +function gamipress_get_parent_of_achievement( $achievement_id = 0 ) {
104 +
105 + // Grab the current post ID if no achievement_id was specified
106 + if ( ! $achievement_id ) {
107 + global $post;
108 + $achievement_id = $post->ID;
109 + }
110 +
111 + // Grab our achievement's parent
112 + $parents = gamipress_get_achievements( array( 'parent_of' => $achievement_id ) );
113 +
114 + // If it has a parent, return it, otherwise return false
115 + if ( ! empty( $parents ) )
116 + return $parents[0];
117 + else
118 + return false;
119 +
120 +}
121 +
122 +/**
123 + * Get an achievement's children posts
124 + *
125 + * @since 1.0.0
126 + *
127 + * @param integer $achievement_id The given achievement's post ID
128 + *
129 + * @return array An array of our achievement's children (empty if none)
130 + */
131 +function gamipress_get_children_of_achievement( $achievement_id = 0 ) {
132 +
133 + // Grab the current post ID if no achievement_id was specified
134 + if ( ! $achievement_id ) {
135 + global $post;
136 + $achievement_id = $post->ID;
137 + }
138 +
139 + // Grab and return our achievement's children
140 + return gamipress_get_achievements( array( 'children_of' => $achievement_id, 'achievement_relationship' => 'required' ) );
141 +
142 +}
143 +
144 +/**
145 + * Get step's achievement
146 + *
147 + * @since 1.5.1
148 + *
149 + * @param integer $step_id The given step's post ID
150 + *
151 + * @return object|bool The post object of the achievement, or false if none
152 + */
153 +function gamipress_get_step_achievement( $step_id = 0 ) {
154 +
155 + // Grab the current post ID if no step ID was specified
156 + if ( ! $step_id ) {
157 + global $post;
158 + $step_id = $post->ID;
159 + }
160 +
161 + // The step's achievement is the post parent
162 + $achievement_id = absint( gamipress_get_post_field( 'post_parent', $step_id ) );
163 +
164 + if( $achievement_id !== 0 ) {
165 + // If has parent, return his post object
166 + return gamipress_get_post( $achievement_id );
167 + } else {
168 + return false;
169 + }
170 +
171 +}
172 +
173 +/**
174 + * Get achievement steps
175 + *
176 + * @since 1.5.1
177 + *
178 + * @param integer $achievement_id The given achievement requirement's post ID
179 + * @param string $post_status The steps status (publish by default)
180 + *
181 + * @return array An array of post objects with the achievement's steps
182 + */
183 +function gamipress_get_achievement_steps( $achievement_id = 0, $post_status = 'publish' ) {
184 +
185 + // Grab the current post ID if no achievement_id was specified
186 + if ( ! $achievement_id ) {
187 + global $post;
188 + $achievement_id = $post->ID;
189 + }
190 +
191 + $cache = gamipress_get_cache( 'achievement_steps', array(), false );
192 +
193 + // If result already cached, return it
194 + if( isset( $cache[$achievement_id] ) && isset( $cache[$achievement_id][$post_status] ) ) {
195 + return $cache[$achievement_id][$post_status];
196 + }
197 +
198 + $steps = get_posts( array(
199 + 'post_type' => 'step',
200 + 'post_parent' => $achievement_id,
201 + 'post_status' => $post_status,
202 + 'orderby' => 'menu_order',
203 + 'order' => 'ASC',
204 + 'posts_per_page' => -1,
205 + 'suppress_filters' => false,
206 + ));
207 +
208 + // Cache function result
209 + if( ! isset( $cache[$achievement_id] ) ){
210 + $cache[$achievement_id] = array();
211 + }
212 +
213 + $cache[$achievement_id][$post_status] = $steps;
214 +
215 + gamipress_set_cache( 'achievement_steps', $cache );
216 +
217 + // Return steps array
218 + return $steps;
219 +
220 +}
221 +
222 +/**
223 + * Check if the achievement's child achievements must be earned sequentially
224 + *
225 + * @since 1.0.0
226 + *
227 + * @param integer $achievement_id The given achievement's post ID
228 + *
229 + * @return bool True if steps are sequential, false otherwise
230 + */
231 +function gamipress_is_achievement_sequential( $achievement_id = 0 ) {
232 +
233 + // Grab the current post ID if no achievement_id was specified
234 + if ( ! $achievement_id ) {
235 + global $post;
236 + $achievement_id = $post->ID;
237 + }
238 +
239 + // If our achievement requires sequential steps, return true, otherwise false
240 + if ( gamipress_get_post_meta( $achievement_id, '_gamipress_sequential' ) )
241 + return true;
242 + else
243 + return false;
244 +}
245 +
246 +/**
247 + * Check if user has already earned an achievement the maximum number of times
248 + *
249 + * @since 1.0.0
250 + *
251 + * @param integer $user_id The given user's ID
252 + * @param integer $achievement_id The given achievement's post ID
253 + *
254 + * @return bool True if we've exceed the max possible earnings, false if we're still eligable
255 + */
256 +function gamipress_achievement_user_exceeded_max_earnings( $user_id = 0, $achievement_id = 0 ) {
257 +
258 + // Sanitize vars
259 + $user_id = absint( $user_id );
260 + $achievement_id = absint( $achievement_id );
261 +
262 + // Global max earnings
263 + $global_max_earnings = gamipress_get_post_meta( $achievement_id, '_gamipress_global_maximum_earnings' );
264 +
265 + // -1, 0 or empty means unlimited earnings
266 + if( $global_max_earnings === '-1' || $global_max_earnings === '0' || empty( $global_max_earnings ) ) {
267 + $global_max_earnings = 0;
268 + }
269 +
270 + $global_max_earnings = absint( $global_max_earnings );
271 +
272 + // Only check global max earnings if isn't setup as unlimited
273 + if( $global_max_earnings > 0 ) {
274 +
275 + $earned_times = gamipress_get_earnings_count( array( 'post_id' => $achievement_id ) );
276 +
277 + // Bail if achievement has exceeded its global max earnings
278 + if( $earned_times >= $global_max_earnings ) {
279 + return true;
280 + }
281 +
282 + }
283 +
284 + // Per user max earnings
285 + $max_earnings = gamipress_get_post_meta( $achievement_id, '_gamipress_maximum_earnings' );
286 +
287 + // Unlimited maximum earnings per user check
288 + if( $max_earnings === '-1' || $max_earnings === '0' || empty( $max_earnings ) ) {
289 + return false;
290 + }
291 +
292 + // If the achievement has an earning limit per user, and we've earned it before...
293 + if ( $max_earnings && $user_has_achievement = gamipress_get_earnings_count( array( 'user_id' => absint( $user_id ), 'post_id' => absint( $achievement_id ) ) ) ) {
294 +
295 + // If we've earned it as many (or more) times than allowed, then we have exceeded maximum earnings, thus true
296 + if ( $user_has_achievement >= $max_earnings ) {
297 + return true;
298 + }
299 +
300 + }
301 +
302 + // The post has no limit, or we're under it
303 + return false;
304 +}
305 +
306 +/**
307 + * Helper function for building an object for our achievement
308 + *
309 + * @since 1.0.0
310 + * @param integer $achievement_id The given achievement's post ID
311 + * @param string $context The context in which we're creating this object
312 + *
313 + * @return false|object Our object containing only the relevant bits of information we want
314 + */
315 +function gamipress_build_achievement_object( $achievement_id = 0, $context = 'earned' ) {
316 +
317 + // Grab the new achievement's $post data, and bail if it doesn't exist
318 + $achievement = gamipress_get_post( $achievement_id );
319 +
320 + if ( ! $achievement ) {
321 + return false;
322 + }
323 +
324 + // Setup a new object for the achievement
325 + $achievement_object = new stdClass;
326 + $achievement_object->ID = $achievement_id;
327 + $achievement_object->post_type = $achievement->post_type;
328 + $achievement_object->points = absint( gamipress_get_post_meta( $achievement_id, '_gamipress_points' ) );
329 + $achievement_object->points_type = gamipress_get_post_meta( $achievement_id, '_gamipress_points_type' );
330 +
331 + // Store the current timestamp differently based on context
332 + if ( 'earned' == $context ) {
333 + $achievement_object->date_earned = current_time( 'timestamp' );
334 + } elseif ( 'started' == $context ) {
335 + $achievement_object->date_started = $achievement_object->last_activity_date = current_time( 'timestamp' );
336 + }
337 +
338 + // Return our achievement object, available filter so we can extend it elsewhere
339 + return apply_filters( 'gamipress_achievement_object', $achievement_object, $achievement_id, $context );
340 +
341 +}
342 +
343 +/**
344 + * Get an array of post IDs for achievements that are marked as "hidden"
345 + *
346 + * @since 1.0.0
347 + *
348 + * @param string|array $achievement_type Limit the array to a specific type of achievement
349 + *
350 + * @return array An array of hidden achievement post IDs
351 + */
352 +function gamipress_get_hidden_achievement_ids( $achievement_type = '' ) {
353 +
354 + if( ! is_array( $achievement_type ) ) {
355 + $achievement_type = array( $achievement_type );
356 + }
357 +
358 + $cache = gamipress_get_cache( 'hidden_achievements_ids', array(), false );
359 + $hidden_ids = array();
360 + $all_cached = true;
361 +
362 + // loop al given types looking if all has been cached
363 + foreach( $achievement_type as $type ) {
364 +
365 + if( isset( $cache[$type] ) && is_array( $cache[$type] ) ) {
366 + $hidden_ids = array_merge( $hidden_ids, $cache[$type] );
367 + } else {
368 + $all_cached = false;
369 + }
370 +
371 + }
372 +
373 + // Return hidden achievements cached
374 + if( $all_cached ) {
375 + return $hidden_ids;
376 + }
377 +
378 + // Reset the hidden ids var
379 + $hidden_ids = array();
380 +
381 + // Grab our hidden achievements
382 + $hidden_achievements = get_posts( array(
383 + 'post_type' => $achievement_type,
384 + 'post_status' => 'publish',
385 + 'posts_per_page' => -1,
386 + 'meta_key' => '_gamipress_hidden',
387 + 'meta_value' => 'hidden',
388 + 'suppress_filters' => false,
389 + ) );
390 +
391 + foreach ( $hidden_achievements as $achievement ) {
392 +
393 + $hidden_ids[] = $achievement->ID;
394 +
395 + // Initialize hidden achievement type cache
396 + if( ! isset( $cache[$achievement->post_type] ) ) {
397 + $cache[$achievement->post_type] = array();
398 + }
399 +
400 + $cache[$achievement->post_type][] = $achievement->ID;
401 +
402 + }
403 +
404 + // Cache hidden achievements
405 + gamipress_set_cache( 'hidden_achievements_ids', $cache );
406 +
407 + // Return our results
408 + return $hidden_ids;
409 +}
410 +
411 +/**
412 + * Check if achievement is marked as "hidden"
413 + *
414 + * @since 1.4.7
415 + *
416 + * @param int $achievement_id The achievement ID
417 + *
418 + * @return bool
419 + */
420 +
421 +function gamipress_is_achievement_hidden( $achievement_id ) {
422 +
423 + $hidden = gamipress_get_post_meta( $achievement_id, '_gamipress_hidden' );
424 +
425 + return ( $hidden === 'hidden' );
426 +
427 +}
428 +
429 +/**
430 + * Get an array of post IDs for a user's earned achievements
431 + *
432 + * @since 1.0.0
433 + * @param integer $user_id The given user's ID
434 + * @param string $achievement_type Limit the array to a specific type of achievement
435 + * @return array Our user's array of earned achievement post IDs
436 + */
437 +function gamipress_get_user_earned_achievement_ids( $user_id = 0, $achievement_type = '' ) {
438 +
439 + // Assume we have no earned achievements
440 + $earned_ids = array();
441 +
442 + // Grab our earned achievements
443 + $earned_achievements = gamipress_get_user_achievements( array(
444 + 'user_id' => $user_id,
445 + 'achievement_type' => $achievement_type,
446 + 'display' => true
447 + ) );
448 +
449 + foreach ( $earned_achievements as $achievement ) {
450 + $earned_ids[] = $achievement->ID;
451 + }
452 +
453 + return $earned_ids;
454 +
455 +}
456 +
457 +/**
458 + * Get an array of unique achievement types a user has earned
459 + *
460 + * @since 1.0.0
461 + *
462 + * @param int $user_id The ID of the user earning the achievement
463 + * @return array The array of achievements the user has earned
464 + */
465 +function gamipress_get_user_earned_achievement_types( $user_id = 0 ){
466 +
467 + $achievements = gamipress_get_user_achievements( array( 'user_id' => $user_id ) );
468 +
469 + if( ! $achievements ) {
470 + return array();
471 + }
472 +
473 + $achievement_types = wp_list_pluck( $achievements, 'post_type' );
474 +
475 + return array_unique( $achievement_types );
476 +}
477 +
478 +/**
479 + * Returns achievements that may be earned when the given achievement is earned.
480 + *
481 + * @since 1.0.0
482 + *
483 + * @param integer $achievement_id The given achievement's post ID
484 + *
485 + * @return array An array of achievements that are dependent on the given achievement
486 + */
487 +function gamipress_get_dependent_achievements( $achievement_id = 0 ) {
488 +
489 + global $wpdb;
490 +
491 + // Grab the current achievement ID if none specified
492 + if ( ! $achievement_id ) {
493 + global $post;
494 + $achievement_id = $post->ID;
495 + }
496 +
497 + $posts = GamiPress()->db->posts;
498 + $postmeta = GamiPress()->db->postmeta;
499 +
500 + // Grab posts that can be earned by unlocking the given achievement
501 + $achievements = $wpdb->get_results( $wpdb->prepare(
502 + "SELECT *
503 + FROM {$posts} as posts,
504 + {$postmeta} as meta
505 + WHERE posts.ID = meta.post_id
506 + AND meta.meta_key = '_gamipress_achievement_post'
507 + AND meta.meta_value = %d",
508 + $achievement_id
509 + ) );
510 +
511 + // In addition, the post parent can be earned by unlocking the given achievement
512 + $post_parent = absint( gamipress_get_post_field( 'post_parent', $achievement_id ) );
513 +
514 + if( $post_parent !== 0 ) {
515 + $achievements[] = gamipress_get_post( $post_parent );
516 + }
517 +
518 + // Note: Dependent achievements has been removed since it causes duplicated awards
519 + // Also, on unlock an achievement of a specific type an event is triggered to handle its awards
520 +
521 + // Available filter to modify an achievement's dependents
522 + return apply_filters( 'gamipress_dependent_achievements', $achievements, $achievement_id );
523 +
524 +}
525 +
526 +/**
527 + * Returns achievements that must be earned to earn given achievement.
528 + *
529 + * @since 1.0.0
530 + * @updated 1.5.1 Added $post_status parameter
531 + *
532 + * @param integer $achievement_id The given achievement's post ID
533 + * @param string $post_status The required achievements status (publish by default)
534 + *
535 + * @return array An array of achievements that are dependent on the given achievement
536 + */
537 +function gamipress_get_required_achievements_for_achievement( $achievement_id = 0, $post_status = 'publish' ) {
538 +
539 + global $wpdb;
540 +
541 + // Grab the current achievement ID if none specified
542 + if ( ! $achievement_id ) {
543 + global $post;
544 + $achievement_id = $post->ID;
545 + }
546 +
547 + // Don't retrieve requirements if achievement is not earned by steps
548 + if ( gamipress_get_post_meta( $achievement_id, '_gamipress_earned_by' ) !== 'triggers' )
549 + return false;
550 +
551 + $posts = GamiPress()->db->posts;
552 +
553 + // Setup the post status var
554 + if( ! is_array( $post_status ) ) {
555 + $post_status = explode( ',', $post_status );
556 + }
557 +
558 + if( in_array( 'any', $post_status ) ) {
559 + $post_status = array( 'publish', 'draft', 'pending', 'private' );
560 + }
561 +
562 + // Grab our requirements for this achievement
563 + $requirements = $wpdb->get_results( $wpdb->prepare(
564 + "SELECT *
565 + FROM $posts as posts
566 + WHERE posts.post_parent = %d
567 + AND posts.post_status IN ( '" . implode( "', '", $post_status ) . "' )
568 + ORDER BY CAST( posts.menu_order as SIGNED ) ASC",
569 + $achievement_id
570 + ) );
571 +
572 + return $requirements;
573 +
574 +}
575 +
576 +/**
577 + * Returns achievements that may be earned when the given achievement is earned.
578 + *
579 + * @since 1.0.0
580 + * @updated 1.3.1 added steps with required points
581 + * @updated 1.3.2 improved query
582 + * @updated 1.3.9.4 added $points_type argument
583 + *
584 + * @return array An array of achievements that are dependent on the given achievement
585 + */
586 +function gamipress_get_points_based_achievements( $points_type = '' ) {
587 +
588 + global $wpdb;
589 +
590 + if( empty( $points_type ) ) {
591 + return array();
592 + }
593 +
594 + $achievements = gamipress_get_transient( "gamipress_{$points_type}_based_achievements" );
595 +
596 + if ( empty( $achievements ) ) {
597 +
598 + $posts = GamiPress()->db->posts;
599 + $postmeta = GamiPress()->db->postmeta;
600 +
601 + // Grab posts that can be earned by unlocking the given achievement
602 + $achievements = $wpdb->get_results( $wpdb->prepare(
603 + "SELECT *
604 + FROM {$posts} as p
605 + LEFT JOIN {$postmeta} AS m1 ON ( p.ID = m1.post_id AND m1.meta_key = %s )
606 + LEFT JOIN {$postmeta} AS m2 ON ( p.ID = m2.post_id AND m2.meta_key = %s )
607 + LEFT JOIN {$postmeta} AS m3 ON ( p.ID = m3.post_id AND m3.meta_key = %s )
608 + WHERE m1.meta_value = %s
609 + AND ( m2.meta_value = %s OR m2.meta_value = %s OR m3.meta_value = %s )
610 + GROUP BY p.ID",
611 + '_gamipress_points_type_required', // (m1.meta_key) Of this post type
612 + '_gamipress_trigger_type', // (m2.meta_key) Requirements based on earn points
613 + '_gamipress_earned_by', // (m3.meta_key) Achievements earned by points
614 + $points_type, // (m1.meta_value) Of this post type
615 + 'earn-points', // (m2.meta_value) Requirements based on earn points
616 + 'points-balance', // (m2.meta_value) Requirements based on points balance
617 + 'points' // (m3.meta_value) Achievements earned by points
618 + ) );
619 +
620 + $achievements = gamipress_sort_triggered_rank_requirements( $achievements );
621 +
622 + // Store these posts to a transient for 1 day
623 + gamipress_set_transient( "gamipress_{$points_type}_based_achievements", $achievements, 60*60*24 );
624 + }
625 +
626 + return (array) maybe_unserialize( $achievements );
627 +}
628 +
629 +/**
630 + * Destroy the points-based achievements transient if we edit a points-based achievement
631 + *
632 + * @since 1.0.0
633 + * @param integer $post_id The given post's ID
634 + */
635 +function gamipress_bust_points_based_achievements_cache( $post_id ) {
636 +
637 + $post = get_post( $post_id );
638 +
639 + if ( gamipress_is_achievement( $post )
640 + && ( 'points' === gamipress_get_post_meta( $post_id, '_gamipress_earned_by' )
641 + || ( isset( $_POST['_gamipress_earned_by'] ) && 'points' === $_POST['_gamipress_earned_by'] ) ) ) {
642 +
643 + $points_type = gamipress_get_post_meta( $post_id, '_gamipress_points_type_required' );
644 +
645 + // If the post is one of our achievement types and the achievement is awarded by minimum points, delete the transient
646 + gamipress_delete_transient( "gamipress_{$points_type}_based_achievements" );
647 +
648 + } else if( in_array( gamipress_get_post_type( $post_id ), gamipress_get_requirement_types_slugs() )
649 + && in_array( gamipress_get_post_meta( $post_id, '_gamipress_trigger_type' ), array( 'earn-points', 'points-balance' ) ) ) {
650 +
651 + $points_type = gamipress_get_post_meta( $post_id, '_gamipress_points_type_required' );
652 +
653 + // If the post is one of our requirement types and the trigger type is a points based one, delete the transient
654 + gamipress_delete_transient( "gamipress_{$points_type}_based_achievements" );
655 +
656 + }
657 +
658 +}
659 +add_action( 'save_post', 'gamipress_bust_points_based_achievements_cache' );
660 +add_action( 'trash_post', 'gamipress_bust_points_based_achievements_cache' );
661 +
662 +/**
663 + * Returns achievements that may be earned when the given achievement is earned.
664 + *
665 + * @since 1.3.1
666 + * @updated 1.3.2 improved query
667 + *
668 + * @return array An array of achievements that are dependent on the given achievement
669 + */
670 +function gamipress_get_rank_based_achievements() {
671 +
672 + global $wpdb;
673 +
674 + $achievements = gamipress_get_transient( 'gamipress_rank_based_achievements' );
675 +
676 + if ( empty( $achievements ) ) {
677 +
678 + $posts = GamiPress()->db->posts;
679 + $postmeta = GamiPress()->db->postmeta;
680 +
681 + // Grab posts that can be earned by unlocking the given achievement
682 + $achievements = $wpdb->get_results( $wpdb->prepare(
683 + "SELECT *
684 + FROM {$posts} as posts
685 + LEFT JOIN {$postmeta} AS m1 ON ( posts.ID = m1.post_id AND m1.meta_key = %s )
686 + LEFT JOIN {$postmeta} AS m2 ON ( posts.ID = m2.post_id AND m2.meta_key = %s )
687 + WHERE m1.meta_value = %s
688 + OR m2.meta_value = %s",
689 + '_gamipress_trigger_type', // (m1.meta_key) Requirements based on earn rank
690 + '_gamipress_earned_by', // (m2.meta_key) Achievements earned by rank
691 + 'earn-rank', // (m1.meta_value) Requirements based on earn rank
692 + 'rank' // (m2.meta_value) Achievements earned by rank
693 + ) );
694 +
695 + // Store these posts to a transient for 1 days
696 + gamipress_set_transient( 'gamipress_rank_based_achievements', $achievements, 60*60*24 );
697 + }
698 +
699 + return (array) maybe_unserialize( $achievements );
700 +
701 +}
702 +
703 +/**
704 + * Destroy the rank-based achievements transient if we edit a rank-based achievement
705 + *
706 + * @deprecated Removed the transient usage since 1.3.2
707 + *
708 + * @since 1.3.1
709 + *
710 + * @param integer $post_id The given post's ID
711 + */
712 +function gamipress_bust_rank_based_achievements_cache( $post_id ) {
713 +
714 + $post = get_post( $post_id );
715 +
716 + if (
717 + gamipress_is_achievement( $post )
718 + && (
719 + 'rank' === gamipress_get_post_meta( $post_id, '_gamipress_earned_by' )
720 + || ( isset( $_POST['_gamipress_earned_by'] ) && 'rank' === $_POST['_gamipress_earned_by'] )
721 + )
722 + ) {
723 +
724 + // If the post is one of our achievement types, and the achievement is awarded by a rank, delete the transient
725 + gamipress_delete_transient( 'gamipress_rank_based_achievements' );
726 +
727 + } else if(
728 + in_array( gamipress_get_post_type( $post_id ), gamipress_get_requirement_types_slugs() )
729 + && 'earn-rank' == gamipress_get_post_meta( $post_id, '_gamipress_trigger_type' )
730 + ) {
731 +
732 + // If the post is one of our requirement types and the trigger type is a rank based one, delete the transient
733 + gamipress_delete_transient( 'gamipress_rank_based_achievements' );
734 +
735 + }
736 +
737 +}
738 +add_action( 'save_post', 'gamipress_bust_rank_based_achievements_cache' );
739 +add_action( 'trash_post', 'gamipress_bust_rank_based_achievements_cache' );
740 +
741 +/**
742 + * Helper function to retrieve an achievement post thumbnail
743 + *
744 + * Falls back to achievement type's thumbnail.
745 + *
746 + * @since 1.0.0
747 + *
748 + * @param integer $post_id The achievement's post ID
749 + * @param string $image_size The name of a registered custom image size
750 + * @param string $class A custom class to use for the image tag
751 + *
752 + * @return string Our formatted image tag
753 + */
754 +function gamipress_get_achievement_post_thumbnail( $post_id = 0, $image_size = 'gamipress-achievement', $class = 'gamipress-achievement-thumbnail' ) {
755 +
756 + // Get our achievement thumbnail
757 + $image = gamipress_get_the_post_thumbnail( $post_id, $image_size, array( 'class' => $class ) );
758 +
759 + // If achievement don't have an image...
760 + if ( ! $image ) {
761 +
762 + // Grab our achievement type's post thumbnail
763 + $achievement = get_page_by_path( gamipress_get_post_type( $post_id ), OBJECT, 'achievement-type' );
764 + $image = is_object( $achievement ) ? gamipress_get_the_post_thumbnail( $achievement->ID, $image_size, array( 'class' => $class ) ) : false;
765 +
766 + // If we still have no image
767 + if ( ! $image ) {
768 +
769 + // If we already have an array for image size
770 + if ( is_array( $image_size ) ) {
771 + // Write our sizes to an associative array
772 + $image_sizes['width'] = $image_size[0];
773 + $image_sizes['height'] = $image_size[1];
774 +
775 + // Otherwise, attempt to grab the width/height from our specified image size
776 + } else {
777 + global $_wp_additional_image_sizes;
778 + if ( isset( $_wp_additional_image_sizes[$image_size] ) )
779 + $image_sizes = $_wp_additional_image_sizes[$image_size];
780 + }
781 +
782 + // If we can't get the defined width/height, set our own
783 + if ( empty( $image_sizes ) ) {
784 + $image_sizes = array(
785 + 'width' => 100,
786 + 'height' => 100
787 + );
788 + }
789 +
790 + // Available filter: 'gamipress_default_achievement_post_thumbnail'
791 + $default_thumbnail = apply_filters( 'gamipress_default_achievement_post_thumbnail', '', $achievement, $image_sizes );
792 +
793 + if( ! empty( $default_thumbnail ) ) {
794 + $image = '<img src="' . $default_thumbnail . '" width="' . $image_sizes['width'] . '" height="' . $image_sizes['height'] . '" class="' . $class . '">';
795 + }
796 +
797 + }
798 + }
799 +
800 + // Finally, return our image tag
801 + return $image;
802 +}
803 +
804 +/**
805 + * Get an array of all users who have earned a given achievement
806 + *
807 + * @since 1.0.0
808 + * @updated 1.6.7 Added $args parameter and removed check to meet if user has earned the achievement, also removed 1.2.8 compatibility
809 + *
810 + * @param int $achievement_id The given achievement's post ID
811 + * @param array $args Array of arguments that modifies the query result
812 + *
813 + * @return array Array of user objects
814 + */
815 +function gamipress_get_achievement_earners( $achievement_id = 0, $args = array() ) {
816 +
817 + global $wpdb;
818 +
819 + // Setup vars
820 + $user_earnings = GamiPress()->db->user_earnings;
821 + $from = "{$user_earnings} AS u ";
822 + $where = "u.post_id = {$achievement_id} ";
823 + $group_by = "u.user_id";
824 + $order_by = '';
825 + $limit = '';
826 +
827 + // Setup default args
828 + $defaults = array(
829 + 'limit' => -1,
830 + 'orderby' => 'u.date',
831 + 'order' => 'DESC',
832 + );
833 +
834 + // On multisite, get earners only of the current site
835 + if( is_multisite() && ! gamipress_is_network_wide_active() ) {
836 + $from .= "LEFT JOIN {$wpdb->usermeta} AS umcap ON ( umcap.user_id = u.user_id ) ";
837 + $where .= "AND umcap.meta_key = '" . $wpdb->get_blog_prefix( gamipress_get_original_site_id() ) . "capabilities' ";
838 + }
839 +
840 + /**
841 + * Filters the earners args
842 + *
843 + * @since 1.6.7
844 + *
845 + * @param array $args Array of given arguments
846 + * @param int $achievement_id The given achievement's post ID
847 + *
848 + * @return array
849 + */
850 + $args = apply_filters( 'gamipress_get_achievement_earners_args', $args, $achievement_id );
851 +
852 + $args = wp_parse_args( $args, $defaults );
853 +
854 + // Sanitization
855 + $args['limit'] = absint( $args['limit'] );
856 + $args['order'] = gamipress_validate_from_array( strtoupper( $args['order'] ), array( 'ASC', 'DESC' ), 'DESC' );
857 +
858 + // Setup FROM
859 + if( ! empty( $args['from'] ) )
860 + $from .= $args['from'];
861 +
862 + // Setup WHERE
863 + if( ! empty( $args['where'] ) )
864 + $where .= $args['where'];
865 +
866 + // Setup LIMIT
867 + if( (int)( $args['limit'] ) > 0 )
868 + $limit = '0, ' . $args['limit'];
869 +
870 + // Setup ORDER BY
871 + if( ! empty( $args['orderby'] ) )
872 + $order_by = $args['orderby'] . ' ' . $args['order'];
873 +
874 + $earners = $wpdb->get_col(
875 + "SELECT u.user_id
876 + FROM {$from}
877 + WHERE {$where}
878 + GROUP BY {$group_by} "
879 + . ( ! empty( $order_by ) ? "ORDER BY {$order_by} " : '' )
880 + . ( ! empty( $limit ) ? "LIMIT {$limit} " : '' )
881 + );
882 +
883 + $earned_users = array();
884 +
885 + foreach( $earners as $earner_id ) {
886 + $earned_users[] = new WP_User( $earner_id );
887 + }
888 +
889 + return $earned_users;
890 +
891 +}
892 +
893 +/**
894 + * Build an unordered list of users who have earned a given achievement
895 + *
896 + * @since 1.0.0
897 + * @updated 1.6.7 Added $args parameter
898 + *
899 + * @param int $achievement_id The given achievement's post ID
900 + * @param array $args Array of arguments that modifies the earners list result
901 + *
902 + * @return string Concatenated markup
903 + */
904 +function gamipress_get_achievement_earners_list( $achievement_id = 0, $args = array() ) {
905 +
906 + /**
907 + * Filters the earners list args
908 + *
909 + * @since 1.6.7
910 + *
911 + * @param array $args Array of given arguments
912 + * @param int $achievement_id The given achievement's post ID
913 + *
914 + * @return array
915 + */
916 + $args = apply_filters( 'gamipress_get_achievement_earners_list_args', $args, $achievement_id );
917 +
918 + // Grab our users
919 + $earners = gamipress_get_achievement_earners( $achievement_id, $args );
920 + $output = '';
921 +
922 + // Only generate output if we have earners
923 + if ( ! empty( $earners ) ) {
924 +
925 + /**
926 + * Filters the achievement earners heading text
927 + *
928 + * @since 1.8.6
929 + *
930 + * @param string $heading_text The heading text
931 + * @param int $achievement_id The given achievement's post ID
932 + * @param array $args Array of given arguments
933 + *
934 + * @return string
935 + */
936 + $heading_text = apply_filters( 'gamipress_achievement_earners_heading', __( 'People who have earned this:', 'gamipress' ), $achievement_id, $args );
937 +
938 + // Loop through each user and build our output
939 + $output .= '<h4>' . $heading_text . '</h4>';
940 +
941 + $output .= '<ul class="gamipress-achievement-earners-list achievement-' . $achievement_id . '-earners-list">';
942 +
943 + foreach ( $earners as $user ) {
944 +
945 + /**
946 + * Filters the achievement earner url
947 + *
948 + * @since 1.8.6
949 + *
950 + * @param string $user_url The user URl, by default the get_author_posts_url()
951 + * @param int $user_id The rendered user ID
952 + * @param int $achievement_id The given achievement's post ID
953 + * @param array $args Array of given arguments
954 + *
955 + * @return string
956 + */
957 + $user_url = apply_filters( 'gamipress_achievement_earner_user_url', get_author_posts_url( $user->ID ), $user->ID, $achievement_id, $args );
958 +
959 + /**
960 + * Filters the achievement earner display
961 + *
962 + * @since 1.8.6
963 + *
964 + * @param string $user_display The user display, by default the user display name
965 + * @param int $user_id The rendered user ID
966 + * @param int $achievement_id The given achievement's post ID
967 + * @param array $args Array of given arguments
968 + *
969 + * @return string
970 + */
971 + $user_display = apply_filters( 'gamipress_achievement_earner_user_display', $user->display_name, $user->ID, $achievement_id, $args );
972 +
973 + $user_content = '<li>'
974 + . '<a href="' . $user_url . '">'
975 + . get_avatar( $user->ID )
976 + . '<span class="earner-display-name">' . $user_display . '</span>'
977 + . '</a>'
978 + . '</li>';
979 +
980 + /**
981 + * Filters the earners list user output
982 + *
983 + * @since 1.0.0
984 + * @updated 1.6.7 Added $achievement_id and $args arguments
985 + *
986 + * @param string $user_content User output
987 + * @param int $user_id The rendered user ID
988 + * @param int $achievement_id The given achievement's post ID
989 + * @param array $args Array of given arguments
990 + *
991 + * @return string
992 + */
993 + $output .= apply_filters( 'gamipress_get_achievement_earners_list_user', $user_content, $user->ID, $achievement_id, $args );
994 +
995 + }
996 +
997 + $output .= '</ul>';
998 + }
999 +
1000 + /**
1001 + * Filters the achievement earners list output
1002 + *
1003 + * @since 1.0.0
1004 + *
1005 + * @param string $output Achievement earners list output
1006 + * @param int $achievement_id The given achievement's post ID
1007 + * @param array $args Array of given arguments
1008 + * @param array $earners Array of achievement earners
1009 + *
1010 + * @return string
1011 + */
1012 + return apply_filters( 'gamipress_get_achievement_earners_list', $output, $achievement_id, $args, $earners );
1013 +}
1014 +
1015 +/**
1016 + * Flush rewrite rules whenever an achievement type is published.
1017 + *
1018 + * @since 1.0.0
1019 + *
1020 + * @param string $new_status New status.
1021 + * @param string $old_status Old status.
1022 + * @param object $post Post object.
1023 + */
1024 +function gamipress_flush_rewrite_on_published_achievement( $new_status, $old_status, $post ) {
1025 +
1026 + if ( 'achievement-type' === $post->post_type && 'publish' === $new_status && 'publish' !== $old_status ) {
1027 + gamipress_flush_rewrite_rules();
1028 + }
1029 +
1030 +}
1031 +add_action( 'transition_post_status', 'gamipress_flush_rewrite_on_published_achievement', 10, 3 );
1032 +
1033 +/**
1034 + * Update all dependent data if achievement type name has changed.
1035 + *
1036 + * @since 1.0.0
1037 + *
1038 + * @param array $data Post data.
1039 + * @param array $post_args Post args.
1040 + *
1041 + * @return array Updated post data.
1042 + */
1043 +function gamipress_maybe_update_achievement_type( $data = array(), $post_args = array() ) {
1044 +
1045 + // Bail if not is main site, on network wide installs achievements are just available on main site
1046 + if( gamipress_is_network_wide_active() && ! is_main_site() ) {
1047 + return $data;
1048 + }
1049 +
1050 + // Bail if not is a achievement type
1051 + if( $post_args['post_type'] !== 'achievement-type') {
1052 + return $data;
1053 + }
1054 +
1055 + // If user set an empty slug, then generate it
1056 + if( empty( $post_args['post_name'] ) ) {
1057 + $post_args['post_name'] = wp_unique_post_slug(
1058 + sanitize_title( $post_args['post_title'] ),
1059 + $post_args['ID'],
1060 + $post_args['post_status'],
1061 + $post_args['post_type'],
1062 + $post_args['post_parent']
1063 + );
1064 + }
1065 +
1066 + // Sanitize post_name
1067 + $post_args['post_name'] = gamipress_sanitize_slug( $post_args['post_name'] );
1068 +
1069 + if ( gamipress_achievement_type_changed( $post_args ) ) {
1070 +
1071 + $original_type = get_post( $post_args['ID'] )->post_name;
1072 + $new_type = $post_args['post_name'];
1073 +
1074 + $data['post_name'] = gamipress_update_achievement_types( $original_type, $new_type );
1075 +
1076 + /**
1077 + * Action triggered when an achievement type has been changed
1078 + *
1079 + * @since 1.5.1
1080 + *
1081 + * @param array $data Post data.
1082 + * @param array $post_args Post args.
1083 + * @param string $original_type The original type slug.
1084 + * @param string $new_type The new type slug.
1085 + */
1086 + do_action( 'gamipress_achievement_type_changed', $data, $post_args, $original_type, $new_type );
1087 +
1088 + // Add a filter for redirect user to renamed achievement
1089 + add_filter( 'redirect_post_location', 'gamipress_achievement_type_rename_redirect', 99 );
1090 +
1091 + }
1092 +
1093 + return $data;
1094 +}
1095 +add_filter( 'wp_insert_post_data' , 'gamipress_maybe_update_achievement_type' , 99, 2 );
1096 +
1097 +/**
1098 + * Check if an achievement type name has changed.
1099 + *
1100 + * @since 1.0.0
1101 + *
1102 + * @param array $post_args Post args.
1103 + * @return bool True if name has changed, otherwise false.
1104 + */
1105 +function gamipress_achievement_type_changed( $post_args = array() ) {
1106 +
1107 + if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) {
1108 + return false;
1109 + }
1110 +
1111 + $original_post = ( !empty( $post_args['ID'] ) && isset( $post_args['ID'] ) ) ? get_post( $post_args['ID'] ) : null;
1112 + $status = false;
1113 +
1114 + if ( is_object( $original_post ) ) {
1115 + if (
1116 + $post_args['post_type'] === 'achievement-type'
1117 + && $original_post->post_status !== 'auto-draft'
1118 + && ! empty( $original_post->post_name )
1119 + && $original_post->post_name !== $post_args['post_name']
1120 + ) {
1121 + $status = true;
1122 + }
1123 + }
1124 +
1125 + return $status;
1126 +}
1127 +
1128 +/**
1129 + * Replace all instances of one achievement type with another.
1130 + *
1131 + * @since 1.0.0
1132 + *
1133 + * @param string $original_type Original achievement type.
1134 + * @param string $new_type New achievement type.
1135 + * @return string New achievement type.
1136 + */
1137 +function gamipress_update_achievement_types( $original_type = '', $new_type = '' ) {
1138 +
1139 + // Sanity check to prevent altering core posts
1140 + if ( empty( $original_type ) || in_array( $original_type, array( 'post', 'page', 'attachment', 'revision', 'nav_menu_item' ) ) ) {
1141 + return $new_type;
1142 + }
1143 +
1144 + gamipress_update_achievements_achievement_types( $original_type, $new_type );
1145 + gamipress_update_earned_meta_achievement_types( $original_type, $new_type );
1146 +
1147 + /**
1148 + * Action triggered when an achievement type gets updated (called before flush rewrite rules)
1149 + *
1150 + * @since 1.5.1
1151 + *
1152 + * @param string $original_type The original type slug.
1153 + * @param string $new_type The new type slug.
1154 + */
1155 + do_action( 'gamipress_update_achievement_type', $original_type, $new_type );
1156 +
1157 + gamipress_flush_rewrite_rules();
1158 +
1159 + return $new_type;
1160 +
1161 +}
1162 +
1163 +/**
1164 + * Change all achievements of one type to a new type.
1165 + *
1166 + * @since 1.0.0
1167 + *
1168 + * @param string $original_type Original achievement type.
1169 + * @param string $new_type New achievement type.
1170 + */
1171 +function gamipress_update_achievements_achievement_types( $original_type = '', $new_type = '' ) {
1172 +
1173 + $items = get_posts( array(
1174 + 'posts_per_page' => -1,
1175 + 'post_status' => 'any',
1176 + 'post_type' => $original_type,
1177 + 'fields' => 'id',
1178 + 'suppress_filters' => false,
1179 + ) );
1180 +
1181 + foreach ( $items as $item ) {
1182 + set_post_type( $item->ID, $new_type );
1183 + }
1184 +
1185 +}
1186 +
1187 +/**
1188 + * Change all earned meta from one achievement type to another.
1189 + *
1190 + * @since 1.0.0
1191 + *
1192 + * @param string $original_type Original achievement type.
1193 + * @param string $new_type New achievement type.
1194 + */
1195 +function gamipress_update_earned_meta_achievement_types( $original_type = '', $new_type = '' ) {
1196 +
1197 + // Setup CT object
1198 + $ct_table = ct_setup_table( 'gamipress_user_earnings' );
1199 +
1200 + $ct_table->db->update(
1201 + array( 'post_type' => $new_type ),
1202 + array( 'post_type' => $original_type )
1203 + );
1204 +
1205 + ct_reset_setup_table();
1206 +
1207 +}
1208 +
1209 +/**
1210 + * Get unserialized user achievement metas.
1211 + *
1212 + * @since 1.0.0
1213 + *
1214 + * @param string $meta_key Meta key.
1215 + * @param string $original_type Achievement type.
1216 + *
1217 + * @return array User achievement metas.
1218 + */
1219 +function gamipress_get_unserialized_achievement_metas( $meta_key = '', $original_type = '' ) {
1220 +
1221 + $metas = gamipress_get_achievement_metas( $meta_key, $original_type );
1222 +
1223 + if ( ! empty( $metas ) ) {
1224 + foreach ( $metas as $key => $meta ) {
1225 + $metas[ $key ]->meta_value = maybe_unserialize( $meta->meta_value );
1226 + }
1227 + }
1228 +
1229 + return $metas;
1230 +
1231 +}
1232 +
1233 +/**
1234 + * Get serialized user achievement metas.
1235 + *
1236 + * @since 1.0.0
1237 + *
1238 + * @param string $meta_key Meta key.
1239 + * @param string $original_type Achievement type.
1240 + * @return array User achievement metas.
1241 + */
1242 +function gamipress_get_achievement_metas( $meta_key = '', $original_type = '' ) {
1243 +
1244 + global $wpdb;
1245 +
1246 + return $wpdb->get_results( $wpdb->prepare(
1247 + "SELECT *
1248 + FROM {$wpdb->usermeta}
1249 + WHERE meta_key = %s
1250 + AND meta_value LIKE '%%%s%%'",
1251 + $meta_key,
1252 + $original_type
1253 + ) );
1254 +
1255 +}
1256 +
1257 +/**
1258 + * Change user achievement meta from one achievement type to another.
1259 + *
1260 + * @since 1.0.0
1261 + *
1262 + * @param array $achievements Array of achievements.
1263 + * @param string $original_type Original achievement type.
1264 + * @param string $new_type New achievement type.
1265 + *
1266 + * @return array $achievements
1267 + */
1268 +function gamipress_update_meta_achievement_types( $achievements = array(), $original_type = '', $new_type = '' ) {
1269 +
1270 + if ( is_array( $achievements ) && ! empty( $achievements ) ) {
1271 +
1272 + foreach ( $achievements as $key => $achievement ) {
1273 + if ( $achievement->post_type === $original_type ) {
1274 + $achievements[ $key ]->post_type = $new_type;
1275 + }
1276 + }
1277 +
1278 + }
1279 +
1280 + return $achievements;
1281 +}
1282 +
1283 +/**
1284 + * Redirect to include custom rename message.
1285 + *
1286 + * @since 1.0.0
1287 + *
1288 + * @param string $location Original URI.
1289 + * @return string Updated URI.
1290 + */
1291 +function gamipress_achievement_type_rename_redirect( $location = '' ) {
1292 +
1293 + remove_filter( 'redirect_post_location', __FUNCTION__, 99 );
1294 +
1295 + return add_query_arg( 'message', 99, $location );
1296 +
1297 +}
1298 +
1299 +/**
1300 + * Filter the "post updated" messages to include support for achievement types.
1301 + *
1302 + * @since 1.0.0
1303 + *
1304 + * @param array $messages Array of messages to display.
1305 + *
1306 + * @return array $messages Compiled list of messages.
1307 + */
1308 +function gamipress_achievement_type_update_messages( $messages ) {
1309 +
1310 + $messages['achievement-type'] = array_fill( 1, 10, __( 'Achievement Type saved successfully.', 'gamipress' ) );
1311 + $messages['achievement-type']['99'] = sprintf( __('Achievement Type renamed successfully. <p>All achievements of this type, and all active and earned user achievements, have been updated <strong>automatically</strong>.</p> All shortcodes, %s, and URIs that reference the old achievement type slug must be updated <strong>manually</strong>.', 'gamipress'), '<a href="' . esc_url( admin_url( 'widgets.php' ) ) . '">' . __( 'widgets', 'gamipress' ) . '</a>' );
1312 +
1313 + return $messages;
1314 +
1315 +}
1316 +add_filter( 'post_updated_messages', 'gamipress_achievement_type_update_messages' );
1317 +
1318 +/**
1319 + * Log a user's achievements earns/awards
1320 + *
1321 + * @since 1.0.0
1322 + * @updated 1.4.7 Added $trigger parameter
1323 + *
1324 + * @param int $user_id The user ID
1325 + * @param int $achievement_id The associated achievement ID
1326 + * @param int $admin_id An admin ID (if admin-awarded)
1327 + * @param string $trigger The trigger that fires this function
1328 + */
1329 +function gamipress_log_user_achievement_award( $user_id, $achievement_id, $admin_id = 0, $trigger = '' ) {
1330 +
1331 + $post_type = gamipress_get_post_type( $achievement_id );
1332 +
1333 + $log_meta = array(
1334 + 'achievement_id' => $achievement_id,
1335 + );
1336 +
1337 + $access = 'public';
1338 +
1339 + // Alter our log pattern if this was an admin action
1340 + if ( $admin_id ) {
1341 + $type = 'achievement_award';
1342 + $access = 'private';
1343 +
1344 + $log_meta['pattern'] = apply_filters( 'gamipress_achievement_awarded_log_pattern', __( '{admin} awarded {user} with the the {achievement} {achievement_type}', 'gamipress' ) );
1345 + $log_meta['admin_id'] = $admin_id;
1346 +
1347 + if( empty( $trigger ) ) {
1348 + $trigger = 'gamipress_award_achievement';
1349 + }
1350 + } else {
1351 + $type = 'achievement_earn';
1352 +
1353 + if( in_array( $post_type, gamipress_get_requirement_types_slugs() ) ) {
1354 + $log_meta['pattern'] = apply_filters( 'gamipress_requirement_complete_log_pattern', __( '{user} completed the {achievement_type} {achievement}', 'gamipress' ) );
1355 + } else {
1356 + $log_meta['pattern'] = apply_filters( 'gamipress_achievement_earned_log_pattern', __( '{user} unlocked the {achievement} {achievement_type}', 'gamipress' ) );
1357 + }
1358 +
1359 + if( empty( $trigger ) ) {
1360 + $trigger = 'gamipress_unlock_achievement';
1361 + }
1362 + }
1363 +
1364 + // Create the log entry
1365 + gamipress_insert_log( $type, $user_id, $access, $trigger, $log_meta );
1366 +
1367 +}