, Ruben Garcia * @since 1.0.5 */ // Exit if accessed directly if( !defined( 'ABSPATH' ) ) exit; /** * Check if post is a registered GamiPress requirement. * * @since 1.3.0 * * @param object|int $post Post object or ID. * @return bool True if post is an requirement, otherwise false. */ function gamipress_is_requirement( $post = null ) { // Assume we are working with a requirement object $return = true; // If post type is NOT a registered requirement type, it cannot be a requirement if ( ! in_array( gamipress_get_post_type( $post ), gamipress_get_requirement_types_slugs() ) ) { $return = false; } // If we pass both previous tests, this is a valid requirement (with filter to override) return apply_filters( 'gamipress_is_requirement', $return, $post ); } /** * Get GamiPress Requirement Types * * Returns a multidimensional array of slug, single name and plural name for all requirement types. * * @since 1.0.5 * * @return array An array of our registered requirement types */ function gamipress_get_requirement_types() { return GamiPress()->requirement_types; } /** * Get GamiPress Requirement Type Slugs * * @since 1.0.5 * * @return array An array of all our registered requirement type slugs (empty array if none) */ function gamipress_get_requirement_types_slugs() { // Assume we have no registered requirement types $requirement_type_slugs = array(); // If we do have any requirement types, loop through each and add their slug to our array foreach ( GamiPress()->requirement_types as $slug => $data ) { $requirement_type_slugs[] = $slug; } // Finally, return our data return $requirement_type_slugs; } /** * Build a requirement object * * @since 1.0.5 * @updated 1.5.1 Added the order key (based on menu_order) * * @param int $requirement_id * * @return array */ function gamipress_get_requirement_object( $requirement_id = 0 ) { $requirement_type = gamipress_get_post_type( $requirement_id ); // Setup our default requirements array, assume we require nothing $requirement = array( 'ID' => $requirement_id, 'title' => gamipress_get_post_field( 'post_title', $requirement_id ), 'order' => gamipress_get_post_field( 'menu_order', $requirement_id ), 'count' => absint( gamipress_get_post_meta( $requirement_id, '_gamipress_count' ) ), 'limit' => absint( gamipress_get_post_meta( $requirement_id, '_gamipress_limit' ) ), 'limit_type' => gamipress_get_post_meta( $requirement_id, '_gamipress_limit_type' ), 'trigger_type' => gamipress_get_post_meta( $requirement_id, '_gamipress_trigger_type' ), 'optional' => (bool) gamipress_get_post_meta( $requirement_id, '_gamipress_optional' ), 'url' => gamipress_get_post_meta( $requirement_id, '_gamipress_url' ), // Points vars 'points_condition' => gamipress_get_post_meta( $requirement_id, '_gamipress_points_condition' ), 'points_required' => absint( gamipress_get_post_meta( $requirement_id, '_gamipress_points_required' ) ), 'points_type_required' => gamipress_get_post_meta( $requirement_id, '_gamipress_points_type_required' ), // Rank vars 'rank_required' => absint( gamipress_get_post_meta( $requirement_id, '_gamipress_rank_required' ) ), 'rank_type_required' => gamipress_get_post_meta( $requirement_id, '_gamipress_rank_type_required' ), // Post type vars 'post_type_required' => gamipress_get_post_meta( $requirement_id, '_gamipress_post_type_required' ), // User role vars 'user_role_required' => gamipress_get_post_meta( $requirement_id, '_gamipress_user_role_required' ), // Achievement vars 'achievement_type' => gamipress_get_post_meta( $requirement_id, '_gamipress_achievement_type' ), 'achievement_post' => absint( gamipress_get_post_meta( $requirement_id, '_gamipress_achievement_post' ) ), 'achievement_post_site_id' => get_current_blog_id(), // Meta vars 'meta_key_required' => gamipress_get_post_meta( $requirement_id, '_gamipress_meta_key_required' ), 'meta_value_required' => gamipress_get_post_meta( $requirement_id, '_gamipress_meta_value_required' ), ); // Initialize points condition for backward compatibility if( empty( $requirement['points_condition'] ) ) { $requirement['points_condition'] = 'greater_or_equal'; } // Specific points award/deduct data if( $requirement_type === 'points-award' || $requirement_type === 'points-deduct' ) { $requirement['points'] = absint( gamipress_get_post_meta( $requirement_id, '_gamipress_points' ) ); $requirement['points_type'] = gamipress_get_post_meta( $requirement_id, '_gamipress_points_type' ); $requirement['maximum_earnings'] = gamipress_get_post_meta( $requirement_id, '_gamipress_maximum_earnings' ); if( $requirement['maximum_earnings'] === '' ) { $requirement['maximum_earnings'] = 0; } else { $requirement['maximum_earnings'] = absint( $requirement['maximum_earnings'] ); } } // Check the achievement post site ID if ( in_array( $requirement['trigger_type'], array_keys( gamipress_get_specific_activity_triggers() ) ) ) { if ( $requirement['achievement_post'] > 0 ) { $achievement_post_site_id = gamipress_get_post_meta( $requirement_id, '_gamipress_achievement_post_site_id' ); if( ! empty( $achievement_post_site_id ) ) { $requirement['achievement_post_site_id'] = absint( $achievement_post_site_id ); } } } // Available filter for overriding elsewhere return apply_filters( 'gamipress_requirement_object', $requirement, $requirement_id ); } /** * Helper function to query requirements * * @since 1.8.8 * * @param array $query_args * * @return array|object|null */ function gamipress_query_requirements( $query_args = array() ) { $query_args = wp_parse_args( $query_args, array( 'fields' => 'all', 'post_status' => 'publish', 'meta_query' => array(), ) ); global $wpdb; $posts = GamiPress()->db->posts; $postmeta = GamiPress()->db->postmeta; // SELECT $select = 'SELECT *'; // Setup fields to return if( $query_args['fields'] === 'ids' ) { $select = 'SELECT p.ID'; } // FROM $from = array( "FROM {$posts} AS p" ); // WHERE $where = array( '1=1' ); // Post type $requirement_types = gamipress_get_requirement_types_slugs(); $where[] = "p.post_type IN ( '" . implode( "', '", $requirement_types ) . "' )"; // Post status $where[] = "p.post_status = '" . $query_args['post_status'] . "'"; // Meta query if( is_array( $query_args['meta_query'] ) && ! empty( $query_args['meta_query'] ) ) { $i = 1; foreach ( $query_args['meta_query'] as $meta_key => $meta_value ) { $meta_key = sanitize_text_field( $meta_key ); $from[] = "LEFT JOIN {$postmeta} AS pm{$i} ON ( p.ID = pm{$i}.post_id AND pm{$i}.meta_key = '{$meta_key}' )"; // Check for array meta values if( is_array( $meta_value ) ) { foreach( $meta_value as $k => $value ) { $meta_value[$k] = sanitize_text_field( $value ); } $where[] = "pm{$i}.meta_value IN ( '" . implode( "', '", $meta_value ) . "' )"; } else { $meta_value = sanitize_text_field( $meta_value ); $where[] = "pm{$i}.meta_value = '{$meta_value}'"; } $i++; } } // ORDER BY $order_by = array( "p.menu_order ASC" ); // Setup vars $from = implode( ' ', $from ); $where = "WHERE " . implode( ' AND ', $where ); $order_by = "ORDER BY " . implode( ', ', $order_by ); // Get all requirements with this trigger and post assigned $results = $wpdb->get_results( "{$select} {$from} {$where} {$order_by}" ); return $results; } /** * Helper function to determine if user is able to earn a requirement * * @since 1.9.0 * * @param int $requirement_id The requirement ID * @param int $user_id The user ID * * @return bool */ function gamipress_can_user_earn_requirement( $requirement_id = 0, $user_id = 0 ) { if( $user_id === 0 ) { $user_id = get_current_user_id(); } $post_type = gamipress_get_post_type( $requirement_id ); $can_earn = false; switch( $post_type ) { case 'points-award': case 'points-deduct': $maximum_earnings = absint( gamipress_get_post_meta( $requirement_id, '_gamipress_maximum_earnings' ) ); // An unlimited maximum of earnings means points deducts could be earned anyway if( $maximum_earnings > 0 ) { $earned_times = gamipress_get_earnings_count( array( 'user_id' => absint( $user_id ), 'post_id' => absint( $requirement_id ), ) ); // User has earned it more times than required times, so is earned if( $earned_times < $maximum_earnings ) { $can_earn = true; } } else { // If maximum earnings is unlimited, then can be earned $can_earn = true; } break; case 'step': $achievement = gamipress_get_step_achievement( $requirement_id ); // First check if user has not exceeded the maximum earnings allowed for this achievement if( $achievement && ! gamipress_achievement_user_exceeded_max_earnings( $user_id, $achievement->ID ) ) { // Next, check if user has earned the step based on the last time he has completed the achievement if( gamipress_get_earnings_count( array( 'user_id' => $user_id, 'post_id' => $requirement_id, 'since' => gamipress_achievement_last_user_activity( $achievement->ID, $user_id ) ) ) === 0 ) { $can_earn = true; } } break; case 'rank-requirement': $rank = gamipress_get_rank_requirement_rank( $requirement_id ); // First check if user has earned the rank if( $rank && ! gamipress_has_user_earned_achievement( $rank->ID, $user_id ) ) { // Next, check if has earned the rank requirement if( ! gamipress_has_user_earned_achievement( $requirement_id, $user_id ) ) { $can_earn = true; } } break; } // Check if user deserves limit requirements for this requirement if( $can_earn ) { $trigger_type = gamipress_get_post_meta( $requirement_id, '_gamipress_trigger_type' ); if( ! in_array( $trigger_type, gamipress_get_activity_triggers_excluded_from_activity_limit() ) ) { // Check if is limited over time $since = gamipress_get_achievement_limit_timestamp( $requirement_id ); if( $since > 0 ) { // Activity count limit over time $count = absint( gamipress_get_post_meta( $requirement_id, '_gamipress_count' ) ); // Activity count limited to a timestamp $activity_count = absint( gamipress_get_achievement_activity_count_limited( $user_id, $requirement_id, $since ) ); // Force bail if user exceeds the limit over time if( $activity_count >= $count ) { $can_earn = false; } } } } /** * Filter available to override if user is able to earn a requirement * * @since 1.9.0 * * @param bool $can_earn If user can earn this requirement or not * @param int $requirement_id The requirement ID * @param int $user_id The user ID * * @return bool */ return apply_filters( 'gamipress_can_user_earn_requirement', $can_earn, $requirement_id, $user_id ); } /** * Get the reward requirements count * * @since 1.0.0 * * @param integer $post_id The reward ID to check (achievement, rank or points type) * @param string $post_type The children post type (step, rank-requirement, points-award or points-deduct) * * @return integer */ function gamipress_get_reward_requirements_count( $post_id, $post_type = '' ) { global $wpdb; $post_id = absint( $post_id ); // Bail if post ID not valid if( $post_id === 0 ) return 0; // Bail if post type is not a valid requirement type if( ! in_array( $post_type, gamipress_get_requirement_types_slugs() ) ) return 0; $cache = gamipress_get_cache( 'reward_requirement_count', array(), false ); // If result already cached, return it if( isset( $cache[$post_id] ) && isset( $cache[$post_id][$post_type] ) ) { return absint( $cache[$post_id][$post_type] ); } $posts_table = GamiPress()->db->posts; $count = absint( $wpdb->get_var( "SELECT COUNT(*) FROM {$posts_table} WHERE 1=1 AND post_parent = '{$post_id}' AND post_type = '{$post_type}' AND post_status = 'publish'" ) ); // Cache function result if( ! isset( $cache[$post_id] ) ){ $cache[$post_id] = array(); } $cache[$post_id][$post_type] = $count; gamipress_set_cache( 'reward_requirement_count', $cache ); // Return requirements count return $count; } /** * Get the achievement steps count * * @since 1.0.0 * * @param integer $achievement_id * * @return integer */ function gamipress_get_achievement_steps_count( $achievement_id ) { return gamipress_get_reward_requirements_count( $achievement_id, 'step' ); } /** * Get the rank requirements count * * @since 1.0.0 * * @param integer $rank_id * * @return integer */ function gamipress_get_rank_requirements_count( $rank_id ) { return gamipress_get_reward_requirements_count( $rank_id, 'rank-requirement' ); } /** * Get the points awards count * * @since 1.0.0 * * @param integer $points_type_id * * @return integer */ function gamipress_get_points_awards_count( $points_type_id ) { return gamipress_get_reward_requirements_count( $points_type_id, 'points-award' ); } /** * Get the points deducts count * * @since 1.0.0 * * @param integer $points_type_id * * @return integer */ function gamipress_get_points_deducts_count( $points_type_id ) { return gamipress_get_reward_requirements_count( $points_type_id, 'points-deduct' ); }