Plugin Directory

Changeset 3335490


Ignore:
Timestamp:
07/28/2025 04:51:38 PM (7 months ago)
Author:
logtivity
Message:

Closes Milestone #16 - Improve app connection

Location:
logtivity
Files:
4 added
34 edited
1 copied

Legend:

Unmodified
Added
Removed
  • logtivity/tags/3.2.0/Admin/Logtivity_Log_Index_Controller.php

    r3294349 r3335490  
    4141    {
    4242        if (current_user_can(Logtivity::ACCESS_LOGS)) {
    43             $response = (new Logtivity_Api())
    44                 ->get(
    45                     '/logs',
    46                     [
    47                         'page'        => $this->getInput('page'),
    48                         'action'      => $this->getInput('search_action'),
    49                         'context'     => $this->getInput('search_context'),
    50                         'action_user' => $this->getInput('action_user'),
    51                     ]
    52                 );
     43            $api = Logtivity::log();
    5344
    54             if ($response) {
    55                 $this->successResponse(json_decode(json_encode($response)));
     45            if ($api->getOption()->getApiKey()) {
     46                $response = $api
     47                    ->get(
     48                        '/logs',
     49                        [
     50                            'page'        => $this->getInput('page'),
     51                            'action'      => $this->getInput('search_action'),
     52                            'context'     => $this->getInput('search_context'),
     53                            'action_user' => $this->getInput('action_user'),
     54                        ]
     55                    );
    5656
    57             } elseif ((new Logtivity_Options())->getApiKey() == false) {
    58                 $this->welcomeResponse();
     57                if ($response) {
     58                    $response = json_decode(json_encode($response));
     59
     60                    $this->renderView([
     61                        'message'     => $response->error,
     62                        'logs'        => $response->body->data ?? [],
     63                        'meta'        => $response->body->meta ?? null,
     64                        'hasNextPage' => $response->body->links->next ?? null,
     65                    ]);
     66                } else {
     67                    $this->renderView($api->getConnectionMessage());
     68                }
    5969
    6070            } else {
    61                 $this->errorResponse((new Logtivity_Api())->getConnectionMessage());
     71                $this->welcomeResponse();
    6272            }
    6373        } else {
    64             $this->errorResponse(__('You do not have sufficient permissions to access this page.'));
     74            $this->renderView(__('You do not have sufficient permissions to access this page.'));
    6575        }
    6676    }
    6777
    6878    /**
    69      * @param object $response
     79     * @param string $field
     80     *
     81     * @return ?string
     82     */
     83    protected function getInput(string $field): ?string
     84    {
     85        return sanitize_text_field($_GET[$field] ?? null);
     86    }
     87
     88    /**
     89     * @param string|array $response
    7090     *
    7191     * @return void
    7292     */
    73     private function successResponse(object $response): void
     93    protected function renderView($viewData): void
    7494    {
     95        if (is_string($viewData)) {
     96            $viewData = ['message' => $viewData];
     97        }
     98
     99        $viewData = array_merge(
     100            [
     101                'message'     => null,
     102                'logs'        => [],
     103                'meta'        => null,
     104                'hasNextPage' => null,
     105            ],
     106            $viewData
     107        );
     108
    75109        wp_send_json([
    76             'view' => logtivity_view('_logs-loop', [
    77                 'logs'        => $response->data,
    78                 'meta'        => $response->meta,
    79                 'hasNextPage' => $response->links->next,
    80             ]),
    81         ]);
    82     }
    83 
    84     /**
    85      * @param string $message
    86      *
    87      * @return void
    88      */
    89     private function errorResponse(string $message): void
    90     {
    91         wp_send_json([
    92             'view' => logtivity_view('_logs-loop', [
    93                 'message' => $message,
    94                 'logs'    => [],
    95             ]),
     110            'view' => logtivity_view('_logs-loop', $viewData),
    96111        ]);
    97112    }
     
    100115     * @return void
    101116     */
    102     private function welcomeResponse()
     117    protected function welcomeResponse()
    103118    {
    104119        wp_send_json([
     
    109124        ]);
    110125    }
    111 
    112     /**
    113      * @param string $field
    114      *
    115      * @return ?string
    116      */
    117     private function getInput(string $field): ?string
    118     {
    119         return sanitize_text_field($_GET[$field] ?? null);
    120     }
    121126}
    122127
  • logtivity/tags/3.2.0/Admin/Logtivity_Options.php

    r3306623 r3335490  
    2828class Logtivity_Options
    2929{
     30    /**
     31     * Checkin for settings updates in designated minutes
     32     *
     33     * @var int
     34     */
     35    protected int $checkinDelay = 10;
     36
    3037    /**
    3138     * The option keys that we can save to the options table
     
    175182
    176183            if ($lastCheckin) {
    177                 return time() - strtotime($lastCheckin) > 10 * MINUTE_IN_SECONDS;
     184                return time() - strtotime($lastCheckin) > ($this->checkinDelay * MINUTE_IN_SECONDS);
    178185            }
    179186        }
     
    232239    public function update(array $data = [], bool $checkApiKey = true): void
    233240    {
    234         if (count($data)) {
    235             foreach ($this->settings as $setting => $default) {
    236                 if (array_key_exists($setting, $data) && $this->validateSetting($setting, $data[$setting])) {
    237                     update_option($setting, $data[$setting]);
    238                 }
    239             }
    240         } else {
    241             foreach ($this->settings as $setting => $default) {
    242                 if (array_key_exists($setting, $_POST) && $this->validateSetting($setting, $_POST[$setting])) {
    243                     update_option($setting, $_POST[$setting]);
    244                 }
     241        $settings = array_intersect_key($data ?: $_POST, $this->settings);
     242        foreach ($settings as $key => $value) {
     243            if ($this->validateSetting($key, $value)) {
     244                update_option($key, $value);
    245245            }
    246246        }
     
    249249            $this->checkApiKey($data['logtivity_site_api_key'] ?? $_POST['logtivity_site_api_key'] ?? null);
    250250        }
     251    }
     252
     253    /**
     254     * Validate that the passed parameters are in the correct format
     255     *
     256     * @param string $key
     257     * @param mixed  $value
     258     *
     259     * @return bool
     260     */
     261    protected function validateSetting(string $key, $value): bool
     262    {
     263        $setting = $this->settings[$key] ?? null;
     264
     265        if ($setting) {
     266            $method = $setting['rule'] ?? null;
     267
     268            if ($method == 'is_bool') {
     269                return $method((bool)$value);
     270            }
     271
     272            return $method($value);
     273        }
     274
     275        return true;
    251276    }
    252277
     
    262287                ->setAction('Settings Updated')
    263288                ->setContext('Logtivity')
    264                 ->ignoreStatus()
    265289                ->waitForResponse()
    266290                ->send();
    267291        }
    268292    }
    269 
    270     /**
    271      * Validate that the passed parameters are in the correct format
    272      *
    273      * @param string $key
    274      * @param mixed  $value
    275      *
    276      * @return bool
    277      */
    278     protected function validateSetting(string $key, $value): bool
    279     {
    280         $setting = $this->settings[$key] ?? null;
    281 
    282         if ($setting) {
    283             $method = $setting['rule'] ?? null;
    284 
    285             if ($method == 'is_bool') {
    286                 return $method((bool)$value);
    287             }
    288 
    289             return $method($value);
    290         }
    291 
    292         return true;
    293     }
    294293}
  • logtivity/tags/3.2.0/Errors/Logtivity_Error_Logger.php

    r3272661 r3335490  
    7070     * @return ?array
    7171     */
    72     public function send(): ?array
     72    public function send(): void
    7373    {
    7474        $message = $this->error['message'] ?? '';
     
    7979
    8080            if ($this->active) {
    81                 $response = $this->makeRequest('/errors/store', $this->getData());
     81                $this->makeRequest('/errors/store', $this->getData());
    8282            }
    8383        }
    84 
    85         return $response ?? null;
    8684    }
    8785
  • logtivity/tags/3.2.0/Helpers/Helpers.php

    r3272661 r3335490  
    5151{
    5252    extract($vars);
     53    unset($vars);
    5354
    5455    ob_start();
  • logtivity/tags/3.2.0/Helpers/Logtivity_Log_Global_Function.php

    r3290027 r3335490  
    3333 * @deprecated v3.1.8 - use Logtivity::log() instead
    3434 */
    35 function logtivity_log(?string $action = null, ?string $meta = null, ?string $user_id = null)
     35function logtivity_log(?string $action = null, ?string $meta = null, ?string $user_id = null): void
    3636{
    37     return Logtivity::log($action, $meta, $user_id)->send();
     37    Logtivity::log($action, $meta, $user_id)->send();
    3838}
  • logtivity/tags/3.2.0/Logs/Core/Logtivity_Core.php

    r3290027 r3335490  
    197197                ->setAction('Settings Updated')
    198198                ->setContext('Core:' . $optionPage)
    199                 ->ignoreStatus()
    200199                ->send();
    201200        }
  • logtivity/tags/3.2.0/Logs/Core/Logtivity_User.php

    r3272661 r3335490  
    2525class Logtivity_User extends Logtivity_Abstract_Logger
    2626{
    27     protected static $loggedUserlogin = false;
     27    protected static bool $loggedUserlogin = false;
    2828
    2929    /**
    30      * @inheritDoc
     30     * @param string  $username
     31     * @param WP_User $user
     32     *
     33     * @return void
    3134     */
    32     protected function registerHooks(): void
     35    public function userLoggedIn(string $username, WP_User $user)
    3336    {
    34         add_action('wp_login', [$this, 'wpLogin'], 10, 2);
    35         add_action('wp_logout', [$this, 'userLoggedOut'], 10, 1);
    36         add_action('user_register', [$this, 'userCreated'], 10, 1);
    37         add_action('delete_user', [$this, 'userDeleted']);
    38         add_action('profile_update', [$this, 'profileUpdated'], 10, 2);
     37        if (static::$loggedUserlogin == false) {
     38            static::$loggedUserlogin = true;
     39
     40            $logger = Logtivity::log()->setUser($user->ID);
     41
     42            $logger->setAction('User Logged In')
     43                ->setContext($logger->user->getRole())
     44                ->send();
     45        }
    3946    }
    4047
    41     public function wpLogin($user_login, $user)
     48    /**
     49     * @param int $userId
     50     *
     51     * @return void
     52     */
     53    public function userLoggedOut(int $userId): void
    4254    {
    43         if (self::$loggedUserlogin) {
    44             return;
     55        if ($userId) {
     56            $logger = Logtivity::log()->setUser($userId);
     57
     58            $logger
     59                ->setAction('User Logged Out')
     60                ->setContext($logger->user->getRole())
     61                ->send();
    4562        }
    46 
    47         return $this->userLoggedIn($user->ID);
    4863    }
    4964
    50     public function userLoggedIn($user_id)
    51     {
    52         self::$loggedUserlogin = true;
    53 
    54         $logtivityUser = new Logtivity_WP_User($user_id);
    55 
    56         return (new Logtivity_Logger($user_id))
    57             ->setAction('User Logged In')
    58             ->setContext($logtivityUser->getRole())
    59             ->send();
    60     }
    61 
    62     public function userLoggedOut($user_id)
    63     {
    64         if ($user_id == 0) {
    65             return;
    66         }
    67 
    68         $user = new Logtivity_WP_User($user_id);
    69 
    70         return (new Logtivity_Logger($user_id))
    71             ->setAction('User Logged Out')
    72             ->setContext($user->getRole())
    73             ->send();
    74     }
    75 
    76     public function userCreated($user_id)
     65    /**
     66     * @param int $userId
     67     *
     68     * @return void
     69     */
     70    public function userCreated(int $userId): void
    7771    {
    7872        $log = Logtivity::log();
    7973
    80         if (!is_user_logged_in()) {
    81             $log->setUser($user_id);
     74        if (is_user_logged_in() == false) {
     75            $log->setUser($userId);
     76            $user = $log->user;
     77        } else {
     78            $user = new Logtivity_WP_User($userId);
    8279        }
    83 
    84         $user = new Logtivity_WP_User($user_id);
    8580
    8681        $log->setAction('User Created')
     
    9085    }
    9186
    92     public function userDeleted($user_id)
     87    /**
     88     * @param int $userId
     89     *
     90     * @return void
     91     */
     92    public function userDeleted(int $userId): void
    9393    {
    94         $user = new Logtivity_WP_User($user_id);
     94        $user = new Logtivity_WP_User($userId);
    9595
    9696        Logtivity::log()
     
    101101    }
    102102
    103     public function profileUpdated($user_id, $old_user_data)
     103    public function profileUpdated($userId)
    104104    {
    105         $user = new Logtivity_WP_User($user_id);
     105        $user = new Logtivity_WP_User($userId);
    106106
    107107        Logtivity::log()
     
    111111            ->send();
    112112    }
     113
     114    /**
     115     * @inheritDoc
     116     */
     117    protected function registerHooks(): void
     118    {
     119        add_action('wp_login', [$this, 'userLoggedIn'], 10, 2);
     120        add_action('wp_logout', [$this, 'userLoggedOut'], 10, 1);
     121        add_action('user_register', [$this, 'userCreated'], 10, 1);
     122        add_action('delete_user', [$this, 'userDeleted']);
     123        add_action('profile_update', [$this, 'profileUpdated'], 10, 1);
     124    }
    113125}
    114126
  • logtivity/tags/3.2.0/Logs/Memberpress/Logtivity_Memberpress.php

    r3272661 r3335490  
    2525class Logtivity_Memberpress extends Logtivity_Abstract_Logger
    2626{
    27     /**
    28      * @inheritDoc
    29      */
    30     protected function registerHooks(): void
    31     {
    32         add_action('mepr-event-member-signup-completed', [$this, 'freeSubscriptionCreated']);
    33         add_action('mepr-event-subscription-created', [$this, 'subscriptionCreated']);
    34         add_action('mepr-event-subscription-paused', [$this, 'subscriptionPaused']);
    35         add_action('mepr-event-subscription-resumed', [$this, 'subscriptionResumed']);
    36         add_action('mepr-event-subscription-stopped', [$this, 'subscriptionStopped']);
    37         add_action('init', [$this, 'profileUpdated']);
    38         add_filter('mepr_create_transaction', [$this, 'transactionCreated'], 10, 3);
    39         add_filter('mepr_update_transaction', [$this, 'transactionUpdated'], 10, 3);
    40         add_action('mepr_email_sent', [$this, 'emailSent'], 10, 3);
    41         add_action('mepr-process-options', [$this, 'settingsUpdated'], 10, 1);
    42     }
    43 
    44     public function freeSubscriptionCreated($event)
     27    public function freeSubscriptionCreated($event): void
    4528    {
    4629        $user         = $event->get_data();
    4730        $subscription = $this->getSubscription($user);
    4831
    49         if (!$subscription) {
    50             return;
     32        if ($subscription && $subscription->gateway == 'free') {
     33            $product = $subscription->product();
     34
     35            Logtivity::log(
     36                'Free Subscription Created',
     37                [
     38                    'Subscription ID' => $subscription->id,
     39                ]
     40            )
     41                ->setContext($product->post_title)
     42                ->send();
    5143        }
    52 
    53         if ($subscription->gateway != 'free') {
    54             return;
    55         }
    56 
    57         $product = $subscription->product();
    58 
    59         return (new Logtivity_Logger($user->ID))
    60             ->setAction('Free Subscription Created')
    61             ->setContext($product->post_title)
    62             ->addMeta('Subscription ID', $subscription->id)
    63             ->send();
    6444    }
    6545
     
    7353    }
    7454
    75     public function subscriptionCreated($event)
     55    /**
     56     * @param $event
     57     *
     58     * @return void
     59     */
     60    public function subscriptionCreated($event): void
    7661    {
    7762        $subscription  = $event->get_data();
     
    8065        $paymentMethod = $subscription->payment_method();
    8166
    82         return (new Logtivity_Logger($user->ID))
    83             ->setAction('Subscription Created')
     67        Logtivity::log(
     68            'Subscription Created',
     69            [
     70                'Transaction Total' => $subscription->total,
     71                'Payment Method'    => $paymentMethod->name,
     72                'Subscription ID'   => $subscription->id,
     73            ],
     74            $user->ID
     75        )
    8476            ->setContext($product->post_title)
    85             ->addMeta('Transaction Total', $subscription->total)
    86             ->addMeta('Payment Method', $paymentMethod->name)
    87             ->addMeta('Subscription ID', $subscription->id)
    8877            ->send();
    8978    }
     
    188177            ->send();
    189178    }
     179
     180    /**
     181     * @inheritDoc
     182     */
     183    protected function registerHooks(): void
     184    {
     185        add_action('mepr-event-member-signup-completed', [$this, 'freeSubscriptionCreated']);
     186        add_action('mepr-event-subscription-created', [$this, 'subscriptionCreated']);
     187        add_action('mepr-event-subscription-paused', [$this, 'subscriptionPaused']);
     188        add_action('mepr-event-subscription-resumed', [$this, 'subscriptionResumed']);
     189        add_action('mepr-event-subscription-stopped', [$this, 'subscriptionStopped']);
     190        add_action('init', [$this, 'profileUpdated']);
     191        add_filter('mepr_create_transaction', [$this, 'transactionCreated'], 10, 3);
     192        add_filter('mepr_update_transaction', [$this, 'transactionUpdated'], 10, 3);
     193        add_action('mepr_email_sent', [$this, 'emailSent'], 10, 3);
     194        add_action('mepr-process-options', [$this, 'settingsUpdated'], 10, 1);
     195    }
    190196}
    191197
  • logtivity/tags/3.2.0/Services/Logtivity_Api.php

    r3306623 r3335490  
    2525class Logtivity_Api
    2626{
     27    public const CURL_TIMEOUT = 28;
     28
     29    /**
     30     * @var array
     31     */
     32    protected static $responseData = [
     33        'code'    => null,
     34        'message' => null,
     35        'error'   => null,
     36        'body'    => null,
     37    ];
     38
    2739    /**
    2840     * Option class to access the plugin settings
     
    6274     * @return $this
    6375     */
    64     public function waitForResponse(): self
    65     {
    66         $this->waitForResponse = true;
     76    public function ignoreStatus(): self
     77    {
     78        $this->ignoreStatus = true;
    6779
    6880        return $this;
    69     }
    70 
    71     /**
    72      * @return $this
    73      */
    74     public function ignoreStatus(): self
    75     {
    76         $this->ignoreStatus = true;
    77 
    78         return $this;
    79     }
    80 
    81     /**
    82      * @param string $endpoint
    83      *
    84      * @return string
    85      */
    86     public function getEndpoint(string $endpoint): string
    87     {
    88         return logtivity_get_api_url() . $endpoint;
    8981    }
    9082
     
    9890    {
    9991        return $this->makeRequest($url, $body);
    100     }
    101 
    102     /**
    103      * @param string $url
    104      * @param ?array $body
    105      *
    106      * @return ?array
    107      */
    108     public function get(string $url, ?array $body = null): ?array
    109     {
    110         return $this->waitForResponse()->makeRequest($url, $body, 'GET');
    111     }
    112 
    113     /**
    114      * @return ?string
    115      */
    116     public function getApiKey(): ?string
    117     {
    118         if ($this->api_key == false) {
    119             $this->api_key = $this->options->getApiKey();
    120         }
    121 
    122         return $this->api_key;
    123     }
    124 
    125     /**
    126      * @param string $apikey
    127      *
    128      * @return $this
    129      */
    130     public function setApiKey(string $apikey): self
    131     {
    132         $this->api_key = $apikey;
    133 
    134         return $this;
    13592    }
    13693
     
    151108
    152109        if ($this->ready()) {
    153             if ($this->options->getOption('logtivity_app_verify_url')) {
     110            if ($this->getOption('logtivity_app_verify_url')) {
    154111                $body = array_merge(
    155112                    $body ?: [],
     
    177134            // @TODO: Switch to Logtivity_Response class to get standardized responses
    178135            $response = wp_remote_request($this->getEndpoint($url), $request);
    179             if ($this->notUpdatingWidgetInCustomizer()) {
     136            if ($waitForResponse && $this->notUpdatingWidgetInCustomizer()) {
    180137                // We waited and received a response
    181138                if ($response instanceof WP_Error) {
    182                     $responseData = [
     139                    $responseData = array_merge(static::$responseData, [
    183140                        'code'    => 500,
    184141                        'message' => $response->get_error_code(),
    185142                        'error'   => $response->get_error_message() ?: $response->get_error_code(),
    186143                        'body'    => get_object_vars($response),
    187                     ];
     144                    ]);
    188145
    189146                } else {
     
    197154                        $responseError   = $responseMessage;
    198155                        $responseMessage = (($responseBody['message'] ?? $responseMessage) ?: 'Unknown error');
    199 
    200156                    }
    201157                    $responseData = [
     
    207163                }
    208164
     165                $newStatus = 'success';
    209166                if ($responseData['code']) {
    210167                    if ($responseData['code'] < 400) {
    211168                        // Successful request, check if api is telling us to pause
    212                         $newStatus = $responseData['error'] ? 'paused' : 'success';
    213                         $this->updateSettings($response['body']);
     169                        if ($responseData['error']) {
     170                            $newStatus = 'paused';
     171                        }
     172
     173                        $body = json_decode($response['body'] ?? 'null', true);
     174                        $this->updateSettings($body['settings'] ?? []);
    214175
    215176                    } else {
    216                         // Something went wrong submitting the api request
    217                         $newStatus = 'fail';
    218                         update_option('logtivity_last_settings_check_in_at', ['date' => date('Y-m-d H:i:s')]);
     177                        if ($this->getCurlError($responseData['error']) != static::CURL_TIMEOUT) {
     178                            // Something other than a timeout went wrong with the request
     179                            $newStatus = 'fail';
     180                        }
     181
     182                        $this->updateLastCheckin();
    219183                    }
    220184
     
    229193                    );
    230194
    231                     return $responseData['body'];
     195                    return $responseData;
    232196                }
    233197            }
     
    238202
    239203    /**
     204     * @return bool
     205     */
     206    protected function ready(): bool
     207    {
     208        return ($this->getApiKey())
     209            && logtivity_has_site_url_changed() == false
     210            && (
     211                $this->ignoreStatus
     212                || $this->getOption('logtivity_api_key_check') == 'success'
     213                || $this->options->shouldCheckInWithApi()
     214            );
     215    }
     216
     217    /**
     218     * @return ?string
     219     */
     220    public function getApiKey(): ?string
     221    {
     222        if ($this->api_key == false) {
     223            $this->api_key = $this->options->getApiKey();
     224        }
     225
     226        return $this->api_key;
     227    }
     228
     229    /**
     230     * @param string $apikey
     231     *
     232     * @return $this
     233     */
     234    public function setApiKey(string $apikey): self
     235    {
     236        $this->api_key = $apikey;
     237
     238        return $this;
     239    }
     240
     241    /**
     242     * @param ?string $key
     243     *
     244     * @return Logtivity_Options|mixed
     245     */
     246    public function getOption(?string $key = null)
     247    {
     248        if ($key) {
     249            return $this->options->getOption($key);
     250        }
     251
     252        return $this->options;
     253    }
     254
     255    /**
     256     * @param string $endpoint
     257     *
     258     * @return string
     259     */
     260    public function getEndpoint(string $endpoint): string
     261    {
     262        return logtivity_get_api_url() . $endpoint;
     263    }
     264
     265    /**
     266     * You cannot call an extra update_option during a widget update so we make
     267     * sure not to log the most recent log response in this case.
     268     *
     269     * @return bool
     270     */
     271    protected function notUpdatingWidgetInCustomizer(): bool
     272    {
     273        $customize = sanitize_text_field($_POST['wp_customize'] ?? null);
     274        $action    = sanitize_text_field($_POST['action'] ?? null);
     275
     276        return ($action == 'update-widget' && $customize == 'on') == false;
     277    }
     278
     279    /**
    240280     * @param array|object $body
    241281     *
    242282     * @return void
    243283     */
    244     public function updateSettings($body): void
    245     {
    246         $settings = (object)($body->settings ?? $body['settings'] ?? null);
    247 
     284    public function updateSettings(array $settings): void
     285    {
    248286        if ($settings) {
    249             $this->options->update(
    250                 [
    251                     'logtivity_global_disabled_logs'         => $settings->disabled_logs ?? null,
    252                     'logtivity_enable_white_label_mode'      => $settings->enable_white_label_mode ?? null,
    253                     'logtivity_disabled_error_levels'        => $settings->disabled_error_levels ?? null,
    254                     'logtivity_disable_error_logging'        => $settings->disable_error_logging ?? null,
    255                     'logtivity_hide_plugin_from_ui'          => $settings->hide_plugin_from_ui ?? null,
    256                     'logtivity_disable_default_logging'      => $settings->disable_default_logging ?? null,
    257                     'logtivity_enable_options_table_logging' => $settings->enable_options_table_logging ?? null,
    258                     'logtivity_enable_post_meta_logging'     => $settings->enable_post_meta_logging ?? null,
    259                     'logtivity_custom_plugin_name'           => $settings->custom_plugin_name ?? null,
    260                 ],
    261                 false
    262             );
    263 
    264             update_option('logtivity_last_settings_check_in_at', ['date' => date('Y-m-d H:i:s')]);
    265         }
     287            $verifiedSettings = [];
     288            foreach ($settings as $key => $value) {
     289                if ($key == 'disabled_logs') {
     290                    $key = 'global_' . $key;
     291                }
     292                $verifiedSettings['logtivity_' . $key] = is_null($value) ? '' : $value;
     293            }
     294
     295            $this->options->update($verifiedSettings, false);
     296        }
     297
     298        $this->updateLastCheckin();
     299    }
     300
     301    /**
     302     * @return void
     303     */
     304    protected function updateLastCheckin(): void
     305    {
     306        update_option('logtivity_last_settings_check_in_at', ['date' => date('Y-m-d H:i:s')]);
     307    }
     308
     309    /**
     310     * @param ?string $message
     311     *
     312     * @return int
     313     */
     314    protected function getCurlError(?string $message): ?int
     315    {
     316        preg_match('/curl\s+error\s+(\d+)/i', $message, $matches);
     317
     318        return $matches[1] ?? null;
    266319    }
    267320
     
    271324    public function getLatestResponse(): ?array
    272325    {
    273         $response = $this->options->getOption('logtivity_latest_response');
     326        $response = $this->getOption('logtivity_latest_response');
    274327
    275328        return $response ?: null;
     
    277330
    278331    /**
    279      * @return ?string
    280      */
    281     public function getConnectionStatus(): ?string
    282     {
    283         $status = null;
    284         $apiKey = $this->getApiKey();
    285 
    286         if ($apiKey) {
    287             $status = $this->options->getOption('logtivity_api_key_check');
    288         }
    289 
    290         return $status;
     332     * @param string $url
     333     * @param ?array $body
     334     *
     335     * @return ?array
     336     */
     337    public function get(string $url, ?array $body = null): ?array
     338    {
     339        return $this->waitForResponse()->makeRequest($url, $body, 'GET');
     340    }
     341
     342    /**
     343     * @return $this
     344     */
     345    public function waitForResponse(): self
     346    {
     347        $this->waitForResponse = true;
     348
     349        return $this;
    291350    }
    292351
     
    310369                $error   = $this->getLatestResponse();
    311370                $code    = $error['code'] ?? null;
    312                 $message = $error['message'] ?? null;
     371                $message = $error['error'] ?? $error['message'] ?? null;
    313372
    314373                if ($code && $message) {
     
    329388
    330389    /**
    331      * You cannot call an extra update_option during a widget update so we make
    332      * sure not to log the most recent log response in this case.
    333      *
    334      * @return bool
    335      */
    336     private function notUpdatingWidgetInCustomizer(): bool
    337     {
    338         $customize = sanitize_text_field($_POST['wp_customize'] ?? null);
    339         $action    = sanitize_text_field($_POST['action'] ?? null);
    340 
    341         return ($action == 'update-widget' && $customize == 'on') == false;
    342     }
    343 
    344     /**
    345      * @return bool
    346      */
    347     private function ready(): bool
    348     {
    349         //var_dump($this->ignoreStatus);
    350         return ($this->getApiKey())
    351             && logtivity_has_site_url_changed() == false
    352             && (
    353                 $this->ignoreStatus
    354                 || $this->options->getOption('logtivity_api_key_check') == 'success'
    355                 || $this->options->shouldCheckInWithApi()
    356             );
     390     * @return ?string
     391     */
     392    public function getConnectionStatus(): ?string
     393    {
     394        $status = null;
     395        $apiKey = $this->getApiKey();
     396
     397        if ($apiKey) {
     398            $status = $this->getOption('logtivity_api_key_check');
     399        }
     400
     401        return $status;
    357402    }
    358403}
  • logtivity/tags/3.2.0/Services/Logtivity_Check_For_Disabled_Individual_Logs.php

    r3246625 r3335490  
    2525class Logtivity_Check_For_Disabled_Individual_Logs
    2626{
    27     public function __construct()
    28     {
    29         add_action('wp_logtivity_instance', [$this, 'handle'], 10, 999);
    30     }
     27    /**
     28     * @var Logtivity_Options
     29     */
     30    protected Logtivity_Options $options;
    3131
    32     public function handle($Logtivity_Logger)
    33     {
    34         foreach ($this->getLogsToExclude() as $log) {
    35             if ($this->check($Logtivity_Logger, $log)) {
    36                 $Logtivity_Logger->stop();
    37             }
    38         }
     32    public function __construct()
     33    {
     34        add_action('wp_logtivity_instance', [$this, 'handle'], 10, 2);
     35    }
    3936
    40         foreach ($this->globalLogsToExclude() as $log) {
    41             if ($this->check($Logtivity_Logger, $log)) {
    42                 $Logtivity_Logger->stop();
    43             }
    44         }
    45     }
     37    /**
     38     * @param Logtivity_Logger $logger
     39     *
     40     * @return void
     41     */
     42    public function handle(Logtivity_Logger $logger): void
     43    {
     44        // Refresh options whenever invoked
     45        $this->options = new Logtivity_Options();
    4646
    47     public function check($Logtivity_Logger, $log)
    48     {
    49         $array = explode('&&', $log);
     47        $exclusions = array_unique(
     48            array_merge(
     49                $this->parseExcludeEntries('logtivity_disable_individual_logs'),
     50                $this->parseExcludeEntries('logtivity_global_disabled_logs')
     51            )
     52        );
    5053
    51         if (isset($array[0]) && isset($array[1])) {
    52             if (trim($array[0]) === '*' && trim($array[1]) === '*') {
    53                 return;
    54             }
    55             if ($this->matches($Logtivity_Logger->action, $array[0]) && $this->matches($Logtivity_Logger->context, $array[1])) {
    56                 return true;
    57             }
    58         } elseif(isset($array[0])) {
    59             if (trim($array[0]) === '*') {
    60                 return;
    61             }
    62             if ($this->matches($Logtivity_Logger->action, $array[0])) {
    63                 return true;
    64             }
    65         }
    66         return false;
    67     }
     54        foreach ($exclusions as $exclusion) {
     55            if ($this->isDisabled($logger, $exclusion)) {
     56                $logger->stop();
    6857
    69     private function matches($keyword, $disabledKeyword)
    70     {
    71         // @TODO: this may not be the best way to check the arguments
    72         if (is_string($keyword) && is_string($disabledKeyword)) {
    73             $keyword = trim(strtolower($keyword));
    74             $disabledKeyword = trim(strtolower($disabledKeyword));
     58                return;
     59            }
     60        }
     61    }
    7562
    76             if ($disabledKeyword === '*') {
    77                 return true;
    78             }
     63    /**
     64     * @param string $option
     65     *
     66     * @return array
     67     */
     68    protected function parseExcludeEntries(string $option): array
     69    {
     70        $value = (string)$this->options->getOption($option);
    7971
    80             if (strpos($disabledKeyword, '*') !== false) {
    81                 return strpos($keyword, str_replace('*', '', $disabledKeyword)) !== false;
    82             }
     72        $entries = preg_split("/\\r\\n|\\r|\\n/", $value);
    8373
    84             return $keyword == $disabledKeyword;
    85         }
     74        return array_unique(array_filter($entries));
     75    }
    8676
    87         return false;
    88     }
     77    /**
     78     * @param Logtivity_Logger $logger
     79     * @param string           $exclusion
     80     *
     81     * @return bool
     82     */
     83    protected function isDisabled(Logtivity_Logger $logger, string $exclusion): bool
     84    {
     85        $array   = explode('&&', $exclusion);
     86        $action  = strtolower(trim((string)array_shift($array)));
     87        $context = strtolower(trim((string)array_shift($array)));
    8988
    90     private function getLogsToExclude()
    91     {
    92         $value = (new Logtivity_Options)->getOption('logtivity_disable_individual_logs');
     89        if ($action == '*' && $context == '*') {
     90            return false;
     91        } elseif ($this->matches($logger->action, $action) && $this->matches($logger->context, $context)) {
     92            return true;
     93        } elseif ($action == '*') {
     94            return false;
     95        } elseif ($this->matches($logger->action, $action)) {
     96            return true;
     97        }
    9398
    94         if ($value == '') {
    95             return [];
    96         }
     99        return false;
     100    }
    97101
    98         return preg_split("/\\r\\n|\\r|\\n/", $value);
    99     }
     102    /**
     103     * @param ?string $keywordTarget
     104     * @param ?string $keywordCheck
     105     *
     106     * @return bool
     107     */
     108    protected function matches(?string $keywordTarget, ?string $keywordCheck): bool
     109    {
     110        $keywordTarget = strtolower(trim((string)$keywordTarget));
     111        $keywordCheck  = strtolower(trim((string)$keywordCheck));
    100112
    101     public function globalLogsToExclude()
    102     {
    103         $value = (new Logtivity_Options)->getOption('logtivity_global_disabled_logs');
     113        if ($keywordTarget && $keywordCheck) {
     114            if ($keywordCheck == '*') {
     115                return true;
     116            }
    104117
    105         if ($value == '') {
    106             return [];
    107         }
     118            if (strpos($keywordCheck, '*') !== false) {
     119                $regex = str_replace(['*', '/'], ['.*?', '\/'], $keywordCheck);
     120                return preg_match('/' . $regex . '/', $keywordTarget);
     121            }
    108122
    109         return preg_split("/\\r\\n|\\r|\\n/", $value);
    110     }
     123            return $keywordCheck == $keywordTarget;
     124        }
     125
     126        return false;
     127    }
    111128}
    112129
    113 $CheckForDisabledIndividualLogs = new Logtivity_Check_For_Disabled_Individual_Logs;
     130new Logtivity_Check_For_Disabled_Individual_Logs();
  • logtivity/tags/3.2.0/Services/Logtivity_Check_For_New_Settings.php

    r3290027 r3335490  
    4141
    4242    /**
     43     * @return bool
     44     */
     45    public function shouldCheckInWithApi(): bool
     46    {
     47        return (new Logtivity_Options())->shouldCheckInWithApi();
     48    }
     49
     50    /**
    4351     * @return void
    4452     */
     
    5361
    5462        try {
    55             $api = new Logtivity_Api();
    56 
    5763            $theme = wp_get_theme();
    5864
     
    6369            $latestMinMySqlVersion = $coreUpdates[0]->mysql_version ?? null;
    6470
    65             $response = $api->ignoreStatus()->post('/settings-check', [
    66                 'php_version'                 => phpversion(),
    67                 'plugins'                     => $this->getPluginsWithStatuses(),
    68                 'theme_name'                  => $theme ? $theme->name : null,
    69                 'theme_version'               => $theme ? $theme->version : null,
    70                 'themes'                      => $this->getThemesListWithStatuses(),
    71                 'wordpress_version'           => $wp_version,
    72                 'latest_wp_version'           => $latestWPVersion,
    73                 'latest_wp_min_php_version'   => $latestMinPhpVersion,
    74                 'latest_wp_min_mysql_version' => $latestMinMySqlVersion,
    75             ]);
     71            $api = new Logtivity_Api();
    7672
    77             if ($response) {
    78                 $api->updateSettings($response);
     73            $response = $api
     74                ->ignoreStatus()
     75                ->waitForResponse()
     76                ->makeRequest(
     77                    '/settings-check',
     78                    [
     79                        'php_version'                 => phpversion(),
     80                        'plugins'                     => $this->getPluginsWithStatuses(),
     81                        'theme_name'                  => $theme ? $theme->name : null,
     82                        'theme_version'               => $theme ? $theme->version : null,
     83                        'themes'                      => $this->getThemesListWithStatuses(),
     84                        'wordpress_version'           => $wp_version,
     85                        'latest_wp_version'           => $latestWPVersion,
     86                        'latest_wp_min_php_version'   => $latestMinPhpVersion,
     87                        'latest_wp_min_mysql_version' => $latestMinMySqlVersion,
     88                    ]);
     89
     90            if ($settings = $response['body']['settings'] ?? null) {
     91                $api->updateSettings($settings);
    7992            }
    8093
     
    8295            // Ignore
    8396        }
    84     }
    85 
    86     /**
    87      * @return array[]
    88      */
    89     private function getThemesListWithStatuses(): array
    90     {
    91         $themes = wp_get_themes();
    92 
    93         $themesDetails = [];
    94 
    95         foreach ($themes as $theme) {
    96             $themesDetails[] = [
    97                 'name'    => $theme->name,
    98                 'version' => $theme->version,
    99             ];
    100         }
    101 
    102         return $themesDetails;
    10397    }
    10498
     
    174168
    175169    /**
    176      * @return bool
     170     * @return array[]
    177171     */
    178     public function shouldCheckInWithApi(): bool
     172    private function getThemesListWithStatuses(): array
    179173    {
    180         return (new Logtivity_Options())->shouldCheckInWithApi();
     174        $themes = wp_get_themes();
     175
     176        $themesDetails = [];
     177
     178        foreach ($themes as $theme) {
     179            $themesDetails[] = [
     180                'name'    => $theme->name,
     181                'version' => $theme->version,
     182            ];
     183        }
     184
     185        return $themesDetails;
    181186    }
    182187}
  • logtivity/tags/3.2.0/Services/Logtivity_Logger.php

    r3290027 r3335490  
    225225     * Send the logged data to Logtivity
    226226     *
    227      * @return ?array
    228      */
    229     public function send(): ?array
     227     * @return void
     228     */
     229    public function send(): void
    230230    {
    231231        $this->maybeAddProfileLink();
     
    234234
    235235        if ($this->active) {
    236             return $this->makeRequest('/logs/store', $this->getData());
    237         }
    238 
    239         return null;
     236            $this->makeRequest('/logs/store', $this->getData());
     237        }
    240238    }
    241239
  • logtivity/tags/3.2.0/assets/app.js

    r3306623 r3335490  
    44            this.container = $('#logtivity-log-index');
    55
    6             if (!this.container.length) {
    7                 return;
     6            if (this.container.length) {
     7                this.form = $('#logtivity-log-index-search-form');
     8                this.listenForPagination();
     9                this.listenForChange();
     10                this.filter();
     11                this.listenForViewLog()
     12                this.listenForCloseModal();
    813            }
    9 
    10             this.form = $('#logtivity-log-index-search-form');
    11             this.listenForPagination();
    12             this.listenForChange();
    13             this.filter();
    14             this.listenForViewLog()
    15             this.listenForCloseModal();
    1614        },
    1715
     
    1917            let listenForCloseModal = this;
    2018
    21             $("body").on("click", ".js-logtivity-notice-dismiss", function(e) {
     19            $('body').on('click', '.js-logtivity-notice-dismiss', function(e) {
    2220                e.preventDefault();
    2321
     
    2624
    2725            $(document).on('keyup', function(e) {
    28                 if (e.key === "Escape") {
     26                if (e.key === 'Escape') {
    2927                    listenForCloseModal.hideModal();
    3028                }
     
    4947            let listenForViewLog = this;
    5048
    51             $("body").on("click", ".js-logtivity-view-log", function(e) {
     49            $('body').on('click', '.js-logtivity-view-log', function(e) {
    5250                e.preventDefault();
    5351
     
    7472                timeout         = null;
    7573
    76             $("body").on("input", "#logtivity-log-index-search-form input", function(e) {
     74            $('body').on('input', '#logtivity-log-index-search-form input', function(e) {
    7775                e.preventDefault();
    7876
     
    9391        loading: function() {
    9492            this.container.html(
    95                 '<div style="text-align: center; padding-bottom: 20px"><div class="spinner is-active" style="float:none;width:auto;height:auto;padding:10px 0 10px 50px;background-position:20px 0;"></div></div>'
     93                '<div style="text-align: center; padding-bottom: 20px">'
     94                + '<div class="spinner is-active" style="float:none;width:auto;height:auto;padding:10px 0 10px 50px;background-position:20px 0;"></div>'
     95                + '</div>'
    9696            );
    9797        },
     
    100100            let listenForPagination = this;
    101101
    102             $("body").on("click", ".js-logtivity-pagination", function(e) {
     102            $('body').on('click', '.js-logtivity-pagination', function(e) {
    103103                e.preventDefault();
    104104
  • logtivity/tags/3.2.0/composer.json

    r3290027 r3335490  
    2525    },
    2626    "require": {
    27         "php": ">=7.4",
     27        "php"     : ">=7.4",
    2828        "ext-json": "*"
    2929    },
  • logtivity/tags/3.2.0/logtivity.php

    r3306623 r3335490  
    66 * Description:       Record activity logs and errors logs across all your WordPress sites.
    77 * Author:            Logtivity
    8  * Version:           3.1.12
     8 * Version:           3.2.0
    99 * Text Domain:       logtivity
    1010 * Requires at least: 4.7
     
    4545     * @var string
    4646     */
    47     protected string $version = '3.1.12';
     47    protected string $version = '3.2.0';
    4848
    4949    /**
    5050     * List all classes here with their file paths. Keep class names the same as filenames.
    5151     * Ordering of this list matters!
    52      * @TODO: Implement pst-0 autoloading
     52     * @TODO: Implement psr-0 autoloading
    5353     *
    5454     * @var string[]
     
    7171        'Services/Logtivity_Check_For_Disabled_Individual_Logs',
    7272        'Services/Logtivity_Check_For_New_Settings',
     73        'Services/Logtivity_Rest_Endpoints',
    7374        /**
    7475         * Error logging
     
    7980        'Errors/Logtivity_Error_Log',
    8081    ];
    81 
    8282    /**
    8383     * @var string[]
     
    9696        'Logs/Core/Logtivity_Meta',
    9797    ];
    98 
    9998    /**
    10099     * List all integration dependencies
     
    177176    {
    178177        require_once plugin_dir_path(__FILE__) . $filePath . '.php';
     178    }
     179
     180    /**
     181     * Review updates based on version
     182     *
     183     * @return void
     184     */
     185    public function updateCheck(): void
     186    {
     187        $currentVersion = get_option('logtivity_version');
     188
     189        if (version_compare($currentVersion, '3.1.6', '<=')) {
     190            static::checkCapabilities();
     191        }
     192
     193        if ($currentVersion && version_compare($currentVersion, '3.1.7', '<=')) {
     194            // Default for updating sites should be no behavior change
     195            update_option('logtivity_app_verify_url', 0);
     196        }
     197
     198        update_option('logtivity_version', $this->version);
     199    }
     200
     201    /**
     202     * Custom capabilities added prior to v3.1.7
     203     *
     204     * @return void
     205     */
     206    public static function checkCapabilities(): void
     207    {
     208        $capabilities = array_filter(
     209            array_keys(logtivity_get_capabilities()),
     210            function (string $capability): bool {
     211                return in_array($capability, [Logtivity::ACCESS_LOGS, Logtivity::ACCESS_SETTINGS]);
     212            }
     213        );
     214
     215        if ($capabilities == false) {
     216            // Make sure at least admins can access us
     217            if ($role = get_role('administrator')) {
     218                if ($role->has_cap(Logtivity::ACCESS_LOGS) == false) {
     219                    $role->add_cap(Logtivity::ACCESS_LOGS);
     220                }
     221                if ($role->has_cap(Logtivity::ACCESS_SETTINGS) == false) {
     222                    $role->add_cap(Logtivity::ACCESS_SETTINGS);
     223                }
     224            }
     225        }
    179226    }
    180227
     
    270317                        if ($created) {
    271318                            $createdTimestamp = strtotime($created);
    272                             $creationText = sprintf(
     319                            $creationText     = sprintf(
    273320                                'It was created on %s at %s ',
    274321                                wp_date(get_option('date_format'), $createdTimestamp),
     
    290337        }
    291338
    292         return $response ?? null;
     339        return $response;
    293340    }
    294341
     
    313360    {
    314361        return new Logtivity_Error_Logger($error);
    315     }
    316 
    317     /**
    318      * Review updates based on version
    319      *
    320      * @return void
    321      */
    322     public function updateCheck(): void
    323     {
    324         $currentVersion = get_option('logtivity_version');
    325 
    326         if (version_compare($currentVersion, '3.1.6', '<=')) {
    327             static::checkCapabilities();
    328         }
    329 
    330         if ($currentVersion && version_compare($currentVersion, '3.1.7', '<=')) {
    331             // Default for updating sites should be no behavior change
    332             update_option('logtivity_app_verify_url', 0);
    333         }
    334 
    335         update_option('logtivity_version', $this->version);
    336362    }
    337363
     
    423449
    424450    /**
    425      * Custom capabilities added prior to v3.1.7
    426      *
    427      * @return void
    428      */
    429     public static function checkCapabilities(): void
    430     {
    431         $capabilities = array_filter(
    432             array_keys(logtivity_get_capabilities()),
    433             function (string $capability): bool {
    434                 return in_array($capability, [Logtivity::ACCESS_LOGS, Logtivity::ACCESS_SETTINGS]);
    435             }
    436         );
    437 
    438         if ($capabilities == false) {
    439             // Make sure at least admins can access us
    440             if ($role = get_role('administrator')) {
    441                 if ($role->has_cap(Logtivity::ACCESS_LOGS) == false) {
    442                     $role->add_cap(Logtivity::ACCESS_LOGS);
    443                 }
    444                 if ($role->has_cap(Logtivity::ACCESS_SETTINGS) == false) {
    445                     $role->add_cap(Logtivity::ACCESS_SETTINGS);
    446                 }
    447             }
    448         }
    449     }
    450 
    451     /**
    452451     * @return void
    453452     */
     
    469468            current_user_can(static::ACCESS_SETTINGS)
    470469            && logtivity_has_site_url_changed()
     470            && (new Logtivity_Options())->isWhiteLabelMode() == false
    471471            && !get_transient('dismissed-logtivity-site-url-has-changed-notice')
    472472        ) {
  • logtivity/tags/3.2.0/readme.txt

    r3306623 r3335490  
    55Requires at least: 6.6
    66Tested up to: 6.8
    7 Stable tag: 3.1.12
     7Stable tag: 3.2.0
    88Requires PHP: 7.4
    99License: GPLv2 or later
     
    262262
    263263== Changelog ==
     264
     265= 3.2.0 =
     266
     267* Add JSON Web Token halper class
     268* Add REST api listener
     269* Retrieve more detailed error information on connection fails
     270* Normalize response data
     271* Fix automatic settings checkin
     272* Prevent disconnect on timeout errors
     273
     274_Release Date - Monday, July 28, 2025_
    264275
    265276= 3.1.12 =
  • logtivity/tags/3.2.0/views/_logs-loop.php

    r3272661 r3335490  
    2424
    2525/**
     26 * @var string   $fileName
     27 * @var ?string  $message
    2628 * @var object[] $logs
    27  * @var ?string  $hasNextPage
     29 * @var object   $meta
     30 * @var string   $hasNextPage
    2831 */
    2932
  • logtivity/trunk/Admin/Logtivity_Log_Index_Controller.php

    r3294349 r3335490  
    4141    {
    4242        if (current_user_can(Logtivity::ACCESS_LOGS)) {
    43             $response = (new Logtivity_Api())
    44                 ->get(
    45                     '/logs',
    46                     [
    47                         'page'        => $this->getInput('page'),
    48                         'action'      => $this->getInput('search_action'),
    49                         'context'     => $this->getInput('search_context'),
    50                         'action_user' => $this->getInput('action_user'),
    51                     ]
    52                 );
     43            $api = Logtivity::log();
    5344
    54             if ($response) {
    55                 $this->successResponse(json_decode(json_encode($response)));
     45            if ($api->getOption()->getApiKey()) {
     46                $response = $api
     47                    ->get(
     48                        '/logs',
     49                        [
     50                            'page'        => $this->getInput('page'),
     51                            'action'      => $this->getInput('search_action'),
     52                            'context'     => $this->getInput('search_context'),
     53                            'action_user' => $this->getInput('action_user'),
     54                        ]
     55                    );
    5656
    57             } elseif ((new Logtivity_Options())->getApiKey() == false) {
    58                 $this->welcomeResponse();
     57                if ($response) {
     58                    $response = json_decode(json_encode($response));
     59
     60                    $this->renderView([
     61                        'message'     => $response->error,
     62                        'logs'        => $response->body->data ?? [],
     63                        'meta'        => $response->body->meta ?? null,
     64                        'hasNextPage' => $response->body->links->next ?? null,
     65                    ]);
     66                } else {
     67                    $this->renderView($api->getConnectionMessage());
     68                }
    5969
    6070            } else {
    61                 $this->errorResponse((new Logtivity_Api())->getConnectionMessage());
     71                $this->welcomeResponse();
    6272            }
    6373        } else {
    64             $this->errorResponse(__('You do not have sufficient permissions to access this page.'));
     74            $this->renderView(__('You do not have sufficient permissions to access this page.'));
    6575        }
    6676    }
    6777
    6878    /**
    69      * @param object $response
     79     * @param string $field
     80     *
     81     * @return ?string
     82     */
     83    protected function getInput(string $field): ?string
     84    {
     85        return sanitize_text_field($_GET[$field] ?? null);
     86    }
     87
     88    /**
     89     * @param string|array $response
    7090     *
    7191     * @return void
    7292     */
    73     private function successResponse(object $response): void
     93    protected function renderView($viewData): void
    7494    {
     95        if (is_string($viewData)) {
     96            $viewData = ['message' => $viewData];
     97        }
     98
     99        $viewData = array_merge(
     100            [
     101                'message'     => null,
     102                'logs'        => [],
     103                'meta'        => null,
     104                'hasNextPage' => null,
     105            ],
     106            $viewData
     107        );
     108
    75109        wp_send_json([
    76             'view' => logtivity_view('_logs-loop', [
    77                 'logs'        => $response->data,
    78                 'meta'        => $response->meta,
    79                 'hasNextPage' => $response->links->next,
    80             ]),
    81         ]);
    82     }
    83 
    84     /**
    85      * @param string $message
    86      *
    87      * @return void
    88      */
    89     private function errorResponse(string $message): void
    90     {
    91         wp_send_json([
    92             'view' => logtivity_view('_logs-loop', [
    93                 'message' => $message,
    94                 'logs'    => [],
    95             ]),
     110            'view' => logtivity_view('_logs-loop', $viewData),
    96111        ]);
    97112    }
     
    100115     * @return void
    101116     */
    102     private function welcomeResponse()
     117    protected function welcomeResponse()
    103118    {
    104119        wp_send_json([
     
    109124        ]);
    110125    }
    111 
    112     /**
    113      * @param string $field
    114      *
    115      * @return ?string
    116      */
    117     private function getInput(string $field): ?string
    118     {
    119         return sanitize_text_field($_GET[$field] ?? null);
    120     }
    121126}
    122127
  • logtivity/trunk/Admin/Logtivity_Options.php

    r3306623 r3335490  
    2828class Logtivity_Options
    2929{
     30    /**
     31     * Checkin for settings updates in designated minutes
     32     *
     33     * @var int
     34     */
     35    protected int $checkinDelay = 10;
     36
    3037    /**
    3138     * The option keys that we can save to the options table
     
    175182
    176183            if ($lastCheckin) {
    177                 return time() - strtotime($lastCheckin) > 10 * MINUTE_IN_SECONDS;
     184                return time() - strtotime($lastCheckin) > ($this->checkinDelay * MINUTE_IN_SECONDS);
    178185            }
    179186        }
     
    232239    public function update(array $data = [], bool $checkApiKey = true): void
    233240    {
    234         if (count($data)) {
    235             foreach ($this->settings as $setting => $default) {
    236                 if (array_key_exists($setting, $data) && $this->validateSetting($setting, $data[$setting])) {
    237                     update_option($setting, $data[$setting]);
    238                 }
    239             }
    240         } else {
    241             foreach ($this->settings as $setting => $default) {
    242                 if (array_key_exists($setting, $_POST) && $this->validateSetting($setting, $_POST[$setting])) {
    243                     update_option($setting, $_POST[$setting]);
    244                 }
     241        $settings = array_intersect_key($data ?: $_POST, $this->settings);
     242        foreach ($settings as $key => $value) {
     243            if ($this->validateSetting($key, $value)) {
     244                update_option($key, $value);
    245245            }
    246246        }
     
    249249            $this->checkApiKey($data['logtivity_site_api_key'] ?? $_POST['logtivity_site_api_key'] ?? null);
    250250        }
     251    }
     252
     253    /**
     254     * Validate that the passed parameters are in the correct format
     255     *
     256     * @param string $key
     257     * @param mixed  $value
     258     *
     259     * @return bool
     260     */
     261    protected function validateSetting(string $key, $value): bool
     262    {
     263        $setting = $this->settings[$key] ?? null;
     264
     265        if ($setting) {
     266            $method = $setting['rule'] ?? null;
     267
     268            if ($method == 'is_bool') {
     269                return $method((bool)$value);
     270            }
     271
     272            return $method($value);
     273        }
     274
     275        return true;
    251276    }
    252277
     
    262287                ->setAction('Settings Updated')
    263288                ->setContext('Logtivity')
    264                 ->ignoreStatus()
    265289                ->waitForResponse()
    266290                ->send();
    267291        }
    268292    }
    269 
    270     /**
    271      * Validate that the passed parameters are in the correct format
    272      *
    273      * @param string $key
    274      * @param mixed  $value
    275      *
    276      * @return bool
    277      */
    278     protected function validateSetting(string $key, $value): bool
    279     {
    280         $setting = $this->settings[$key] ?? null;
    281 
    282         if ($setting) {
    283             $method = $setting['rule'] ?? null;
    284 
    285             if ($method == 'is_bool') {
    286                 return $method((bool)$value);
    287             }
    288 
    289             return $method($value);
    290         }
    291 
    292         return true;
    293     }
    294293}
  • logtivity/trunk/Errors/Logtivity_Error_Logger.php

    r3272661 r3335490  
    7070     * @return ?array
    7171     */
    72     public function send(): ?array
     72    public function send(): void
    7373    {
    7474        $message = $this->error['message'] ?? '';
     
    7979
    8080            if ($this->active) {
    81                 $response = $this->makeRequest('/errors/store', $this->getData());
     81                $this->makeRequest('/errors/store', $this->getData());
    8282            }
    8383        }
    84 
    85         return $response ?? null;
    8684    }
    8785
  • logtivity/trunk/Helpers/Helpers.php

    r3272661 r3335490  
    5151{
    5252    extract($vars);
     53    unset($vars);
    5354
    5455    ob_start();
  • logtivity/trunk/Helpers/Logtivity_Log_Global_Function.php

    r3290027 r3335490  
    3333 * @deprecated v3.1.8 - use Logtivity::log() instead
    3434 */
    35 function logtivity_log(?string $action = null, ?string $meta = null, ?string $user_id = null)
     35function logtivity_log(?string $action = null, ?string $meta = null, ?string $user_id = null): void
    3636{
    37     return Logtivity::log($action, $meta, $user_id)->send();
     37    Logtivity::log($action, $meta, $user_id)->send();
    3838}
  • logtivity/trunk/Logs/Core/Logtivity_Core.php

    r3290027 r3335490  
    197197                ->setAction('Settings Updated')
    198198                ->setContext('Core:' . $optionPage)
    199                 ->ignoreStatus()
    200199                ->send();
    201200        }
  • logtivity/trunk/Logs/Core/Logtivity_User.php

    r3272661 r3335490  
    2525class Logtivity_User extends Logtivity_Abstract_Logger
    2626{
    27     protected static $loggedUserlogin = false;
     27    protected static bool $loggedUserlogin = false;
    2828
    2929    /**
    30      * @inheritDoc
     30     * @param string  $username
     31     * @param WP_User $user
     32     *
     33     * @return void
    3134     */
    32     protected function registerHooks(): void
     35    public function userLoggedIn(string $username, WP_User $user)
    3336    {
    34         add_action('wp_login', [$this, 'wpLogin'], 10, 2);
    35         add_action('wp_logout', [$this, 'userLoggedOut'], 10, 1);
    36         add_action('user_register', [$this, 'userCreated'], 10, 1);
    37         add_action('delete_user', [$this, 'userDeleted']);
    38         add_action('profile_update', [$this, 'profileUpdated'], 10, 2);
     37        if (static::$loggedUserlogin == false) {
     38            static::$loggedUserlogin = true;
     39
     40            $logger = Logtivity::log()->setUser($user->ID);
     41
     42            $logger->setAction('User Logged In')
     43                ->setContext($logger->user->getRole())
     44                ->send();
     45        }
    3946    }
    4047
    41     public function wpLogin($user_login, $user)
     48    /**
     49     * @param int $userId
     50     *
     51     * @return void
     52     */
     53    public function userLoggedOut(int $userId): void
    4254    {
    43         if (self::$loggedUserlogin) {
    44             return;
     55        if ($userId) {
     56            $logger = Logtivity::log()->setUser($userId);
     57
     58            $logger
     59                ->setAction('User Logged Out')
     60                ->setContext($logger->user->getRole())
     61                ->send();
    4562        }
    46 
    47         return $this->userLoggedIn($user->ID);
    4863    }
    4964
    50     public function userLoggedIn($user_id)
    51     {
    52         self::$loggedUserlogin = true;
    53 
    54         $logtivityUser = new Logtivity_WP_User($user_id);
    55 
    56         return (new Logtivity_Logger($user_id))
    57             ->setAction('User Logged In')
    58             ->setContext($logtivityUser->getRole())
    59             ->send();
    60     }
    61 
    62     public function userLoggedOut($user_id)
    63     {
    64         if ($user_id == 0) {
    65             return;
    66         }
    67 
    68         $user = new Logtivity_WP_User($user_id);
    69 
    70         return (new Logtivity_Logger($user_id))
    71             ->setAction('User Logged Out')
    72             ->setContext($user->getRole())
    73             ->send();
    74     }
    75 
    76     public function userCreated($user_id)
     65    /**
     66     * @param int $userId
     67     *
     68     * @return void
     69     */
     70    public function userCreated(int $userId): void
    7771    {
    7872        $log = Logtivity::log();
    7973
    80         if (!is_user_logged_in()) {
    81             $log->setUser($user_id);
     74        if (is_user_logged_in() == false) {
     75            $log->setUser($userId);
     76            $user = $log->user;
     77        } else {
     78            $user = new Logtivity_WP_User($userId);
    8279        }
    83 
    84         $user = new Logtivity_WP_User($user_id);
    8580
    8681        $log->setAction('User Created')
     
    9085    }
    9186
    92     public function userDeleted($user_id)
     87    /**
     88     * @param int $userId
     89     *
     90     * @return void
     91     */
     92    public function userDeleted(int $userId): void
    9393    {
    94         $user = new Logtivity_WP_User($user_id);
     94        $user = new Logtivity_WP_User($userId);
    9595
    9696        Logtivity::log()
     
    101101    }
    102102
    103     public function profileUpdated($user_id, $old_user_data)
     103    public function profileUpdated($userId)
    104104    {
    105         $user = new Logtivity_WP_User($user_id);
     105        $user = new Logtivity_WP_User($userId);
    106106
    107107        Logtivity::log()
     
    111111            ->send();
    112112    }
     113
     114    /**
     115     * @inheritDoc
     116     */
     117    protected function registerHooks(): void
     118    {
     119        add_action('wp_login', [$this, 'userLoggedIn'], 10, 2);
     120        add_action('wp_logout', [$this, 'userLoggedOut'], 10, 1);
     121        add_action('user_register', [$this, 'userCreated'], 10, 1);
     122        add_action('delete_user', [$this, 'userDeleted']);
     123        add_action('profile_update', [$this, 'profileUpdated'], 10, 1);
     124    }
    113125}
    114126
  • logtivity/trunk/Logs/Memberpress/Logtivity_Memberpress.php

    r3272661 r3335490  
    2525class Logtivity_Memberpress extends Logtivity_Abstract_Logger
    2626{
    27     /**
    28      * @inheritDoc
    29      */
    30     protected function registerHooks(): void
    31     {
    32         add_action('mepr-event-member-signup-completed', [$this, 'freeSubscriptionCreated']);
    33         add_action('mepr-event-subscription-created', [$this, 'subscriptionCreated']);
    34         add_action('mepr-event-subscription-paused', [$this, 'subscriptionPaused']);
    35         add_action('mepr-event-subscription-resumed', [$this, 'subscriptionResumed']);
    36         add_action('mepr-event-subscription-stopped', [$this, 'subscriptionStopped']);
    37         add_action('init', [$this, 'profileUpdated']);
    38         add_filter('mepr_create_transaction', [$this, 'transactionCreated'], 10, 3);
    39         add_filter('mepr_update_transaction', [$this, 'transactionUpdated'], 10, 3);
    40         add_action('mepr_email_sent', [$this, 'emailSent'], 10, 3);
    41         add_action('mepr-process-options', [$this, 'settingsUpdated'], 10, 1);
    42     }
    43 
    44     public function freeSubscriptionCreated($event)
     27    public function freeSubscriptionCreated($event): void
    4528    {
    4629        $user         = $event->get_data();
    4730        $subscription = $this->getSubscription($user);
    4831
    49         if (!$subscription) {
    50             return;
     32        if ($subscription && $subscription->gateway == 'free') {
     33            $product = $subscription->product();
     34
     35            Logtivity::log(
     36                'Free Subscription Created',
     37                [
     38                    'Subscription ID' => $subscription->id,
     39                ]
     40            )
     41                ->setContext($product->post_title)
     42                ->send();
    5143        }
    52 
    53         if ($subscription->gateway != 'free') {
    54             return;
    55         }
    56 
    57         $product = $subscription->product();
    58 
    59         return (new Logtivity_Logger($user->ID))
    60             ->setAction('Free Subscription Created')
    61             ->setContext($product->post_title)
    62             ->addMeta('Subscription ID', $subscription->id)
    63             ->send();
    6444    }
    6545
     
    7353    }
    7454
    75     public function subscriptionCreated($event)
     55    /**
     56     * @param $event
     57     *
     58     * @return void
     59     */
     60    public function subscriptionCreated($event): void
    7661    {
    7762        $subscription  = $event->get_data();
     
    8065        $paymentMethod = $subscription->payment_method();
    8166
    82         return (new Logtivity_Logger($user->ID))
    83             ->setAction('Subscription Created')
     67        Logtivity::log(
     68            'Subscription Created',
     69            [
     70                'Transaction Total' => $subscription->total,
     71                'Payment Method'    => $paymentMethod->name,
     72                'Subscription ID'   => $subscription->id,
     73            ],
     74            $user->ID
     75        )
    8476            ->setContext($product->post_title)
    85             ->addMeta('Transaction Total', $subscription->total)
    86             ->addMeta('Payment Method', $paymentMethod->name)
    87             ->addMeta('Subscription ID', $subscription->id)
    8877            ->send();
    8978    }
     
    188177            ->send();
    189178    }
     179
     180    /**
     181     * @inheritDoc
     182     */
     183    protected function registerHooks(): void
     184    {
     185        add_action('mepr-event-member-signup-completed', [$this, 'freeSubscriptionCreated']);
     186        add_action('mepr-event-subscription-created', [$this, 'subscriptionCreated']);
     187        add_action('mepr-event-subscription-paused', [$this, 'subscriptionPaused']);
     188        add_action('mepr-event-subscription-resumed', [$this, 'subscriptionResumed']);
     189        add_action('mepr-event-subscription-stopped', [$this, 'subscriptionStopped']);
     190        add_action('init', [$this, 'profileUpdated']);
     191        add_filter('mepr_create_transaction', [$this, 'transactionCreated'], 10, 3);
     192        add_filter('mepr_update_transaction', [$this, 'transactionUpdated'], 10, 3);
     193        add_action('mepr_email_sent', [$this, 'emailSent'], 10, 3);
     194        add_action('mepr-process-options', [$this, 'settingsUpdated'], 10, 1);
     195    }
    190196}
    191197
  • logtivity/trunk/Services/Logtivity_Api.php

    r3306623 r3335490  
    2525class Logtivity_Api
    2626{
     27    public const CURL_TIMEOUT = 28;
     28
     29    /**
     30     * @var array
     31     */
     32    protected static $responseData = [
     33        'code'    => null,
     34        'message' => null,
     35        'error'   => null,
     36        'body'    => null,
     37    ];
     38
    2739    /**
    2840     * Option class to access the plugin settings
     
    6274     * @return $this
    6375     */
    64     public function waitForResponse(): self
    65     {
    66         $this->waitForResponse = true;
     76    public function ignoreStatus(): self
     77    {
     78        $this->ignoreStatus = true;
    6779
    6880        return $this;
    69     }
    70 
    71     /**
    72      * @return $this
    73      */
    74     public function ignoreStatus(): self
    75     {
    76         $this->ignoreStatus = true;
    77 
    78         return $this;
    79     }
    80 
    81     /**
    82      * @param string $endpoint
    83      *
    84      * @return string
    85      */
    86     public function getEndpoint(string $endpoint): string
    87     {
    88         return logtivity_get_api_url() . $endpoint;
    8981    }
    9082
     
    9890    {
    9991        return $this->makeRequest($url, $body);
    100     }
    101 
    102     /**
    103      * @param string $url
    104      * @param ?array $body
    105      *
    106      * @return ?array
    107      */
    108     public function get(string $url, ?array $body = null): ?array
    109     {
    110         return $this->waitForResponse()->makeRequest($url, $body, 'GET');
    111     }
    112 
    113     /**
    114      * @return ?string
    115      */
    116     public function getApiKey(): ?string
    117     {
    118         if ($this->api_key == false) {
    119             $this->api_key = $this->options->getApiKey();
    120         }
    121 
    122         return $this->api_key;
    123     }
    124 
    125     /**
    126      * @param string $apikey
    127      *
    128      * @return $this
    129      */
    130     public function setApiKey(string $apikey): self
    131     {
    132         $this->api_key = $apikey;
    133 
    134         return $this;
    13592    }
    13693
     
    151108
    152109        if ($this->ready()) {
    153             if ($this->options->getOption('logtivity_app_verify_url')) {
     110            if ($this->getOption('logtivity_app_verify_url')) {
    154111                $body = array_merge(
    155112                    $body ?: [],
     
    177134            // @TODO: Switch to Logtivity_Response class to get standardized responses
    178135            $response = wp_remote_request($this->getEndpoint($url), $request);
    179             if ($this->notUpdatingWidgetInCustomizer()) {
     136            if ($waitForResponse && $this->notUpdatingWidgetInCustomizer()) {
    180137                // We waited and received a response
    181138                if ($response instanceof WP_Error) {
    182                     $responseData = [
     139                    $responseData = array_merge(static::$responseData, [
    183140                        'code'    => 500,
    184141                        'message' => $response->get_error_code(),
    185142                        'error'   => $response->get_error_message() ?: $response->get_error_code(),
    186143                        'body'    => get_object_vars($response),
    187                     ];
     144                    ]);
    188145
    189146                } else {
     
    197154                        $responseError   = $responseMessage;
    198155                        $responseMessage = (($responseBody['message'] ?? $responseMessage) ?: 'Unknown error');
    199 
    200156                    }
    201157                    $responseData = [
     
    207163                }
    208164
     165                $newStatus = 'success';
    209166                if ($responseData['code']) {
    210167                    if ($responseData['code'] < 400) {
    211168                        // Successful request, check if api is telling us to pause
    212                         $newStatus = $responseData['error'] ? 'paused' : 'success';
    213                         $this->updateSettings($response['body']);
     169                        if ($responseData['error']) {
     170                            $newStatus = 'paused';
     171                        }
     172
     173                        $body = json_decode($response['body'] ?? 'null', true);
     174                        $this->updateSettings($body['settings'] ?? []);
    214175
    215176                    } else {
    216                         // Something went wrong submitting the api request
    217                         $newStatus = 'fail';
    218                         update_option('logtivity_last_settings_check_in_at', ['date' => date('Y-m-d H:i:s')]);
     177                        if ($this->getCurlError($responseData['error']) != static::CURL_TIMEOUT) {
     178                            // Something other than a timeout went wrong with the request
     179                            $newStatus = 'fail';
     180                        }
     181
     182                        $this->updateLastCheckin();
    219183                    }
    220184
     
    229193                    );
    230194
    231                     return $responseData['body'];
     195                    return $responseData;
    232196                }
    233197            }
     
    238202
    239203    /**
     204     * @return bool
     205     */
     206    protected function ready(): bool
     207    {
     208        return ($this->getApiKey())
     209            && logtivity_has_site_url_changed() == false
     210            && (
     211                $this->ignoreStatus
     212                || $this->getOption('logtivity_api_key_check') == 'success'
     213                || $this->options->shouldCheckInWithApi()
     214            );
     215    }
     216
     217    /**
     218     * @return ?string
     219     */
     220    public function getApiKey(): ?string
     221    {
     222        if ($this->api_key == false) {
     223            $this->api_key = $this->options->getApiKey();
     224        }
     225
     226        return $this->api_key;
     227    }
     228
     229    /**
     230     * @param string $apikey
     231     *
     232     * @return $this
     233     */
     234    public function setApiKey(string $apikey): self
     235    {
     236        $this->api_key = $apikey;
     237
     238        return $this;
     239    }
     240
     241    /**
     242     * @param ?string $key
     243     *
     244     * @return Logtivity_Options|mixed
     245     */
     246    public function getOption(?string $key = null)
     247    {
     248        if ($key) {
     249            return $this->options->getOption($key);
     250        }
     251
     252        return $this->options;
     253    }
     254
     255    /**
     256     * @param string $endpoint
     257     *
     258     * @return string
     259     */
     260    public function getEndpoint(string $endpoint): string
     261    {
     262        return logtivity_get_api_url() . $endpoint;
     263    }
     264
     265    /**
     266     * You cannot call an extra update_option during a widget update so we make
     267     * sure not to log the most recent log response in this case.
     268     *
     269     * @return bool
     270     */
     271    protected function notUpdatingWidgetInCustomizer(): bool
     272    {
     273        $customize = sanitize_text_field($_POST['wp_customize'] ?? null);
     274        $action    = sanitize_text_field($_POST['action'] ?? null);
     275
     276        return ($action == 'update-widget' && $customize == 'on') == false;
     277    }
     278
     279    /**
    240280     * @param array|object $body
    241281     *
    242282     * @return void
    243283     */
    244     public function updateSettings($body): void
    245     {
    246         $settings = (object)($body->settings ?? $body['settings'] ?? null);
    247 
     284    public function updateSettings(array $settings): void
     285    {
    248286        if ($settings) {
    249             $this->options->update(
    250                 [
    251                     'logtivity_global_disabled_logs'         => $settings->disabled_logs ?? null,
    252                     'logtivity_enable_white_label_mode'      => $settings->enable_white_label_mode ?? null,
    253                     'logtivity_disabled_error_levels'        => $settings->disabled_error_levels ?? null,
    254                     'logtivity_disable_error_logging'        => $settings->disable_error_logging ?? null,
    255                     'logtivity_hide_plugin_from_ui'          => $settings->hide_plugin_from_ui ?? null,
    256                     'logtivity_disable_default_logging'      => $settings->disable_default_logging ?? null,
    257                     'logtivity_enable_options_table_logging' => $settings->enable_options_table_logging ?? null,
    258                     'logtivity_enable_post_meta_logging'     => $settings->enable_post_meta_logging ?? null,
    259                     'logtivity_custom_plugin_name'           => $settings->custom_plugin_name ?? null,
    260                 ],
    261                 false
    262             );
    263 
    264             update_option('logtivity_last_settings_check_in_at', ['date' => date('Y-m-d H:i:s')]);
    265         }
     287            $verifiedSettings = [];
     288            foreach ($settings as $key => $value) {
     289                if ($key == 'disabled_logs') {
     290                    $key = 'global_' . $key;
     291                }
     292                $verifiedSettings['logtivity_' . $key] = is_null($value) ? '' : $value;
     293            }
     294
     295            $this->options->update($verifiedSettings, false);
     296        }
     297
     298        $this->updateLastCheckin();
     299    }
     300
     301    /**
     302     * @return void
     303     */
     304    protected function updateLastCheckin(): void
     305    {
     306        update_option('logtivity_last_settings_check_in_at', ['date' => date('Y-m-d H:i:s')]);
     307    }
     308
     309    /**
     310     * @param ?string $message
     311     *
     312     * @return int
     313     */
     314    protected function getCurlError(?string $message): ?int
     315    {
     316        preg_match('/curl\s+error\s+(\d+)/i', $message, $matches);
     317
     318        return $matches[1] ?? null;
    266319    }
    267320
     
    271324    public function getLatestResponse(): ?array
    272325    {
    273         $response = $this->options->getOption('logtivity_latest_response');
     326        $response = $this->getOption('logtivity_latest_response');
    274327
    275328        return $response ?: null;
     
    277330
    278331    /**
    279      * @return ?string
    280      */
    281     public function getConnectionStatus(): ?string
    282     {
    283         $status = null;
    284         $apiKey = $this->getApiKey();
    285 
    286         if ($apiKey) {
    287             $status = $this->options->getOption('logtivity_api_key_check');
    288         }
    289 
    290         return $status;
     332     * @param string $url
     333     * @param ?array $body
     334     *
     335     * @return ?array
     336     */
     337    public function get(string $url, ?array $body = null): ?array
     338    {
     339        return $this->waitForResponse()->makeRequest($url, $body, 'GET');
     340    }
     341
     342    /**
     343     * @return $this
     344     */
     345    public function waitForResponse(): self
     346    {
     347        $this->waitForResponse = true;
     348
     349        return $this;
    291350    }
    292351
     
    310369                $error   = $this->getLatestResponse();
    311370                $code    = $error['code'] ?? null;
    312                 $message = $error['message'] ?? null;
     371                $message = $error['error'] ?? $error['message'] ?? null;
    313372
    314373                if ($code && $message) {
     
    329388
    330389    /**
    331      * You cannot call an extra update_option during a widget update so we make
    332      * sure not to log the most recent log response in this case.
    333      *
    334      * @return bool
    335      */
    336     private function notUpdatingWidgetInCustomizer(): bool
    337     {
    338         $customize = sanitize_text_field($_POST['wp_customize'] ?? null);
    339         $action    = sanitize_text_field($_POST['action'] ?? null);
    340 
    341         return ($action == 'update-widget' && $customize == 'on') == false;
    342     }
    343 
    344     /**
    345      * @return bool
    346      */
    347     private function ready(): bool
    348     {
    349         //var_dump($this->ignoreStatus);
    350         return ($this->getApiKey())
    351             && logtivity_has_site_url_changed() == false
    352             && (
    353                 $this->ignoreStatus
    354                 || $this->options->getOption('logtivity_api_key_check') == 'success'
    355                 || $this->options->shouldCheckInWithApi()
    356             );
     390     * @return ?string
     391     */
     392    public function getConnectionStatus(): ?string
     393    {
     394        $status = null;
     395        $apiKey = $this->getApiKey();
     396
     397        if ($apiKey) {
     398            $status = $this->getOption('logtivity_api_key_check');
     399        }
     400
     401        return $status;
    357402    }
    358403}
  • logtivity/trunk/Services/Logtivity_Check_For_Disabled_Individual_Logs.php

    r3246625 r3335490  
    2525class Logtivity_Check_For_Disabled_Individual_Logs
    2626{
    27     public function __construct()
    28     {
    29         add_action('wp_logtivity_instance', [$this, 'handle'], 10, 999);
    30     }
     27    /**
     28     * @var Logtivity_Options
     29     */
     30    protected Logtivity_Options $options;
    3131
    32     public function handle($Logtivity_Logger)
    33     {
    34         foreach ($this->getLogsToExclude() as $log) {
    35             if ($this->check($Logtivity_Logger, $log)) {
    36                 $Logtivity_Logger->stop();
    37             }
    38         }
     32    public function __construct()
     33    {
     34        add_action('wp_logtivity_instance', [$this, 'handle'], 10, 2);
     35    }
    3936
    40         foreach ($this->globalLogsToExclude() as $log) {
    41             if ($this->check($Logtivity_Logger, $log)) {
    42                 $Logtivity_Logger->stop();
    43             }
    44         }
    45     }
     37    /**
     38     * @param Logtivity_Logger $logger
     39     *
     40     * @return void
     41     */
     42    public function handle(Logtivity_Logger $logger): void
     43    {
     44        // Refresh options whenever invoked
     45        $this->options = new Logtivity_Options();
    4646
    47     public function check($Logtivity_Logger, $log)
    48     {
    49         $array = explode('&&', $log);
     47        $exclusions = array_unique(
     48            array_merge(
     49                $this->parseExcludeEntries('logtivity_disable_individual_logs'),
     50                $this->parseExcludeEntries('logtivity_global_disabled_logs')
     51            )
     52        );
    5053
    51         if (isset($array[0]) && isset($array[1])) {
    52             if (trim($array[0]) === '*' && trim($array[1]) === '*') {
    53                 return;
    54             }
    55             if ($this->matches($Logtivity_Logger->action, $array[0]) && $this->matches($Logtivity_Logger->context, $array[1])) {
    56                 return true;
    57             }
    58         } elseif(isset($array[0])) {
    59             if (trim($array[0]) === '*') {
    60                 return;
    61             }
    62             if ($this->matches($Logtivity_Logger->action, $array[0])) {
    63                 return true;
    64             }
    65         }
    66         return false;
    67     }
     54        foreach ($exclusions as $exclusion) {
     55            if ($this->isDisabled($logger, $exclusion)) {
     56                $logger->stop();
    6857
    69     private function matches($keyword, $disabledKeyword)
    70     {
    71         // @TODO: this may not be the best way to check the arguments
    72         if (is_string($keyword) && is_string($disabledKeyword)) {
    73             $keyword = trim(strtolower($keyword));
    74             $disabledKeyword = trim(strtolower($disabledKeyword));
     58                return;
     59            }
     60        }
     61    }
    7562
    76             if ($disabledKeyword === '*') {
    77                 return true;
    78             }
     63    /**
     64     * @param string $option
     65     *
     66     * @return array
     67     */
     68    protected function parseExcludeEntries(string $option): array
     69    {
     70        $value = (string)$this->options->getOption($option);
    7971
    80             if (strpos($disabledKeyword, '*') !== false) {
    81                 return strpos($keyword, str_replace('*', '', $disabledKeyword)) !== false;
    82             }
     72        $entries = preg_split("/\\r\\n|\\r|\\n/", $value);
    8373
    84             return $keyword == $disabledKeyword;
    85         }
     74        return array_unique(array_filter($entries));
     75    }
    8676
    87         return false;
    88     }
     77    /**
     78     * @param Logtivity_Logger $logger
     79     * @param string           $exclusion
     80     *
     81     * @return bool
     82     */
     83    protected function isDisabled(Logtivity_Logger $logger, string $exclusion): bool
     84    {
     85        $array   = explode('&&', $exclusion);
     86        $action  = strtolower(trim((string)array_shift($array)));
     87        $context = strtolower(trim((string)array_shift($array)));
    8988
    90     private function getLogsToExclude()
    91     {
    92         $value = (new Logtivity_Options)->getOption('logtivity_disable_individual_logs');
     89        if ($action == '*' && $context == '*') {
     90            return false;
     91        } elseif ($this->matches($logger->action, $action) && $this->matches($logger->context, $context)) {
     92            return true;
     93        } elseif ($action == '*') {
     94            return false;
     95        } elseif ($this->matches($logger->action, $action)) {
     96            return true;
     97        }
    9398
    94         if ($value == '') {
    95             return [];
    96         }
     99        return false;
     100    }
    97101
    98         return preg_split("/\\r\\n|\\r|\\n/", $value);
    99     }
     102    /**
     103     * @param ?string $keywordTarget
     104     * @param ?string $keywordCheck
     105     *
     106     * @return bool
     107     */
     108    protected function matches(?string $keywordTarget, ?string $keywordCheck): bool
     109    {
     110        $keywordTarget = strtolower(trim((string)$keywordTarget));
     111        $keywordCheck  = strtolower(trim((string)$keywordCheck));
    100112
    101     public function globalLogsToExclude()
    102     {
    103         $value = (new Logtivity_Options)->getOption('logtivity_global_disabled_logs');
     113        if ($keywordTarget && $keywordCheck) {
     114            if ($keywordCheck == '*') {
     115                return true;
     116            }
    104117
    105         if ($value == '') {
    106             return [];
    107         }
     118            if (strpos($keywordCheck, '*') !== false) {
     119                $regex = str_replace(['*', '/'], ['.*?', '\/'], $keywordCheck);
     120                return preg_match('/' . $regex . '/', $keywordTarget);
     121            }
    108122
    109         return preg_split("/\\r\\n|\\r|\\n/", $value);
    110     }
     123            return $keywordCheck == $keywordTarget;
     124        }
     125
     126        return false;
     127    }
    111128}
    112129
    113 $CheckForDisabledIndividualLogs = new Logtivity_Check_For_Disabled_Individual_Logs;
     130new Logtivity_Check_For_Disabled_Individual_Logs();
  • logtivity/trunk/Services/Logtivity_Check_For_New_Settings.php

    r3290027 r3335490  
    4141
    4242    /**
     43     * @return bool
     44     */
     45    public function shouldCheckInWithApi(): bool
     46    {
     47        return (new Logtivity_Options())->shouldCheckInWithApi();
     48    }
     49
     50    /**
    4351     * @return void
    4452     */
     
    5361
    5462        try {
    55             $api = new Logtivity_Api();
    56 
    5763            $theme = wp_get_theme();
    5864
     
    6369            $latestMinMySqlVersion = $coreUpdates[0]->mysql_version ?? null;
    6470
    65             $response = $api->ignoreStatus()->post('/settings-check', [
    66                 'php_version'                 => phpversion(),
    67                 'plugins'                     => $this->getPluginsWithStatuses(),
    68                 'theme_name'                  => $theme ? $theme->name : null,
    69                 'theme_version'               => $theme ? $theme->version : null,
    70                 'themes'                      => $this->getThemesListWithStatuses(),
    71                 'wordpress_version'           => $wp_version,
    72                 'latest_wp_version'           => $latestWPVersion,
    73                 'latest_wp_min_php_version'   => $latestMinPhpVersion,
    74                 'latest_wp_min_mysql_version' => $latestMinMySqlVersion,
    75             ]);
     71            $api = new Logtivity_Api();
    7672
    77             if ($response) {
    78                 $api->updateSettings($response);
     73            $response = $api
     74                ->ignoreStatus()
     75                ->waitForResponse()
     76                ->makeRequest(
     77                    '/settings-check',
     78                    [
     79                        'php_version'                 => phpversion(),
     80                        'plugins'                     => $this->getPluginsWithStatuses(),
     81                        'theme_name'                  => $theme ? $theme->name : null,
     82                        'theme_version'               => $theme ? $theme->version : null,
     83                        'themes'                      => $this->getThemesListWithStatuses(),
     84                        'wordpress_version'           => $wp_version,
     85                        'latest_wp_version'           => $latestWPVersion,
     86                        'latest_wp_min_php_version'   => $latestMinPhpVersion,
     87                        'latest_wp_min_mysql_version' => $latestMinMySqlVersion,
     88                    ]);
     89
     90            if ($settings = $response['body']['settings'] ?? null) {
     91                $api->updateSettings($settings);
    7992            }
    8093
     
    8295            // Ignore
    8396        }
    84     }
    85 
    86     /**
    87      * @return array[]
    88      */
    89     private function getThemesListWithStatuses(): array
    90     {
    91         $themes = wp_get_themes();
    92 
    93         $themesDetails = [];
    94 
    95         foreach ($themes as $theme) {
    96             $themesDetails[] = [
    97                 'name'    => $theme->name,
    98                 'version' => $theme->version,
    99             ];
    100         }
    101 
    102         return $themesDetails;
    10397    }
    10498
     
    174168
    175169    /**
    176      * @return bool
     170     * @return array[]
    177171     */
    178     public function shouldCheckInWithApi(): bool
     172    private function getThemesListWithStatuses(): array
    179173    {
    180         return (new Logtivity_Options())->shouldCheckInWithApi();
     174        $themes = wp_get_themes();
     175
     176        $themesDetails = [];
     177
     178        foreach ($themes as $theme) {
     179            $themesDetails[] = [
     180                'name'    => $theme->name,
     181                'version' => $theme->version,
     182            ];
     183        }
     184
     185        return $themesDetails;
    181186    }
    182187}
  • logtivity/trunk/Services/Logtivity_Logger.php

    r3290027 r3335490  
    225225     * Send the logged data to Logtivity
    226226     *
    227      * @return ?array
    228      */
    229     public function send(): ?array
     227     * @return void
     228     */
     229    public function send(): void
    230230    {
    231231        $this->maybeAddProfileLink();
     
    234234
    235235        if ($this->active) {
    236             return $this->makeRequest('/logs/store', $this->getData());
    237         }
    238 
    239         return null;
     236            $this->makeRequest('/logs/store', $this->getData());
     237        }
    240238    }
    241239
  • logtivity/trunk/assets/app.js

    r3306623 r3335490  
    44            this.container = $('#logtivity-log-index');
    55
    6             if (!this.container.length) {
    7                 return;
     6            if (this.container.length) {
     7                this.form = $('#logtivity-log-index-search-form');
     8                this.listenForPagination();
     9                this.listenForChange();
     10                this.filter();
     11                this.listenForViewLog()
     12                this.listenForCloseModal();
    813            }
    9 
    10             this.form = $('#logtivity-log-index-search-form');
    11             this.listenForPagination();
    12             this.listenForChange();
    13             this.filter();
    14             this.listenForViewLog()
    15             this.listenForCloseModal();
    1614        },
    1715
     
    1917            let listenForCloseModal = this;
    2018
    21             $("body").on("click", ".js-logtivity-notice-dismiss", function(e) {
     19            $('body').on('click', '.js-logtivity-notice-dismiss', function(e) {
    2220                e.preventDefault();
    2321
     
    2624
    2725            $(document).on('keyup', function(e) {
    28                 if (e.key === "Escape") {
     26                if (e.key === 'Escape') {
    2927                    listenForCloseModal.hideModal();
    3028                }
     
    4947            let listenForViewLog = this;
    5048
    51             $("body").on("click", ".js-logtivity-view-log", function(e) {
     49            $('body').on('click', '.js-logtivity-view-log', function(e) {
    5250                e.preventDefault();
    5351
     
    7472                timeout         = null;
    7573
    76             $("body").on("input", "#logtivity-log-index-search-form input", function(e) {
     74            $('body').on('input', '#logtivity-log-index-search-form input', function(e) {
    7775                e.preventDefault();
    7876
     
    9391        loading: function() {
    9492            this.container.html(
    95                 '<div style="text-align: center; padding-bottom: 20px"><div class="spinner is-active" style="float:none;width:auto;height:auto;padding:10px 0 10px 50px;background-position:20px 0;"></div></div>'
     93                '<div style="text-align: center; padding-bottom: 20px">'
     94                + '<div class="spinner is-active" style="float:none;width:auto;height:auto;padding:10px 0 10px 50px;background-position:20px 0;"></div>'
     95                + '</div>'
    9696            );
    9797        },
     
    100100            let listenForPagination = this;
    101101
    102             $("body").on("click", ".js-logtivity-pagination", function(e) {
     102            $('body').on('click', '.js-logtivity-pagination', function(e) {
    103103                e.preventDefault();
    104104
  • logtivity/trunk/composer.json

    r3290027 r3335490  
    2525    },
    2626    "require": {
    27         "php": ">=7.4",
     27        "php"     : ">=7.4",
    2828        "ext-json": "*"
    2929    },
  • logtivity/trunk/logtivity.php

    r3306623 r3335490  
    66 * Description:       Record activity logs and errors logs across all your WordPress sites.
    77 * Author:            Logtivity
    8  * Version:           3.1.12
     8 * Version:           3.2.0
    99 * Text Domain:       logtivity
    1010 * Requires at least: 4.7
     
    4545     * @var string
    4646     */
    47     protected string $version = '3.1.12';
     47    protected string $version = '3.2.0';
    4848
    4949    /**
    5050     * List all classes here with their file paths. Keep class names the same as filenames.
    5151     * Ordering of this list matters!
    52      * @TODO: Implement pst-0 autoloading
     52     * @TODO: Implement psr-0 autoloading
    5353     *
    5454     * @var string[]
     
    7171        'Services/Logtivity_Check_For_Disabled_Individual_Logs',
    7272        'Services/Logtivity_Check_For_New_Settings',
     73        'Services/Logtivity_Rest_Endpoints',
    7374        /**
    7475         * Error logging
     
    7980        'Errors/Logtivity_Error_Log',
    8081    ];
    81 
    8282    /**
    8383     * @var string[]
     
    9696        'Logs/Core/Logtivity_Meta',
    9797    ];
    98 
    9998    /**
    10099     * List all integration dependencies
     
    177176    {
    178177        require_once plugin_dir_path(__FILE__) . $filePath . '.php';
     178    }
     179
     180    /**
     181     * Review updates based on version
     182     *
     183     * @return void
     184     */
     185    public function updateCheck(): void
     186    {
     187        $currentVersion = get_option('logtivity_version');
     188
     189        if (version_compare($currentVersion, '3.1.6', '<=')) {
     190            static::checkCapabilities();
     191        }
     192
     193        if ($currentVersion && version_compare($currentVersion, '3.1.7', '<=')) {
     194            // Default for updating sites should be no behavior change
     195            update_option('logtivity_app_verify_url', 0);
     196        }
     197
     198        update_option('logtivity_version', $this->version);
     199    }
     200
     201    /**
     202     * Custom capabilities added prior to v3.1.7
     203     *
     204     * @return void
     205     */
     206    public static function checkCapabilities(): void
     207    {
     208        $capabilities = array_filter(
     209            array_keys(logtivity_get_capabilities()),
     210            function (string $capability): bool {
     211                return in_array($capability, [Logtivity::ACCESS_LOGS, Logtivity::ACCESS_SETTINGS]);
     212            }
     213        );
     214
     215        if ($capabilities == false) {
     216            // Make sure at least admins can access us
     217            if ($role = get_role('administrator')) {
     218                if ($role->has_cap(Logtivity::ACCESS_LOGS) == false) {
     219                    $role->add_cap(Logtivity::ACCESS_LOGS);
     220                }
     221                if ($role->has_cap(Logtivity::ACCESS_SETTINGS) == false) {
     222                    $role->add_cap(Logtivity::ACCESS_SETTINGS);
     223                }
     224            }
     225        }
    179226    }
    180227
     
    270317                        if ($created) {
    271318                            $createdTimestamp = strtotime($created);
    272                             $creationText = sprintf(
     319                            $creationText     = sprintf(
    273320                                'It was created on %s at %s ',
    274321                                wp_date(get_option('date_format'), $createdTimestamp),
     
    290337        }
    291338
    292         return $response ?? null;
     339        return $response;
    293340    }
    294341
     
    313360    {
    314361        return new Logtivity_Error_Logger($error);
    315     }
    316 
    317     /**
    318      * Review updates based on version
    319      *
    320      * @return void
    321      */
    322     public function updateCheck(): void
    323     {
    324         $currentVersion = get_option('logtivity_version');
    325 
    326         if (version_compare($currentVersion, '3.1.6', '<=')) {
    327             static::checkCapabilities();
    328         }
    329 
    330         if ($currentVersion && version_compare($currentVersion, '3.1.7', '<=')) {
    331             // Default for updating sites should be no behavior change
    332             update_option('logtivity_app_verify_url', 0);
    333         }
    334 
    335         update_option('logtivity_version', $this->version);
    336362    }
    337363
     
    423449
    424450    /**
    425      * Custom capabilities added prior to v3.1.7
    426      *
    427      * @return void
    428      */
    429     public static function checkCapabilities(): void
    430     {
    431         $capabilities = array_filter(
    432             array_keys(logtivity_get_capabilities()),
    433             function (string $capability): bool {
    434                 return in_array($capability, [Logtivity::ACCESS_LOGS, Logtivity::ACCESS_SETTINGS]);
    435             }
    436         );
    437 
    438         if ($capabilities == false) {
    439             // Make sure at least admins can access us
    440             if ($role = get_role('administrator')) {
    441                 if ($role->has_cap(Logtivity::ACCESS_LOGS) == false) {
    442                     $role->add_cap(Logtivity::ACCESS_LOGS);
    443                 }
    444                 if ($role->has_cap(Logtivity::ACCESS_SETTINGS) == false) {
    445                     $role->add_cap(Logtivity::ACCESS_SETTINGS);
    446                 }
    447             }
    448         }
    449     }
    450 
    451     /**
    452451     * @return void
    453452     */
     
    469468            current_user_can(static::ACCESS_SETTINGS)
    470469            && logtivity_has_site_url_changed()
     470            && (new Logtivity_Options())->isWhiteLabelMode() == false
    471471            && !get_transient('dismissed-logtivity-site-url-has-changed-notice')
    472472        ) {
  • logtivity/trunk/readme.txt

    r3306623 r3335490  
    55Requires at least: 6.6
    66Tested up to: 6.8
    7 Stable tag: 3.1.12
     7Stable tag: 3.2.0
    88Requires PHP: 7.4
    99License: GPLv2 or later
     
    262262
    263263== Changelog ==
     264
     265= 3.2.0 =
     266
     267* Add JSON Web Token halper class
     268* Add REST api listener
     269* Retrieve more detailed error information on connection fails
     270* Normalize response data
     271* Fix automatic settings checkin
     272* Prevent disconnect on timeout errors
     273
     274_Release Date - Monday, July 28, 2025_
    264275
    265276= 3.1.12 =
  • logtivity/trunk/views/_logs-loop.php

    r3272661 r3335490  
    2424
    2525/**
     26 * @var string   $fileName
     27 * @var ?string  $message
    2628 * @var object[] $logs
    27  * @var ?string  $hasNextPage
     29 * @var object   $meta
     30 * @var string   $hasNextPage
    2831 */
    2932
Note: See TracChangeset for help on using the changeset viewer.