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/admin/users.php +941 -0 7.9.9.4 → 8.0.5 View file →
@@ -1,0 +1,941 @@
1 +<?php
2 +/**
3 + * Admin Users
4 + *
5 + * @package GamiPress\Admin\Users
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 + * Display achievements for a user on their profile screen
14 + *
15 + * @since 1.0.0
16 + * @param object $user The current user's $user object
17 + * @return void
18 + */
19 +function gamipress_user_profile_data( $user = null ) {
20 + ?>
21 +
22 + <?php // Verify user meets minimum role to manage earned achievements
23 + if ( current_user_can( gamipress_get_manager_capability() ) ) : ?>
24 +
25 + <h2><?php echo gamipress_dashicon( 'gamipress' ); ?> <?php _e( 'GamiPress', 'gamipress' ); ?></h2>
26 +
27 + <?php endif; ?>
28 +
29 + <?php // Output markup to user rank
30 + gamipress_profile_user_rank( $user );
31 +
32 + // Output markup to list user points
33 + gamipress_profile_user_points( $user );
34 +
35 + // Output markup to list user earnings
36 + gamipress_profile_user_earnings( $user );
37 +
38 + // Output markup for awarding achievement for user
39 + gamipress_profile_award_achievement( $user );
40 +
41 + // Output markup for awarding requirement for user
42 + gamipress_profile_award_requirement( $user );
43 +
44 +}
45 +add_action( 'show_user_profile', 'gamipress_user_profile_data' );
46 +add_action( 'edit_user_profile', 'gamipress_user_profile_data' );
47 +
48 +/**
49 + * Update user rank ajax handler
50 + *
51 + * @since 1.5.9
52 + */
53 +function gamipress_ajax_profile_update_user_rank() {
54 + // Security check, forces to die if not security passed
55 + check_ajax_referer( 'gamipress_admin', 'nonce' );
56 +
57 + $rank_id = absint( $_POST['rank_id'] );
58 + $user_id = absint( $_POST['user_id'] );
59 +
60 + // Check if user has permissions
61 + if ( ! current_user_can( 'edit_user', $user_id ) )
62 + wp_send_json_error( __( 'You can perform this action.', 'gamipress' ) );
63 +
64 + // Check if valid user ID
65 + if( $user_id === 0 )
66 + wp_send_json_error( __( 'Invalid user ID.', 'gamipress' ) );
67 +
68 + $rank = gamipress_get_post( $rank_id );
69 +
70 + // Check if is a valid rank
71 + if ( ! $rank )
72 + wp_send_json_error( __( 'Invalid post ID.', 'gamipress' ) );
73 +
74 + if ( ! gamipress_is_rank( $rank ) )
75 + wp_send_json_error( __( 'Invalid rank ID.', 'gamipress' ) );
76 +
77 + // Update the user rank
78 + gamipress_update_user_rank( $user_id, absint( $rank_id ), get_current_user_id() );
79 +
80 + wp_send_json_success( array(
81 + 'message' => __( 'Rank updated successfully.', 'gamipress' ),
82 + 'rank' => array(
83 + 'ID' => $rank->ID,
84 + 'post_title' => $rank->post_title,
85 + 'thumbnail' => gamipress_get_rank_post_thumbnail( $rank->ID, array( 32, 32 ) ),
86 + )
87 + ) );
88 +
89 +}
90 +add_action( 'wp_ajax_gamipress_profile_update_user_rank', 'gamipress_ajax_profile_update_user_rank' );
91 +
92 +/**
93 + * Update user points ajax handler
94 + *
95 + * @since 1.5.9
96 + * @updated 1.6.0 Now also return the current user ranks in order to see any rank change through the points earned
97 + */
98 +function gamipress_ajax_profile_update_user_points() {
99 + // Security check, forces to die if not security passed
100 + check_ajax_referer( 'gamipress_admin', 'nonce' );
101 +
102 + $points = absint( $_POST['points'] );
103 + $register_movement = ( bool ) $_POST['register_movement'];
104 + $earnings_text = stripslashes( sanitize_text_field( $_POST['earnings_text'] ) );
105 + $points_type = sanitize_text_field( $_POST['points_type'] );
106 + $user_id = absint( $_POST['user_id'] );
107 +
108 + // Check if user can edit other users
109 + if ( ! current_user_can( 'edit_user', $user_id ) ) {
110 + wp_send_json_error( __( 'You can perform this action.', 'gamipress' ) );
111 + }
112 +
113 + // Check if user can manage GamiPress
114 + if( ! current_user_can( gamipress_get_manager_capability() ) ) {
115 + wp_send_json_error( __( 'You can perform this action.', 'gamipress' ) );
116 + }
117 +
118 + // Check if valid user ID
119 + if( $user_id === 0 ) {
120 + wp_send_json_error( __( 'Invalid user ID.', 'gamipress' ) );
121 + }
122 +
123 + // Check if valid amount
124 + if( ! is_numeric( $points ) ) {
125 + wp_send_json_error( __( 'Invalid points amount.', 'gamipress' ) );
126 + }
127 +
128 + // Check if is valid points type
129 + if( $points_type !== '' && ! in_array( $points_type, gamipress_get_points_types_slugs() ) ) {
130 + wp_send_json_error( __( 'Invalid points type.', 'gamipress' ) );
131 + }
132 +
133 + // Grab the user's current points
134 + $current_points = gamipress_get_user_points( $user_id, $points_type );
135 +
136 + // Update the user points
137 + gamipress_update_user_points( $user_id, $points, get_current_user_id(), null, $points_type );
138 +
139 + if( $register_movement ) {
140 +
141 + // Insert the custom user earning for the manual balance adjustment
142 + gamipress_insert_user_earning( $user_id, array(
143 + 'title' => $earnings_text,
144 + 'user_id' => $user_id,
145 + 'post_id' => gamipress_get_points_type_id( $points_type ),
146 + 'post_type' => 'points-type',
147 + 'points' => $points - $current_points,
148 + 'points_type' => $points_type,
149 + 'date' => date( 'Y-m-d H:i:s', current_time( 'timestamp' ) ),
150 + ) );
151 +
152 + }
153 +
154 + // After update the user points balance, is possible that user unlocks a rank
155 + // For that, we need to return the current user ranks again and check for differences
156 + $ranks = array();
157 +
158 + foreach( gamipress_get_rank_types_slugs() as $rank_type ) {
159 +
160 + // Get the rank object to build the same response as gamipress_ajax_profile_update_user_rank() function
161 + $rank = gamipress_get_user_rank( $user_id, $rank_type );
162 +
163 + $ranks[] = array(
164 + 'ID' => $rank->ID,
165 + 'post_title' => $rank->post_title,
166 + 'post_type' => $rank->post_type, // Included to meet the rank type
167 + 'thumbnail' => gamipress_get_rank_post_thumbnail( $rank->ID, array( 32, 32 ) ),
168 + );
169 + }
170 +
171 + wp_send_json_success( array(
172 + 'message' => __( 'Points updated successfully.', 'gamipress' ),
173 + 'points' => gamipress_format_amount( $points, $points_type ),
174 + 'ranks' => $ranks
175 + ) );
176 +
177 +}
178 +add_action( 'wp_ajax_gamipress_profile_update_user_points', 'gamipress_ajax_profile_update_user_points' );
179 +
180 +/**
181 + * Generate markup to show user rank
182 + *
183 + * @since 1.0.0
184 + *
185 + * @param object $user The current user's $user object
186 + *
187 + * @return string concatenated markup
188 + */
189 +function gamipress_profile_user_rank( $user = null ) {
190 +
191 + $rank_types = gamipress_get_rank_types();
192 +
193 + $can_manage = current_user_can( gamipress_get_manager_capability() );
194 +
195 + // Return if not rank types and user is not a manager
196 + if( empty( $rank_types ) && ! $can_manage ) {
197 + return;
198 + }
199 + ?>
200 +
201 + <table class="form-table">
202 + <tr>
203 + <th>
204 + <label><?php echo $can_manage ? __( 'Ranks', 'gamipress' ) : __( 'Your Ranks', 'gamipress' ); ?></label>
205 + </th>
206 + <td>
207 +
208 + <?php if( empty( $rank_types ) && $can_manage ) : ?>
209 +
210 + <?php // No rank types configured yet ?>
211 + <span class="description">
212 + <?php echo sprintf( __( 'No rank types configured, visit %s to configure some rank types.', 'gamipress' ), '<a href="' . admin_url( 'edit.php?post_type=rank-type' ) . '">' . __( 'this page', 'gamipress' ) . '</a>' ); ?>
213 + </span>
214 +
215 + <?php else : ?>
216 +
217 + <div class="profile-ranks gamipress-profile-cards">
218 +
219 + <?php // Show the information of each user rank ?>
220 + <?php foreach( $rank_types as $rank_type => $data ) : ?>
221 +
222 + <div class="profile-rank-wrapper gamipress-profile-card-wrapper">
223 + <div class="profile-rank profile-rank-<?php echo $rank_type; ?> gamipress-profile-card">
224 +
225 + <span class="profile-rank-type-name"><?php echo $data['singular_name']; ?></span>
226 +
227 + <?php // Get and display the current user rank
228 + $user_rank = gamipress_get_user_rank( $user->ID, $rank_type );
229 +
230 + if( $user_rank ) : ?>
231 +
232 + <div class="profile-rank-thumbnail"><?php echo gamipress_get_rank_post_thumbnail( $user_rank->ID, array( 32, 32 ) ); ?></div>
233 +
234 + <span class="profile-rank-title"><?php echo $user_rank->post_title; ?></span>
235 +
236 + <?php endif; ?>
237 +
238 +
239 + <?php if( $can_manage ) :
240 + // Show an editable form of ranks
241 +
242 + // Get all published ranks of this type
243 + $ranks = gamipress_get_ranks( array(
244 + 'post_type' => $rank_type,
245 + 'posts_per_page' => -1
246 + ) ); ?>
247 +
248 + <?php if( empty( $ranks ) ) : ?>
249 +
250 + <?php // No ranks of this type configured yet ?>
251 + <span class="description">
252 + <?php echo sprintf( __( 'No %1$s configured, visit %2$s to configure some %1$s.', 'gamipress' ),
253 + strtolower( $data['plural_name'] ),
254 + '<a href="' . admin_url( 'edit.php?post_type=' . $rank_type ) . '">' . __( 'this page', 'gamipress' ) . '</a>'
255 + ); ?>
256 + </span>
257 +
258 + <?php else : ?>
259 +
260 + <a href="#" class="profile-rank-toggle"><?php echo __( 'Edit', 'gamipress' ); ?></a>
261 +
262 + <div class="profile-rank-form-wrapper">
263 +
264 + <select name="user_<?php echo $rank_type; ?>_rank" id="user_<?php echo $rank_type; ?>_rank" style="min-width: 15em;">
265 + <?php foreach( $ranks as $rank ) : ?>
266 + <option value="<?php echo $rank->ID; ?>" <?php selected( $user_rank->ID, $rank->ID ); ?>><?php echo $rank->post_title; ?></option>
267 + <?php endforeach; ?>
268 + </select>
269 +
270 + <span class="description"><?php echo sprintf( __( '%s listed are ordered by priority.', 'gamipress' ), $data['plural_name'] ); ?></span>
271 +
272 + <div class="profile-rank-form-buttons">
273 + <a href="#" class="button button-primary profile-rank-save"><?php echo __( 'Save', 'gamipress' ); ?></a>
274 + <a href="#" class="button profile-rank-cancel"><?php echo __( 'Cancel', 'gamipress' ); ?></a>
275 + <span class="spinner"></span>
276 + </div>
277 +
278 + </div>
279 +
280 + <?php endif; ?>
281 +
282 + <?php endif; ?>
283 +
284 + </div>
285 + </div>
286 +
287 + <?php endforeach; ?>
288 +
289 + </div>
290 +
291 + <?php endif; ?>
292 +
293 + </td>
294 + </tr>
295 + </table>
296 +
297 + <?php
298 +}
299 +
300 +/**
301 + * Generate markup to list user earned points
302 + *
303 + * @since 1.0.0
304 + *
305 + * @param object $user The current user's $user object
306 + *
307 + * @return string concatenated markup
308 + */
309 +function gamipress_profile_user_points( $user = null ) {
310 +
311 + $points_types = gamipress_get_points_types();
312 +
313 + $can_manage = current_user_can( gamipress_get_manager_capability() );
314 +
315 + // Return if not points types and user is not a manager
316 + if( empty( $points_types ) && ! $can_manage ) {
317 + return;
318 + }
319 + ?>
320 +
321 + <table class="form-table">
322 + <tr>
323 + <th>
324 + <label><?php echo $can_manage ? __( 'Points Balances', 'gamipress' ) : __( 'Your Balances', 'gamipress' ); ?></label>
325 + </th>
326 + <td>
327 +
328 + <?php if( empty( $points_types ) && $can_manage ) : ?>
329 +
330 + <span class="description">
331 + <?php echo sprintf( __( 'No points types configured, visit %s to configure some points types.', 'gamipress' ), '<a href="' . admin_url( 'edit.php?post_type=points-type' ) . '">' . __( 'this page', 'gamipress' ) . '</a>' ); ?>
332 + </span>
333 +
334 + <?php else : ?>
335 +
336 + <div class="profile-points gamipress-profile-cards">
337 +
338 + <?php // Filter available to re-enable the default points
339 + if( apply_filters( 'gamipress_user_points_backward_compatibility', false ) ) :
340 + $user_points = gamipress_get_user_points( $user->ID ); ?>
341 +
342 + <div class="profile-points-wrapper gamipress-profile-card-wrapper">
343 +
344 + <div class="profile-points profile-points-default gamipress-profile-card">
345 +
346 + <span class="profile-points-type-name"><?php _e( 'Points', 'gamipress' ); ?></span>
347 +
348 + <div class="profile-points-thumbnail"></div>
349 +
350 + <span class="profile-points-amount"><?php echo $user_points; ?></span>
351 +
352 + <?php if( $can_manage ) :
353 + // Show an editable form of points ?>
354 +
355 + <a href="#" class="profile-points-toggle"><?php echo __( 'Edit', 'gamipress' ); ?></a>
356 +
357 + <div class="profile-points-form-wrapper">
358 +
359 + <input type="number" name="user_points" id="user_points" value="<?php echo $user_points; ?>" class="regular-text" data-points-type="" />
360 +
361 + <span class="description"><?php echo __( 'Enter a new total will automatically log the change and difference between totals.', 'gamipress' ); ?></span>
362 +
363 + <div class="profile-points-form-buttons">
364 + <a href="#" class="button button-primary profile-points-save"><?php echo __( 'Save', 'gamipress' ); ?></a>
365 + <a href="#" class="button profile-points-cancel"><?php echo __( 'Cancel', 'gamipress' ); ?></a>
366 + <span class="spinner"></span>
367 + </div>
368 +
369 + </div>
370 +
371 + <?php endif; ?>
372 +
373 + </div>
374 +
375 + </div>
376 +
377 + <?php endif; ?>
378 +
379 + <?php foreach( $points_types as $points_type => $data ) :
380 + $user_points = gamipress_get_user_points( $user->ID, $points_type ); ?>
381 +
382 + <div class="profile-points-wrapper gamipress-profile-card-wrapper">
383 +
384 + <div class="profile-points profile-points-<?php echo $points_type; ?> gamipress-profile-card">
385 +
386 + <span class="profile-points-type-name"><?php echo $data['plural_name']; ?></span>
387 +
388 + <div class="profile-points-thumbnail"><?php echo gamipress_get_points_type_thumbnail( $points_type, array( 32, 32 ) ); ?></div>
389 +
390 + <span class="profile-points-amount"><?php echo gamipress_format_amount( $user_points, $points_type ); ?></span>
391 +
392 + <?php if( $can_manage ) :
393 + // Show an editable form of points ?>
394 +
395 + <a href="#" class="profile-points-toggle"><?php echo __( 'Edit', 'gamipress' ); ?></a>
396 +
397 + <div class="profile-points-form-wrapper">
398 +
399 + <div class="profile-points-new-balance-input">
400 + <label for="user_<?php echo $points_type; ?>_points"><?php echo __( 'New balance:', 'gamipress' ); ?></label>
401 + <input type="number" name="user_<?php echo $points_type; ?>_points" id="user_<?php echo $points_type; ?>_points" value="<?php echo $user_points; ?>" class="regular-text" data-points-type="<?php echo $points_type; ?>" />
402 + <span class="description"><?php echo __( 'Enter a new total will automatically log the change and difference between totals.', 'gamipress' ); ?></span>
403 + </div>
404 +
405 + <label for="user_<?php echo $points_type; ?>_register_points_movement" class="profile-points-register-movement-input-label"><?php echo __( 'Register on user earnings:', 'gamipress' ); ?></label>
406 + <div class="gamipress-switch gamipress-switch-small profile-points-register-movement-input">
407 + <input type="checkbox" name="user_<?php echo $points_type; ?>_register_points_movement" id="user_<?php echo $points_type; ?>_register_points_movement">
408 + <label for="user_<?php echo $points_type; ?>_register_points_movement"><?php echo __( 'Check this option to register this balance movement on user earnings.', 'gamipress' ); ?></label>
409 + </div>
410 +
411 + <div class="profile-points-earning-text-input" style="display: none;">
412 + <label for="user_<?php echo $points_type; ?>_points_earning_text"><?php echo __( 'Earning entry text:', 'gamipress' ); ?></label>
413 + <input type="text" name="user_<?php echo $points_type; ?>_points_earning_text" id="user_<?php echo $points_type; ?>_points_earning_text" value="<?php echo __( 'Manual balance adjustment', 'gamipress' ); ?>" class="regular-text" />
414 + <span class="description"><?php echo __( 'Enter the line text to be displayed on user earnings.', 'gamipress' ); ?></span>
415 + </div>
416 +
417 + <div class="profile-points-form-buttons">
418 + <a href="#" class="button button-primary profile-points-save"><?php echo __( 'Save', 'gamipress' ); ?></a>
419 + <a href="#" class="button profile-points-cancel"><?php echo __( 'Cancel', 'gamipress' ); ?></a>
420 + <span class="spinner"></span>
421 + </div>
422 +
423 + </div>
424 +
425 + <?php endif; ?>
426 +
427 + </div>
428 +
429 + </div>
430 +
431 + <?php endforeach; ?>
432 +
433 + </div>
434 +
435 + <?php endif; ?>
436 + </td>
437 + </tr>
438 + </table>
439 +
440 + <?php
441 +}
442 +
443 +/**
444 + * Generate markup to list user earnings
445 + *
446 + * @since 1.0.0
447 + *
448 + * @param object $user The current user's $user object
449 + *
450 + * @return string concatenated markup
451 + */
452 +function gamipress_profile_user_earnings( $user = null ) {
453 +
454 + /**
455 + * Filter to allow set the number of user earnings to show on user profile
456 + *
457 + * @since 1.8.3
458 + *
459 + * @param int $items_per_page
460 + *
461 + * @return int
462 + */
463 + $items_per_page = apply_filters( 'gamipress_user_profile_earnings_items_per_page', 10 );
464 + ?>
465 +
466 + <h2><?php echo current_user_can( gamipress_get_manager_capability() ) ? __( 'User Earnings', 'gamipress' ) : __( 'Your Achievements', 'gamipress' ); ?></h2>
467 +
468 + <?php ct_render_ajax_list_table( 'gamipress_user_earnings',
469 + array(
470 + 'user_id' => absint( $user->ID ),
471 + 'items_per_page' => $items_per_page,
472 + ),
473 + array(
474 + 'views' => false,
475 + 'search_box' => false
476 + )
477 + ); ?>
478 +
479 + <?php
480 +}
481 +
482 +/**
483 + * Generate markup for awarding an achievement to a user
484 + *
485 + * @since 1.0.0
486 + *
487 + * @param object $user The current user's $user object
488 + *
489 + * @return string concatenated markup
490 + */
491 +function gamipress_profile_award_achievement( $user = null ) {
492 +
493 + // Return if user is not a manager
494 + if( ! current_user_can( gamipress_get_manager_capability() ) ) {
495 + return;
496 + }
497 +
498 + // Grab our types
499 + $achievement_types = gamipress_get_achievement_types();
500 + ?>
501 +
502 + <h2><?php _e( 'Award Achievement', 'gamipress' ); ?></h2>
503 +
504 + <table class="form-table">
505 +
506 + <tr>
507 + <th><label for="gamipress-award-achievement-type-select"><?php _e( 'Select an achievement type to award:', 'gamipress' ); ?></label></th>
508 + <td>
509 + <select id="gamipress-award-achievement-type-select">
510 + <option value=""><?php _e( 'Choose an achievement type', 'gamipress' ); ?></option>
511 + <?php foreach ( $achievement_types as $slug => $data ) :
512 + echo '<option value="'. $slug .'">' . ucwords( $data['singular_name'] ) .'</option>';
513 + endforeach; ?>
514 + </select>
515 + </td>
516 + </tr>
517 +
518 + </table>
519 +
520 + <div id="gamipress-awards-options">
521 + <?php foreach ( $achievement_types as $slug => $data ) : ?>
522 + <div id="<?php echo esc_attr( $slug ); ?>" data-loaded="false" style="display: none;">
523 + <span class="spinner is-active"></span>
524 + </div>
525 + <?php endforeach; ?>
526 +
527 + </div><!-- #gamipress-awards-options -->
528 +
529 + <?php
530 +}
531 +
532 +/**
533 + * Ajax handler to load the award table of the given post type
534 + *
535 + * @since 1.8.7
536 + */
537 +function gamipress_ajax_profile_load_award_achievement_award() {
538 +
539 + // Security check, forces to die if not security passed
540 + check_ajax_referer( 'gamipress_admin', 'nonce' );
541 +
542 + // Return if user is not a manager
543 + if( ! current_user_can( gamipress_get_manager_capability() ) ) {
544 + wp_send_json_error( __( 'You can perform this action.', 'gamipress' ) );
545 + }
546 +
547 + $post_type = sanitize_text_field( $_POST['post_type'] );
548 + $user_id = absint( $_POST['user_id'] );
549 +
550 + // Check if user has permissions
551 + if ( ! current_user_can( 'edit_user', $user_id ) ) {
552 + wp_send_json_error( __( 'You can perform this action.', 'gamipress' ) );
553 + }
554 +
555 + // Grab our types
556 + $achievement_types = gamipress_get_achievement_types();
557 +
558 + $achievements = gamipress_get_user_achievements( array(
559 + 'user_id' => absint( $user_id ),
560 + 'achievement_type' => $post_type
561 + ) );
562 +
563 + $achievement_ids = array_map( function( $achievement ) {
564 + return $achievement->ID;
565 + }, $achievements );
566 +
567 + // On network wide active installs, we need to switch to main blog mostly for posts permalinks and thumbnails
568 + $blog_id = gamipress_switch_to_main_site_if_network_wide_active();
569 +
570 + ob_start(); ?>
571 +
572 + <?php foreach ( $achievement_types as $slug => $data ) :
573 +
574 + // Skip if not is the points type to render
575 + if( $post_type !== $slug ) {
576 + continue;
577 + } ?>
578 + <div id="<?php echo esc_attr( $slug ); ?>">
579 + <table id="<?php echo esc_attr( $slug ); ?>-table" class="wp-list-table widefat fixed striped gamipress-table">
580 +
581 + <thead>
582 + <tr>
583 + <th><?php echo ucwords( $data['singular_name'] ); ?></th>
584 + <th><?php _e( 'Actions', 'gamipress' ); ?></th>
585 + </tr>
586 + </thead>
587 +
588 + <tbody>
589 + <?php
590 + // Load achievement type entries
591 + $the_query = new WP_Query( array(
592 + 'post_type' => $slug,
593 + 'posts_per_page' => -1,
594 + 'post_status' => 'publish',
595 + 'suppress_filters' => false
596 + ) );
597 +
598 + if ( $the_query->have_posts() ) : ?>
599 +
600 + <?php while ( $the_query->have_posts() ) : $the_query->the_post();
601 +
602 + // Setup our award URL
603 + $award_url = add_query_arg( array(
604 + 'action' => 'award',
605 + 'achievement_id' => absint( get_the_ID() ),
606 + 'user_id' => absint( $user_id )
607 + ) );
608 + ?>
609 + <tr>
610 + <td>
611 + <?php // Thumbnail ?>
612 + <?php echo gamipress_get_achievement_post_thumbnail( get_the_ID(), array( 32, 32 ) ); ?>
613 +
614 + <?php // Title ?>
615 + <strong><?php echo '<a href="' . get_edit_post_link( get_the_ID() ) . '">' . gamipress_get_post_field( 'post_title', get_the_ID() ) . '</a>'; ?></strong>
616 + </td>
617 + <td>
618 + <a class="gamipress-award-achievement" href="<?php echo esc_url( wp_nonce_url( $award_url, 'gamipress_award_achievement' ) ); ?>"><?php printf( __( 'Award %s', 'gamipress' ), ucwords( $data['singular_name'] ) ); ?></a>
619 + <?php if ( in_array( get_the_ID(), (array) $achievement_ids ) ) :
620 + // Setup our revoke URL
621 + $revoke_url = add_query_arg( array(
622 + 'action' => 'revoke',
623 + 'user_id' => absint( $user_id ),
624 + 'achievement_id' => absint( get_the_ID() ),
625 + ) );
626 + ?>
627 + | <span class="delete"><a class="error gamipress-revoke-achievement" href="<?php echo esc_url( wp_nonce_url( $revoke_url, 'gamipress_revoke_achievement' ) ); ?>"><?php _e( 'Revoke Award', 'gamipress' ); ?></a></span>
628 + <?php endif; ?>
629 +
630 + </td>
631 + </tr>
632 + <?php endwhile; ?>
633 +
634 + <?php else : ?>
635 + <tr>
636 + <td colspan="3"><?php printf( __( 'No %s found.', 'gamipress' ), $data['plural_name'] ); ?></td>
637 + </tr>
638 + <?php endif; wp_reset_postdata(); ?>
639 +
640 + </tbody>
641 +
642 + </table><!-- #<?php echo esc_attr( $slug ); ?>-table -->
643 + </div><!-- #<?php echo esc_attr( $slug ); ?> -->
644 +
645 + <?php endforeach; ?>
646 +
647 + <?php $content = ob_get_clean();
648 +
649 + // If switched to blog, return back to que current blog
650 + if( $blog_id !== get_current_blog_id() && is_multisite() ) {
651 + restore_current_blog();
652 + }
653 +
654 + if( empty( $content ) ) {
655 + wp_send_json_error( __( 'Couldn\'t load this content.', 'gamipress' ) );
656 + }
657 +
658 + // Return the table rendered
659 + wp_send_json_success( $content );
660 +
661 +}
662 +add_action( 'wp_ajax_gamipress_profile_load_award_achievement_table', 'gamipress_ajax_profile_load_award_achievement_award' );
663 +
664 +/**
665 + * Generate markup for awarding an achievement to a user
666 + *
667 + * @since 1.6.8
668 + *
669 + * @param object $user The current user's $user object
670 + *
671 + * @return string concatenated markup
672 + */
673 +function gamipress_profile_award_requirement( $user = null ) {
674 +
675 + // Return if user is not a manager
676 + if( ! current_user_can( gamipress_get_manager_capability() ) ) {
677 + return;
678 + }
679 +
680 + // Grab our types
681 + $requirement_types = gamipress_get_requirement_types();
682 +
683 + ?>
684 +
685 + <h2><?php _e( 'Award Requirement', 'gamipress' ); ?></h2>
686 +
687 + <table class="form-table">
688 +
689 + <tr>
690 + <th><label for="gamipress-award-requirement-type-select"><?php _e( 'Select a requirement type to award:', 'gamipress' ); ?></label></th>
691 + <td>
692 + <select id="gamipress-award-requirement-type-select">
693 + <option value=""><?php _e( 'Choose a requirement type', 'gamipress' ); ?></option>
694 + <?php foreach ( $requirement_types as $slug => $data ) :
695 + echo '<option value="'. $slug .'">' . ucwords( $data['singular_name'] ) .'</option>';
696 + endforeach; ?>
697 + </select>
698 + </td>
699 + </tr>
700 +
701 + </table>
702 +
703 + <div id="gamipress-awards-options">
704 + <?php foreach ( $requirement_types as $slug => $data ) : ?>
705 + <div id="<?php echo esc_attr( $slug ); ?>" data-loaded="false" style="display: none;">
706 + <span class="spinner is-active"></span>
707 + </div>
708 + <?php endforeach; ?>
709 +
710 + </div><!-- #gamipress-awards-options -->
711 +
712 + <?php
713 +}
714 +
715 +/**
716 + * Ajax handler to load the requiremetn award table of the given post type
717 + *
718 + * @since 1.8.7
719 + */
720 +function gamipress_ajax_profile_load_award_requirement_table() {
721 +
722 + // Security check, forces to die if not security passed
723 + check_ajax_referer( 'gamipress_admin', 'nonce' );
724 +
725 + // Return if user is not a manager
726 + if( ! current_user_can( gamipress_get_manager_capability() ) ) {
727 + wp_send_json_error( __( 'You can perform this action.', 'gamipress' ) );
728 + }
729 +
730 + $post_type = sanitize_text_field( $_POST['post_type'] );
731 + $user_id = absint( $_POST['user_id'] );
732 +
733 + // Check if user has permissions
734 + if ( ! current_user_can( 'edit_user', $user_id ) ) {
735 + wp_send_json_error( __( 'You can perform this action.', 'gamipress' ) );
736 + }
737 +
738 + // Grab our types
739 + $achievement_types = gamipress_get_achievement_types();
740 + $rank_types = gamipress_get_rank_types();
741 + $requirement_types = gamipress_get_requirement_types();
742 +
743 + $achievements = gamipress_get_user_achievements( array(
744 + 'user_id' => absint( $user_id ),
745 + 'achievement_type' => $post_type
746 + ) );
747 +
748 + $achievement_ids = array_map( function( $achievement ) {
749 + return $achievement->ID;
750 + }, $achievements );
751 +
752 + // On network wide active installs, we need to switch to main blog mostly for posts permalinks and thumbnails
753 + $blog_id = gamipress_switch_to_main_site_if_network_wide_active();
754 +
755 + ob_start(); ?>
756 +
757 + <?php foreach ( $requirement_types as $slug => $data ) :
758 +
759 + // Skip if not is the points type to render
760 + if( $post_type !== $slug ) {
761 + continue;
762 + } ?>
763 + <div id="<?php echo esc_attr( $slug ); ?>">
764 + <table id="<?php echo esc_attr( $slug ); ?>-table" class="wp-list-table widefat fixed striped gamipress-table">
765 +
766 + <thead>
767 + <tr>
768 + <th><?php echo ucwords( $data['singular_name'] ); ?></th>
769 + <th><?php _e( 'Actions', 'gamipress' ); ?></th>
770 + </tr>
771 + </thead>
772 +
773 + <tbody>
774 + <?php
775 + // Load achievement type entries
776 + $the_query = new WP_Query( array(
777 + 'post_type' => $slug,
778 + 'posts_per_page' => -1,
779 + 'post_status' => 'publish',
780 + 'suppress_filters' => false
781 + ) );
782 +
783 + if ( $the_query->have_posts() ) : ?>
784 +
785 + <?php while ( $the_query->have_posts() ) : $the_query->the_post();
786 +
787 + // If not parent object, skip
788 + if( $slug === 'step' && ! $achievement = gamipress_get_step_achievement( get_the_ID() ) ) {
789 + continue;
790 + } else if( $slug === 'points-award' && ! $points_type = gamipress_get_points_award_points_type( get_the_ID() ) ) {
791 + continue;
792 + } else if( $slug === 'points-deduct' && ! $points_type = gamipress_get_points_deduct_points_type( get_the_ID() ) ) {
793 + continue;
794 + } else if( $slug === 'rank-requirement' && ! $rank = gamipress_get_rank_requirement_rank( get_the_ID() ) ) {
795 + continue;
796 + }
797 +
798 + // Setup our award URL
799 + $award_url = add_query_arg( array(
800 + 'action' => 'award',
801 + 'achievement_id' => absint( get_the_ID() ),
802 + 'user_id' => absint( $user_id )
803 + ) );
804 + ?>
805 + <tr>
806 + <td>
807 + <?php // Output parent achievement
808 + if( $slug === 'step' && $achievement ) : ?>
809 +
810 + <?php // Achievement thumbnail ?>
811 + <?php echo gamipress_get_achievement_post_thumbnail( $achievement->ID, array( 32, 32 ) ); ?>
812 +
813 + <?php // Step title ?>
814 + <strong><?php echo gamipress_get_post_field( 'post_title', get_the_ID() ); ?></strong>
815 +
816 + <?php // Step relationship details ?>
817 + <?php echo ( isset( $achievement_types[$achievement->post_type] ) ? '<br> ' . $achievement_types[$achievement->post_type]['singular_name'] . ': ' : '' ); ?>
818 + <?php echo '<a href="' . get_edit_post_link( $achievement->ID ) . '">' . gamipress_get_post_field( 'post_title', $achievement->ID ) . '</a>'; ?>
819 +
820 + <?php elseif( in_array( $slug, array( 'points-award', 'points-deduct' ) ) && $points_type ) : ?>
821 +
822 + <?php // Points type thumbnail ?>
823 + <?php echo gamipress_get_points_type_thumbnail( $points_type->ID, array( 32, 32 ) ); ?>
824 +
825 + <?php // Points award/deduct title ?>
826 + <strong><?php echo gamipress_get_post_field( 'post_title', get_the_ID() ); ?></strong>
827 + <br>
828 + <?php echo '<a href="' . get_edit_post_link( $points_type->ID ) . '">' . gamipress_get_post_field( 'post_title', $points_type->ID ) . '</a>'; ?>
829 +
830 + <?php elseif( $slug === 'rank-requirement' && $rank ) : ?>
831 +
832 + <?php // Rank thumbnail ?>
833 + <?php echo gamipress_get_rank_post_thumbnail( $rank->ID, array( 32, 32 ) ); ?>
834 +
835 + <?php // Rank requirement title ?>
836 + <strong><?php echo gamipress_get_post_field( 'post_title', get_the_ID() ); ?></strong>
837 +
838 + <?php // Rank requirement relationship details ?>
839 + <?php echo ( isset( $rank_types[$rank->post_type] ) ? '<br> ' . $rank_types[$rank->post_type]['singular_name'] . ': ' : '' ); ?>
840 + <?php echo '<a href="' . get_edit_post_link( $rank->ID ) . '">' . gamipress_get_post_field( 'post_title', $rank->ID ) . '</a>'; ?>
841 +
842 + <?php endif; ?>
843 + </td>
844 + <td>
845 + <a class="gamipress-award-achievement" href="<?php echo esc_url( wp_nonce_url( $award_url, 'gamipress_award_achievement' ) ); ?>"><?php printf( __( 'Award %s', 'gamipress' ), ucwords( $data['singular_name'] ) ); ?></a>
846 + <?php if ( in_array( get_the_ID(), (array) $achievement_ids ) ) :
847 + // Setup our revoke URL
848 + $revoke_url = add_query_arg( array(
849 + 'action' => 'revoke',
850 + 'user_id' => absint( $user_id ),
851 + 'achievement_id' => absint( get_the_ID() ),
852 + ) );
853 + ?>
854 + | <span class="delete"><a class="error gamipress-revoke-achievement" href="<?php echo esc_url( wp_nonce_url( $revoke_url, 'gamipress_revoke_achievement' ) ); ?>"><?php _e( 'Revoke Award', 'gamipress' ); ?></a></span>
855 + <?php endif; ?>
856 +
857 + </td>
858 + </tr>
859 + <?php endwhile; ?>
860 +
861 + <?php else : ?>
862 + <tr>
863 + <td colspan="3"><?php printf( __( 'No %s found.', 'gamipress' ), $data['plural_name'] ); ?></td>
864 + </tr>
865 + <?php endif; wp_reset_postdata(); ?>
866 +
867 + </tbody>
868 +
869 + </table><!-- #<?php echo esc_attr( $slug ); ?> -->
870 + </div><!-- #<?php echo esc_attr( $slug ); ?> -->
871 +
872 + <?php endforeach; ?>
873 +
874 + <?php $content = ob_get_clean();
875 +
876 + // If switched to blog, return back to que current blog
877 + if( $blog_id !== get_current_blog_id() && is_multisite() ) {
878 + restore_current_blog();
879 + }
880 +
881 + if( empty( $content ) ) {
882 + wp_send_json_error( __( 'Couldn\'t load this content.', 'gamipress' ) );
883 + }
884 +
885 + // Return the table rendered
886 + wp_send_json_success( $content );
887 +
888 +}
889 +add_action( 'wp_ajax_gamipress_profile_load_award_requirement_table', 'gamipress_ajax_profile_load_award_requirement_table' );
890 +
891 +
892 +/**
893 + * Process the adding/revoking of achievements on the user profile page
894 + *
895 + * @since 1.0.0
896 + */
897 +function gamipress_process_user_data() {
898 +
899 + // verify user meets minimum role to view earned achievements
900 + if ( current_user_can( gamipress_get_manager_capability() ) ) {
901 +
902 + // Process awarding achievement to user
903 + if ( isset( $_GET['action'] ) && $_GET['action'] === 'award' && isset( $_GET['user_id'] ) && isset( $_GET['achievement_id'] ) ) {
904 +
905 + // Verify our nonce
906 + check_admin_referer( 'gamipress_award_achievement' );
907 +
908 + // Award the achievement
909 + gamipress_award_achievement_to_user( absint( $_GET['achievement_id'] ), absint( $_GET['user_id'] ), get_current_user_id() );
910 +
911 + // Redirect back to the user editor
912 + wp_redirect( add_query_arg( 'user_id', absint( $_GET['user_id'] ), admin_url( 'user-edit.php' ) ) );
913 + exit();
914 + }
915 +
916 + // Process revoking achievement from a user
917 + if ( isset( $_GET['action'] ) && $_GET['action'] === 'revoke' && isset( $_GET['user_id'] ) && isset( $_GET['achievement_id'] ) ) {
918 +
919 + // Verify our nonce
920 + check_admin_referer( 'gamipress_revoke_achievement' );
921 +
922 + $earning_id = isset( $_GET['user_earning_id'] ) ? absint( $_GET['user_earning_id'] ) : 0 ;
923 +
924 + // Revoke the achievement
925 + gamipress_revoke_achievement_to_user( absint( $_GET['achievement_id'] ), absint( $_GET['user_id'] ), $earning_id );
926 +
927 + // If revoking from user earnings screen return directly without redirect
928 + if( isset( $_GET['page'] ) && $_GET['page'] === 'gamipress_user_earnings' ) {
929 + exit();
930 + }
931 +
932 + // Redirect back to the user editor
933 + wp_redirect( add_query_arg( 'user_id', absint( $_GET['user_id'] ), admin_url( 'user-edit.php' ) ) );
934 + exit();
935 +
936 + }
937 +
938 + }
939 +
940 +}
941 +add_action( 'admin_init', 'gamipress_process_user_data' );