/** * Award an achievement to a user * * @alias add * @since Achievements (3.4) * @synopsis --user_id=<id> --achievement=<postname> */ public function award($args, $assoc_args) { if (!$assoc_args['user_id'] || !get_userdata($assoc_args['user_id'])) { WP_CLI::error('Invalid User ID specified.'); } // Get the achievement ID $achievement_id = $this->_get_achievement_id_by_post_name($assoc_args['achievement']); if (!$achievement_id) { WP_CLI::error(sprintf('Achievement ID not found for post_name: %1$s', $achievement_id)); } // If the user has already unlocked this achievement, bail out. if (dpa_has_user_unlocked_achievement($assoc_args['user_id'], $achievement_id)) { WP_CLI::warning(sprintf('User ID %1$s has already unlocked achievement ID %2$s', $assoc_args['user_id'], $achievement_id)); return; } $achievement_obj = dpa_get_achievements(array('no_found_rows' => true, 'nopaging' => true, 'numberposts' => 1, 'p' => $achievement_id)); $achievement_obj = $achievement_obj[0]; // Find any still-locked progress for this achievement for this user, as dpa_maybe_unlock_achievement() needs it. $progress_obj = dpa_get_progress(array('author' => $assoc_args['user_id'], 'no_found_rows' => true, 'nopaging' => true, 'numberposts' => 1, 'post_status' => dpa_get_locked_status_id())); if (empty($progress_obj)) { $progress_obj = array(); } // Award the achievement dpa_maybe_unlock_achievement($assoc_args['user_id'], 'skip_validation', $progress_obj, $achievement_obj); WP_CLI::success(sprintf('Achievement ID %1$s has been awarded to User ID %2$s', $achievement_id, $assoc_args['user_id'])); }
/** * The PHP side of Achievements' live notifications system using WordPress 3.6's heartbeat API; we grab the image, * post ID, permalink, and title of all achievements that have recently been unlocked, and send that back using * WordPress' heartbeat_recieved filter. * * The heartbeat JS makes periodic AJAX connections back to WordPress. WordPress sees those requests, and fires the * heartbeat_recieved filter. The filter allows plugins to change the server's response before it's sent back to * the originating user's browser. * * @param array $response The data we want to send back to user whose heart beat. * @param array $data An array of $_POST data received from the originating AJAX request. * @return array The data we want to send back to user. * @since Achievements (3.5) */ public static function notifications_heartbeat_response($response, $data) { // Bail if user is not active, or $data isn't in the expected format if (!dpa_is_user_active() || !isset($data['achievements']) || !is_array($data['achievements'])) { return $response; } $ids = array_keys(dpa_get_user_notifications()); if (empty($ids)) { return $response; } // If multisite and running network-wide, switch_to_blog to the data store site if (is_multisite() && dpa_is_running_networkwide()) { switch_to_blog(DPA_DATA_STORE); } $achievements = dpa_get_achievements(array('no_found_rows' => true, 'nopaging' => true, 'post__in' => $ids, 'post_status' => 'any')); $new_response = array(); foreach ($achievements as $achievement) { /** * Check that the post status is published or privately published. We need to check this here to work * around WP_Query not constructing the query correctly with private post statuses. */ if (!in_array($achievement->post_status, array('publish', 'private'))) { continue; } $item = array(); $item['ID'] = $achievement->ID; $item['title'] = esc_html(apply_filters('dpa_get_achievement_title', $achievement->post_title, $achievement->ID)); $item['permalink'] = esc_url_raw(home_url('/?p=' . $achievement->ID)); // Thumbnail is optional and may not be set $thumbnail = get_post_thumbnail_id($achievement->ID); if (!empty($thumbnail)) { $thumbnail = wp_get_attachment_image_src($thumbnail, 'medium'); if ($thumbnail) { $item['image_url'] = esc_url_raw($thumbnail[0]); $item['image_width'] = (int) $thumbnail[1]; } } // Achievements 3.5+ supports showing multiple unlock notifications at the same time $new_response[] = $item; } // If multisite and running network-wide, undo the switch_to_blog if (is_multisite() && dpa_is_running_networkwide()) { restore_current_blog(); } // Clear all pending notifications dpa_update_user_notifications(); $new_response = array_merge($response, array('achievements' => $new_response)); return apply_filters('dpa_theme_compat_notifications_heartbeat_response', $new_response, $ids, $response, $data); }
/** * Handles the redeem achievement form submission. * * Finds any achievements with the specific redemption code, and if the user hasn't already unlocked * that achievement, it's awarded to the user. * * @param string $action Optional. If 'dpa-redeem-achievement', handle the form submission. * @since Achievements (3.1) */ function dpa_form_redeem_achievement($action = '') { if ('dpa-redeem-achievement' !== $action || !dpa_is_user_active()) { return; } // Check required form values are present $redemption_code = isset($_POST['dpa_code']) ? sanitize_text_field(stripslashes($_POST['dpa_code'])) : ''; $redemption_code = apply_filters('dpa_form_redeem_achievement_code', $redemption_code); if (empty($redemption_code) || !dpa_verify_nonce_request('dpa-redeem-achievement')) { return; } // If multisite and running network-wide, switch_to_blog to the data store site if (is_multisite() && dpa_is_running_networkwide()) { switch_to_blog(DPA_DATA_STORE); } // Find achievements that match the same redemption code $achievements = dpa_get_achievements(array('meta_key' => '_dpa_redemption_code', 'meta_value' => $redemption_code)); // Bail out early if no achievements found if (empty($achievements)) { dpa_add_error('dpa_redeem_achievement_nonce', __('That code was invalid. Try again!', 'achievements')); // If multisite and running network-wide, undo the switch_to_blog if (is_multisite() && dpa_is_running_networkwide()) { restore_current_blog(); } return; } $existing_progress = dpa_get_progress(array('author' => get_current_user_id())); foreach ($achievements as $achievement_obj) { $progress_obj = array(); // If we have existing progress, pass that to dpa_maybe_unlock_achievement(). foreach ($existing_progress as $progress) { if ($achievement_obj->ID === $progress->post_parent) { // If the user has already unlocked this achievement, don't give it to them again. if (dpa_get_unlocked_status_id() === $progress->post_status) { $progress_obj = false; } else { $progress_obj = $progress; } break; } } if (false !== $progress_obj) { dpa_maybe_unlock_achievement(get_current_user_id(), 'skip_validation', $progress_obj, $achievement_obj); } } // If multisite and running network-wide, undo the switch_to_blog if (is_multisite() && dpa_is_running_networkwide()) { restore_current_blog(); } }
/** * Update the user's "User Points" meta information when the Edit User page has been saved, * and modify the user's current achievements as appropriate. * * The action that this function is hooked to is only executed on a succesful update, * which is behind a nonce and capability check (see wp-admin/user-edit.php). * * @param int $user_id * @since Achievements (3.0) */ public function save_profile_fields($user_id) { if (!isset($_POST['dpa_achievements']) || !is_super_admin()) { return; } if (!isset($_POST['dpa_user_achievements'])) { $_POST['dpa_user_achievements'] = array(); } // If multisite and running network-wide, switch_to_blog to the data store site if (is_multisite() && dpa_is_running_networkwide()) { switch_to_blog(DPA_DATA_STORE); } // Update user's points dpa_update_user_points((int) $_POST['dpa_achievements'], $user_id); // Get unlocked achievements $unlocked_achievements = dpa_get_progress(array('author' => $user_id, 'post_status' => dpa_get_unlocked_status_id())); $old_unlocked_achievements = wp_list_pluck($unlocked_achievements, 'post_parent'); $new_unlocked_achievements = array_filter(wp_parse_id_list($_POST['dpa_user_achievements'])); // Figure out which achievements to add or remove $achievements_to_add = array_diff($new_unlocked_achievements, $old_unlocked_achievements); $achievements_to_remove = array_diff($old_unlocked_achievements, $new_unlocked_achievements); // Remove achievements :( if (!empty($achievements_to_remove)) { foreach ($achievements_to_remove as $achievement_id) { dpa_delete_achievement_progress($achievement_id, $user_id); } } // Award achievements! :D if (!empty($achievements_to_add)) { // Get achievements to add $new_achievements = dpa_get_achievements(array('post__in' => $achievements_to_add, 'posts_per_page' => count($achievements_to_add))); // Get any still-locked progress for this user $existing_progress = dpa_get_progress(array('author' => $user_id, 'post_status' => dpa_get_locked_status_id())); foreach ($new_achievements as $achievement_obj) { $progress_obj = array(); // If we have existing progress, pass that to dpa_maybe_unlock_achievement(). foreach ($existing_progress as $progress) { if ($achievement_obj->ID === $progress->post_parent) { $progress_obj = $progress; break; } } dpa_maybe_unlock_achievement($user_id, 'skip_validation', $progress_obj, $achievement_obj); } } // If multisite and running network-wide, undo the switch_to_blog if (is_multisite() && dpa_is_running_networkwide()) { restore_current_blog(); } }