/** * Delete a user's progress for an achievement. Essentially, un-reward the achievement for this user. * * @param int $achievement_id Achievement ID * @param int $user_id User ID * @since Achievements (3.0) */ function dpa_delete_achievement_progress($achievement_id, $user_id) { $achievement_id = dpa_get_achievement_id($achievement_id); if (empty($achievement_id) || !dpa_is_achievement($achievement_id)) { return; } $progress_id = dpa_get_progress(array('author' => $user_id, 'fields' => 'ids', 'no_found_rows' => true, 'nopaging' => true, 'numberposts' => 1, 'post_parent' => $achievement_id)); if (empty($progress_id)) { return; } $progress_id = apply_filters('dpa_delete_achievement_progress', array_pop($progress_id), $achievement_id, $user_id); do_action('dpa_before_delete_achievement_progress', $progress_id, $achievement_id, $user_id); wp_delete_post($progress_id, true); // Check that the delete achievement isn't in the user's pending notifications $notifications = dpa_get_user_notifications($user_id); if (isset($notifications[$achievement_id])) { unset($notifications[$achievement_id]); } // Update the user's notifications in case we cleared any above dpa_update_user_notifications($notifications, $user_id); // Decrease user unlocked count dpa_update_user_unlocked_count($user_id, dpa_get_user_unlocked_count($user_id) - 1); /** * If the progress was linked to an achievement that is the same achievement that is stored in * this user's "last unlocked" meta, then clear the "last unlocked" meta, too. */ if ((int) dpa_get_user_last_unlocked($user_id) === $achievement_id) { dpa_update_user_last_unlocked($user_id, 0); } do_action('dpa_after_delete_achievement_progress', $progress_id, $achievement_id, $user_id); }
/** * Has a specific user unlocked a specific achievement? * * @param int $user_id * @param int $achievement_id * @return bool True if user has unlocked the achievement * @since Achievements (3.4) */ function dpa_has_user_unlocked_achievement($user_id, $achievement_id) { if (!dpa_is_user_active($user_id)) { return false; } $achievement_id = dpa_get_achievement_id($achievement_id); if (empty($achievement_id) || !dpa_is_achievement($achievement_id)) { return false; } // Try to fetched an unlocked progress item for this user pair/achievement pair $progress = dpa_get_progress(array('author' => $user_id, 'fields' => 'ids', 'no_found_rows' => true, 'nopaging' => true, 'numberposts' => 1, 'post_parent' => $achievement_id, 'post_status' => dpa_get_unlocked_status_id())); return apply_filters('dpa_has_user_unlocked_achievement', !empty($progress), $progress, $user_id, $achievement_id); }
/** * How many times this Achievement has been unlocked? * * @since 2.0 * @param int $achievement_id Achievement ID * @return int */ function dpa_get_achievement_unlocked_count($achievement_id = 0) { if (!$achievement_id) { $achievement_id = dpa_get_achievement_id(); } $achievement_meta = dpa_get_achievements_meta(); if (isset($achievement_meta[$achievement_id]) && $achievement_meta[$achievement_id]['no_of_unlocks']) { return apply_filters('dpa_get_achievement_unlocked_count', (int) $achievement_meta[$achievement_id]['no_of_unlocks']); } else { return apply_filters('dpa_get_achievement_unlocked_count', 0); } }
/** * Displays achievement notices * * @since Achievements (3.0) */ function dpa_achievement_notices() { // Bail if not viewing an achievement if (!dpa_is_single_achievement()) { return; } // Filter notice text and bail if empty $notice_text = apply_filters('dpa_achievement_notices', '', dpa_get_achievement_id()); if (empty($notice_text)) { return; } dpa_add_error('achievement_notice', $notice_text, 'message'); }
/** * Has the current achievement in the progress loop been unlocked by the current user? * * The "current" user refers to the user in the dpa_has_achievements() loop, which is not necessarily * the currently-logged in user. * * @param int $achievement_id Optional. Achievement ID to check. * @return bool True if achievement has been unlocked * @since Achievements (3.0) */ function dpa_is_achievement_unlocked($achievement_id = 0) { $achievement_id = dpa_get_achievement_id($achievement_id); // Look in the progress posts and match the achievement against a post_parent $progress = wp_filter_object_list(achievements()->progress_query->posts, array('post_parent' => $achievement_id)); $progress = array_shift($progress); $retval = !empty($progress) && dpa_get_unlocked_status_id() === $progress->post_status; return apply_filters('dpa_is_achievement_unlocked', $retval, $achievement_id, $progress); }
/** * Checks if an Achievement's criteria has been met, and if it has, unlock the Achievement. * Achievements with an action count of 0 are, effectively, unlocked each time but only * the points are added to user's total. * * @global object $bp BuddyPress global settings * @param int $user_id * @param string $skip_validation Set to 'force' to skip Achievement validation, e.g. the Achievement is unlocked regardless of its criteria. * @since 2.0 */ function dpa_maybe_unlock_achievement($user_id, $skip_validation = '') { global $bp; $action_count = dpa_get_achievement_action_count(); if (dpa_is_achievement_unlocked() && $action_count > 0) { return; } $counters = get_user_meta($user_id, 'achievements_counters', true); $achievement_id = dpa_get_achievement_id(); $skip_validation = 'force' == $skip_validation; // No point saving a count of 1 if the action_count is 1. $unlocked = false; if (0 === $action_count || 1 == $action_count) { $unlocked = true; } elseif (!$skip_validation) { if (!$counters && !is_array($counters)) { $counters = array(); } $counters[$achievement_id] += apply_filters('dpa_achievement_counter_increment_by', 1); update_user_meta($user_id, 'achievements_counters', $counters); do_action('dpa_achievement_counter_incremented'); } if (!$unlocked && ($skip_validation || $counters[$achievement_id] >= $action_count)) { if (isset($counters[$achievement_id])) { unset($counters[$achievement_id]); update_user_meta($user_id, 'achievements_counters', $counters); } $unlocked = true; } // Update points, insert unlocked record into DB and send notifications. if ($unlocked) { dpa_points_increment(dpa_get_achievement_points(), $user_id); // Let Achievements with action_count == 0, which have already been unlocked, only increment points. if (dpa_is_achievement_unlocked() && 0 === $action_count) { return; } dpa_unlock_achievement($user_id); if (apply_filters('dpa_achievement_unlocked_tell_user', true, $achievement_id, $user_id)) { bp_core_add_notification($achievement_id, $user_id, $bp->achievements->id, 'new_achievement', $user_id); dpa_record_activity($user_id, dpa_format_activity($user_id, $achievement_id), $achievement_id); } do_action('dpa_achievement_unlocked', $achievement_id, $user_id); } }
/** * Reset main query vars and filter 'the_content' to output an Achievements template part as needed. * * @param string $template Optional * @since Achievements (3.0) */ function dpa_template_include_theme_compat($template = '') { // Bail if a root template was already found. This prevents unintended recursive filtering of 'the_content'. if (dpa_is_template_included()) { return $template; } // Bail if shortcodes are unset somehow if (!is_a(achievements()->shortcodes, 'DPA_Shortcodes')) { return $template; } // Achievements archive if (dpa_is_achievement_archive()) { // Page exists where this archive should be $page = dpa_get_page_by_path(dpa_get_root_slug()); // Should we replace the content... if (empty($page->post_content)) { $new_content = achievements()->shortcodes->display_achievements_index(); // ...or use the existing page content? } else { $new_content = apply_filters('the_content', $page->post_content); } // Should we replace the title... if (empty($page->post_title)) { $new_title = dpa_get_achievement_archive_title(); // ...or use the existing page title? } else { $new_title = apply_filters('the_title', $page->post_title); } dpa_theme_compat_reset_post(array('comment_status' => 'closed', 'ID' => !empty($page->ID) ? $page->ID : 0, 'is_archive' => true, 'post_author' => 0, 'post_content' => $new_content, 'post_date' => 0, 'post_status' => 'publish', 'post_title' => $new_title, 'post_type' => dpa_get_achievement_post_type())); // Single Achievement } elseif (dpa_is_single_achievement()) { dpa_theme_compat_reset_post(array('comment_status' => 'closed', 'ID' => dpa_get_achievement_id(), 'is_single' => true, 'post_author' => dpa_get_achievement_author_id(), 'post_content' => achievements()->shortcodes->display_achievement(array('id' => dpa_get_achievement_id())), 'post_date' => 0, 'post_status' => 'publish', 'post_title' => dpa_get_achievement_title(), 'post_type' => dpa_get_achievement_post_type())); // Single user's achievements template } elseif (dpa_is_single_user_achievements()) { dpa_theme_compat_reset_post(array('comment_status' => 'closed', 'ID' => 0, 'is_archive' => true, 'post_author' => 0, 'post_content' => achievements()->shortcodes->display_user_achievements(), 'post_date' => 0, 'post_status' => 'publish', 'post_title' => sprintf(_x("%s's achievements", 'possesive noun', 'achievements'), get_the_author_meta('display_name', dpa_get_displayed_user_id())), 'post_type' => dpa_get_achievement_post_type())); } /** * Bail if the template already matches an Achievements template. This includes * archive-* and single-* WordPress post_type matches (allowing themes to use the * expected format) as well as all other Achievements-specific template files. */ if (dpa_is_template_included()) { return $template; /** * If we are relying on Achievements' built-in theme compatibility to load * the proper content, we need to intercept the_content, replace the * output, and display ours instead. * * To do this, we first remove all filters from 'the_content' and hook * our own function into it, which runs a series of checks to determine * the context, and then uses the built in shortcodes to output the * correct results from inside an output buffer. * * Uses dpa_get_theme_compat_templates() to provide fall-backs that * should be coded without superfluous mark-up and logic (prev/next * navigation, comments, date/time, etc...) * * Hook into the 'dpa_get_achievements_template' to override the array of * possible templates, or 'dpa_achievements_template' to override the result. */ } elseif (dpa_is_theme_compat_active()) { dpa_remove_all_filters('the_content'); $template = dpa_get_theme_compat_templates(); } return apply_filters('dpa_template_include_theme_compat', $template); }