feat(badges): auto-award medals from catalog thresholds
Evaluate medal get_type metrics on login/checkin/content and grant matching badges; optional search-history slot on search widget.
This commit is contained in:
@@ -0,0 +1,168 @@
|
||||
<?php
|
||||
/**
|
||||
* Auto-grant medals from catalog thresholds (zibll zib_auto_add_user_medal lite).
|
||||
*
|
||||
* @package AKSRT
|
||||
*/
|
||||
|
||||
namespace AKSRT\User;
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
class BadgeAutoAward {
|
||||
|
||||
private static $instance = null;
|
||||
|
||||
public static function get_instance(): self {
|
||||
if (null === self::$instance) {
|
||||
self::$instance = new self();
|
||||
}
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
public function init(): void {
|
||||
// Run after login / current user bootstrap.
|
||||
add_action('wp_login', [$this, 'on_login'], 20, 2);
|
||||
add_action('wp', [$this, 'maybe_for_current_user'], 30);
|
||||
// Cheap hooks for common counters.
|
||||
add_action('publish_post', [$this, 'on_content_change'], 20, 1);
|
||||
add_action('comment_post', [$this, 'on_content_change'], 20, 1);
|
||||
add_action('aksrt_user_followed', [$this, 'on_follow'], 20, 2);
|
||||
add_action('aksrt_checkin_done', [$this, 'on_checkin'], 20, 1);
|
||||
}
|
||||
|
||||
public function on_login($user_login, $user): void {
|
||||
if ($user instanceof \WP_User) {
|
||||
$this->evaluate((int) $user->ID);
|
||||
}
|
||||
}
|
||||
|
||||
public function maybe_for_current_user(): void {
|
||||
if (!is_user_logged_in() || is_admin()) {
|
||||
return;
|
||||
}
|
||||
// Throttle: once per 10 minutes per user.
|
||||
$uid = get_current_user_id();
|
||||
$key = 'aksrt_badge_auto_' . $uid;
|
||||
if (get_transient($key)) {
|
||||
return;
|
||||
}
|
||||
set_transient($key, 1, 10 * MINUTE_IN_SECONDS);
|
||||
$this->evaluate($uid);
|
||||
}
|
||||
|
||||
public function on_content_change($arg = null): void {
|
||||
$uid = get_current_user_id();
|
||||
if ($uid > 0) {
|
||||
$this->evaluate($uid);
|
||||
}
|
||||
}
|
||||
|
||||
public function on_follow(int $follower_id, int $following_id): void {
|
||||
if ($following_id > 0) {
|
||||
$this->evaluate($following_id);
|
||||
}
|
||||
if ($follower_id > 0) {
|
||||
$this->evaluate($follower_id);
|
||||
}
|
||||
}
|
||||
|
||||
public function on_checkin(int $user_id): void {
|
||||
if ($user_id > 0) {
|
||||
$this->evaluate($user_id);
|
||||
}
|
||||
}
|
||||
|
||||
public function evaluate(int $user_id): void {
|
||||
if ($user_id <= 0 || !class_exists('\AKSRT\User\Badges') || !Badges::enabled()) {
|
||||
return;
|
||||
}
|
||||
if (!class_exists('\AKSRT\Settings\Settings')) {
|
||||
return;
|
||||
}
|
||||
$identity = \AKSRT\Settings\Settings::get_user_identity_settings();
|
||||
$medal = is_array($identity['medal'] ?? null) ? $identity['medal'] : [];
|
||||
$items = is_array($medal['items_by_slug'] ?? null) ? $medal['items_by_slug'] : [];
|
||||
if (!$items) {
|
||||
// Fall back to badge dictionary without thresholds.
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ($items as $slug => $item) {
|
||||
$get_type = sanitize_key((string) ($item['get_type'] ?? 'manually_add'));
|
||||
if ($get_type === 'manually_add' || $get_type === '') {
|
||||
continue;
|
||||
}
|
||||
$need = max(0, (int) ($item['get_val'] ?? 0));
|
||||
if ($need <= 0) {
|
||||
continue;
|
||||
}
|
||||
$val = $this->metric($user_id, $get_type);
|
||||
if ($val >= $need) {
|
||||
Badges::grant($user_id, (string) $slug, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function metric(int $user_id, string $type): int {
|
||||
switch ($type) {
|
||||
case 'registration_time':
|
||||
$user = get_userdata($user_id);
|
||||
if (!$user) {
|
||||
return 0;
|
||||
}
|
||||
$reg = strtotime((string) $user->user_registered);
|
||||
if ($reg <= 0) {
|
||||
return 0;
|
||||
}
|
||||
return max(0, (int) floor((time() - $reg) / DAY_IN_SECONDS));
|
||||
|
||||
case 'checkin_all_day':
|
||||
$v = (int) get_user_meta($user_id, 'aksrt_checkin_all_day', true);
|
||||
if ($v <= 0) {
|
||||
$v = (int) get_user_meta($user_id, 'checkin_all_day', true);
|
||||
}
|
||||
return $v;
|
||||
|
||||
case 'new_post':
|
||||
return (int) count_user_posts($user_id, 'post', true);
|
||||
|
||||
case 'bbs_post':
|
||||
return post_type_exists('aksrt_forum_topic')
|
||||
? (int) count_user_posts($user_id, 'aksrt_forum_topic', true)
|
||||
: 0;
|
||||
|
||||
case 'comment':
|
||||
return (int) get_comments(['user_id' => $user_id, 'count' => true, 'status' => 'approve']);
|
||||
|
||||
case 'followed':
|
||||
if (class_exists('\AKSRT\User\Follow')) {
|
||||
return (int) Follow::get_instance()->get_follower_count($user_id);
|
||||
}
|
||||
return 0;
|
||||
|
||||
case 'like':
|
||||
return (int) get_user_meta($user_id, 'aksrt_like_count', true)
|
||||
?: (int) get_user_meta($user_id, 'like-posts', true);
|
||||
|
||||
case 'views':
|
||||
return (int) get_user_meta($user_id, 'aksrt_user_views', true);
|
||||
|
||||
case 'pay_income':
|
||||
return (int) get_user_meta($user_id, 'aksrt_pay_income', true);
|
||||
|
||||
case 'pay_rebate':
|
||||
return (int) get_user_meta($user_id, 'aksrt_pay_rebate', true);
|
||||
|
||||
default:
|
||||
/**
|
||||
* Allow extensions to provide metric values.
|
||||
*
|
||||
* @param int $value
|
||||
* @param int $user_id
|
||||
* @param string $type
|
||||
*/
|
||||
return (int) apply_filters('aksrt_badge_metric_value', 0, $user_id, $type);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user