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/meta-boxes/requirements-ui.php +1395 -0 7.9.2 → 8.0.5 View file →
@@ -1,0 +1,1395 @@
1 +<?php
2 +/**
3 + * Requirements UI
4 + *
5 + * @package GamiPress\Admin\Requirements_UI
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 + * Add requirements meta boxes
14 + *
15 + * @since 1.0.5
16 + *
17 + * @return void
18 + */
19 +function gamipress_add_requirements_ui_meta_box() {
20 +
21 + // Points Awards
22 + add_meta_box( 'gamipress-requirements-ui-points-award', __( 'Automatic Points Awards', 'gamipress' ), 'gamipress_requirements_ui_meta_box', 'points-type', 'advanced', 'default', array( 'requirement_type' => 'points-award' ) );
23 +
24 + // Points Deductions
25 + add_meta_box( 'gamipress-requirements-ui-points-deduct', __( 'Automatic Points Deductions', 'gamipress' ), 'gamipress_requirements_ui_meta_box', 'points-type', 'advanced', 'default', array( 'requirement_type' => 'points-deduct' ) );
26 +
27 + // Steps
28 + foreach ( gamipress_get_achievement_types_slugs() as $achievement_type ) {
29 + add_meta_box( 'gamipress-requirements-ui', __( 'Required Steps', 'gamipress' ), 'gamipress_requirements_ui_meta_box', $achievement_type, 'advanced', 'default', array( 'requirement_type' => 'step' ) );
30 + }
31 +
32 + // Rank Requirements
33 + foreach ( gamipress_get_rank_types_slugs() as $rank_type ) {
34 + add_meta_box( 'gamipress-requirements-ui', __( 'Rank Requirements', 'gamipress' ), 'gamipress_requirements_ui_meta_box', $rank_type, 'advanced', 'default', array( 'requirement_type' => 'rank-requirement' ) );
35 + }
36 +
37 +}
38 +add_action( 'add_meta_boxes', 'gamipress_add_requirements_ui_meta_box' );
39 +
40 +/**
41 + * Renders the HTML for meta box, refreshes whenever a new requirement is added
42 + *
43 + * @since 1.0.5
44 + * @updated 1.3.7 Added $metabox
45 + *
46 + * @param WP_Post $post The current post object.
47 + * @param array $metabox With metabox id, title, callback, and args elements.
48 + *
49 + * @return void
50 + */
51 +function gamipress_requirements_ui_meta_box( $post = null, $metabox = array() ) {
52 +
53 + // Define the requirement type to use
54 + $requirement_type = gamipress_requirements_ui_get_requirement_type( $post, $metabox );
55 +
56 + // If is lowest priority rank then show a notice and prevent to show requirements UI
57 + if( $requirement_type === 'rank-requirement' ) {
58 +
59 + if( ( $post->post_status === 'auto-draft' && gamipress_get_rank_priority( $post->ID ) === 1 )
60 + || ( $post->post_status !== 'auto-draft' && gamipress_is_lowest_priority_rank( $post->ID ) ) ) {
61 +
62 + echo '<p>' .
63 + __( 'The rank with the lowest priority is set as default rank for all users so this rank should be created without requirements.', 'gamipress' )
64 + . '<br>'
65 + . __( 'You will be able to set requirements on next ranks. After save this rank, if it is not configured as the lowest priority you will be able to edit the requirements again.', 'gamipress' )
66 + . '</p>';
67 + return;
68 +
69 + }
70 + }
71 +
72 + // Setup the requirement object based on the requirement type
73 + $requirement_types = gamipress_get_requirement_types();
74 + $requirement_type_object = $requirement_types[$requirement_type];
75 +
76 + // Get assigned requirements
77 + $assigned_requirements = gamipress_get_assigned_requirements( $post->ID, $requirement_type, 'any' );
78 +
79 + if( ! $assigned_requirements ) {
80 + $assigned_requirements = array();
81 + }
82 +
83 + // Sort the requirements by their order
84 + uasort( $assigned_requirements, 'gamipress_compare_requirements_order' );
85 +
86 + switch( $requirement_type ) {
87 + case 'points-award':
88 + echo '<p>' .
89 + __( 'Define the automatic ways a user could retrieve an amount of this points type. Use the "Label" field to optionally customize the titles of each one.', 'gamipress' )
90 + . '</p>';
91 + break;
92 + case 'points-deduct':
93 + echo '<p>' .
94 + __( 'Define the automatic ways a user could lose an amount of this points type. Use the "Label" field to optionally customize the titles of each one.', 'gamipress' )
95 + . '</p>';
96 + break;
97 + case 'step':
98 + echo '<p>' .
99 + __( 'Define the required "steps" for this achievement to be considered complete. Use the "Label" field to optionally customize the titles of each step.', 'gamipress' )
100 + . '</p>';
101 + break;
102 + case 'rank-requirement':
103 + echo '<p>' .
104 + __( 'Define the required requirements for this rank to be considered that user is ranked on it. Use the "Label" field to optionally customize the titles of each requirement.', 'gamipress' )
105 + . '<br>'
106 + . __( '<strong>Important!</strong> User will not earn this requirements until be ranked on the previous rank.', 'gamipress' )
107 + . '</p>';
108 + break;
109 + }
110 +
111 + // Sequential input
112 + if( in_array( $requirement_type, array( 'step', 'rank-requirement' ) ) ) : ?>
113 +
114 + <?php wp_nonce_field( '_gamipress_sequential_' . $post->ID, '_gamipress_sequential_nonce' ); ?>
115 +
116 + <label for="_gamipress_sequential">
117 + <strong><?php echo ( $requirement_type === 'step'
118 + ? __( 'Sequential Steps', 'gamipress' )
119 + : __( 'Sequential Requirements', 'gamipress' )
120 + ); ?></strong>
121 + </label>
122 +
123 + <div class="gamipress-switch gamipress-switch-small gamipress-requirements-sequential">
124 + <input type="checkbox" id="_gamipress_sequential" name="_gamipress_sequential" <?php checked( true, (bool) gamipress_get_post_meta( $post->ID, '_gamipress_sequential' ) ); ?> value="on">
125 + <label for="_gamipress_sequential"><?php echo ( $requirement_type === 'step'
126 + ? __( 'Check this option to force users to complete steps in order.', 'gamipress' )
127 + : __( 'Check this option to force users to complete requirements in order.', 'gamipress' )
128 + ); ?></label>
129 + </div>
130 +
131 + <?php endif;
132 +
133 + do_action( 'gamipress_before_requirements_list', $requirement_type, $requirement_type_object, $assigned_requirements );
134 +
135 + // Concatenate our requirements output
136 + echo '<ul id="requirements-list" class="requirements-list">';
137 + foreach ( $assigned_requirements as $requirement ) {
138 + gamipress_requirement_ui_html( $requirement->ID, $post->ID );
139 + }
140 + echo '</ul>';
141 +
142 + // Render the add and save buttons ?>
143 + <div class="button gamipress-button-success gamipress-add-requirement" data-post-id="<?php echo $post->ID; ?>" data-requirement-type="<?php echo $requirement_type; ?>">
144 + <span class="dashicons dashicons-plus"></span><?php printf( __( 'Add New %s', 'gamipress' ), $requirement_type_object['singular_name'] ); ?>
145 + </div>
146 + <div class="button-primary gamipress-save-requirements">
147 + <span class="dashicons dashicons-saved"></span><?php printf( __( 'Save All %s', 'gamipress' ), $requirement_type_object['plural_name'] ); ?>
148 + </div>
149 + <span class="spinner requirements-spinner"></span>
150 + <?php
151 +}
152 +
153 +/**
154 + * Return post assigned requirements
155 + *
156 + * @since 1.0.0
157 + * @updated 1.5.1 Added $post_status parameter
158 + *
159 + * @param int $post_id
160 + * @param string $requirement_type
161 + * @param string $post_status
162 + *
163 + * @return array|bool
164 + */
165 +function gamipress_get_assigned_requirements( $post_id = null, $requirement_type = '', $post_status = 'publish' ) {
166 +
167 + global $post;
168 +
169 + if( $post_id === null ) {
170 + $post_id = $post->ID;
171 + }
172 +
173 + // Grab post's requirements
174 + return get_posts( array(
175 + 'post_type' => $requirement_type,
176 + 'post_parent' => $post_id,
177 + 'post_status' => $post_status,
178 + 'orderby' => 'menu_order',
179 + 'order' => 'ASC',
180 + 'posts_per_page' => -1,
181 + 'suppress_filters' => false,
182 + ) );
183 +
184 +}
185 +
186 +/**
187 + * Helper function for generating the HTML output for configuring a given requirement
188 + *
189 + * @since 1.0.5
190 + *
191 + * @param integer $requirement_id The given requirement's ID
192 + * @param integer $post_id The given requirement's parent $post ID
193 + *
194 + * @return string The concatenated HTML input for the requirement
195 + */
196 +function gamipress_requirement_ui_html( $requirement_id = 0, $post_id = 0 ) {
197 +
198 + // Grab our requirement's requirements and measurement
199 + $requirements = gamipress_get_requirement_object( $requirement_id );
200 + $requirement_type = gamipress_get_post_type( $requirement_id );
201 + $status = gamipress_get_post_status( $requirement_id );
202 + $count = ! empty( $requirements['count'] ) ? $requirements['count'] : 1;
203 + $limit = ! empty( $requirements['limit'] ) ? $requirements['limit'] : 1;
204 + $limit_type = ! empty( $requirements['limit_type'] ) ? $requirements['limit_type'] : 'unlimited';
205 +
206 + // Setup the requirement object based on the requirement type
207 + $requirement_types = gamipress_get_requirement_types();
208 + $requirement_type_object = $requirement_types[$requirement_type];
209 + ?>
210 +
211 + <li class="requirement-row requirement-<?php echo $requirement_id; ?> <?php echo ( $status === 'publish' ? 'requirement-published' : '' ); ?>" data-requirement-id="<?php echo $requirement_id; ?>">
212 +
213 + <?php
214 + /**
215 + * Available action to add custom HTML at top of the requirement HTML
216 + *
217 + * @since 1.0.0
218 + *
219 + * @param int $requirement_id The requirement ID
220 + * @param int $post_id The post ID where requirements are displayed
221 + */
222 + do_action( 'gamipress_requirement_ui_html_requirement_top', $requirement_id, $post_id ); ?>
223 +
224 + <input type="hidden" name="requirement_id" value="<?php echo $requirement_id; ?>" />
225 + <input type="hidden" name="requirement_type" value="<?php echo $requirement_type; ?>" />
226 + <input type="hidden" name="post_id" value="<?php echo $post_id; ?>" />
227 + <input type="hidden" name="order" value="<?php echo absint( gamipress_get_requirement_menu_order( $requirement_id ) ); ?>" />
228 +
229 + <div class="requirement-header">
230 +
231 + <div class="requirement-header-title">
232 + <strong><?php echo get_the_title( $requirement_id ); ?></strong>
233 + </div>
234 +
235 + <div class="requirement-actions">
236 +
237 + <?php $change_status_title = ( $status === 'publish' ? __( 'Disable %s', 'gamipress' ) : __( 'Enable %s', 'gamipress' ) ) ?>
238 +
239 + <div class="requirement-action requirement-action-change-status"
240 + title="<?php echo sprintf( $change_status_title, $requirement_type_object['singular_name'] ); ?>"
241 + data-enabled-title="<?php echo sprintf( __( 'Disable %s', 'gamipress' ), $requirement_type_object['singular_name'] ); ?>"
242 + data-disabled-title="<?php echo sprintf( __( 'Enable %s', 'gamipress' ), $requirement_type_object['singular_name'] ); ?>"
243 + >
244 + <div class="gamipress-switch gamipress-switch-small">
245 + <input type="checkbox" id="requirement-action-change-status-input-<?php echo $requirement_id; ?>" <?php checked( 'publish', $status ) ?> />
246 + <label for="requirement-action-change-status-input-<?php echo $requirement_id; ?>"></label>
247 + </div>
248 + </div>
249 +
250 + <?php
251 +
252 + // Setup the default requirement actions
253 + $requirement_actions = array(
254 + 'duplicate' => array(
255 + 'label' => __( 'Duplicate', 'gamipress' ),
256 + 'icon' => 'dashicons-admin-page',
257 + ),
258 + 'delete' => array(
259 + 'label' => __( 'Delete', 'gamipress' ),
260 + 'icon' => 'dashicons-trash',
261 + )
262 + );
263 +
264 + /**
265 + * Available filter to add custom requirement actions
266 + *
267 + * @since 1.4.6
268 + *
269 + * @param array $requirement_actions
270 + */
271 + $requirement_actions = apply_filters( 'gamipress_requirement_ui_requirement_actions', $requirement_actions );
272 +
273 + foreach( $requirement_actions as $requirement_action => $requirement_action_args ) : ?>
274 +
275 + <div class="requirement-action requirement-action-<?php echo $requirement_action; ?>" title="<?php echo $requirement_action_args['label']; ?>">
276 + <span class="dashicons <?php echo $requirement_action_args['icon']; ?>"></span>
277 + </div>
278 +
279 + <?php endforeach; ?>
280 +
281 + </div>
282 +
283 + </div>
284 +
285 + <?php
286 + /**
287 + * Available action to add custom HTML after the requirement header
288 + *
289 + * @since 1.0.0
290 + *
291 + * @param int $requirement_id The requirement ID
292 + * @param int $post_id The post ID where requirements are displayed
293 + */
294 + do_action( 'gamipress_requirement_ui_html_after_requirement_header', $requirement_id, $post_id ); ?>
295 +
296 + <label for="select-trigger-type-<?php echo $requirement_id; ?>"><?php _e( 'When', 'gamipress' ); ?>:</label>
297 +
298 + <?php
299 + /**
300 + * Available action to add custom HTML after requirement text
301 + *
302 + * @since 1.0.0
303 + *
304 + * @param int $requirement_id The requirement ID
305 + * @param int $post_id The post ID where requirements are displayed
306 + */
307 + do_action( 'gamipress_requirement_ui_html_after_require_text', $requirement_id, $post_id ); ?>
308 +
309 + <select id="select-trigger-type-<?php echo $requirement_id; ?>" class="select-trigger-type" data-requirement-id="<?php echo $requirement_id; ?>">
310 + <?php
311 + $activity_triggers = gamipress_get_activity_triggers();
312 + ksort( $activity_triggers );
313 +
314 + // Grouped activity triggers
315 + foreach ( $activity_triggers as $group => $group_triggers ) : ?>
316 + <optgroup label="<?php echo esc_attr( $group ); ?>">
317 + <?php foreach( $group_triggers as $trigger => $label ) : ?>
318 + <option value="<?php echo esc_attr( $trigger ); ?>" <?php selected( $requirements['trigger_type'], $trigger, true ); ?>><?php echo $label; ?></option>
319 + <?php endforeach; ?>
320 + </optgroup>
321 + <?php endforeach; ?>
322 + </select>
323 +
324 + <?php
325 + /**
326 + * Available action to add custom HTML after trigger type
327 + *
328 + * @since 1.0.0
329 + *
330 + * @param int $requirement_id The requirement ID
331 + * @param int $post_id The post ID where requirements are displayed
332 + */
333 + do_action( 'gamipress_requirement_ui_html_after_trigger_type', $requirement_id, $post_id ); ?>
334 +
335 + <?php
336 + $points_conditions = gamipress_number_condition_options(); ?>
337 +
338 + <select class="select-points-condition select-points-condition-<?php echo $requirement_id; ?>">
339 + <?php foreach ( $points_conditions as $value => $label ) :
340 + echo '<option value="' . $value . '" ' . selected( $requirements['points_condition'], $value, false ) . '>' . $label . '</option>';
341 + endforeach; ?>
342 + </select>
343 +
344 + <?php
345 + /**
346 + * Available action to add custom HTML after points condition
347 + *
348 + * @since 1.0.0
349 + *
350 + * @param int $requirement_id The requirement ID
351 + * @param int $post_id The post ID where requirements are displayed
352 + */
353 + do_action( 'gamipress_requirement_ui_html_after_points_condition', $requirement_id, $post_id ); ?>
354 +
355 + <input type="number" name="requirement-points-required" id="requirement-<?php echo $requirement_id; ?>-points-required" class="points-required" value="<?php echo ( $requirements['points_required'] === 0 ? 1 : $requirements['points_required'] ); ?>" />
356 +
357 + <?php
358 + /**
359 + * Available action to add custom HTML after points required
360 + *
361 + * @since 1.0.0
362 + *
363 + * @param int $requirement_id The requirement ID
364 + * @param int $post_id The post ID where requirements are displayed
365 + */
366 + do_action( 'gamipress_requirement_ui_html_after_points_required', $requirement_id, $post_id ); ?>
367 +
368 + <input type="text" name="requirement-meta-key-required" id="requirement-<?php echo $requirement_id; ?>-meta-key-required" class="meta-key-required" value="<?php echo esc_attr( $requirements['meta_key_required'] ); ?>" placeholder="<?php echo esc_attr( __( 'Meta key', 'gamipress' ) ); ?>" />
369 + <input type="text" name="requirement-meta-value-required" id="requirement-<?php echo $requirement_id; ?>-meta-value-required" class="meta-value-required" value="<?php echo esc_attr( $requirements['meta_value_required'] ); ?>" placeholder="<?php echo esc_attr( __( 'Meta value', 'gamipress' ) ); ?>" />
370 +
371 + <?php
372 + /**
373 + * Available action to add custom HTML after meta required
374 + *
375 + * @since 1.0.0
376 + *
377 + * @param int $requirement_id The requirement ID
378 + * @param int $post_id The post ID where requirements are displayed
379 + */
380 + do_action( 'gamipress_requirement_ui_html_after_meta_required', $requirement_id, $post_id ); ?>
381 +
382 + <select class="select-points-type-required select-points-type-required-<?php echo $requirement_id; ?>">
383 + <option value=""><?php _e( 'Choose a points type', 'gamipress'); ?></option>
384 + <?php foreach ( gamipress_get_points_types() as $slug => $data ) :
385 +
386 + echo '<option value="' . $slug . '" ' . selected( $requirements['points_type_required'], $slug, false ) . '>' . $data['plural_name'] . '</option>';
387 +
388 + endforeach; ?>
389 + </select>
390 +
391 + <?php
392 + /**
393 + * Available action to add custom HTML after points type required
394 + *
395 + * @since 1.0.0
396 + *
397 + * @param int $requirement_id The requirement ID
398 + * @param int $post_id The post ID where requirements are displayed
399 + */
400 + do_action( 'gamipress_requirement_ui_html_after_points_type_required', $requirement_id, $post_id ); ?>
401 +
402 + <select class="select-rank-type-required select-rank-type-required-<?php echo $requirement_id; ?>">
403 + <?php foreach ( gamipress_get_rank_types() as $slug => $data ) :
404 +
405 + echo '<option value="' . $slug . '" ' . selected( $requirements['rank_type_required'], $slug, false ) . '>' . $data['singular_name'] . '</option>';
406 +
407 + endforeach; ?>
408 + </select>
409 +
410 + <?php
411 + /**
412 + * Available action to add custom HTML after rank type required
413 + *
414 + * @since 1.2.0
415 + *
416 + * @param int $requirement_id The requirement ID
417 + * @param int $post_id The post ID where requirements are displayed
418 + */
419 + do_action( 'gamipress_requirement_ui_html_after_rank_type_required', $requirement_id, $post_id ); ?>
420 +
421 + <select class="select-rank-required select-rank-required-<?php echo $requirement_id; ?>">
422 + <option value=""><?php _e( 'Choose a rank', 'gamipress'); ?></option>
423 + </select>
424 +
425 + <?php
426 + /**
427 + * Available action to add custom HTML after rank required
428 + *
429 + * @since 1.2.0
430 + *
431 + * @param int $requirement_id The requirement ID
432 + * @param int $post_id The post ID where requirements are displayed
433 + */
434 + do_action( 'gamipress_requirement_ui_html_after_rank_required', $requirement_id, $post_id ); ?>
435 +
436 + <?php $post_types = get_post_types( array( 'public' => true ), 'objects' ); ?>
437 +
438 + <select class="select-post-type-required select-post-type-required-<?php echo $requirement_id; ?>">
439 + <option value=""><?php _e( 'Choose a post type', 'gamipress'); ?></option>
440 + <?php foreach( $post_types as $post_type => $post_type_args ) : ?>
441 + <option value="<?php echo esc_attr( $post_type ); ?>" <?php selected( $requirements['post_type_required'], $post_type ); ?>><?php echo $post_type_args->labels->singular_name ; ?></option>
442 + <?php endforeach; ?>
443 + </select>
444 +
445 + <?php
446 + /**
447 + * Available action to add custom HTML after post type field
448 + *
449 + * @since 1.0.0
450 + *
451 + * @param int $requirement_id The requirement ID
452 + * @param int $post_id The post ID where requirements are displayed
453 + */
454 + do_action( 'gamipress_requirement_ui_html_after_post_type_required', $requirement_id, $post_id ); ?>
455 +
456 + <select class="select-user-role-required select-user-role-required-<?php echo $requirement_id; ?>">
457 + <?php foreach ( gamipress_get_editable_roles() as $role => $details ) :
458 +
459 + echo '<option value="' . $role . '" ' . selected( $requirements['user_role_required'], $role, false ) . '>' . translate_user_role( $details['name'] ) . '</option>';
460 +
461 + endforeach; ?>
462 + </select>
463 +
464 + <?php
465 + /**
466 + * Available action to add custom HTML after user role required
467 + *
468 + * @since 1.8.8
469 + *
470 + * @param int $requirement_id The requirement ID
471 + * @param int $post_id The post ID where requirements are displayed
472 + */
473 + do_action( 'gamipress_requirement_ui_html_after_user_role_required', $requirement_id, $post_id ); ?>
474 +
475 + <select class="select-achievement-type select-achievement-type-<?php echo $requirement_id; ?>">
476 + <option value=""><?php _e( 'Choose an achievement type', 'gamipress'); ?></option>
477 + <?php foreach ( gamipress_get_achievement_types() as $slug => $data ) :
478 +
479 + echo '<option value="' . $slug . '" ' . selected( $requirements['achievement_type'], $slug, false ) . '>' . $data['plural_name'] . '</option>';
480 +
481 + endforeach; ?>
482 + </select>
483 +
484 + <?php
485 + /**
486 + * Available action to add custom HTML after achievement type
487 + *
488 + * @since 1.0.0
489 + *
490 + * @param int $requirement_id The requirement ID
491 + * @param int $post_id The post ID where requirements are displayed
492 + */
493 + do_action( 'gamipress_requirement_ui_html_after_achievement_type', $requirement_id, $post_id ); ?>
494 +
495 + <select class="select-achievement-post select-achievement-post-<?php echo $requirement_id; ?>">
496 + <option value=""><?php _e( 'Choose an achievement', 'gamipress'); ?></option>
497 + </select>
498 +
499 + <?php // For multisite installs, we need to store site ID of attached post ?>
500 + <input type="hidden" class="select-post-site-id" value="<?php echo $requirements['achievement_post_site_id']; ?>" />
501 +
502 + <select class="select-post select-post-<?php echo $requirement_id; ?>">
503 + <?php if( ! empty( $requirements['achievement_post'] ) ) :
504 + $achievement_post_title = gamipress_get_specific_activity_trigger_post_title( $requirements['achievement_post'], $requirements['trigger_type'], $requirements['achievement_post_site_id'] ); ?>
505 + <option value="<?php esc_attr_e( $requirements['achievement_post'] ); ?>" selected="selected"><?php echo $achievement_post_title; ?> (#<?php echo $requirements['achievement_post']; ?>)</option>
506 + <?php endif; ?>
507 + </select>
508 +
509 + <?php
510 + /**
511 + * Available action to add custom HTML after achievement post field
512 + *
513 + * @since 1.0.0
514 + *
515 + * @param int $requirement_id The requirement ID
516 + * @param int $post_id The post ID where requirements are displayed
517 + */
518 + do_action( 'gamipress_requirement_ui_html_after_achievement_post', $requirement_id, $post_id ); ?>
519 +
520 + <input class="count" type="number" min="1" value="<?php echo $count; ?>" placeholder="1">
521 + <span class="count-text"><?php _e( 'time(s)', 'gamipress' ); ?></span>
522 +
523 + <?php do_action( 'gamipress_requirement_ui_html_after_count', $requirement_id, $post_id ); ?>
524 +
525 + <span class="limit-text"><?php _e( 'limited to', 'gamipress' ); ?></span>
526 + <input class="limit" type="number" min="1" value="<?php echo $limit; ?>" placeholder="1">
527 + <?php
528 + $limit_intervals = array(
529 + 'unlimited' => __( 'Unlimited', 'gamipress' ),
530 + 'minutely' => __( 'Per minute', 'gamipress' ),
531 + 'hourly' => __( 'Per hour', 'gamipress' ),
532 + 'daily' => __( 'Per day', 'gamipress' ),
533 + 'weekly' => __( 'Per week', 'gamipress' ),
534 + 'monthly' => __( 'Per month', 'gamipress' ),
535 + 'yearly' => __( 'Per year', 'gamipress' ),
536 + );
537 +
538 + /**
539 + * Filter available to override limit intervals available
540 + *
541 + * @since 1.9.0
542 + *
543 + * @param array $limit_intervals Default limit intervals (unlimited, hourly, daily, weekly, monthly and yearly)
544 + * @param int $requirement_id The requirement ID
545 + * @param int $post_id The post ID where requirements are displayed
546 + *
547 + * @return array
548 + */
549 + $limit_intervals = apply_filters( 'gamipress_requirement_limit_intervals', $limit_intervals, $requirement_id, $post_id );
550 + ?>
551 + <select class="limit-type">
552 + <?php foreach( $limit_intervals as $limit_interval => $limit_interval_label ) : ?>
553 + <option value="<?php echo $limit_interval; ?>" <?php selected( $limit_type, $limit_interval ); ?>><?php echo $limit_interval_label; ?></option>
554 + <?php endforeach; ?>
555 + </select>
556 +
557 + <?php
558 + /**
559 + * Available action to add custom HTML after requirement limit
560 + *
561 + * @since 1.0.0
562 + *
563 + * @param int $requirement_id The requirement ID
564 + * @param int $post_id The post ID where requirements are displayed
565 + */
566 + do_action( 'gamipress_requirement_ui_html_after_limit', $requirement_id, $post_id ); ?>
567 +
568 + <?php // Required points fields
569 + if( in_array( $requirement_type, array( 'points-award', 'points-deduct' ) ) ) :
570 + $points = ! empty( $requirements['points'] ) ? $requirements['points'] : 1;
571 + $points_singular_name = get_post_field( 'post_title', $post_id );
572 + $post_name = get_post_field( 'post_name', $post_id );
573 + $points_type = $requirements['points_type'];
574 + $maximum_earnings = absint( $requirements['maximum_earnings'] );
575 +
576 + // Ensure points type is properly set, if not force update it
577 + if( $points_type !== $post_name ) {
578 + update_post_meta( $requirement_id, '_gamipress_points_type', $post_name );
579 + $points_type = $post_name;
580 + }
581 +
582 + if( ! $points_singular_name ) {
583 + $points_singular_name = __( 'point(s)', 'gamipress' );
584 + } else {
585 + $points_singular_name = strtolower( $points_singular_name . '(s)' );
586 + }
587 + ?>
588 + <div class="requirement-awards">
589 + <label for="requirement-<?php echo $requirement_id; ?>-points"><?php echo ( $requirement_type === 'points-award' ? __( 'Earn', 'gamipress' ) : __( 'Deduct', 'gamipress' ) ); ?>:</label>
590 + <input type="number" name="requirement-points" id="requirement-<?php echo $requirement_id; ?>-points" class="points" value="<?php echo $points; ?>" />
591 + <span class="points-text"><?php echo $points_singular_name; ?></span>
592 + <input type="hidden" name="points_type" value="<?php echo $points_type; ?>">
593 +
594 +
595 + <span class="maximum-earnings-text"><?php echo ( $requirement_type === 'points-award' ? __( 'with a maximum number of times to earn it of', 'gamipress' ) : __( 'with a maximum number of times to deduct it of', 'gamipress' ) ); ?></span>
596 + <input type="number" min="0" name="requirement-maximum-earnings" id="requirement-<?php echo $requirement_id; ?>-maximum-earnings" class="maximum-earnings" value="<?php echo $maximum_earnings; ?>" />
597 + <span class="maximum-earnings-notice"><?php _e( '(0 for no maximum)', 'gamipress' ); ?></span>
598 + </div>
599 +
600 + <?php
601 + /**
602 + * Available action to add custom HTML after requirement points ( just available for points awards and deducts )
603 + *
604 + * @since 1.0.0
605 + *
606 + * @param int $requirement_id The requirement ID
607 + * @param int $post_id The post ID where requirements are displayed
608 + */
609 + do_action( 'gamipress_requirement_ui_html_after_points', $requirement_id, $post_id ); ?>
610 + <?php endif; ?>
611 +
612 + <?php // Optional field
613 + if( in_array( $requirement_type, array( 'step', 'rank-requirement' ) ) ) :
614 + $optional = (bool) $requirements['optional'];
615 + ?>
616 + <div class="requirement-clear"></div>
617 + <div class="requirement-optional">
618 + <label for="requirement-<?php echo $requirement_id; ?>-optional"><?php _e( 'Optional', 'gamipress' ); ?>:</label>
619 + <select class="select-optional select-optional-<?php echo $requirement_id; ?>">
620 + <option value="0" <?php selected( false, $optional ); ?>><?php _e( 'Required', 'gamipress'); ?></option>
621 + <option value="1" <?php selected( true, $optional ); ?>><?php _e( 'Optional', 'gamipress'); ?></option>
622 + </select>
623 + </div>
624 +
625 + <?php
626 + /**
627 + * Available action to add custom HTML after requirement optional ( just available for steps and rank requirements )
628 + *
629 + * @since 1.0.0
630 + *
631 + * @param int $requirement_id The requirement ID
632 + * @param int $post_id The post ID where requirements are displayed
633 + */
634 + do_action( 'gamipress_requirement_ui_html_after_optional', $requirement_id, $post_id ); ?>
635 + <?php endif; ?>
636 +
637 + <div class="requirement-title">
638 + <label for="requirement-<?php echo $requirement_id; ?>-title"><?php _e( 'Label', 'gamipress' ); ?>:</label>
639 + <input type="text" name="requirement-title" id="requirement-<?php echo $requirement_id; ?>-title" class="title" value="<?php echo esc_attr( get_the_title( $requirement_id ) ); ?>" placeholder="<?php echo esc_attr( __( 'Enter the label here or leave it empty to be autogenerated...', 'gamipress' ) ); ?>" />
640 + </div>
641 +
642 + <div class="requirement-url">
643 + <label for="requirement-<?php echo $requirement_id; ?>-url"><?php _e( 'URL', 'gamipress' ); ?>:</label>
644 + <input type="text" id="requirement-<?php echo $requirement_id; ?>-url" class="url" value="<?php echo esc_attr( $requirements['url'] ); ?>" placeholder="https://..." />
645 + </div>
646 +
647 + <?php
648 + /**
649 + * Available action to add custom HTML after requirement URL
650 + *
651 + * @since 1.0.0
652 + *
653 + * @param int $requirement_id The requirement ID
654 + * @param int $post_id The post ID where requirements are displayed
655 + */
656 + do_action( 'gamipress_requirement_ui_html_after_requirement_url', $requirement_id, $post_id ); ?>
657 +
658 + <?php // TODO: Keep here for backward compatibility
659 + /**
660 + * Available action to add custom HTML after requirement title
661 + *
662 + * @since 1.0.0
663 + *
664 + * @param int $requirement_id The requirement ID
665 + * @param int $post_id The post ID where requirements are displayed
666 + */
667 + do_action( 'gamipress_requirement_ui_html_after_requirement_title', $requirement_id, $post_id ); ?>
668 +
669 + <?php
670 + /**
671 + * Available action to add custom HTML at bottom of the requirement HTML
672 + *
673 + * @since 1.0.0
674 + *
675 + * @param int $requirement_id The requirement ID
676 + * @param int $post_id The post ID where requirements are displayed
677 + */
678 + do_action( 'gamipress_requirement_ui_html_requirement_bottom', $requirement_id, $post_id ); ?>
679 +
680 + </li>
681 + <?php
682 +}
683 +
684 +/**
685 + * Define the requirement type to use
686 + *
687 + * @since 1.3.1
688 + *
689 + * @param WP_Post $post
690 + * @param array $metabox
691 + *
692 + * @return string
693 + */
694 +function gamipress_requirements_ui_get_requirement_type( $post, $metabox ) {
695 +
696 + $requirement_type = '';
697 +
698 + if( $post->post_type === 'points-type' ) {
699 + // Requirement type passed through the add_meta_box()
700 + $requirement_type = $metabox['args']['requirement_type'];
701 + } else if( gamipress_is_achievement( $post ) ) {
702 + $requirement_type = 'step';
703 + } else if( gamipress_is_rank( $post ) ) {
704 + $requirement_type = 'rank-requirement';
705 + } else if( isset( $metabox['args'] ) && isset( $metabox['args']['requirement_type'] ) ) {
706 + $requirement_type = $metabox['args']['requirement_type'];
707 + }
708 +
709 + return apply_filters( 'gamipress_requirements_ui_get_requirement_type', $requirement_type, $post );
710 +}
711 +
712 +/**
713 + * Get all the requirements of a given points award
714 + *
715 + * @since 1.0.0
716 + *
717 + * @param integer $point_award_id The given requirement's post ID
718 + *
719 + * @return array|bool An array of all the requirement requirements if it has any, false if not
720 + */
721 +function gamipress_get_points_award_requirements( $point_award_id = 0 ) {
722 +
723 + $requirements = gamipress_get_requirement_object( $point_award_id );
724 +
725 + return apply_filters( 'gamipress_get_points_award_requirements', $requirements, $point_award_id );
726 +}
727 +
728 +/**
729 + * Get all the requirements of a given points deduct
730 + *
731 + * @since 1.3.7
732 + *
733 + * @param integer $point_deduct_id The given requirement's post ID
734 + *
735 + * @return array|bool An array of all the requirement requirements if it has any, false if not
736 + */
737 +function gamipress_get_points_deduct_requirements( $point_deduct_id = 0 ) {
738 +
739 + $requirements = gamipress_get_requirement_object( $point_deduct_id );
740 +
741 + return apply_filters( 'gamipress_get_points_deduct_requirements', $requirements, $point_deduct_id );
742 +}
743 +
744 +/**
745 + * Get all the requirements of a given step
746 + *
747 + * @since 1.0.0
748 + *
749 + * @param integer $step_id The given requirement's post ID
750 + *
751 + * @return array|bool An array of all the requirement requirements if it has any, false if not
752 + */
753 +function gamipress_get_step_requirements( $step_id = 0 ) {
754 +
755 + $requirements = gamipress_get_requirement_object( $step_id );
756 +
757 + return apply_filters( 'gamipress_get_step_requirements', $requirements, $step_id );
758 +}
759 +
760 +/**
761 + * Get all the requirements of a given rank requirement
762 + *
763 + * @since 1.3.1
764 + *
765 + * @param integer $rank_requirement_id The given requirement's post ID
766 + *
767 + * @return array|bool An array of all the requirement requirements if it has any, false if not
768 + */
769 +function gamipress_get_rank_requirement_requirements( $rank_requirement_id = 0 ) {
770 +
771 + $requirements = gamipress_get_requirement_object( $rank_requirement_id );
772 +
773 + return apply_filters( 'gamipress_get_rank_requirement_requirements', $requirements, $rank_requirement_id );
774 +}
775 +
776 +/**
777 + * AJAX Handler for adding a new requirement
778 + *
779 + * @since 1.0.0
780 + */
781 +function gamipress_add_requirement_ajax_handler() {
782 + // Security check, forces to die if not security passed
783 + check_ajax_referer( 'gamipress_admin', 'nonce' );
784 +
785 + // Check user capabilities
786 + if( ! current_user_can( gamipress_get_manager_capability() ) )
787 + wp_send_json_error( __( 'You are not allowed to perform this action.', 'gamipress' ) );
788 +
789 + $post_id = isset( $_POST['post_id'] ) ? absint( $_POST['post_id'] ) : 0;
790 +
791 + $post = get_post( $post_id );
792 +
793 + if( ! $post )
794 + wp_send_json_error( __( 'Invalid post.', 'gamipress' ) );
795 +
796 + $requirement_type = gamipress_requirements_ui_get_requirement_type( $post, array(
797 + 'args' => array(
798 + 'requirement_type' => ( isset( $_POST['requirement_type'] ) ? sanitize_text_field( $_POST['requirement_type'] ) : '' )
799 + )
800 + ) );
801 +
802 + // Create a new requirement post and grab it's ID
803 + $requirement_id = wp_insert_post( array(
804 + 'post_type' => $requirement_type,
805 + 'post_status' => 'publish',
806 + 'post_parent' => $post->ID,
807 + 'menu_order' => 0,
808 + ) );
809 +
810 + // Output the edit requirement html to insert into the requirements meta box
811 + gamipress_requirement_ui_html( $requirement_id, $post->ID );
812 + die;
813 +}
814 +add_action( 'wp_ajax_gamipress_add_requirement', 'gamipress_add_requirement_ajax_handler' );
815 +
816 +/**
817 + * AJAX Handler for duplicate a requirement
818 + *
819 + * @since 1.0.0
820 + */
821 +function gamipress_duplicate_requirement_ajax_handler() {
822 + // Security check, forces to die if not security passed
823 + check_ajax_referer( 'gamipress_admin', 'nonce' );
824 +
825 + // Check user capabilities
826 + if( ! current_user_can( gamipress_get_manager_capability() ) )
827 + wp_send_json_error( __( 'You are not allowed to perform this action.', 'gamipress' ) );
828 +
829 + $post_id = isset( $_POST['post_id'] ) ? absint( $_POST['post_id'] ) : 0;
830 + $requirement_id = isset( $_POST['requirement_id'] ) ? absint( $_POST['requirement_id'] ) : 0;
831 +
832 + $post = get_post( $post_id );
833 +
834 + if( ! $post )
835 + wp_send_json_error( __( 'Invalid post.', 'gamipress' ) );
836 +
837 + $requirement = gamipress_get_requirement_object( $requirement_id );
838 +
839 + // Create a new requirement post and grab it's ID
840 + $clone_requirement_id = wp_insert_post( array(
841 + 'post_title' => gamipress_get_post_field( 'post_title', $requirement['ID'] ),
842 + 'post_type' => gamipress_get_post_field( 'post_type', $requirement['ID'] ),
843 + 'post_status' => 'publish',
844 + 'post_parent' => $post->ID,
845 + 'menu_order' => absint( $post->menu_order ) + 1,
846 + ) );
847 +
848 + // Set the cloned ID to let gamipress_update_requirement() clone the data
849 + $requirement['ID'] = $clone_requirement_id;
850 +
851 + gamipress_update_requirement( $requirement );
852 +
853 + // Reset the trigger cache
854 + gamipress_delete_trigger_cache( $requirement['trigger_type'] );
855 +
856 + // Output the edit requirement html to insert into the requirements meta box
857 + gamipress_requirement_ui_html( $clone_requirement_id, $post->ID );
858 + die;
859 +}
860 +add_action( 'wp_ajax_gamipress_duplicate_requirement', 'gamipress_duplicate_requirement_ajax_handler' );
861 +
862 +/**
863 + * AJAX Handler for deleting a requirement
864 + *
865 + * @since 1.0.0
866 + */
867 +function gamipress_delete_requirement_ajax_handler() {
868 + // Security check, forces to die if not security passed
869 + check_ajax_referer( 'gamipress_admin', 'nonce' );
870 +
871 + // Check user capabilities
872 + if( ! current_user_can( gamipress_get_manager_capability() ) )
873 + wp_send_json_error( __( 'You are not allowed to perform this action.', 'gamipress' ) );
874 +
875 + $requirement_id = isset( $_POST['requirement_id'] ) ? absint( $_POST['requirement_id'] ) : 0;
876 +
877 + // Reset the trigger cache
878 + $trigger = gamipress_get_post_meta( $requirement_id, '_gamipress_trigger_type' );
879 +
880 + gamipress_delete_trigger_cache( $trigger );
881 +
882 + /**
883 + * Action to delete custom requirement
884 + *
885 + * @since 1.0.0
886 + *
887 + * @param int $requirement_id
888 + */
889 + do_action( 'gamipress_delete_requirement', $requirement_id );
890 +
891 + // Delete the requirement post
892 + wp_delete_post( $requirement_id );
893 + die;
894 +}
895 +add_action( 'wp_ajax_gamipress_delete_requirement', 'gamipress_delete_requirement_ajax_handler' );
896 +
897 +/**
898 + * AJAX Handler for saving all requirements
899 + *
900 + * @since 1.0.5
901 + */
902 +function gamipress_update_requirements_ajax_handler() {
903 + // Security check, forces to die if not security passed
904 + check_ajax_referer( 'gamipress_admin', 'nonce' );
905 +
906 + // Check user capabilities
907 + if( ! current_user_can( gamipress_get_manager_capability() ) ) {
908 + wp_send_json_error( __( 'You are not allowed to perform this action.', 'gamipress' ) );
909 + }
910 +
911 + $post_id = isset( $_POST['post_id'] ) ? absint( $_POST['post_id'] ) : 0;
912 + $requirements_limit = 20;
913 + $order = absint( $_POST['loop'] ) * $requirements_limit;
914 +
915 + // Save sequential steps (now placed on requirements UI)
916 + if( isset( $_POST['_gamipress_sequential'] ) && ! empty( $_POST['_gamipress_sequential'] ) ) {
917 + gamipress_update_post_meta( $post_id, '_gamipress_sequential', 'on' );
918 + } else {
919 + gamipress_delete_post_meta( $post_id, '_gamipress_sequential', 'on' );
920 + }
921 +
922 + // Only continue if we have any requirements
923 + if ( isset( $_POST['requirements'] ) && is_array( $_POST['requirements'] ) ) {
924 +
925 + // Setup an array for storing all our requirement titles
926 + // This let's us dynamically update the Label field when requirements are saved
927 + $new_titles = array();
928 + $triggers = array();
929 +
930 + // Loop through each of the created requirements
931 + foreach ( $_POST['requirements'] as $requirement ) {
932 +
933 + $requirement_updated = gamipress_update_requirement( $requirement, $order );
934 +
935 + // Grab the requirement ID
936 + $requirement_id = $requirement['requirement_id'];
937 +
938 + // Add the title to our AJAX return
939 + $new_titles[$requirement_id] = stripslashes( $requirement_updated['title'] );
940 +
941 + // Add the trigger to the triggers array if not added yet
942 + if( ! in_array( $requirement['trigger_type'], $triggers ) ) {
943 + $triggers[] = $requirement['trigger_type'];
944 + }
945 +
946 + // Update order
947 + $order++;
948 +
949 + }
950 +
951 + // Loop all saved triggers to reset their caches
952 + foreach( $triggers as $trigger ) {
953 + // Reset the trigger cache
954 + gamipress_delete_trigger_cache( $trigger );
955 + }
956 +
957 + // Send back all our requirement titles
958 + echo json_encode( $new_titles );
959 +
960 + }
961 +
962 + // We're done here.
963 + die;
964 +}
965 +add_action( 'wp_ajax_gamipress_update_requirements', 'gamipress_update_requirements_ajax_handler' );
966 +
967 +/**
968 + * Save a requirement
969 + *
970 + * @since 1.4.6
971 + *
972 + * @param array $requirement
973 + * @param int $order
974 + *
975 + * @return array The updated requirement object
976 + */
977 +function gamipress_update_requirement( $requirement, $order = 0 ) {
978 +
979 + // Check user capabilities
980 + if( ! current_user_can( gamipress_get_manager_capability() ) ) {
981 +
982 + if( defined( 'DOING_AJAX' ) ) {
983 + wp_send_json_error( __('You are not allowed to perform this action.', 'gamipress') );
984 + } else {
985 + return $requirement;
986 + }
987 +
988 + }
989 +
990 + // Grab all of the relevant values of that requirement
991 + $requirement_id = isset( $requirement['ID'] ) ? absint( $requirement['ID'] ) : absint( $requirement['requirement_id'] );
992 + $requirement_type = gamipress_get_post_type( $requirement_id );
993 + $count = ( ! empty( $requirement['count'] ) ) ? absint( $requirement['count'] ) : 1;
994 + $points_condition = ( ! empty( $requirement['points_condition'] ) ) ? sanitize_text_field( $requirement['points_condition'] ) : 'greater_or_equal';
995 + $points_required = ( ! empty( $requirement['points_required'] ) ) ? absint( $requirement['points_required'] ) : 1;
996 + $points_type_required = ( ! empty( $requirement['points_type_required'] ) ) ? sanitize_text_field( $requirement['points_type_required'] ) : '';
997 + $rank_type_required = ( ! empty( $requirement['rank_type_required'] ) ) ? sanitize_text_field( $requirement['rank_type_required'] ) : '';
998 + $rank_required = ( ! empty( $requirement['rank_required'] ) ) ? absint( $requirement['rank_required'] ) : 0;
999 + $post_type_required = ( ! empty( $requirement['post_type_required'] ) ) ? sanitize_text_field( $requirement['post_type_required'] ) : '';
1000 + $user_role_required = ( ! empty( $requirement['user_role_required'] ) ) ? sanitize_text_field( $requirement['user_role_required'] ) : '';
1001 + $meta_key_required = ( ! empty( $requirement['meta_key_required'] ) ) ? sanitize_text_field( $requirement['meta_key_required'] ) : '';
1002 + $meta_value_required = ( ! empty( $requirement['meta_value_required'] ) ) ? sanitize_text_field( $requirement['meta_value_required'] ) : '';
1003 + $limit = ( ! empty( $requirement['limit'] ) ) ? absint( $requirement['limit'] ) : 1;
1004 + $limit_type = ( ! empty( $requirement['limit_type'] ) ) ? sanitize_text_field( $requirement['limit_type'] ) : 'unlimited';
1005 + $optional = ( isset( $requirement['optional'] ) ) ? (bool) $requirement['optional'] : false;
1006 + $url = ( ! empty( $requirement['url'] ) ) ? sanitize_url( $requirement['url'] ) : '';
1007 + $trigger_type = sanitize_text_field( $requirement['trigger_type'] );
1008 + $achievement_type = sanitize_text_field( $requirement['achievement_type'] );
1009 +
1010 + // Connect the achievement with the requirement
1011 + if( in_array( $trigger_type, array( 'specific-achievement', 'revoke-specific-achievement' ) ) ) {
1012 +
1013 + $achievement_post_id = absint( $requirement['achievement_post'] );
1014 +
1015 + // Update achievement post to check it on rules engine
1016 + update_post_meta( $requirement_id, '_gamipress_achievement_post', $achievement_post_id );
1017 + }
1018 +
1019 + // Specific activity trigger type
1020 + if( in_array( $trigger_type, array_keys( gamipress_get_specific_activity_triggers() ) ) ) {
1021 +
1022 + $achievement_post_id = absint( $requirement['achievement_post'] );
1023 + $achievement_post_site_id = ( empty( $requirement['achievement_post_site_id'] ) ? get_current_blog_id() : absint( $requirement['achievement_post_site_id'] ) );
1024 +
1025 + // Update achievement post to check it on rules engine
1026 + update_post_meta( $requirement_id, '_gamipress_achievement_post', $achievement_post_id );
1027 + update_post_meta( $requirement_id, '_gamipress_achievement_post_site_id', $achievement_post_site_id );
1028 + }
1029 +
1030 + // Update our relevant meta
1031 + update_post_meta( $requirement_id, '_gamipress_points_condition', $points_condition );
1032 + update_post_meta( $requirement_id, '_gamipress_points_required', $points_required );
1033 + update_post_meta( $requirement_id, '_gamipress_points_type_required', $points_type_required );
1034 + update_post_meta( $requirement_id, '_gamipress_rank_type_required', $rank_type_required );
1035 + update_post_meta( $requirement_id, '_gamipress_rank_required', $rank_required );
1036 + update_post_meta( $requirement_id, '_gamipress_post_type_required', $post_type_required );
1037 + update_post_meta( $requirement_id, '_gamipress_user_role_required', $user_role_required );
1038 + update_post_meta( $requirement_id, '_gamipress_meta_key_required', $meta_key_required );
1039 + update_post_meta( $requirement_id, '_gamipress_meta_value_required', $meta_value_required );
1040 + update_post_meta( $requirement_id, '_gamipress_count', $count );
1041 + update_post_meta( $requirement_id, '_gamipress_limit', $limit );
1042 + update_post_meta( $requirement_id, '_gamipress_limit_type', $limit_type );
1043 + update_post_meta( $requirement_id, '_gamipress_trigger_type', $trigger_type );
1044 + update_post_meta( $requirement_id, '_gamipress_achievement_type', $achievement_type );
1045 + update_post_meta( $requirement_id, '_gamipress_optional', ( $optional ? '1' : '0' ) );
1046 + update_post_meta( $requirement_id, '_gamipress_url', $url );
1047 +
1048 + // Specific points award data
1049 + if( $requirement_type === 'points-award' || $requirement_type === 'points-deduct' ) {
1050 + $points = ( ! empty( $requirement['points'] ) ) ? absint( $requirement['points'] ) : 1;
1051 + $points_type = ( ! empty( $requirement['points_type'] ) ) ? sanitize_text_field( $requirement['points_type'] ) : '';
1052 + $maximum_earnings = ( ! $requirement['maximum_earnings'] !== "" ) ? absint( $requirement['maximum_earnings'] ) : 1;
1053 +
1054 + update_post_meta( $requirement_id, '_gamipress_points', $points );
1055 + update_post_meta( $requirement_id, '_gamipress_points_type', $points_type );
1056 + update_post_meta( $requirement_id, '_gamipress_maximum_earnings', $maximum_earnings );
1057 + }
1058 +
1059 + // If a new meta key is in use, delete cache and rebuild it
1060 + if( $meta_key_required !== '' ) {
1061 + gamipress_delete_cache( 'gamipress_meta_keys_in_use' );
1062 + gamipress_get_meta_keys_in_use();
1063 + }
1064 +
1065 + /**
1066 + * Action to store custom requirement data when saved
1067 + *
1068 + * @since 1.0.0
1069 + *
1070 + * @param int $requirement_id
1071 + * @param array $requirement
1072 + */
1073 + do_action( 'gamipress_update_requirement', $requirement_id, $requirement );
1074 +
1075 + if( defined( 'DOING_AJAX' ) ) {
1076 + /**
1077 + * Action to store custom requirement data when saved through ajax
1078 + *
1079 + * @since 1.0.0
1080 + *
1081 + * @param int $requirement_id
1082 + * @param array $requirement
1083 + */
1084 + do_action( 'gamipress_ajax_update_requirement', $requirement_id, $requirement );
1085 + }
1086 +
1087 + // Setup a new title if no set
1088 + $post_title = ! empty( $requirement['title'] ) ? sanitize_text_field( $requirement['title'] ) : gamipress_build_requirement_title( $requirement_id, $requirement );
1089 +
1090 + /**
1091 + * Post title filter
1092 + *
1093 + * @since 1.0.0
1094 + *
1095 + * @param int $post_title The post title
1096 + * @param int $requirement_id The requirement ID
1097 + * @param array $requirement The requirement
1098 + */
1099 + $post_title = apply_filters( 'gamipress_requirement_post_title', $post_title, $requirement_id, $requirement );
1100 +
1101 + // Update our original post with the new title
1102 + wp_update_post( array(
1103 + 'ID' => $requirement_id,
1104 + 'post_title' => $post_title,
1105 + 'post_status' => isset( $requirement['status'] ) ? sanitize_text_field( $requirement['status'] ) : gamipress_get_post_status( $requirement_id ),
1106 + 'menu_order' => absint( $order )
1107 + ) );
1108 +
1109 + return gamipress_get_requirement_object( $requirement_id );
1110 +
1111 +}
1112 +
1113 +/**
1114 + * Save custom fields
1115 + *
1116 + * @since 1.4.6
1117 + *
1118 + * @param int $post_id
1119 + */
1120 +function gamipress_on_save_requirements_post_parent( $post_id ) {
1121 +
1122 + $post_type = gamipress_get_post_type( $post_id );
1123 + $allowed_post_types = array_merge( gamipress_get_achievement_types_slugs(), gamipress_get_rank_types_slugs() );
1124 +
1125 + // Bail if not is an allowed type
1126 + if( ! in_array( $post_type, $allowed_post_types ) ) {
1127 + return;
1128 + }
1129 +
1130 + if ( ! isset( $_POST['_gamipress_sequential_nonce'] )
1131 + || ! wp_verify_nonce( $_POST['_gamipress_sequential_nonce'], '_gamipress_sequential_' . $post_id ) ) {
1132 + return;
1133 + }
1134 +
1135 + // Save sequential steps (now placed on requirements UI)
1136 + if( isset( $_REQUEST['_gamipress_sequential'] ) ) {
1137 + gamipress_update_post_meta( $post_id, '_gamipress_sequential', 'on' );
1138 + } else {
1139 + gamipress_delete_post_meta( $post_id, '_gamipress_sequential', 'on' );
1140 + }
1141 +
1142 +}
1143 +add_action( 'save_post', 'gamipress_on_save_requirements_post_parent' );
1144 +
1145 +/**
1146 + * Generate a requirement title based on his configuration
1147 + *
1148 + * @since 1.0.5
1149 + *
1150 + * @param $requirement_id integer The requirement ID
1151 + * @param $requirement array The requirement array object (Optional)
1152 + *
1153 + * @return string
1154 + */
1155 +function gamipress_build_requirement_title( $requirement_id, $requirement = array() ) {
1156 +
1157 + if( empty( $requirement ) ) {
1158 + $requirement = gamipress_get_requirement_object( $requirement_id );
1159 + }
1160 +
1161 + $requirement_type = gamipress_get_post_type( $requirement_id );
1162 + $points_condition = ( ! empty( $requirement['points_condition'] ) ) ? $requirement['points_condition'] : 'greater_or_equal';
1163 + $points_required = ( ! empty( $requirement['points_required'] ) ) ? absint( $requirement['points_required'] ) : 1;
1164 + $points_type_required = ( ! empty( $requirement['points_type_required'] ) ) ? $requirement['points_type_required'] : '';
1165 + $rank_type_required = ( ! empty( $requirement['rank_type_required'] ) ) ? $requirement['rank_type_required'] : '';
1166 + $rank_required = ( ! empty( $requirement['rank_required'] ) ) ? absint( $requirement['rank_required'] ) : 0;
1167 + $post_type_required = ( ! empty( $requirement['post_type_required'] ) ) ? $requirement['post_type_required'] : '';
1168 + $post_types = get_post_types( array(), 'objects' );
1169 + $post_type_label = ( isset( $post_types[$post_type_required] ) ? strtolower( $post_types[$post_type_required]->labels->singular_name ) : __( 'post', 'gamipress' ) );
1170 + $user_role_required = ( ! empty( $requirement['user_role_required'] ) ) ? $requirement['user_role_required'] : '';
1171 + $roles = gamipress_get_editable_roles();
1172 + $count = ( ! empty( $requirement['count'] ) ) ? absint( $requirement['count'] ) : 1;
1173 + $limit = ( ! empty( $requirement['limit'] ) ) ? absint( $requirement['limit'] ) : 1;
1174 + $limit_type = ( ! empty( $requirement['limit_type'] ) ) ? $requirement['limit_type'] : 'unlimited';
1175 + $trigger_type = $requirement['trigger_type'];
1176 + $achievement_type = $requirement['achievement_type'];
1177 + $meta_key_required = ( isset( $requirement['meta_key_required'] ) ) ? $requirement['meta_key_required'] : '';
1178 + $meta_value_required = ( isset( $requirement['meta_value_required'] ) ) ? $requirement['meta_value_required'] : '';
1179 +
1180 + // Flip between our requirement types and make an appropriate connection
1181 + switch ( $trigger_type ) {
1182 + // Points triggers
1183 + case 'earn-points':
1184 + $title = sprintf( __( 'Earn %s', 'gamipress' ), gamipress_format_points( $points_required, $points_type_required, false ) );
1185 + break;
1186 + case 'points-balance':
1187 + $points_conditions = gamipress_number_condition_options();
1188 +
1189 + $title = sprintf( __( 'Reach a balance %s %s', 'gamipress' ), $points_conditions[$points_condition], gamipress_format_points( $points_required, $points_type_required, false ) );
1190 + break;
1191 + case 'gamipress_expend_points':
1192 + $title = sprintf( __( 'Expend %s', 'gamipress' ), gamipress_format_points( $points_required, $points_type_required, false ) );
1193 + break;
1194 + // Rank triggers
1195 + case 'earn-rank':
1196 + $rank = gamipress_get_post( $rank_required );
1197 +
1198 + $title = sprintf( __( 'Reach %s %s', 'gamipress' ), ( $rank ? gamipress_get_rank_type_singular( $rank->post_type, true ) : '' ), ( $rank ? $rank->post_title : '' ) );
1199 + break;
1200 + case 'revoke-rank':
1201 + $rank = gamipress_get_post( $rank_required );
1202 +
1203 + $title = sprintf( __( 'Get %s %s revoked', 'gamipress' ), ( $rank ? gamipress_get_rank_type_singular( $rank->post_type, true ) : '' ), ( $rank ? $rank->post_title : '' ) );
1204 + break;
1205 + // Achievement triggers
1206 + case 'specific-achievement':
1207 + $title = sprintf( __( 'Unlock "%s"', 'gamipress' ), gamipress_get_specific_activity_trigger_post_title( $requirement['achievement_post'], $trigger_type, $requirement['achievement_post_site_id'] ) );
1208 + break;
1209 + case 'any-achievement':
1210 + $title = sprintf( __( 'Unlock any %s', 'gamipress' ), $achievement_type );
1211 + break;
1212 + case 'all-achievements':
1213 + $title = sprintf( __( 'Unlock all %s', 'gamipress' ), $achievement_type );
1214 + break;
1215 + case 'revoke-specific-achievement':
1216 + $title = sprintf( __( 'Get "%s" revoked', 'gamipress' ), gamipress_get_specific_activity_trigger_post_title( $requirement['achievement_post'], $trigger_type, $requirement['achievement_post_site_id'] ) );
1217 + break;
1218 + case 'revoke-any-achievement':
1219 + $title = sprintf( __( 'Get any %s revoked', 'gamipress' ), $achievement_type );
1220 + break;
1221 + // Post type triggers
1222 + case 'gamipress_new_comment_post_type':
1223 + $title = sprintf( __( 'Comment on a %s', 'gamipress' ), $post_type_label );
1224 + break;
1225 + case 'gamipress_user_post_comment_post_type':
1226 + $title = sprintf( __( 'Get a comment on a %s', 'gamipress' ), $post_type_label );
1227 + break;
1228 + case 'gamipress_spam_comment_post_type':
1229 + $title = sprintf( __( 'Get a comment on a %s marked as spam', 'gamipress' ), $post_type_label );
1230 + break;
1231 + case 'gamipress_publish_post_type':
1232 + $title = sprintf( __( 'Publish a new %s', 'gamipress' ), $post_type_label );
1233 + break;
1234 + case 'gamipress_delete_post_type':
1235 + $title = sprintf( __( 'Delete a %s', 'gamipress' ), $post_type_label );
1236 + break;
1237 + case 'gamipress_post_type_visit':
1238 + $title = sprintf( __( 'Daily visit a %s', 'gamipress' ), $post_type_label );
1239 + break;
1240 + case 'gamipress_user_post_type_visit':
1241 + $title = sprintf( __( 'Get visits on a %s', 'gamipress' ), $post_type_label );
1242 + break;
1243 + // Role triggers
1244 + case 'gamipress_add_specific_role':
1245 + $title = sprintf( __( 'Get added to %s role', 'gamipress' ), translate_user_role( $roles[$user_role_required]['name'] ) );
1246 + break;
1247 + case 'gamipress_set_specific_role':
1248 + $title = sprintf( __( 'Get assigned to %s role', 'gamipress' ), translate_user_role( $roles[$user_role_required]['name'] ) );
1249 + break;
1250 + case 'gamipress_remove_specific_role':
1251 + $title = sprintf( __( 'Get removed from %s role', 'gamipress' ), translate_user_role( $roles[$user_role_required]['name'] ) );
1252 + break;
1253 + // Metas
1254 + case 'gamipress_update_post_meta_any_value':
1255 + $title = sprintf( __( 'Update post meta %s', 'gamipress' ), $meta_key_required );
1256 + break;
1257 + case 'gamipress_update_user_meta_any_value':
1258 + $title = sprintf( __( 'Update user meta %s', 'gamipress' ), $meta_key_required );
1259 + break;
1260 + case 'gamipress_update_post_meta_specific_value':
1261 + $title = sprintf( __( 'Update post meta %s with %s', 'gamipress' ), $meta_key_required, $meta_value_required );
1262 + break;
1263 + case 'gamipress_update_user_meta_specific_value':
1264 + $title = sprintf( __( 'Update user meta %s with %s', 'gamipress' ), $meta_key_required, $meta_value_required );
1265 + break;
1266 + // Default
1267 + default:
1268 + $title = gamipress_get_activity_trigger_label( $trigger_type );
1269 + break;
1270 +
1271 + }
1272 +
1273 + // Specific activity trigger type
1274 + if( in_array( $trigger_type, array_keys( gamipress_get_specific_activity_triggers() ) ) ) {
1275 + $achievement_post_id = absint( $requirement['achievement_post'] );
1276 + $achievement_post_site_id = absint( $requirement['achievement_post_site_id'] );
1277 +
1278 + if( $achievement_post_id ) {
1279 + // Filtered title
1280 + $title = sprintf(
1281 + gamipress_get_specific_activity_trigger_label( $trigger_type ),
1282 + gamipress_get_specific_activity_trigger_post_title( $achievement_post_id, $trigger_type, $achievement_post_site_id )
1283 + );
1284 + }
1285 + }
1286 +
1287 + // Filter to completely override activity trigger label
1288 + $title = apply_filters( 'gamipress_activity_trigger_label', $title, $requirement_id, $requirement );
1289 +
1290 + // Prepend "%d %s for" with the points to title
1291 + if( $requirement_type === 'points-award' || $requirement_type === 'points-deduct' ) {
1292 +
1293 + $points = ( ! empty( $requirement['points'] ) ) ? absint( $requirement['points'] ) : 1;
1294 + $points_type = ( ! empty( $requirement['points_type'] ) ) ? $requirement['points_type'] : '';
1295 + // This var is correctly set but not used to generate the title
1296 + //$maximum_earnings = ( ! empty( $requirement['maximum_earnings'] ) ) ? absint( $requirement['maximum_earnings'] ) : 1;
1297 +
1298 + if ( $points > 0 ) {
1299 +
1300 + $points_title = '';
1301 +
1302 + if( $requirement_type === 'points-award' ) {
1303 + $points_title = sprintf( __( '%s for', 'gamipress' ), gamipress_format_points( $points, $points_type, false ) ); // 1 Points for
1304 + } else if( $requirement_type === 'points-deduct' ) {
1305 + $points_title = sprintf( __( '-%s for', 'gamipress' ), gamipress_format_points( $points, $points_type, false ) ); // -1 Points for
1306 + }
1307 +
1308 + // Prepend the points title to the title
1309 + $title = sprintf( __( '%1$s %2$s', 'gamipress' ), $points_title, lcfirst( $title ) );
1310 +
1311 + }
1312 +
1313 + }
1314 +
1315 + // Check if trigger is excluded from activity limits
1316 + if( ! in_array( $trigger_type, gamipress_get_activity_triggers_excluded_from_activity_limit() ) ) {
1317 + // Add "%d time(s)" to title
1318 + $title = sprintf( __( '%1$s %2$s', 'gamipress' ), $title, sprintf( _n( '%d time', '%d times', $count, 'gamipress' ), $count ) );
1319 + }
1320 +
1321 + // Add "(limited to %d per %s)" to title if is limited
1322 + if( $limit_type !== 'unlimited' ) {
1323 +
1324 + $limit_type_label = '';
1325 +
1326 + switch( $limit_type ) {
1327 + case 'minutely':
1328 + $limit_type_label = __( 'minute', 'gamipress' );
1329 + break;
1330 + case 'hourly':
1331 + $limit_type_label = __( 'hour', 'gamipress' );
1332 + break;
1333 + case 'daily':
1334 + $limit_type_label = __( 'day', 'gamipress' );
1335 + break;
1336 + case 'weekly':
1337 + $limit_type_label = __( 'week', 'gamipress' );
1338 + break;
1339 + case 'monthly':
1340 + $limit_type_label = __( 'month', 'gamipress' );
1341 + break;
1342 + case 'yearly':
1343 + $limit_type_label = __( 'year', 'gamipress' );
1344 + break;
1345 + }
1346 +
1347 + $title = sprintf( __( '%1$s %2$s', 'gamipress' ), $title, sprintf( __( '(limited to %d per %s)', 'gamipress' ), $limit, $limit_type_label ) );
1348 + }
1349 +
1350 + $optional = ( isset( $requirement['optional'] ) ) ? (bool) $requirement['optional'] : false;
1351 +
1352 + if( $optional ) {
1353 + $title = sprintf( __( '%1$s %2$s', 'gamipress' ), $title, __( '(Optional)', 'gamipress' ) );
1354 + }
1355 +
1356 + // Available hook for custom Activity Triggers
1357 + return apply_filters( 'gamipress_requirement_title', $title, $requirement_id, $requirement );
1358 +
1359 +}
1360 +
1361 +/**
1362 + * Get the sort order for a given requirement
1363 + *
1364 + * @since 1.0.5
1365 + * @updated 1.5.1
1366 + *
1367 + * @param int $requirement_id The given requirement's post ID
1368 + *
1369 + * @return int The requirement's sort order
1370 + */
1371 +function gamipress_get_requirement_menu_order( $requirement_id = 0 ) {
1372 +
1373 + return gamipress_get_post_field( 'menu_order', $requirement_id );
1374 +
1375 +}
1376 +
1377 +/**
1378 + * Helper function for comparing our requirement sort order (used in uasort() in gamipress_create_points_awards_meta_box())
1379 + *
1380 + * @since 1.0.5
1381 + * @updated 1.5.1
1382 + *
1383 + * @param WP_Post $requirement_x The given requirement
1384 + * @param WP_Post $requirement_y The requirement we're comparing against
1385 + *
1386 + * @return int 0 if the order matches, -1 if it's lower, 1 if it's higher
1387 + */
1388 +function gamipress_compare_requirements_order( $requirement_x, $requirement_y ) {
1389 +
1390 + if ( $requirement_x->menu_order == $requirement_y->menu_order )
1391 + return 0;
1392 +
1393 + return ( $requirement_x->menu_order < $requirement_y->menu_order ) ? -1 : 1;
1394 +
1395 +}