, Ruben Garcia * @since 1.0.0 */ // Exit if accessed directly if( !defined( 'ABSPATH' ) ) exit; /** * Register [gamipress_achievements] shortcode * * @since 1.0.0 */ function gamipress_register_achievements_shortcode() { $achievement_fields = GamiPress()->shortcodes['gamipress_achievement']->fields; unset( $achievement_fields['id'] ); gamipress_register_shortcode( 'gamipress_achievements', array( 'name' => __( 'Achievement List', 'gamipress' ), 'description' => __( 'Display a list of achievements.', 'gamipress' ), 'icon' => 'awards', 'group' => 'gamipress', 'output_callback' => 'gamipress_achievements_shortcode', 'tabs' => array( 'general' => array( 'icon' => 'dashicons-admin-generic', 'title' => __( 'General', 'gamipress' ), 'fields' => array( 'type', 'columns', 'columns_small', 'filter', 'filter_value', 'search', 'search_value', 'load_more', ), ), 'achievement' => array( 'icon' => 'dashicons-awards', 'title' => __( 'Achievement', 'gamipress' ), 'fields' => array_keys( $achievement_fields ), ), 'query' => array( 'icon' => 'dashicons-search', 'title' => __( 'Query', 'gamipress' ), 'fields' => array( 'limit', 'orderby', 'order', 'current_user', 'user_id', 'include', 'exclude', 'wpms', ), ), ), 'fields' => array_merge( array( 'type' => array( 'name' => __( 'Achievement Type(s)', 'gamipress' ), 'tooltip' => __( 'Achievement type(s) to display.', 'gamipress' ), 'label_cb' => 'cmb_tooltip_label_cb', 'shortcode_desc' => __( 'Single or comma-separated list of achievement type(s) to display.', 'gamipress' ), 'type' => 'advanced_select', 'classes' => 'gamipress-selector', 'attributes' => array( 'data-placeholder' => __( 'Default: All', 'gamipress' ), ), 'multiple' => true, 'options_cb' => 'gamipress_options_cb_achievement_types', 'default' => 'all', ), 'columns' => array( 'name' => __( 'Columns', 'gamipress' ), 'tooltip' => __( 'Columns to divide achievements.', 'gamipress' ), 'label_cb' => 'cmb_tooltip_label_cb', 'type' => 'select', 'options' => array( '1' => __( '1 Column', 'gamipress' ), '2' => __( '2 Columns', 'gamipress' ), '3' => __( '3 Columns', 'gamipress' ), '4' => __( '4 Columns', 'gamipress' ), '5' => __( '5 Columns', 'gamipress' ), '6' => __( '6 Columns', 'gamipress' ), ), 'default' => '1' ), 'columns_small' => array( 'name' => __( 'Columns in small screens', 'gamipress' ), 'tooltip' => __( 'Columns to divide achievements in small screens.', 'gamipress' ), 'label_cb' => 'cmb_tooltip_label_cb', 'type' => 'select', 'options' => array( '1' => __( '1 Column', 'gamipress' ), '2' => __( '2 Columns', 'gamipress' ), '3' => __( '3 Columns', 'gamipress' ), '4' => __( '4 Columns', 'gamipress' ), '5' => __( '5 Columns', 'gamipress' ), '6' => __( '6 Columns', 'gamipress' ), ), 'default' => '1' ), 'filter' => array( 'name' => __( 'Show Filter', 'gamipress' ), 'tooltip' => __( 'Display filter input.', 'gamipress' ), 'label_cb' => 'cmb_tooltip_label_cb', 'type' => 'checkbox', 'classes' => 'gamipress-switch', 'default' => 'yes' ), 'filter_value' => array( 'name' => __( 'Initial Filter Value', 'gamipress' ), 'tooltip' => __( 'Set filter initial value. If you hide the filter, user won\'t be able to change this value.', 'gamipress' ), 'label_cb' => 'cmb_tooltip_label_cb', 'type' => 'select', 'options' => array( 'all' => __( 'All Achievements', 'gamipress' ), 'completed' => __( 'Completed Achievements', 'gamipress' ), 'not-completed' => __( 'Not Completed Achievements', 'gamipress' ), ), 'default' => 'all' ), 'search' => array( 'name' => __( 'Show Search', 'gamipress' ), 'tooltip' => __( 'Display a search input.', 'gamipress' ), 'label_cb' => 'cmb_tooltip_label_cb', 'type' => 'checkbox', 'classes' => 'gamipress-switch', 'default' => 'yes' ), 'search_value' => array( 'name' => __( 'Initial Search Value', 'gamipress' ), 'tooltip' => __( 'Set search initial value. If you hide the search, user won\'t be able to change this value.', 'gamipress' ), 'label_cb' => 'cmb_tooltip_label_cb', 'type' => 'text', 'default' => '' ), 'load_more' => array( 'name' => __( 'Show the "Load More" button', 'gamipress' ), 'tooltip' => __( 'Display a load more button.', 'gamipress' ), 'label_cb' => 'cmb_tooltip_label_cb', 'type' => 'checkbox', 'classes' => 'gamipress-switch', 'default' => 'yes' ), 'limit' => array( 'name' => __( 'Limit', 'gamipress' ), 'tooltip' => __( 'Number of achievements to display.', 'gamipress' ), 'label_cb' => 'cmb_tooltip_label_cb', 'type' => 'text', 'default' => 10, ), 'orderby' => array( 'name' => __( 'Order By', 'gamipress' ), 'tooltip' => __( 'Parameter to use for sorting.', 'gamipress' ), 'label_cb' => 'cmb_tooltip_label_cb', 'type' => 'select', 'options' => array( 'menu_order' => __( 'Menu order', 'gamipress' ), 'ID' => __( 'Achievement', 'gamipress' ), 'title' => __( 'Title', 'gamipress' ), 'date' => __( 'Published date', 'gamipress' ), 'modified' => __( 'Last modified date', 'gamipress' ), 'author' => __( 'Author', 'gamipress' ), 'rand' => __( 'Random', 'gamipress' ), 'points_awarded' => __( 'Points awarded', 'gamipress' ), 'points_to_unlock' => __( 'Points to unlock', 'gamipress' ), ), 'default' => 'menu_order', ), 'order' => array( 'name' => __( 'Order', 'gamipress' ), 'tooltip' => __( 'Sort order.', 'gamipress' ), 'label_cb' => 'cmb_tooltip_label_cb', 'type' => 'select', 'options' => array( 'ASC' => __( 'Ascending', 'gamipress' ), 'DESC' => __( 'Descending', 'gamipress' ) ), 'default' => 'ASC', ), 'current_user' => array( 'name' => __( 'Current User', 'gamipress' ), 'tooltip' => __( 'Show achievements earned by the current logged in user.', 'gamipress' ), 'label_cb' => 'cmb_tooltip_label_cb', 'type' => 'checkbox', 'classes' => 'gamipress-switch', ), 'user_id' => array( 'name' => __( 'User', 'gamipress' ), 'tooltip' => __( 'Show achievements earned by a specific user.', 'gamipress' ), 'label_cb' => 'cmb_tooltip_label_cb', 'type' => 'select', 'default' => '', 'options_cb' => 'gamipress_options_cb_users' ), 'include' => array( 'name' => __( 'Include', 'gamipress' ), 'tooltip' => __( 'Achievements to include.', 'gamipress' ), 'label_cb' => 'cmb_tooltip_label_cb', 'shortcode_desc' => __( 'Comma-separated list of specific achievement IDs to include.', 'gamipress' ), 'type' => 'advanced_select', 'multiple' => true, 'classes' => 'gamipress-post-selector', 'attributes' => array( 'data-post-type' => implode( ',', gamipress_get_achievement_types_slugs() ), 'data-placeholder' => __( 'Select achievements', 'gamipress' ), ), 'default' => '', 'options_cb' => 'gamipress_options_cb_posts' ), 'exclude' => array( 'name' => __( 'Exclude', 'gamipress' ), 'tooltip' => __( 'Achievements to exclude.', 'gamipress' ), 'label_cb' => 'cmb_tooltip_label_cb', 'shortcode_desc' => __( 'Comma-separated list of specific achievement IDs to exclude.', 'gamipress' ), 'type' => 'advanced_select', 'multiple' => true, 'classes' => 'gamipress-post-selector', 'attributes' => array( 'data-post-type' => implode( ',', gamipress_get_achievement_types_slugs() ), 'data-placeholder' => __( 'Select achievements', 'gamipress' ), ), 'default' => '', 'options_cb' => 'gamipress_options_cb_posts' ), 'wpms' => array( 'name' => __( 'Include Multisite Achievements', 'gamipress' ), 'tooltip' => __( 'Show achievements from all network sites.', 'gamipress' ), 'label_cb' => 'cmb_tooltip_label_cb', 'type' => 'checkbox', 'classes' => 'gamipress-switch', ), ), $achievement_fields ), ) ); } add_action( 'init', 'gamipress_register_achievements_shortcode' ); /** * Achievement List Shortcode * * @since 1.0.0 * * @param array $atts Shortcode attributes * @param string $content Shortcode content * * @return string HTML markup */ function gamipress_achievements_shortcode( $atts = array(), $content = '' ) { global $gamipress_template_args; // Initialize GamiPress template args global $gamipress_template_args = array(); $shortcode = 'gamipress_achievements'; $atts = shortcode_atts( array_merge( array( // Achievements atts 'type' => 'all', 'columns' => '1', 'columns_small' => '1', 'filter' => 'yes', 'filter_value' => 'all', 'search' => 'yes', 'search_value' => '', 'load_more' => 'yes', 'current_user' => 'no', 'user_id' => '0', 'limit' => '10', 'orderby' => 'menu_order', 'order' => 'ASC', 'include' => '', 'exclude' => '', 'wpms' => 'no', ), gamipress_achievement_shortcode_defaults() ), $atts, $shortcode ); // Single type check to use dynamic template $is_single_type = false; $types = explode( ',', $atts['type'] ); if ( $atts['type'] !== 'all' && count( $types ) === 1 ) { $is_single_type = true; } // --------------------------- // Shortcode Errors // --------------------------- if( $is_single_type ) { // Check if achievement type is valid if ( ! in_array( $atts['type'], gamipress_get_achievement_types_slugs() ) ) return gamipress_shortcode_error( __( 'The type provided isn\'t a valid registered achievement type.', 'gamipress' ), $shortcode ); } else if( $atts['type'] !== 'all' ) { // Let's check if all types provided are wrong $all_types_wrong = true; foreach( $types as $type ) { if ( in_array( $type, gamipress_get_achievement_types_slugs() ) ) $all_types_wrong = false; } // just notify error if all types are wrong if( $all_types_wrong ) return gamipress_shortcode_error( __( 'All types provided aren\'t valid registered achievement types.', 'gamipress' ), $shortcode ); } // --------------------------- // Shortcode Processing // --------------------------- // Enqueue assets gamipress_enqueue_scripts(); // Force to set current user as user ID if( $atts['current_user'] === 'yes' ) { $atts['user_id'] = get_current_user_id(); } else if( absint( $atts['user_id'] ) === 0 ) { $atts['user_id'] = get_current_user_id(); } // Setup a query args to get the first page of achievements $query_args = $atts; // Initializes filter and search values $query_args['filter'] = $atts['filter_value']; $query_args['search'] = isset( $atts['search_value'] ) ? $atts['search_value'] : ''; $query = gamipress_achievements_shortcode_query( $query_args ); // GamiPress template args global // Important! This var need to be set before render the achievements list to prevent get overwritten // by gamipress_render_achievement() used on gamipress_achievements_shortcode_query() $gamipress_template_args = $atts; $gamipress_template_args['query'] = $query; $gamipress_template_args['types'] = $types; // If we're dealing with multiple achievement types if ( 'all' === $gamipress_template_args['type'] ) { $gamipress_template_args['plural_label'] = __( 'achievements', 'gamipress' ); } else { $gamipress_template_args['plural_label'] = ( 1 == count( $types ) && ! empty( $types[0] ) ) ? get_post_type_object( $types[0] )->labels->name : __( 'achievements', 'gamipress' ); } ob_start(); if( $is_single_type ) { gamipress_get_template_part( 'achievements', $atts['type'] ); } else { gamipress_get_template_part( 'achievements' ); } $output = ob_get_clean(); /** * Filter to override shortcode output * * @since 1.6.5 * * @param string $output Final output * @param array $atts Shortcode attributes * @param string $content Shortcode content */ return apply_filters( 'gamipress_achievements_shortcode_output', $output, $atts, $content ); } /** * Achievement List Shortcode Query * * @since 1.4.5 * * @param array $args * * @return array */ function gamipress_achievements_shortcode_query( $args = array() ) { // Setup our AJAX query vars $type = isset( $args['type'] ) ? $args['type'] : false; $limit = isset( $args['limit'] ) ? $args['limit'] : false; $offset = isset( $args['offset'] ) ? $args['offset'] : false; $filter = isset( $args['filter'] ) ? $args['filter'] : false; $search = isset( $args['search'] ) ? $args['search'] : false; $current_user = isset( $args['current_user'] ) ? $args['current_user'] : false; $user_id = isset( $args['user_id'] ) ? $args['user_id'] : false; $orderby = isset( $args['orderby'] ) ? $args['orderby'] : false; $order = isset( $args['order'] ) ? $args['order'] : false; $wpms = isset( $args['wpms'] ) ? $args['wpms'] : false; $include = isset( $args['include'] ) ? $args['include'] : array(); $exclude = isset( $args['exclude'] ) ? $args['exclude'] : array(); $showed_ids = isset( $args['showed_ids'] ) ? $args['showed_ids'] : array(); // On network wide active installs, we need to switch to main blog mostly for posts permalinks and thumbnails $blog_id = gamipress_switch_to_main_site_if_network_wide_active(); // Sanitization $limit = absint( $limit ); $offset = absint( $offset ); $order = gamipress_validate_from_array( strtoupper( $order ), array( 'ASC', 'DESC' ), 'DESC' ); // Turn no attributes to false if( $current_user === 'no' ) $current_user = false; if( $wpms === 'no' ) $wpms = false; // Force to set current user as user ID if( $current_user ) $user_id = get_current_user_id(); // Get the current user if one wasn't specified if( ! $user_id ) $user_id = get_current_user_id(); // Ensure user ID as int $user_id = absint( $user_id ); // Setup template vars $template_args = array( 'user_id' => $user_id, // User ID on achievement is used to meet to which user apply earned checks ); $achievement_fields = GamiPress()->shortcodes['gamipress_achievement']->fields; unset( $achievement_fields['id'] ); // Loop achievement shortcode fields to pass them to the achievement template foreach( $achievement_fields as $field_id => $field_args ) { if( isset( $args[$field_id] ) ) $template_args[$field_id] = $args[$field_id]; } // Convert $type to properly support multiple achievement types if ( $type === 'all') { $type = gamipress_get_achievement_types_slugs(); } else { $type = explode( ',', $type ); } // Prevent empty strings to be turned an array by explode() if ( ! is_array( $include ) && empty( $include ) ) { $include = array(); } // Build $exclude array if ( ! is_array( $exclude ) && empty( $exclude ) ) { $exclude = array(); } // Build $include array if ( ! is_array( $include ) ) { $include = explode( ',', $include ); } // Build $exclude array if ( ! is_array( $exclude ) ) { $exclude = explode( ',', $exclude ); } // Initialize our output and counters $achievements = ''; $achievement_count = 0; $query_count = 0; // Grab our hidden achievements (used to filter the query) $hidden = gamipress_get_hidden_achievement_ids( $type ); if( $wpms && $wpms !== 'no' ) { // If we're polling all sites, grab an array of site IDs $sites = gamipress_get_network_site_ids(); } else { // Otherwise, use only the current site $sites = array( get_current_blog_id() ); } // On network wide active installs, force to just loop main site if( gamipress_is_network_wide_active() ) $sites = array( get_main_site_id() ); // Loop through each site (default is current site only) foreach( $sites as $site_blog_id ) { // If we're not polling the current site, switch to the site we're polling $current_site_blog_id = get_current_blog_id(); if ( $current_site_blog_id != $site_blog_id ) switch_to_blog( $site_blog_id ); // Grab user earned achievements (used to filter the query) $earned_ids = gamipress_get_user_earned_achievement_ids( $user_id, $type ); // If filter is set to load earned achievements and user hasn't earned anything, don't need to continue if( ! ( $filter === 'completed' && empty( $earned_ids ) ) ) { // Query Achievements $query_args = array( 'post_type' => $type, 'orderby' => $orderby, 'order' => $order, 'posts_per_page' => $limit, 'offset' => $offset, 'post_status' => 'publish', 'post__in' => array(), 'post__not_in' => array_diff( $hidden, $earned_ids ) ); // Filter - query completed or non completed achievements if( $filter === 'completed' && ! empty( $earned_ids ) ) { // Include earned achievements $query_args[ 'post__in' ] = $earned_ids; } else if( $filter === 'not-completed' && ! empty( $earned_ids ) ) { // Exclude earned achievements $query_args[ 'post__not_in' ] = array_merge( $hidden, $earned_ids ); } // Include certain achievements if( ! empty( $include ) ) { $query_args[ 'post__not_in' ] = array_diff( $query_args[ 'post__not_in' ], $include ); $query_args[ 'post__in' ] = array_merge( $query_args[ 'post__in' ], $include ); } // Exclude certain achievements if( ! empty( $exclude ) ) { $query_args[ 'post__not_in' ] = array_merge( $query_args[ 'post__not_in' ], $exclude ); } // Search if( $search ) { $query_args[ 's' ] = $search; } // Order By if( in_array( $orderby, array( 'points_awarded', 'points_to_unlock' ) ) ) { $query_args['meta_key'] = ( $orderby === 'points_awarded' ? '_gamipress_points' : '_gamipress_points_to_unlock' ); $query_args['orderby'] = 'meta_value_num'; } // Process already displayed achievements if( ! empty( $showed_ids ) ) { // Exclude already displayed achievements $query_args[ 'post__in' ] = array_diff( $query_args[ 'post__in' ], $showed_ids ); $query_args[ 'post__not_in' ] = array_merge( $query_args[ 'post__not_in' ], $showed_ids ); // Offset not needed since displayed post are getting already excluded unset( $query_args['offset'] ); } // Prevent to display posts excluded if( ! empty( $query_args[ 'post__in' ] ) && ! empty( $query_args[ 'post__not_in' ] ) ) { $query_args[ 'post__in' ] = array_diff( $query_args[ 'post__in' ], $query_args[ 'post__not_in' ] ); } /** * Filters achievements list query args * * @since 1.5.9 * * @param array $query_args Query args to be passed to WP_Query * @param array $args Function received args (Note: to pass your own args on achievements list request, check JS event 'gamipress_achievements_list_request_data') * * @return array */ $query_args = apply_filters( 'gamipress_achievements_shortcode_query_args', $query_args, $args ); // Query achievements $achievement_posts = new WP_Query( $query_args ); $query_count = absint( $achievement_posts->found_posts ); // Add displayed post to the count since they have been excluded on the query if( ! empty( $showed_ids ) ) $query_count += count( $showed_ids ); // Loop achievements found while( $achievement_posts->have_posts() ) : $achievement_posts->the_post(); // Render the achievement passing the template args $achievements .= gamipress_render_achievement( get_the_ID(), $template_args ); $achievement_count++; endwhile; } // Display a message for no results if( empty( $achievements ) && $offset === 0 ) { $current = current( $type ); // If we have exactly one achievement type, get its plural name, otherwise use "achievements" $post_type_plural = ( count( $type ) == 1 && ! empty( $current ) ) ? gamipress_get_achievement_type_plural( $current ) : __( 'achievements' , 'gamipress' ); // Setup our completion message $achievements .= '
'; if ( 'completed' === $filter ) $no_results_text = sprintf( __( 'No completed %s to display.', 'gamipress' ), strtolower( $post_type_plural ) ); else if ( 'not-completed' === $filter ) $no_results_text = sprintf( __( 'You completed all %s.', 'gamipress' ), strtolower( $post_type_plural ) ); else $no_results_text = sprintf( __( 'No %s to display.', 'gamipress' ), strtolower( $post_type_plural ) ); /** * Filter achievements no results text * * @since 1.0.0 * * @param string $no_results_text * * @return string */ $no_results_text = apply_filters( 'gamipress_achievements_no_results_text', $no_results_text ); $achievements .= '

' . $no_results_text . '

'; $achievements .= '
'; } // Come back to current blog if( $current_site_blog_id != $site_blog_id && is_multisite() ) restore_current_blog(); } // If switched to blog, return back to que current blog if( $blog_id !== get_current_blog_id() && is_multisite() ) restore_current_blog(); $query = array( 'achievements' => $achievements, 'offset' => $offset + $limit, 'query_count' => $query_count, 'achievement_count' => $achievement_count, ); /** * Filter the achievements query * * @since 1.6.5 * * @param array $response * @param array $args * * @return array */ return apply_filters( 'gamipress_achievements_shortcode_query', $query, $args ); }