/** * Maps 'achievements' post type caps to built-in WordPress caps * * @param array $caps Capabilities for meta capability * @param string $cap Capability name * @param int $user_id User id * @param mixed $args Arguments * @return array Actual capabilities for meta capability * @since Achievements (3.0) */ function dpa_map_meta_caps($caps, $cap, $user_id, $args) { switch ($cap) { // Reading case 'read_achievement': case 'read_achievement_progress': $post = get_post($args[0]); if (!empty($post)) { $caps = array(); $post_type = get_post_type_object($post->post_type); // Public post if ('publish' === $post->post_status) { $caps[] = 'read'; } elseif ((int) $user_id === (int) $post->post_author) { $caps[] = 'read'; } else { $caps[] = $post_type->cap->read_private_posts; } } break; case 'read_achievements': // Add do_not_allow cap if user is spam or deleted if (!dpa_is_user_active($user_id)) { $caps = array('do_not_allow'); } break; // Publishing // Publishing case 'publish_achievements': case 'publish_achievement_progresses': // Add do_not_allow cap if user is spam or deleted if (!dpa_is_user_active($user_id)) { $caps = array('do_not_allow'); } break; // Editing // Editing case 'edit_achievements': case 'edit_achievement_progresses': // Add do_not_allow cap if user is spam or deleted if (!dpa_is_user_active($user_id)) { $caps = array('do_not_allow'); } break; case 'edit_achievement': case 'edit_achievement_progress': $_post = get_post($args[0]); if (!empty($_post)) { // Get caps for post type object $post_type = get_post_type_object($_post->post_type); $caps = array(); // Add 'do_not_allow' cap if user is spam or deleted if (!dpa_is_user_active($user_id)) { $caps[] = 'do_not_allow'; // User is author so allow edit } elseif ((int) $user_id === (int) $_post->post_author) { $caps[] = $post_type->cap->edit_posts; // Unknown, so map to edit_others_posts } else { $caps[] = $post_type->cap->edit_others_posts; } } break; // Deleting // Deleting case 'delete_achievement': case 'delete_achievement_progress': $_post = get_post($args[0]); if (!empty($_post)) { // Get caps for post type object $post_type = get_post_type_object($_post->post_type); $caps = array(); // Add 'do_not_allow' cap if user is spam or deleted if (!dpa_is_user_active($user_id)) { $caps[] = 'do_not_allow'; // User is author so allow to delete } elseif ((int) $user_id === (int) $_post->post_author) { $caps[] = $post_type->cap->delete_posts; // Unknown so map to delete_others_posts } else { $caps[] = $post_type->cap->delete_others_posts; } } break; } return apply_filters('dpa_map_meta_caps', $caps, $cap, $user_id, $args); }
/** * 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); }
/** * Return a fancy description of the achievements on the site, including the * number of public and hidden achievements, and the username and avatar of * the most recent person who unlocked the achievement. * * @param mixed $args This function supports these arguments: * - before: Before the text * - after: After the text * - size: Size of the avatar * @return string Fancy description * @since Achievements (3.0) */ function dpa_get_achievements_index_description($args = '') { $defaults = array('after' => '</p></div>', 'before' => '<div class="dpa-template-notice info"><p class="dpa-achievements-description">', 'size' => 14); $r = dpa_parse_args($args, $defaults, 'get_achievements_index_description'); extract($r); // Get count of total achievements $achievement_count = dpa_get_total_achievement_count(); $achievement_text = sprintf(_n('%s achievement', '%s achievements', $achievement_count, 'achievements'), number_format_i18n($achievement_count)); // Get data on the most recent unlocked achievement $recent_achievement_id = dpa_stats_get_last_achievement_id(); $recent_user_id = dpa_stats_get_last_achievement_user_id(); if (!empty($recent_user_id) && !empty($recent_achievement_id)) { // Check user ID is still valid $user = get_userdata($recent_user_id); if (!empty($user) && dpa_is_user_active($user)) { // Check achievement ID is valid $achievement = get_post($recent_achievement_id); if (!empty($achievement) && 'publish' === $achievement->post_status) { // Combine all the things to build the output text $retstr = sprintf(__('This site has %1$s, and the last unlocked was <a href="%2$s">%3$s</a> by %4$s.', 'achievements'), $achievement_text, esc_url(get_permalink($achievement->ID)), esc_html(apply_filters('dpa_get_achievement_title', $achievement->post_title, $achievement->ID)), dpa_get_user_avatar_link(array('size' => $size, 'user_id' => $user->ID))); } } } // If we haven't set a more specific description, fall back to the default. if (!isset($retstr)) { $retstr = sprintf(__('This site has %1$s.', 'achievements'), $achievement_text); } $retstr = $before . $retstr . $after; return apply_filters('dpa_get_achievements_index_description', $retstr, $args); }
/** * Record an activity stream entry when a user unlocks an achievement * * @param WP_Post $achievement Achievement post object * @param int $user_id * @param int $progress_id * @since Achievements (3.2) */ public function achievement_unlocked(WP_Post $achievement, $user_id, $progress_id) { // Bail if user is not active if (!dpa_is_user_active($user_id)) { return; } // Achievement details $achievement_permalink = dpa_get_achievement_permalink($achievement->ID); $achievement_title = get_post_field('post_title', $achievement->ID, 'raw'); $achievement_link = sprintf('<a href="%1$s">%2$s</a>', esc_url($achievement_permalink), esc_html($achievement_title)); // Activity action & text $activity_text = sprintf(__('%1$s unlocked the achievement: %2$s', 'achievements'), bp_core_get_userlink($user_id), $achievement_link); $activity_action = apply_filters('dpa_activity_achievement_unlocked', $activity_text, $achievement->ID, $user_id, $progress_id); // Record the activity $activity = array('action' => $activity_action, 'content' => '', 'item_id' => $achievement->ID, 'hide_sitewide' => false, 'primary_link' => $achievement_permalink, 'type' => $this->achievement_unlocked, 'user_id' => $user_id); $this->record_activity($activity); }
/** * If the specified achievement's criteria has been met, we unlock the * achievement. Otherwise we record progress for the achievement for next time. * * $skip_validation is the second parameter for backpat with Achievements 2.x * * @param int $user_id * @param string $skip_validation Optional. Set to "skip_validation" to skip Achievement validation (unlock achievement regardless of criteria). * @param object $progress_obj Optional. The Progress post object. Defaults to Progress object in the Progress loop. * @param object $achievement_obj Optional. The Achievement post object to maybe_unlock. Defaults to current object in Achievement loop. * @since Achievements (2.0) */ function dpa_maybe_unlock_achievement($user_id, $skip_validation = '', $progress_obj = null, $achievement_obj = null) { // Only proceed if the specified user is active (logged in and not a spammer) if (!dpa_is_user_active($user_id)) { return; } // Only proceed if the specified user can create progress posts if (!user_can($user_id, 'publish_achievement_progresses')) { return; } // Default to current object in the achievement loop if (empty($achievement_obj)) { $achievement_obj = achievements()->achievement_query->post; } // Default to progress object in the progress loop if (empty($progress_obj) && !empty(achievements()->progress_query->posts)) { $progress_obj = wp_filter_object_list(achievements()->progress_query->posts, array('post_parent' => $achievement_obj->ID)); $progress_obj = array_shift($progress_obj); } // Has the user already unlocked the achievement? if (!empty($progress_obj) && dpa_get_unlocked_status_id() === $progress_obj->post_status) { return; } // Prepare default values to create/update a progress post $progress_args = array('comment_status' => 'closed', 'ping_status' => 'closed', 'post_author' => $user_id, 'post_parent' => $achievement_obj->ID, 'post_title' => $achievement_obj->post_title, 'post_type' => dpa_get_progress_post_type()); // If achievement already has some progress, grab the ID so we update the post later if (!empty($progress_obj->ID)) { $progress_args['ID'] = $progress_obj->ID; } // If the achievement does not have a target set, this is an award achievement. $achievement_target = dpa_get_achievement_target($achievement_obj->ID); if ($achievement_target) { // Increment progress count $progress_args['post_content'] = apply_filters('dpa_maybe_unlock_achievement_progress_increment', 1); if (!empty($progress_obj)) { $progress_args['post_content'] = (int) $progress_args['post_content'] + (int) $progress_obj->post_content; } } // Does the progress count now meet the achievement target? if ('skip_validation' === $skip_validation || $achievement_target && (int) $progress_args['post_content'] >= $achievement_target) { // Yes. Unlock achievement. $progress_args['post_status'] = dpa_get_unlocked_status_id(); // No, user needs to make more progress. Make sure the locked status is set correctly. } else { $progress_args['post_status'] = dpa_get_locked_status_id(); } // Create or update the progress post $progress_id = wp_insert_post($progress_args); // If the achievement was just unlocked, do stuff. if (dpa_get_unlocked_status_id() === $progress_args['post_status']) { // Achievement was unlocked. Notifications and points updates are hooked to this function. do_action('dpa_unlock_achievement', $achievement_obj, $user_id, $progress_id); } }
/** * Output the notification JS templates. * * These will be used with underscore.js' _.template() method. It compiles these JS templates into functions * that can be evaluated for rendering. Useful for rendering complicated bits of HTML from JSON data sources, * which is exactly what we're going to do. * * @since Achievements (3.5) */ public static function print_notification_templates() { // If user's not active or is inside the WordPress Admin, bail out. if (!dpa_is_user_active() || is_admin() || is_404()) { return; } echo achievements()->shortcodes->display_notifications_template(); }
/** * Last unlocked achievement column * * @param WP_User $user A singular item (one full row) * @see WP_List_Table::single_row_columns() * @since Achievements (3.0) */ public function column_dpa_last_id(WP_User $user) { $output = true; // Get this user's most recent unlocked achievement $achievement_id = dpa_get_user_last_unlocked($user->ID); if (empty($achievement_id)) { $output = false; } // Check user ID is valid if ($output && !dpa_is_user_active($user->ID)) { $output = false; } // Check achievement is still valid if ($output) { $achievement = get_post($achievement_id); } if ($output && (empty($achievement) || 'publish' !== $achievement->post_status)) { $output = false; } if ($output) { printf('<a href="%1$s">%2$s</a>', esc_url(get_permalink($achievement->ID)), esc_html(apply_filters('dpa_get_achievement_title', $achievement->post_title, $achievement->ID))); } else { echo '—'; } }
/** * Register custom menu items * * @global WP_Admin_Bar $wp_admin_bar * @since Achievements (3.0) */ public function admin_bar_menu() { global $wp_admin_bar; if (!dpa_is_user_active()) { return; } $wp_admin_bar->add_node(array('href' => dpa_get_user_avatar_link('type=url'), 'id' => 'dpa_my_achievements', 'parent' => 'user-actions', 'title' => _x('My Achievements', 'Menu item in the toolbar', 'achievements'))); }
/** * Maps 'achievements' post type caps to built-in WordPress caps * * @param array $caps Capabilities for meta capability * @param string $cap Capability name * @param int $user_id User id * @param mixed $args Arguments * @return array Actual capabilities for meta capability * @since 3.0 */ function dpa_map_meta_caps($caps, $cap, $user_id, $args) { switch ($cap) { // Reading case 'read_achievement': if ($post = get_post($args[0])) { $caps = array(); $post_type = get_post_type_object($post->post_type); if ('published' == $post->post_status) { $caps[] = 'read'; } else { $caps[] = $post_type->cap->read_private_posts; } } break; // Editing // Editing case 'edit_achievements': // Add do_not_allow cap if user is spam or deleted if (!dpa_is_user_active($user_id)) { $caps = array('do_not_allow'); } break; case 'edit_achievement': if ($post = get_post($args[0])) { $caps = array(); $post_type = get_post_type_object($post->post_type); // Add 'do_not_allow' cap if user is spam or deleted if (!dpa_is_user_active($user_id)) { $caps[] = 'do_not_allow'; } elseif ((int) $user_id == (int) $post->post_author) { $caps[] = $post_type->cap->edit_posts; } else { $caps[] = $post_type->cap->edit_others_posts; } } break; // Deleting // Deleting case 'delete_achievement': if ($post = get_post($args[0])) { $caps = array(); $post_type = get_post_type_object($post->post_type); // Add 'do_not_allow' cap if user is spam or deleted if (!dpa_is_user_active($user_id)) { $caps[] = 'do_not_allow'; } elseif ((int) $user_id == (int) $post->post_author) { $caps[] = $post_type->cap->delete_posts; } else { $caps[] = $post_type->cap->delete_others_posts; } } break; } return apply_filters('dpa_map_meta_caps', $caps, $cap, $user_id, $args); }