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/requirements.php +465 -0 7.9.2 → 8.0.5 View file →
@@ -1,0 +1,465 @@
1 +<?php
2 +/**
3 + * Requirement Functions
4 + *
5 + * @package GamiPress\Requirement_Functions
6 + * @author GamiPress <[email protected]>, Ruben Garcia <[email protected]>
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 +}