| @@ -1,11 +1,13 @@ | ||
| 1 | 1 | <?php |
| 2 | 2 | |
| 3 | 3 | namespace FluentCommunity\App\Hooks\Handlers; |
| 4 | 4 | |
| 5 | +use FluentCommunity\App\Functions\Utility; | |
| 5 | 6 | use FluentCommunity\App\Models\Activity; |
| 6 | 7 | use FluentCommunity\App\Models\XProfile; |
| 7 | 8 | use FluentCommunity\App\Services\Helper; |
| 9 | +use FluentCommunity\Framework\Support\Arr; | |
| 8 | 10 | |
| 9 | 11 | class ActivityMonitorHandler |
| 10 | 12 | { |
| 11 | 13 | public function register() |
| @@ -12,14 +14,18 @@ | ||
| 12 | 14 | { |
| 13 | 15 | add_action('fluent_community/comment_added', [$this, 'handleNewCommentEvent'], 10, 2); |
| 14 | 16 | add_action('fluent_community/feed/created', [$this, 'handleFeedCreated'], 10, 1); |
| 15 | 17 | |
| 16 | - add_action('fluent_communit/track_activity', [$this, 'trackActivity'], 10); | |
| 18 | + add_action('fluent_community/track_activity', [$this, 'trackActivity'], 10); | |
| 17 | 19 | |
| 18 | 20 | add_action('profile_update', function ($userId) { |
| 21 | + if (Utility::getPrivacySetting('enable_user_sync') === 'no') { | |
| 22 | + return; | |
| 23 | + } | |
| 24 | + | |
| 19 | 25 | $user = get_user_by('ID', $userId); |
| 20 | 26 | $xprofile = XProfile::where('user_id', $userId)->first(); |
| 21 | - if (!$xprofile) { | |
| 27 | + if (!$user || !$xprofile) { | |
| 22 | 28 | return; |
| 23 | 29 | } |
| 24 | 30 | $firstName = $user->first_name; |
| 25 | 31 | $lastName = $user->last_name; |
| @@ -29,17 +35,43 @@ | ||
| 29 | 35 | $xprofile->save(); |
| 30 | 36 | } |
| 31 | 37 | }); |
| 32 | 38 | |
| 39 | + add_action('wp_ajax_fluent_community_renew_nonce', [$this, 'renewNonce']); | |
| 40 | + | |
| 41 | + // Logged-out: without this, WP returns "0"/200 and the SPA loops on a stale nonce. | |
| 42 | + add_action('wp_ajax_nopriv_fluent_community_renew_nonce', function () { | |
| 43 | + wp_send_json(['message' => __('Your session has expired. Please log in again.', 'fluent-community')], 401); | |
| 44 | + }); | |
| 33 | 45 | } |
| 34 | 46 | |
| 47 | + public function renewNonce() | |
| 48 | + { | |
| 49 | + $ajax_nonce = Arr::get($_REQUEST, 'ajax_nonce'); // phpcs:ignore WordPress.Security.NonceVerification.Recommended | |
| 35 | 50 | |
| 51 | + if (!wp_verify_nonce($ajax_nonce, 'fluent_community_ajax_nonce')) { | |
| 52 | + wp_send_json(['message' => __('Invalid nonce', 'fluent-community')], 400); | |
| 53 | + } | |
| 54 | + | |
| 55 | + $currentProfile = Helper::getCurrentProfile(); | |
| 56 | + if (!$currentProfile) { | |
| 57 | + wp_send_json(['message' => __('Invalid user', 'fluent-community')], 401); | |
| 58 | + } | |
| 59 | + | |
| 60 | + wp_send_json([ | |
| 61 | + 'rest_nonce' => wp_create_nonce('wp_rest'), | |
| 62 | + 'ajax_nonce' => wp_create_nonce('fluent_community_ajax_nonce'), | |
| 63 | + ], 200); | |
| 64 | + } | |
| 65 | + | |
| 66 | + | |
| 36 | 67 | public function handleNewCommentEvent($comment, $feed) |
| 37 | 68 | { |
| 38 | 69 | $isPublic = 1; |
| 70 | + $space = $feed->space_id ? $feed->space : null; | |
| 39 | 71 | |
| 40 | - if ($feed->space_id) { | |
| 41 | - $isPublic = $feed->space->privacy == 'public'; | |
| 72 | + if ($space) { | |
| 73 | + $isPublic = $space->privacy == 'public'; | |
| 42 | 74 | } |
| 43 | 75 | |
| 44 | 76 | $data = [ |
| 45 | 77 | 'user_id' => $comment->user_id, |
| @@ -51,17 +83,18 @@ | ||
| 51 | 83 | ]; |
| 52 | 84 | |
| 53 | 85 | Activity::create($data); |
| 54 | 86 | |
| 55 | - do_action('fluent_communit/track_activity'); | |
| 87 | + do_action('fluent_community/track_activity'); | |
| 56 | 88 | } |
| 57 | 89 | |
| 58 | 90 | public function handleFeedCreated($feed) |
| 59 | 91 | { |
| 60 | 92 | $isPublic = 1; |
| 93 | + $space = $feed->space_id ? $feed->space : null; | |
| 61 | 94 | |
| 62 | - if ($feed->space_id) { | |
| 63 | - $isPublic = $feed->space->privacy == 'public'; | |
| 95 | + if ($space) { | |
| 96 | + $isPublic = $space->privacy == 'public'; | |
| 64 | 97 | } |
| 65 | 98 | |
| 66 | 99 | $data = [ |
| 67 | 100 | 'user_id' => $feed->user_id, |
| @@ -72,9 +105,9 @@ | ||
| 72 | 105 | ]; |
| 73 | 106 | |
| 74 | 107 | Activity::create($data); |
| 75 | 108 | |
| 76 | - do_action('fluent_communit/track_activity'); | |
| 109 | + do_action('fluent_community/track_activity'); | |
| 77 | 110 | } |
| 78 | 111 | |
| 79 | 112 | public function trackActivity() |
| 80 | 113 | { |
| @@ -80,8 +113,16 @@ | ||
| 80 | 113 | { |
| 81 | 114 | $currentProfile = Helper::getCurrentProfile(); |
| 82 | 115 | |
| 83 | 116 | if (!$currentProfile) { |
| 117 | + return false; | |
| 118 | + } | |
| 119 | + | |
| 120 | + // Debounce: skip the write if last_activity was updated within the last 5 minutes. | |
| 121 | + // The ticker polls every ~45-75s per session; without this each poll issues an | |
| 122 | + // xprofile UPDATE, saturating the DB at scale. | |
| 123 | + $throttleSeconds = apply_filters('fluent_community/track_activity_throttle_seconds', 300); | |
| 124 | + if ($currentProfile->last_activity && (current_time('timestamp') - strtotime($currentProfile->last_activity)) < $throttleSeconds) { | |
| 84 | 125 | return false; |
| 85 | 126 | } |
| 86 | 127 | |
| 87 | 128 | $currentProfile->last_activity = current_time('mysql'); |