PluginProbe
GamiPress – Gamification plugin to reward points, badges & ranks in WordPress, now with AI / trunk
GamiPress – Gamification plugin to reward points, badges & ranks in WordPress, now with AI vtrunk
8.0.1 8.0.2 8.0.0 7.9.9.7 7.9.9.6 7.9.9.5 7.9.9.4 7.9.9.3 7.9.9.2 7.9.9.1 7.9.9 7.9.8 7.9.7 7.9.6 7.9.5 7.9.4 7.9.3 7.9.2 7.9.1 7.9.0 7.8.9 7.8.8 7.8.7 7.8.6 7.8.5 All 44 releases
gamipress / includes / functions / requirements.php

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

465 lines 15.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Requirement Functions
4 *
5 * @package GamiPress\Requirement_Functions
6 * @author GamiPress <contact@gamipress.com>, Ruben Garcia <rubengcdev@gmail.com>
7 * @since 1.0.5
8 */
9 // Exit if accessed directly
10 if( !defined( 'ABSPATH' ) ) exit;
11
12 /**
13 * Check if post is a registered GamiPress requirement.
14 *
15 * @since 1.3.0
16 *
17 * @param object|int $post Post object or ID.
18 * @return bool True if post is an requirement, otherwise false.
19 */
20 function gamipress_is_requirement( $post = null ) {
21
22 // Assume we are working with a requirement object
23 $return = true;
24
25 // If post type is NOT a registered requirement type, it cannot be a requirement
26 if ( ! in_array( gamipress_get_post_type( $post ), gamipress_get_requirement_types_slugs() ) ) {
27 $return = false;
28 }
29
30 // If we pass both previous tests, this is a valid requirement (with filter to override)
31 return apply_filters( 'gamipress_is_requirement', $return, $post );
32 }
33
34
35 /**
36 * Get GamiPress Requirement Types
37 *
38 * Returns a multidimensional array of slug, single name and plural name for all requirement types.
39 *
40 * @since 1.0.5
41 *
42 * @return array An array of our registered requirement types
43 */
44 function gamipress_get_requirement_types() {
45
46 return GamiPress()->requirement_types;
47
48 }
49
50 /**
51 * Get GamiPress Requirement Type Slugs
52 *
53 * @since 1.0.5
54 *
55 * @return array An array of all our registered requirement type slugs (empty array if none)
56 */
57 function gamipress_get_requirement_types_slugs() {
58
59 // Assume we have no registered requirement types
60 $requirement_type_slugs = array();
61
62 // If we do have any requirement types, loop through each and add their slug to our array
63 foreach ( GamiPress()->requirement_types as $slug => $data ) {
64 $requirement_type_slugs[] = $slug;
65 }
66
67 // Finally, return our data
68 return $requirement_type_slugs;
69
70 }
71
72 /**
73 * Build a requirement object
74 *
75 * @since 1.0.5
76 * @updated 1.5.1 Added the order key (based on menu_order)
77 *
78 * @param int $requirement_id
79 *
80 * @return array
81 */
82 function gamipress_get_requirement_object( $requirement_id = 0 ) {
83
84 $requirement_type = gamipress_get_post_type( $requirement_id );
85
86 // Setup our default requirements array, assume we require nothing
87 $requirement = array(
88 'ID' => $requirement_id,
89 'title' => gamipress_get_post_field( 'post_title', $requirement_id ),
90 'order' => gamipress_get_post_field( 'menu_order', $requirement_id ),
91 'count' => absint( gamipress_get_post_meta( $requirement_id, '_gamipress_count' ) ),
92 'limit' => absint( gamipress_get_post_meta( $requirement_id, '_gamipress_limit' ) ),
93 'limit_type' => gamipress_get_post_meta( $requirement_id, '_gamipress_limit_type' ),
94 'trigger_type' => gamipress_get_post_meta( $requirement_id, '_gamipress_trigger_type' ),
95 'optional' => (bool) gamipress_get_post_meta( $requirement_id, '_gamipress_optional' ),
96 'url' => gamipress_get_post_meta( $requirement_id, '_gamipress_url' ),
97 // Points vars
98 'points_condition' => gamipress_get_post_meta( $requirement_id, '_gamipress_points_condition' ),
99 'points_required' => absint( gamipress_get_post_meta( $requirement_id, '_gamipress_points_required' ) ),
100 'points_type_required' => gamipress_get_post_meta( $requirement_id, '_gamipress_points_type_required' ),
101 // Rank vars
102 'rank_required' => absint( gamipress_get_post_meta( $requirement_id, '_gamipress_rank_required' ) ),
103 'rank_type_required' => gamipress_get_post_meta( $requirement_id, '_gamipress_rank_type_required' ),
104 // Post type vars
105 'post_type_required' => gamipress_get_post_meta( $requirement_id, '_gamipress_post_type_required' ),
106 // User role vars
107 'user_role_required' => gamipress_get_post_meta( $requirement_id, '_gamipress_user_role_required' ),
108 // Achievement vars
109 'achievement_type' => gamipress_get_post_meta( $requirement_id, '_gamipress_achievement_type' ),
110 'achievement_post' => absint( gamipress_get_post_meta( $requirement_id, '_gamipress_achievement_post' ) ),
111 'achievement_post_site_id' => get_current_blog_id(),
112 // Meta vars
113 'meta_key_required' => gamipress_get_post_meta( $requirement_id, '_gamipress_meta_key_required' ),
114 'meta_value_required' => gamipress_get_post_meta( $requirement_id, '_gamipress_meta_value_required' ),
115 );
116
117 // Initialize points condition for backward compatibility
118 if( empty( $requirement['points_condition'] ) ) {
119 $requirement['points_condition'] = 'greater_or_equal';
120 }
121
122 // Specific points award/deduct data
123 if( $requirement_type === 'points-award' || $requirement_type === 'points-deduct' ) {
124 $requirement['points'] = absint( gamipress_get_post_meta( $requirement_id, '_gamipress_points' ) );
125 $requirement['points_type'] = gamipress_get_post_meta( $requirement_id, '_gamipress_points_type' );
126 $requirement['maximum_earnings'] = gamipress_get_post_meta( $requirement_id, '_gamipress_maximum_earnings' );
127
128 if( $requirement['maximum_earnings'] === '' ) {
129 $requirement['maximum_earnings'] = 0;
130 } else {
131 $requirement['maximum_earnings'] = absint( $requirement['maximum_earnings'] );
132 }
133 }
134
135 // Check the achievement post site ID
136 if ( in_array( $requirement['trigger_type'], array_keys( gamipress_get_specific_activity_triggers() ) ) ) {
137
138 if ( $requirement['achievement_post'] > 0 ) {
139
140 $achievement_post_site_id = gamipress_get_post_meta( $requirement_id, '_gamipress_achievement_post_site_id' );
141
142 if( ! empty( $achievement_post_site_id ) ) {
143 $requirement['achievement_post_site_id'] = absint( $achievement_post_site_id );
144 }
145 }
146
147 }
148
149 // Available filter for overriding elsewhere
150 return apply_filters( 'gamipress_requirement_object', $requirement, $requirement_id );
151
152 }
153
154 /**
155 * Helper function to query requirements
156 *
157 * @since 1.8.8
158 *
159 * @param array $query_args
160 *
161 * @return array|object|null
162 */
163 function gamipress_query_requirements( $query_args = array() ) {
164
165 $query_args = wp_parse_args( $query_args, array(
166 'fields' => 'all',
167 'post_status' => 'publish',
168 'meta_query' => array(),
169 ) );
170
171 global $wpdb;
172
173 $posts = GamiPress()->db->posts;
174 $postmeta = GamiPress()->db->postmeta;
175
176 // SELECT
177 $select = 'SELECT *';
178
179 // Setup fields to return
180 if( $query_args['fields'] === 'ids' ) {
181 $select = 'SELECT p.ID';
182 }
183
184 // FROM
185 $from = array( "FROM {$posts} AS p" );
186
187 // WHERE
188 $where = array( '1=1' );
189
190 // Post type
191 $requirement_types = gamipress_get_requirement_types_slugs();
192 $where[] = "p.post_type IN ( '" . implode( "', '", $requirement_types ) . "' )";
193
194 // Post status
195 $where[] = "p.post_status = '" . $query_args['post_status'] . "'";
196
197 // Meta query
198 if( is_array( $query_args['meta_query'] ) && ! empty( $query_args['meta_query'] ) ) {
199
200 $i = 1;
201
202 foreach ( $query_args['meta_query'] as $meta_key => $meta_value ) {
203
204 $meta_key = sanitize_text_field( $meta_key );
205
206 $from[] = "LEFT JOIN {$postmeta} AS pm{$i} ON ( p.ID = pm{$i}.post_id AND pm{$i}.meta_key = '{$meta_key}' )";
207
208 // Check for array meta values
209 if( is_array( $meta_value ) ) {
210
211 foreach( $meta_value as $k => $value ) {
212 $meta_value[$k] = sanitize_text_field( $value );
213 }
214
215 $where[] = "pm{$i}.meta_value IN ( '" . implode( "', '", $meta_value ) . "' )";
216
217 } else {
218 $meta_value = sanitize_text_field( $meta_value );
219
220 $where[] = "pm{$i}.meta_value = '{$meta_value}'";
221 }
222
223
224
225 $i++;
226
227 }
228
229 }
230
231 // ORDER BY
232 $order_by = array( "p.menu_order ASC" );
233
234 // Setup vars
235 $from = implode( ' ', $from );
236 $where = "WHERE " . implode( ' AND ', $where );
237 $order_by = "ORDER BY " . implode( ', ', $order_by );
238
239 // Get all requirements with this trigger and post assigned
240 $results = $wpdb->get_results( "{$select} {$from} {$where} {$order_by}" );
241
242 return $results;
243
244 }
245
246 /**
247 * Helper function to determine if user is able to earn a requirement
248 *
249 * @since 1.9.0
250 *
251 * @param int $requirement_id The requirement ID
252 * @param int $user_id The user ID
253 *
254 * @return bool
255 */
256 function gamipress_can_user_earn_requirement( $requirement_id = 0, $user_id = 0 ) {
257
258 if( $user_id === 0 ) {
259 $user_id = get_current_user_id();
260 }
261
262 $post_type = gamipress_get_post_type( $requirement_id );
263 $can_earn = false;
264
265 switch( $post_type ) {
266 case 'points-award':
267 case 'points-deduct':
268 $maximum_earnings = absint( gamipress_get_post_meta( $requirement_id, '_gamipress_maximum_earnings' ) );
269
270 // An unlimited maximum of earnings means points deducts could be earned anyway
271 if( $maximum_earnings > 0 ) {
272 $earned_times = gamipress_get_earnings_count( array(
273 'user_id' => absint( $user_id ),
274 'post_id' => absint( $requirement_id ),
275 ) );
276
277 // User has earned it more times than required times, so is earned
278 if( $earned_times < $maximum_earnings ) {
279 $can_earn = true;
280 }
281 } else {
282 // If maximum earnings is unlimited, then can be earned
283 $can_earn = true;
284 }
285 break;
286 case 'step':
287 $achievement = gamipress_get_step_achievement( $requirement_id );
288
289 // First check if user has not exceeded the maximum earnings allowed for this achievement
290 if( $achievement && ! gamipress_achievement_user_exceeded_max_earnings( $user_id, $achievement->ID ) ) {
291
292 // Next, check if user has earned the step based on the last time he has completed the achievement
293 if( gamipress_get_earnings_count( array(
294 'user_id' => $user_id,
295 'post_id' => $requirement_id,
296 'since' => gamipress_achievement_last_user_activity( $achievement->ID, $user_id )
297 ) ) === 0 ) {
298 $can_earn = true;
299 }
300
301 }
302 break;
303 case 'rank-requirement':
304 $rank = gamipress_get_rank_requirement_rank( $requirement_id );
305
306 // First check if user has earned the rank
307 if( $rank && ! gamipress_has_user_earned_achievement( $rank->ID, $user_id ) ) {
308
309 // Next, check if has earned the rank requirement
310 if( ! gamipress_has_user_earned_achievement( $requirement_id, $user_id ) ) {
311 $can_earn = true;
312 }
313 }
314 break;
315 }
316
317 // Check if user deserves limit requirements for this requirement
318 if( $can_earn ) {
319
320 $trigger_type = gamipress_get_post_meta( $requirement_id, '_gamipress_trigger_type' );
321
322 if( ! in_array( $trigger_type, gamipress_get_activity_triggers_excluded_from_activity_limit() ) ) {
323
324 // Check if is limited over time
325 $since = gamipress_get_achievement_limit_timestamp( $requirement_id );
326
327 if( $since > 0 ) {
328
329 // Activity count limit over time
330 $count = absint( gamipress_get_post_meta( $requirement_id, '_gamipress_count' ) );
331
332 // Activity count limited to a timestamp
333 $activity_count = absint( gamipress_get_achievement_activity_count_limited( $user_id, $requirement_id, $since ) );
334
335 // Force bail if user exceeds the limit over time
336 if( $activity_count >= $count ) {
337 $can_earn = false;
338 }
339
340 }
341
342 }
343
344 }
345
346 /**
347 * Filter available to override if user is able to earn a requirement
348 *
349 * @since 1.9.0
350 *
351 * @param bool $can_earn If user can earn this requirement or not
352 * @param int $requirement_id The requirement ID
353 * @param int $user_id The user ID
354 *
355 * @return bool
356 */
357 return apply_filters( 'gamipress_can_user_earn_requirement', $can_earn, $requirement_id, $user_id );
358
359 }
360
361 /**
362 * Get the reward requirements count
363 *
364 * @since 1.0.0
365 *
366 * @param integer $post_id The reward ID to check (achievement, rank or points type)
367 * @param string $post_type The children post type (step, rank-requirement, points-award or points-deduct)
368 *
369 * @return integer
370 */
371 function gamipress_get_reward_requirements_count( $post_id, $post_type = '' ) {
372
373 global $wpdb;
374
375 $post_id = absint( $post_id );
376
377 // Bail if post ID not valid
378 if( $post_id === 0 ) return 0;
379
380 // Bail if post type is not a valid requirement type
381 if( ! in_array( $post_type, gamipress_get_requirement_types_slugs() ) ) return 0;
382
383 $cache = gamipress_get_cache( 'reward_requirement_count', array(), false );
384
385 // If result already cached, return it
386 if( isset( $cache[$post_id] ) && isset( $cache[$post_id][$post_type] ) ) {
387 return absint( $cache[$post_id][$post_type] );
388 }
389
390 $posts_table = GamiPress()->db->posts;
391
392 $count = absint( $wpdb->get_var(
393 "SELECT COUNT(*)
394 FROM {$posts_table}
395 WHERE 1=1
396 AND post_parent = '{$post_id}'
397 AND post_type = '{$post_type}'
398 AND post_status = 'publish'"
399 ) );
400
401 // Cache function result
402 if( ! isset( $cache[$post_id] ) ){
403 $cache[$post_id] = array();
404 }
405
406 $cache[$post_id][$post_type] = $count;
407
408 gamipress_set_cache( 'reward_requirement_count', $cache );
409
410 // Return requirements count
411 return $count;
412
413 }
414
415 /**
416 * Get the achievement steps count
417 *
418 * @since 1.0.0
419 *
420 * @param integer $achievement_id
421 *
422 * @return integer
423 */
424 function gamipress_get_achievement_steps_count( $achievement_id ) {
425 return gamipress_get_reward_requirements_count( $achievement_id, 'step' );
426 }
427
428 /**
429 * Get the rank requirements count
430 *
431 * @since 1.0.0
432 *
433 * @param integer $rank_id
434 *
435 * @return integer
436 */
437 function gamipress_get_rank_requirements_count( $rank_id ) {
438 return gamipress_get_reward_requirements_count( $rank_id, 'rank-requirement' );
439 }
440
441 /**
442 * Get the points awards count
443 *
444 * @since 1.0.0
445 *
446 * @param integer $points_type_id
447 *
448 * @return integer
449 */
450 function gamipress_get_points_awards_count( $points_type_id ) {
451 return gamipress_get_reward_requirements_count( $points_type_id, 'points-award' );
452 }
453
454 /**
455 * Get the points deducts count
456 *
457 * @since 1.0.0
458 *
459 * @param integer $points_type_id
460 *
461 * @return integer
462 */
463 function gamipress_get_points_deducts_count( $points_type_id ) {
464 return gamipress_get_reward_requirements_count( $points_type_id, 'points-deduct' );
465 }